/CloudCore

Framework that enables syncing between iCloud (CloudKit) and Core Data

Primary LanguageSwiftMIT LicenseMIT

CloudCore

Build Status Documentation Version Platform Status Swift

CloudCore is a framework that manages syncing between iCloud (CloudKit) and Core Data written at native Swift.

Features

  • Differential sync, only changed values in object are uploaded and downloaded
  • Support of all relationship types
  • Respect of Core Data options (cascade deletions, external storage options)
  • Unit and performance tests for the most offline methods
  • Private and shared CloudKit databases (to be tested) are supported

Installation

CocoaPods

CloudCore is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'CloudCore', '~> 1.0'

Swift Package Manager

The Swift Package Manager is a tool for automating the distribution of Swift code and is integrated into the swift compiler. You can read more about package manager in An Introduction to the Swift Package Manager article.

Once you have set up Swift package for your application, just add CloudCore as dependency at your Package.swift:

dependencies: [
    .Package(url: "https://github.com/Sorix/CloudCore", majorVersion: 1)
]

How to help?

Current version of framework hasn't been deeply tested and may contain errors. If you can test framework, I will be very glad. If you found an error, please post an issue.

Documentation

Detailed documentation is available at CocoaDocs.

Quick start

  1. Enable CloudKit capability for you application: CloudKit capability

  2. Add 2 service attributes to each entity in CoreData model you want to sync:

  • recordData attribute with Binary type
  • recordID attribute with String type
  1. Make changes in your AppDelegate.swift file:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
	// Register for push notifications about changes
	UIApplication.shared.registerForRemoteNotifications()

	// Enable uploading changed local data to CoreData
	CloudCore.observeCoreDataChanges(persistentContainer: persistentContainer, errorDelegate: nil)

	// Sync on startup if push notifications is missed, disabled etc
	// Also it acts as initial sync if no sync was done before
	CloudCore.fetchAndSave(container: persistentContainer, error: nil, completion: nil)

	return true
}

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
	// Check if it CloudKit's and CloudCore notification
	if CloudCore.isCloudCoreNotification(withUserInfo: userInfo) {
		// Fetch changed data from iCloud
		CloudCore.fetchAndSave(using: userInfo, container: self.persistentContainer, error: nil, completion: { (fetchResult) in
			completionHandler(fetchResult.uiBackgroundFetchResult)
		})
	}
}

func applicationDidEnterBackground(_ application: UIApplication) {
	// Save tokens on exit used to differential sync
	CloudCore.tokens.saveToUserDefaults()
}
  1. Make first run of your application in a development environment, fill an example data in Core Data and wait until sync completes. CloudCore create needed CloudKit schemes automatically.

Service attributes

CloudCore stores service CloudKit information in managed objects, you need to add that attributes to your Core Data model. If required attributes are not found in entity that entity won't be synced.

Required attributes for each synced entity:

  1. Record Data attribute with Binary type
  2. Record ID attribute with String type

You may specify attribute's names in 2 ways (you may combine that ways in different entities).

User Info

First off CloudCore try to search attributes by analyzing User Info at your model, you may specify attribute's key as CloudCoreType to mark that attribute as service one. Values are:

  • Record Data value is recordData.
  • Record ID value is recordID.

Model editor User Info

Default names

The most simple way is to name attributes with default names because you don't need to specify any User Info: recordID and recordData.

💡 Tips

  • You can name attribute as you want, value of User Info is not changed (you can create attribute myid with User Info: CloudCoreType: recordID)
  • I recommend to mark Record ID attribute as Indexed, that can speed up updates in big databases.
  • Record Data attribute is used to store archived version of CKRecord with system fields only (like timestamps, tokens), so don't worry about size, no real data will be stored here.

Example application

You can find example application at Example directory.

How to run it:

  1. Set Bundle Identifier.
  2. Check that embedded binaries has a correct path (you can remove and add again CloudCore.framework).
  3. If you're using simulator, login at iCloud on it.

How to use it:

  • + button adds new object to local storage (that will be automatically synced to Cloud)
  • refresh button calls fetchAndSave to fetch data from Cloud. That is useful button for simulators because Simulator unable to receive push notifications
  • Use CloudKit dashboard to make changes and see it at application, and make change in application and see ones in dashboard. Don't forget to refresh dashboard's page because it doesn't update data on-the-fly.

Roadmap

  • Sync with public CloudKit database (in development)
  • Add tvOS support
  • Update documentation with macOS samples

Author

Vasily Ulianov, va...@me.com