Coder Social home page Coder Social logo

protobuf-swift-realm's Introduction

ProtocolBuffers Swift Realm

An implementation of Protocol Buffers for Realm Data Models in Swift.

Protocol Buffers are a way of encoding structured data in an efficient yet extensible format. This project is based on an implementation of Protocol Buffers from Google. See the Google protobuf project for more information.

๐Ÿ“– Table of content

๐Ÿ”จ How to install

Cocoapods

use_frameworks!
pod 'ProtocolBuffers-Swift-Realm'

๐Ÿšง Compile proto files

Required Protobuf Swift Realm Compiler

protoc -I ./<DESC_DIR> ./<FILENAME>.proto --swift_realm_out="./<OUT_DIR>"

  • DESC_DIR: path to folder that contains /google/protobuf/descriptor.proto & /google/protobuf/swift-descriptor.proto
  • FILENAME.proto: path to your protobuf models, it's possible to set *.proto for getting all proto files
  • OUT_DIR: output path for generated files

Example

Directory structure (/project):

  • /protobuf
    • /google/protobuf
      • /descriptor.proto
      • /swift-descriptor.proto
    • /Employee.proto
  • /ios
    • /ProtoApi.Employee.realm.swift

Run command:

cd project && protoc -I ./protobuf ./protobuf/Employee.proto --swift_realm_out="./ios"

๐Ÿ‘จโ€๐Ÿ’ป Usage

๐Ÿš€ Get started

First of all you need to import swift-descriptor into your .proto files for using custom options:

import 'google/protobuf/swift-descriptor.proto';

Then, you need to add a custom option to generate Realm Data Model Swift Classes:

option (.google.protobuf.swift_message_options) = { generate_realm_object : true};

Example:

Employee.proto will be generated into Example.Employee.realm.swift

syntax = "proto2";
import "google/protobuf/swift-descriptor.proto";

package Example;

message Employee {
    option (.google.protobuf.swift_message_options) = { generate_realm_object : true };
    required string firstName = 1;
    required string lastName = 2;
    required double rank = 3;
    optional int32 level = 4;
}

Generated:

public class Example_Employee:Object {
    @objc dynamic var firstName:String = ""
    @objc dynamic var lastName:String = ""
    @objc dynamic var rank:Double = 0.0
    let level:RealmOptional<Int> = RealmOptional<Int>()
}
extension Example_Employee:ProtoRealm {
    public typealias PBType = Example.Employee
    public typealias RMObject = Example_Employee
    public typealias RepresentationType = Dictionary<String,Any>
    public static func map(_ proto: Example.Employee) -> Example_Employee {
        let rmModel = Example_Employee()
        rmModel.firstName = proto.firstName
        rmModel.lastName = proto.lastName
        rmModel.rank = proto.rank
        if proto.hasLevel {
            rmModel.level.value = Int(proto.level)
        } else {
            rmModel.level.value = nil
        }
        return rmModel
    }
    public func protobuf() throws -> Example.Employee {
        let proto = Example.Employee.Builder()
        proto.firstName = self.firstName
        proto.lastName = self.lastName
        proto.rank = self.rank
        if let valueLevel = self.level.value {
            proto.level = Int32(valueLevel)
        }
        return try proto.build()
    }
}

๐Ÿ”‘ Primary Keys

Protobuf Swift Realm Compiler supports 2 ways to generate a primary key:

Add a field of supported type (string or int) with name "id" which will be automatically translated into a primary key:

message Employee {
    option (.google.protobuf.swift_message_options) = { generate_realm_object : true };
    required string id = 1;
    required string firstName = 2;
    required string lastName = 3;
}

or you can manually set a custom option for a necessary field:

Example:

message Employee {
    option (.google.protobuf.swift_message_options) = { generate_realm_object : true };
    required string key = 1 [(.google.protobuf.swift_field_options).realm_primary_key = true];
    required string firstName = 2;
    required string lastName = 3;
}

Generated:

public class Example_Employee:Object {
    @objc dynamic var key:String = ""
    @objc dynamic var firstName:String = ""
    @objc dynamic var lastName:String = ""
    public override class func indexedProperties() -> [String] {
        return ["key"]
    }
    public override class func primaryKey() -> String? {
        return "key"
    }
}

extension Example_Employee:ProtoRealm {
    public typealias PBType = Example.Employee
    public typealias RMObject = Example_Employee
    public typealias RepresentationType = Dictionary<String,Any>
    public static func map(_ proto: Example.Employee) -> Example_Employee {
        let rmModel = Example_Employee()
        rmModel.key = proto.key
        rmModel.firstName = proto.firstName
        rmModel.lastName = proto.lastName
        return rmModel
    }
    public func protobuf() throws -> Example.Employee {
        let proto = Example.Employee.Builder()
        proto.key = self.key
        proto.firstName = self.firstName
        proto.lastName = self.lastName
        return try proto.build()
    }
}

๐Ÿ‘‰ Indexing Properties

To index a property use a custom option:

option (.google.protobuf.swift_field_options).realm_indexed_propertie = true;

Example:

message Employee {
    option (.google.protobuf.swift_message_options) = { generate_realm_object : true };
    required string id = 1 [(.google.protobuf.swift_field_options).realm_indexed_propertie = true];
    required string firstName = 2;
    required string lastName = 3;
}

Generated:

public class Example_Employee:Object {
    @objc dynamic var id:String = ""
    @objc dynamic var firstName:String = ""
    @objc dynamic var lastName:String = ""
    public override class func indexedProperties() -> [String] {
        return ["id"]
    }
    public override class func primaryKey() -> String? {
        return "id"
    }
}
extension Example_Employee:ProtoRealm {
    public typealias PBType = Example.Employee
    public typealias RMObject = Example_Employee
    public typealias RepresentationType = Dictionary<String,Any>
    public static func map(_ proto: Example.Employee) -> Example_Employee {
        let rmModel = Example_Employee()
        rmModel.id = proto.id
        rmModel.firstName = proto.firstName
        rmModel.lastName = proto.lastName
        return rmModel
    }
    public func protobuf() throws -> Example.Employee {
        let proto = Example.Employee.Builder()
        proto.id = self.id
        proto.firstName = self.firstName
        proto.lastName = self.lastName
        return try proto.build()
    }
}

๐Ÿ”— Linking Objects

To create a link between an object and his owning model object you need to add a custom option:

option (.google.protobuf.swift_message_options) = {
    linkedObjects : [{
        fieldName : "department"
        fromType : "Department"
        propertyName : "employees"
        packageName : "Example"
    }]
};
  • fieldName: field name in owned object
  • fromType: class name of owner object
  • propertyName: field name in owner object
  • packageName: a name of your protobuf package

Example:

syntax = "proto2";
import "google/protobuf/swift-descriptor.proto";
package Example;

message Employee {
    option (.google.protobuf.swift_message_options) = {
        generate_realm_object : true
        linkedObjects : [{
            fieldName : "department"
            fromType : "Department"
            propertyName : "employees"
            packageName : "Example"
        }]
    };
    required string id = 1;
    required string firstName = 2;
    required string lastName = 3;
}

message Department {
    option (.google.protobuf.swift_message_options) = { generate_realm_object : true};
    required string id = 1;
    required string name = 2;
    repeated Employee employees = 3;
}

Generated:

public class Example_Employee:Object {
    @objc dynamic var id:String = ""
    @objc dynamic var firstName:String = ""
    @objc dynamic var lastName:String = ""
    let department = LinkingObjects(fromType: Department.self, property:"employees")
    public override class func indexedProperties() -> [String] {
        return ["id"]
    }
    public override class func primaryKey() -> String? {
        return "id"
    }
}
public class Example_Department:Object {
    @objc dynamic var id:String = ""
    @objc dynamic var name:String = ""
    let employees:List<Example_Employee> = List<Example_Employee>()
    public override class func indexedProperties() -> [String] {
        return ["id"]
    }
    public override class func primaryKey() -> String? {
        return "id"
    }
}
extension Example_Employee:ProtoRealm {
    public typealias PBType = Example.Employee
    public typealias RMObject = Example_Employee
    public typealias RepresentationType = Dictionary<String,Any>
    public static func map(_ proto: Example.Employee) -> Example_Employee {
        let rmModel = Example_Employee()
        rmModel.id = proto.id
        rmModel.firstName = proto.firstName
        rmModel.lastName = proto.lastName
        return rmModel
    }
    public func protobuf() throws -> Example.Employee {
        let proto = Example.Employee.Builder()
        proto.id = self.id
        proto.firstName = self.firstName
        proto.lastName = self.lastName
        return try proto.build()
    }
}
extension Example_Department:ProtoRealm {
    public typealias PBType = Example.Department
    public typealias RMObject = Example_Department
    public typealias RepresentationType = Dictionary<String,Any>
    public static func map(_ proto: Example.Department) -> Example_Department {
        let rmModel = Example_Department()
        rmModel.id = proto.id
        rmModel.name = proto.name
        rmModel.employees.append(objectsIn:Example_Employee.map(proto.employees))
        return rmModel
    }
    public func protobuf() throws -> Example.Department {
        let proto = Example.Department.Builder()
        proto.id = self.id
        proto.name = self.name
        proto.employees += try self.employees.map({ value in
            return try value.protobuf()
        })
        return try proto.build()
    }
}

๐Ÿ”ฃ Enums

Realm actually doesn't support Enums, but we do!

To work with enums add a custom option:

option (.google.protobuf.swift_enum_options) = { generate_realm_object : true};

Example:

enum DepartmentType {
    option (.google.protobuf.swift_enum_options) = { generate_realm_object : true };
    DELIVERY = 0;
    MANAGEMENT = 1;
    SUPPORT = 2;
}	

Generated:

public class Example_DepartmentType:Object {
    @objc dynamic var rawValue:String = ""
    public override class func primaryKey() -> String? {
        return "rawValue"
    }
    public override class func indexedProperties() -> [String] {
        return ["rawValue"]
    }
}

extension Example_DepartmentType:ProtoRealm {
    public typealias PBType = Example.DepartmentType
    public typealias RMObject = Example_DepartmentType
    public typealias RepresentationType = String
    public static func map(_ proto: Example.DepartmentType) -> Example_DepartmentType {
        let rmModel = Example_DepartmentType()
        rmModel.rawValue = proto.toString()
        return rmModel
    }
    public func protobuf() throws -> Example.DepartmentType {
        return try Example.DepartmentType.fromString(self.rawValue)
    }
}

๐Ÿ‘ Credits

Developer - Alexey Khokhlov

Google Protocol Buffers - Cyrus Najmabadi, Sergey Martynov, Kenton Varda, Sanjay Ghemawat, Jeff Dean, and others

protobuf-swift-realm's People

Contributors

alexeyxo avatar evdeve avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Forkers

nexitusor evdeve

protobuf-swift-realm's Issues

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.