Sync 简化了您解析 JSON 响应并与 Core Data 同步的日常工作。 Sync 是一个轻量级的 Swift 库,它使用约定优于配置的范例来简化您的开发流程。
将 JSON 同步到 Core Data 是一项重复性的任务,通常需要添加大量的样板代码。映射属性、映射关系、比较插入、删除和更新等任务在应用程序之间通常会保持不变。考虑到这一点,我们挑战将这一过程抽象成一个库。 Sync 使用您对 Core Data 模型的了解来推断 JSON 与 Core Data 之间的所有映射,一旦您开始使用它,您会觉得这如此显而易见,以至于您会想为什么以前没有这样做。
- 自动将 camelCase 或 snake_case JSON 映射到 Core Data
- 线程安全保存,我们处理在正确的线程中检索和存储对象
- 更改比较,更新、插入和删除的对象(这些将自动为您清除)
- 自动映射关系(一对一、一对多和多对多)
- 智能更新,只有在服务器值与本地值不同时才更新您的
NSManagedObject
- 去重,每个主键只有一个 Core Data 条目
NSOperation
子类,任何 Sync 进程都可以在任何时候排队和取消!
目录
基本示例
模型
JSON
[
{
"id": 6,
"name": "Shawn Merrill",
"email": "[email protected]",
"created_at": "2014-02-14T04:30:10+00:00",
"updated_at": "2014-02-17T10:01:12+00:00",
"notes": [
{
"id": 0,
"text": "Shawn Merril's diary, episode 1",
"created_at": "2014-03-11T19:11:00+00:00",
"updated_at": "2014-04-18T22:01:00+00:00"
}
]
}
]
DataStack
DataStack 是在 Core Data 烤箱之上的一层包装,它封装了与 NSPersistentStoreCoordinator 和 NSManageObjectContexts 的交互。
self.dataStack = DataStack(modelName: "DataModel")
同步
dataStack.sync(json, inEntityNamed: "User") { error in
// New objects have been inserted
// Existing objects have been updated
// And not found objects have been deleted
}
如果您只想同步在过去 24 小时内创建的用户,可以通过使用 NSPredicate
来实现。
let now = NSDate()
let yesterday = now.dateByAddingTimeInterval(-24*60*60)
let predicate = NSPredicate(format:@"createdAt > %@", yesterday)
dataStack.sync(json, inEntityNamed: "User", predicate: predicate) { error in
//..
}
演示项目
我们有一个简单的演示项目,展示如何设置和使用同步来从网络获取数据并在 UITableView 中显示。该项目包含 Networking 和 Alamofire 作为网络库。
DataStack 与 Storyboards
使用 Storyboard 配置 DataStack 与通过依赖注入进行配置不同,您在这里可以找到一个示例项目,说明如何实现这种设置。
https://github.com/3lvis/StoryboardDemo
快速入门
Core Data Stack
将您的Core Data堆栈替换为DataStack的一个实例。
self.dataStack = DataStack(modelName: "Demo")
主键
Sync要求您的实体具有主键,这对于差异比较非常重要,否则Sync不知道如何区分条目。
默认情况下,Sync使用JSON中的id
和Core Data中的id
(或remoteID
)作为主键。
您可以通过添加sync.isPrimaryKey
并设置值为true
(或YES
)来标记任何属性为主键。例如,在我们的Designer News项目中,我们有一个使用body
作为主键的Comment
实体。
如果您将标记sync.isPrimaryKey
添加到属性contractID
,则
- 本地主键将是:
contractID
- 远程主键将是:
contract_id
如果您想将id
用作远程主键,您还必须添加标记sync.remoteKey
并将值设置为id
。
- 本地主键将是:
articleBody
- 远程主键将是:
id
属性映射
您的属性应与JSON中的对应属性匹配,以camelCase
格式而不是snake_case
。例如,first_name
在JSON中对应于Core Data中的firstName
,而address
在JSON中对应于Core Data中的address
。
此规则有一些例外
- 保留属性应以
entityName
(例如,type
将成为userType
,description
将成为userDescription
等)为前缀。在JSON中它们不需要更改,例如,您可以保留type
和description
。完整保留属性列表可在此处找到。 - 包含缩写的属性将进行标准化(
id
、pdf
、url
、png
、jpg
、uri
、json
、xml
)。例如,user_id
将被映射到userID
等等。你可以在这里找到支持的所有缩写列表:此处。
如果你想要将你的Core Data属性映射到具有不同命名的JSON属性,你可以在用户信息框中添加sync.remoteKey
,并使用你想映射的值。
属性类型
数组/字典
要将数组或字典映射,只需在Core Data模型器中将属性设置为二进制数据
。
检索映射的数组
{
"hobbies": [
"football",
"soccer",
"code"
]
}
let hobbies = NSKeyedUnarchiver.unarchiveObjectWithData(managedObject.hobbies) as? [String]
// ==> "football", "soccer", "code"
检索映射的字典
{
"expenses" : {
"cake" : 12.50,
"juice" : 0.50
}
}
let expenses = NSKeyedUnarchiver.unarchiveObjectWithData(managedObject.expenses) as? [String: Double]
// ==> "cake" : 12.50, "juice" : 0.50
日期
我们默认支持ISO8601和Unix时间戳,因为它们是在解析日期时最常见的格式。我们还有一种相当高效的字符串解析方式,这克服了使用NSDateFormatter
时的性能问题。
let values = ["created_at" : "2014-01-01T00:00:00+00:00",
"updated_at" : "2014-01-02",
"published_at": "1441843200"
"number_of_attendes": 20]
managedObject.fill(values)
let createdAt = managedObject.value(forKey: "createdAt")
// ==> "2014-01-01 00:00:00 +00:00"
let updatedAt = managedObject.value(forKey: "updatedAt")
// ==> "2014-01-02 00:00:00 +00:00"
let publishedAt = managedObject.value(forKey: "publishedAt")
// ==> "2015-09-10 00:00:00 +00:00"
关系映射
同步会将您的实体关系映射到它们相应的JSON格式。在本文件的开始部分展示的示例中,您可以看到一个关于关系映射的基础示例。
一对一
让我们考虑以下Core Data模型。
该模型在_USER_和ЛЕ_之间有一个一对一关系,换句话说,一个用户有许多笔记,在Note模型中也可以找到指向用户的反向关系。这对于Sync了解更多您模型的结构非常重要。最后,在Core Data模型中,用户与笔记之间存在级联关系,因此删除用户时,所有关联到该用户的笔记也会被删除(可以指定任何删除规则)。
因此,当Sync查看以下JSON时,它将同步特定用户的全部笔记,进行必要的逆向关系处理。
[
{
"id": 6,
"name": "Shawn Merrill",
"notes": [
{
"id": 0,
"text": "Shawn Merril's diary, episode 1",
}
]
}
]
一对一简化
如您所见,此过程需要包含完整的JSON对象,但在处理API时,有时您可能已经同步了所有需要的项目。Sync也支持这种方式。
例如,在一对多示例中,您有一个用户,他有许多笔记。如果您已经同步了所有这些笔记,那么您的JSON只需要notes_ids
,这可以是一个字符串或整数数组。作为附加说明,仅在您100%确定已同步所有所需项目(笔记)的情况下,才执行此操作,否则这些关系将被忽略,并将记录错误。此外,如果您希望从用户中删除所有笔记,只需提供"notes_ids": null
,然后同步
将为您清理。
[
{
"id": 6,
"name": "Shawn Merrill",
"notes_ids": [0, 1, 2]
}
]
一对一
对于一对一关系,采用类似的处理方式。例如,假设您有以下模型
这个模型很简单,用户代表一个公司。一个兼容的JSON格式可能如下所示
[
{
"id": 6,
"name": "Shawn Merrill",
"company": {
"id": 0,
"text": "Facebook",
}
}
]
一对一简化
如您所见,此过程需要包含完整的JSON对象,但在处理API时,有时您可能已经同步了所有需要的项目。Sync也支持这种方式。
例如,在一对一示例中,你有一个用户,该用户属于一家公司。如果你已经同步了所有公司信息,那么你的JSON只需要包含company_id
。请注意,只有在你100%确信所有所需项目(公司)均已同步的情况下,才执行此操作,否则此关系将被忽略,并记录错误。另外,如果你想从用户中删除公司,只需提供"company_id": null
,然后Sync
将为您进行清理。
[
{
"id": 6,
"name": "Shawn Merrill",
"company_id": 0
}
]
JSON导出
Sync提供了一个简单的方法将NSManagedObject转换回JSON。只需使用export()
方法即可。
let user = //...
user.set(value: "John" for: "firstName")
user.set(value: "Sid" for: "lastName")
let userValues = user.export()
就这样,这就是你需要做的,键将自动转换为snake_case
约定。
{
"first_name": "John",
"last_name": "Sid"
}
排除内容
如果你不想导出某些属性或关系,可以在排除属性或关系的用户信息中添加sync.nonExportable
来禁止导出。
关系
它也支持导出关系。
"first_name": "John",
"last_name": "Sid",
"notes": [
{
"id": 0,
"text": "This is the text for the note A"
},
{
"id": 1,
"text": "This is the text for the note B"
}
]
如果你不想要关系,你还可以忽略关系
let dictionary = user.export(using: .excludedRelationships)
"first_name": "John",
"last_name": "Sid"
或将它们作为嵌套属性获取,例如,对于有多个笔记的用户
var exportOptions = ExportOptions()
exportOptions.relationshipType = .nested
let dictionary = user.export(using: exportOptions)
"first_name": "John",
"last_name": "Sid",
"notes_attributes": [
{
"0": {
"id": 0,
"text": "This is the text for the note A"
},
"1": {
"id": 1,
"text": "This is the text for the note B"
}
}
]
常见问题解答
安装
CocoaPods
pod 'Sync', '~> 6'
Carthage
github "3lvis/Sync" ~> 6.0
支持的 iOS, OS X, watchOS 和 tvOS 版本
- iOS 8 或更高版本
- OS X 10.10 或更高版本
- watchOS 2.0 或更高版本
- tvOS 9.0 或更高版本
赞助商
觉得 Sync 有帮助?考虑通过成为赞助商来支持进一步的开发和支持
许可证
Sync 以 MIT 许可证提供。有关更多信息,请参阅 LICENSE 文件。