Carthage compatible

Ditto MeshKit for Cocoa

Ditto is a library of JSON like data structures for building peer to peer and client server sync applications. Ditto handles the complexities of data sync, conflict resolution, and persistence all with a friendly to use API.

Things you can do with Ditto:

  • You can have a copy of the JSON like documents locally on several devices. Each user can independently update the application state on their local device, even while offline, and save the state to local disk.

  • When a network connection is available, Ditto seamlessly figures out what changes are needed and sync them quickly and efficiently.

  • When states have changed concurrently on devices, Ditto automatically merges the changes together cleanly, so that everybody ends up in the same state, and no changes are lost.

  • Ditto can manage several documents in Mesh allowing fine grained control and access between documents.

Requirements

Requires

Requires Swift 4.x or higher.
  • iOS Minimum Version is 10
  • Features

    • Sync JSON-like data between iOS,
    • Change data offline
    • Generate a transparent image (or not).

    Installation

    Cocoapods

    pod 'DittoMeshKit'
    

    And run: pod install --repo-update

    Getting Started

    Before starting DittoMeshKit, the application will need to set important data about how to connect:

    DittoMeshKit.set(accessToken: "YOUR_ACCESS_TOKEN")
    DittoMeshKit.set(userIdentifier: "\(userIdentifier)")
    DittoMeshKit.set(displayName: "Sally-iPhone-X")
    
    • accessToken: String : the application needs the access token in order make connections to devices. You can obtain this by emailing our team admin@ditto.live
    • userIdentifier: String: This will tell the SDK how to uniquely identify. It is critical that this is completely unique across the mesh.
    • displayName: String: This is a friendly way to describe the device. A good example is "Sallys-IPhone-8"

    A note about the userIdentifier. We mentioned that it must be unique across the mesh. We use this value to understand what data a specific mesh endpoint has or has not seen. It is not uncommon for organizations deploying DittoLive to have a concept of a "userId" representing "Sally". However, Sally will more than likely have several devices like an iPhone 8, iPhone X, and maybe an iPhone X. She may even log into a different device in a future point. Each of these devices needs a compeletely unique identifier. We highly recommend that you design your system so that users are expected at one point or another to have several devices

    Obtaining Debugging Information

    You can obtain information on

    DittoMeshKit.shared().delegate = self
    DittoMeshKit.shared().enableDebugLogging = true
    DittoMeshKit.shared().start()
    

    Reading Manipulating Docs and Collections

    The cornerstone of editing data with ditto is a collection. Collections are groupings of similar objects that your application needs to sync between each other. Docs are individually syncable objects.

    Getting references to a collection

    To get a reference to the collection, supply a name within the collection getter.

    let cars = try DittoMeshKit.shared().collection("cars")
    

    Inserting Docs into a collection

    To construct a doc that you’d like to sync, use the .insert method and pass in a JSON compatible representation of the data that you’d like to sync.

    let usersCollection = try! DittoMeshKit.shared().collection("users")
    try! usersCollection.insert([
                        "_id": "abc123"
                        "name": "Joe",
                        "age": 27,
                        "cars": [
                                [
                                        "make": "Honda",
                                        "weight": 200024.395,
                                        "color": "red",
                                ],
                                [
                                        "make": "Toyota",
                                        "weight": 355.4,
                                        "color": "blue"
                                ]
                        ]
                ])
    

    Note: about the _id special field

    Notice below that there is an _id field. Each doc needs this field. If your incoming JSON does not supply it, one will be generated and appended. Once you set the _id this can never change.

    If you supply an _id and there is already a document occupying the same value then this method will throw an error.

    Getting By Id

    Since each doc has an _id field, you can fetch it using:

    let doc = try! DittoMeshKit.shared().collection("cars").findById("abc123")
    

    If the document doesn’t exist in the current cache then it will return nil.

    Getting a single document with an NSPredicate query

      let cars = DittoMeshKit.shared().collection("cars")
      let doc: [String: Any]? = try! cars
        .findOne(NSPredicate(format: "price <= %@", NSNumber(5600)))
    

    If the document doesn’t exist in the current cache according to the query then it will return nil.

    Getting a document with an NSPredicate query

      let cars = DittoMeshKit.shared().collection("cars")
    
      let docs: [[String: Any]] = try! cars
        .find(NSPredicate(format: "price <= %@", NSNumber(5600)))
        .toArray()
    

    Note: The result set of the query should be able to comfortable fit in memory. Please be judicious about the result set’s size.

    Updating

    Updating a document can happen with 2 different methods:

    Updating By Id

    In this example we will set doc _id "123abc"‘s "title" key to the value "B"

    If we have a doc:

    {
      "_id": "123abc",
      "title": "A"
    }
    

    Now call:

    let updatedDoc = try! collections("posts").updateById("123abc", [
      "title": "B"
    ])
    

    Then now it will look like:

    {
      "_id": "123abc",
      "title": "B"
    }
    

    If the document doesn’t exist, it will throw a .notFound exception.

    Updating By NSPredicate

    In this example we will set all docs written by "Max" with a new "title" key to the value "B"

    let updatedDocs = try! collections("posts").update(NSPredicate(format: "author == %@", "Max"), [
      "title": "B"
    ])
    

    Removing Docs

    Similar to updates, removes can happen with removeById and remove

    Removing by Id

    collections("posts").removeById("123abc")
    

    Removing by NSPredicate

    This will remove all documents that have the author named Max

    collections("posts").remove(NSPredicate(format: "author == %@", "Max"))
    

    Creating Batched Writes

    Batched writes chain up several write operations in a transaction. If any of them should fail, then none of the changes will make it onto disk and will be rolled back.

    Batched updates will only fire observers once.

    let results = try! collections("posts").write { tx in 
      tx.insert([
        "title": "Hello World",
        "body": "Lorem Ipsum dolor...",
      ])
      tx.updateById("123abc", [
        .increment("count", -2)
      ])
      // etc...
    }
    

    Observing Document Changes

    When inserting, updating, or removing documents, you can listen for changes on a collection with the observe method:

    You can observe a document by using the .observe after a .find

    let subscriber = carsCollection
      .find(NSPredicate(format: "price <= 5600")).observe { docs in
        //this will fire with the new set of docs change on the collection
      }
    

    When you no longer want to listen for any of the changes you can call:

    subscriber.dispose()
    

    Author

    https://www.ditto.live