Skip to content

andyshep/GROCloudStore

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

88 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

logo

GROCloudStore

Swift 5.0 Platform

GROCloudStore provides an NSIncrementalStore subclass that is backed by CloudKit, allowing data to be loaded from the cloud into your Core Data model. GROCloudStore works by augmenting your existing Core Data model with attributes so that records and entities can be tracked together. When saving to Core Data, CKRecord objects are created from managed objects and then saved to CloudKit.

GROCloudStore only supports the private CloudKit database. This is because GROCloudStore uses custom record zones which are not supported in the public database.

This library is deprecated in favor of NSPersistentCloudKitContainer

Requirements

  • Xcode 11
  • Swift 5

Configuration

Before using the store, it must be configured with some information about your specific CloudKit setup. At a high level, the store needs to know the CloudKit identifier and zone name and how to map between model objects and cloud records.

Below these configuration points are discussed in more detail.

  1. Create a class conforming to the Configuration protocol. This protocol defines information about your CloudKit environment. Specifically, it defines the bucket and custom zone names and any record subscriptions.
struct DefaultContainer: CloudContainerType {
    var Identifier: String {
        return "iCloud.org.example.domain.App"
    }
    
    var CustomZoneName: String {
        return "examplezonename"
    }
}

struct Subscription: SubscriptionType {
    var Default: [CKSubscription] {
        return []
    }
}

class SampleConfiguration: Configuration {
    var Subscriptions: SubscriptionType {
        return Subscription()
    }
    
    var CloudContainer: CloudContainerType {
        return DefaultContainer()
    }
}
  1. Provide an instance of this configuration object when creating your Core Data stack. This should be done along with specifying GROIncrementalStore.storeType as the Persistent Store type.
let modelURL = Bundle.main.url(forResource: "Todos", withExtension: "momd")!
let model = NSManagedObjectModel(contentsOf: modelURL)!
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: model)

let options = [GROConfigurationKey: SampleConfiguration()]
let type = GROIncrementalStore.storeType
    
try! coordinator.addPersistentStore(ofType: storeType, configurationName: nil, at: url, options: options)

let managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator

// continue to use the managed object context
  1. Add a Data attribute called encodedSystemFields onto your Core Data model objects. This field is used to store the result of calling encodeSystemFieldsWithCoder on your CKRecord objects.
class MyModelObject: NSManagedObject {
	@NSManaged var name: String?
	@NSManaged var date: Date?

	@NSManaged var encodedSystemFields: Data?
}
  1. Conform to the ManagedObjectTransformable and CloudKitTransformable protocols on your model objects. These protocols allow you to define how objects are translated between CKRecords and NSManagedObjects.
extension MyModelObject: ManagedObjectTransformable {

    func transform(using object: NSManagedObject) {
        guard let item = object.value(forKeyPath: "item") as? String else { return }
        self.item = item
        
        guard let created = object.value(forKeyPath: "created") as? Date else { return }
        self.created = created
        
        guard let data = object.value(forKeyPath: "encodedSystemFields") as? Data else { fatalError() }
        self.encodedSystemFields = data
    }
}

extension MyModelObject: CloudKitTransformable {

    func transform(record record: CKRecord) {
        guard let name = record["name"] as? String else { return }
        self.name = name
        
        guard let date = record["date"] as? NSDate else { return }
        self.date = date
        
        self.record = record
    }
    
    func transform() -> CKRecord {
        let record = self.record
        record["name"] = self.name
        record["date"] = self.date
        
        return record
    }
}

Example

There is an example Todos app that shows how to integrate with GROCloudStore. The app displays a task list of todo items, using a single Core Data entity.

Before the example can be used, you'll need to do the following.

  1. Log into iCloud on your Mac or through the iOS simulator
  2. Change the bundle identifier and iCloud bucket to something unique
  3. Generate an entitlements file using your Apple developer account.

Credits

GROCloudStore is inspired by the design of AFIncrementalStore and the Advanced Operations presentation from WWDC 2015.

License

GROCloudStore is released under the MIT license. See LICENSE for details.

About

An Incremental Store for CloudKit and Core Data

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published