同步 6.5.0

同步 6.5.0

测试已测试
语言语言 SwiftSwift
许可证 NOASSERTION
发布最后发布2020 年 10 月
SPM支持 SPM

Elvis Nuñez 维护。



同步 6.5.0

  • 作者:
  • Elvis Nuñez

Sync

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 进程都可以在任何时候排队和取消!

目录

基本示例

模型

Model

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 的更多方法.

同步

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 中显示。该项目包含 NetworkingAlamofire 作为网络库。

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实体。

Custom primary key

如果您将标记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将成为userTypedescription将成为userDescription等)为前缀。在JSON中它们不需要更改,例如,您可以保留typedescription。完整保留属性列表可在此处找到。
  • 包含缩写的属性将进行标准化(idpdfurlpngjpgurijsonxml)。例如,user_id将被映射到userID等等。你可以在这里找到支持的所有缩写列表:此处

如果你想要将你的Core Data属性映射到具有不同命名的JSON属性,你可以在用户信息框中添加sync.remoteKey,并使用你想映射的值。

Custom remote key

属性类型

数组/字典

要将数组或字典映射,只需在Core Data模型器中将属性设置为二进制数据

screen shot 2015-04-02 at 11 10 11 pm

检索映射的数组

{
  "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模型。

One-to-many

该模型在_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]
  }
]

一对一

对于一对一关系,采用类似的处理方式。例如,假设您有以下模型

one-to-one

这个模型很简单,用户代表一个公司。一个兼容的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来禁止导出。

non-exportable

关系

它也支持导出关系。

"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 有帮助?考虑通过成为赞助商来支持进一步的开发和支持👉 https://github.com/sponsors/3lvis

许可证

Sync 以 MIT 许可证提供。有关更多信息,请参阅 LICENSE 文件。