Core Data Ensembles
作者: Drew McCormack
创建: 2013 年 9 月 29 日
最后更新: 2017 年 2 月 15 日
现在 Ensembles 2 已可在 ensembles.io 购买。它具有性能改进、额外的后端(例如 CloudKit、Dropbox 同步)以及其他功能。本版本的 Ensembles 继续维护和支持。
有一个 Google Group 可以与其他开发者讨论最佳实践。
Ensembles 是一个 Objective-C 框架,支持 Swift,它扩展了 Apple 的 Core Data 框架,为 Mac OS 和 iOS 添加了点对点同步。可以通过文件同步平台(如 iCloud 或 Dropbox)将多个 SQLite 持久存储库结合起来。该框架可以很容易地扩展以支持任何可以在设备之间移动文件的服务,包括定制服务器。
下载 Ensembles
要克隆 Ensembles 到您的本地驱动器,使用以下命令
git clone https://github.com/drewmccormack/ensembles.git
Ensembles 使用 Git 子模块。要检索这些子模块,更改到 ensembles
根目录
cd ensembles
并执行以下命令
git submodule update --init
将 Ensembles 集成到 iOS 项目中
要使用 CocoaPods... 将 Ensembles 添加到您的 App 的 Xcode 项目中
-
在您的 Podfile 中添加以下内容
platform :ios, '7.0' pod "Ensembles", "~> 1.0"
如果您想使用 Ensembles 框架(模块),请尝试以下...
- 在Finder中,将
Ensembles iOS.xcodeproj
项目从Framework
目录拖动到您的Xcode项目中。 - 在左侧的源列表中,选择您的应用程序的项目根目录,然后选择应用程序的目标。
- 在“通用”选项卡中,点击“嵌入式二进制”部分的+按钮。
- 在iOS部分中选择
Ensembles.framework
。(不要错误地选择Mac框架。) - 将Ensembles导入您的源代码中。
对于Objective-C:
#import <Ensembles/Ensembles.h>
对于Swift:
import Ensembles
要手动添加Ensembles静态库而不是模块...
-
在Finder中,将
Ensembles iOS.xcodeproj
项目从Framework
目录拖动到您的Xcode项目中。 -
在左侧的源列表中,选择您的应用程序的项目根目录,然后选择应用程序的目标。
-
在“通用”选项卡中,点击“链接器和库”部分的+按钮。
-
选择
libensembles.a
库并将其添加。 -
选择“构建设置”选项卡。找到“其他链接器标志”设置,并添加标志
-ObjC
。 -
选择“构建阶段”选项卡。打开“目标依赖项”,然后单击+按钮。
-
找到
Ensembles Resources iOS
产品,并将其添加为依赖项。 -
在源列表中打开
Ensembles iOS.xcodeproj
项目,然后打开“产品”组。 -
将
Ensembles.bundle
产品拖动到您的应用程序的“复制资源包”构建阶段中。 -
在您的预编译头文件中添加以下导入,或者在任何使用Ensembles的文件中。
#import <Ensembles/Ensembles.h>
在macOS项目中集成Ensembles
要使用 CocoaPods... 将 Ensembles 添加到您的 App 的 Xcode 项目中
-
在您的 Podfile 中添加以下内容
platform :osx, '10.9' pod "Ensembles", "~> 1.0"
如果您想手动将Ensembles添加到您的应用程序Xcode项目...
-
在Finder中,将
Ensembles Mac.xcodeproj
项目从Framework
目录拖动到您的Xcode项目中。 -
在左侧的源列表中,选择您的应用程序的项目根目录,然后选择应用程序的目标。
-
在“通用”选项卡中,点击“嵌入式二进制”部分的+按钮。
-
选择
Ensembles.framework
并将其添加。 -
在您的预编译头文件中添加以下导入,或者在任何使用Ensembles的文件中。
#import <Ensembles/Ensembles.h>
对于Swift使用:
import Ensembles
包括可选云服务
默认情况下,Ensembles只包含对iCloud的支持。要使用其他云服务,如Dropbox,可能需要在上述过程步骤中添加一些步骤。
如果您使用CocoaPods,请将可选子规范添加到Podfile中。例如,要包含Dropbox,请包含
pod "Ensembles/Dropbox", "~> 1.0"
如果您不是使用CocoaPods而是手动安装Ensembles,需要定位与您要支持的服务相关的源文件和框架。您可以在Vendor
文件夹中找到框架,源文件在Framework/Extensions
中。
举例来说,如果您想支持Dropbox,您需要添加DropboxSDK Xcode项目作为依赖项,链接到相应的产品库,并在您的项目中包含文件CDEDropboxCloudFileSystem.h
和CDEDropboxCloudFileSystem.m
。
习惯性应用
习惯性是一个相对简单的应用示例,它集成了Ensembles,并与iCloud或Dropbox协同工作,实现多设备同步。此应用允许您记录想法,添加照片,并添加标签以分组它们。该应用的Core Data模型包含三个实体,包括多对多关系。
习惯性项目是一个了解Ensembles以及其在Core Data应用中集成的良好方法。如果您想了解其工作方式,可以从中App Store下载习惯性。如果想要自己构建和运行它,需要遵循一些准备工作步骤。
- 在选择Xcode项目中源列表中的习惯性项目后,然后选择习惯性目标。
- 选择能力部分,开启iCloud开关。
- 在登录相同iCloud账户的设备和模拟器上构建和安装。
添加笔记,并按所需方式标记它们。当应用变为活动状态时,应用将同步,但您可以通过在分组表下方按钮上点击强制同步。
Dropbox同步应通过精神学院账户进行,但若想使用自己的开发者账户,需执行以下操作
-
在developer.dropbox.com注册Dropbox开发者账户
-
在应用控制台中,点击创建应用按钮。
-
选择Dropbox API应用类型。
-
选择存储'文件和数据存储库'
-
选择'是 - 我的应用只需要访问创建的文件'
-
为应用命名(例如:习惯性)
-
点击创建应用
-
在
IDMSyncManager
类顶部,找到此代码,并将值替换为您在Dropbox网站上创建的字符串。NSString * const IDMDropboxAppKey = @"xxxxxxxxxxxxxxx"; NSString * const IDMDropboxAppSecret = @"xxxxxxxxxxxxxxx";
-
在Xcode中选择习惯性项目,然后选择习惯性iOS目标。
-
选择信息选项卡。
-
打开URL类型部分,将URL方案项更改为
db-<Your Dropbox App Key>
习惯性还包括一个同步服务:IdioSync。这是一个基于Node.js服务器和Amazon S3存储的定制服务。服务器源代码作为购买优先支持包的ensembles.io
提供的。
了解Ensembles
在使用任何文件中的Ensembles之前,您应在预编译头文件或单独的源代码文件中导入框架头文件。
#import <Ensembles/Ensembles.h>
Ensembles框架中最重要的类是CDEPersistentStoreEnsemble
。您为每个想要同步的NSPersistentStore
创建一个此类实例。此类监控对SQLite存储的保存操作,并在其他设备的更改到达时进行合并。
您通常在创建 Core Data 栈的相同代码点附近初始化CDEPersistentStoreEnsemble
。在保存数据之前初始化集合并是非常重要的。
您还需要熟悉另一类类。这些是符合 CDECloudFileSystem
协议的类。任何符合此协议的类都可以作为集合的文件同步后端,以便在不同设备间传输数据。您可以使用现有的类(例如 CDEICloudFileSystem
),或自行开发。
集合的初始化通常只有几行。
// Setup Ensemble
cloudFileSystem = [[CDEICloudFileSystem alloc]
initWithUbiquityContainerIdentifier:@"P7BXV6PHLD.com.mentalfaculty.idiomatic"];
ensemble = [[CDEPersistentStoreEnsemble alloc] initWithEnsembleIdentifier:@"MainStore"
persistentStoreURL:storeURL
managedObjectModelURL:modelURL
cloudFileSystem:cloudFileSystem];
ensemble.delegate = self;
在网络文件系统初始化后,它被传递给 CDEPersistentStoreEnsemble
初始化器,同时传递包含 NSManagedObjectModel
的文件 URL 和 NSPersistentStore
的路径。集合标识符用于在不同设备间匹配存储。确保集合中每个存储的标识符相同非常重要。
CDEPersistentStoreEnsemble
初始化后,可以对其进行 提取。此步骤通常只需要执行一次,用于设置集合并执行本地持久存储中的初始数据导入。一旦进行了提取,即使重启,集合也会保持提取状态。只有您显式请求或网络文件系统出现严重问题时(例如账户切换),才会取消提取。
您可以使用 isLeeched
属性查询集合是否已提取,并使用 leechPersistentStoreWithCompletion:
初始化提取过程。(尝试提取已提取的集合将导致错误。)
if (!ensemble.isLeeched) {
[ensemble leechPersistentStoreWithCompletion:^(NSError *error) {
if (error) NSLog(@"Could not leech to ensemble: %@", error);
}];
}
由于许多集合任务可能涉及网络或长时间操作,大多数方法都是异步的,并在任务完成后包含一个块回调。如果错误参数为 nil
,则任务完成成功。方法应仅在主线程上启动,并将完成回调发送到主队列。
进行了提取的集合可以使用 mergeWithCompletion:
方法启动同步操作。
[ensemble mergeWithCompletion:^(NSError *error) {
if (error) NSLog(@"Error merging: %@", error);
}];
合并涉及从云网络文件系统中检索其他设备的新更改,在后台 NSManagedObjectContext
中合并它们,与新的本地更改合并,并保存到 NSPersistentStore
中。
当发生合并时,将更改合并到您的主 NSManagedObjectContext
中非常重要。您可以在 persistentStoreEnsemble:didSaveMergeChangesWithNotification:
代理方法中执行此操作。
- (void)persistentStoreEnsemble:(CDEPersistentStoreEnsemble *)ensemble didSaveMergeChangesWithNotification:(NSNotification *)notification
{
[managedObjectContext performBlock:^{
[managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}];
}
请注意,此方法是在用于合并更改的后台上下文线程上调用的。您需要确保在主上下文对应的线程上调用 mergeChangesFromContextDidSaveNotification:
方法。
还有一个代理方法,您可能想要实现,以便提供托管对象的全局标识符。
- (NSArray *)persistentStoreEnsemble:(CDEPersistentStoreEnsemble *)ensemble
globalIdentifiersForManagedObjects:(NSArray *)objects
{
return [objects valueForKeyPath:@"uniqueIdentifier"];
}
此方法也是在后台线程上调用。应小心只访问此线程上传递的对象。
提供全局标识符不是强制性的,但如果您提供了,框架将自动确保没有对象因在不同设备上的多次导入而重复。如果您不提供全局标识符,框架将无法识别新对象,并将为新对象分配一个新唯一标识符。
如果您决定提供全局标识符,确定如何生成它们以及在哪里存储它们由您决定。一个常见的选择是在数据模型中的实体添加一个额外属性,并在插入存储时将其设置为 uuid。
故障排除
Ensembles 内置了日志系统,但默认只记录错误。在开发过程中,使用详细日志设置可以看到框架正在做什么是非常有用的。简单地在启动过程的早期进行这个调用即可。
CDESetCurrentLoggingLevel(CDELoggingLevelVerbose);
单元测试
Ensembles 框架在各个平台上都包含了单元测试。要运行测试,在 Xcode 工作区中打开,在上面的工具栏中选择 Ensembles Mac 或 Ensembles iOS 目标,然后选择 产品 > 测试
菜单项。