AlamofireCoreData
一个将JSON转换为CoreData对象的优秀的Alamofire序列化器。
使用AlamofireCoreData,您只需几行代码就可以将JSON映射并插入到您的框架中的NSManagedObject实例。
// User is a `NSManagedObject` subclass
Alamofire.request(url)
.responseInsert(context: context, type: User.self) { response in
switch response.result {
case let .success(user):
// The user object is already inserted in your context!
case .failure:
// handle error
}
}
AlamofireCoreData内部使用Groot将JSON序列化为CoreData对象,因此您需要熟悉它才能使用本库。Groot很棒,并且有很好的文档,所以如果您不熟悉,应该没有什么问题。
AlamofireCoreData基于Alamofire 4.0.x构建。
安装AlamofireCoreData
使用CocoaPods
将以下内容添加到您的Podfile
中
pod 'AlamofireCoreData', '~> 2.0.0'
然后运行 $ pod install
。
最后,在需要使用 AlamofireCoreData 的类中
import AlamofireCoreData
如果您还没有将CocoaPods安装到您的项目中,您可以在这里了解如何操作。
--
用法
第一步
要使用 AlamofireCoreData,首先需要使用 Groot 使你的模型可序列化。
查看 Groot 项目 了解具体方法。
插入单个对象
假设我们有一个名为 User
的 NSManagedObject
子类。我们还有一个 API 将返回一个 JSON,我们希望将其转换为 User
实例并插入到给定的 NSManagedObjectContext
中。
然后,我们只需调用 Alamofire 请求的 responseInsert
方法,并传递 context 和对象的类型作为参数。
// User is a `NSManagedObject` subclass
Alamofire.request(url)
.responseInsert(context: context, type: User.self) { response in
switch response.result {
case let .success(user):
// The user object is already inserted in your context!
case .failure:
// handle error
}
}
如果序列化失败,您将在 .failure(error)
中获得 InsertError.invalisJSON
实例。
插入对象列表
序列化对象列表同样简单。如果你的 API 返回一个 User
列表,你可以使用 Many
作为期望类型,将它们全部插入你的 context 中。
// User is a `NSManagedObject` subclass
Alamofire.request(url)
.responseInsert(context: context, type: Many<User>.self) { response in
switch response.result {
case let .success(users):
// users is a instance of Many<User>
case .failure:
// handle error
}
}
结构 Many
仅仅是围绕 Array
的包装,它旨在以与使用 Array
相同的方式使用。在任何情况下,您都可以通过调用其属性 array
来访问其原始的 Array
。
转换你的 JSON
在某些情况下,我们从服务器获得的数据格式可能不正确。甚至可能遇到XML中的一个字段是我们要解析的JSON(是的,我确实遇到过这样的事情responseInsert
有一个可选的附加参数,您可以使用它将响应转换为所需的JSON。它被称为jsonSerializer
Alamofire.request(url).responseInsert(
jsonSerializer: jsonTransformer,
context: context,
type: User.self)
jsonTransformer
仅仅是Alamofire.DataResponseSerializer<Any>
。您可以根据自己的需求构建序列化器;唯一条件是它必须返回您期望的、可以由Groot
序列化的JSON。
要构建这个序列化器,您可以使用Alamofire内置的方法
public init(serializeResponse: @escaping (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<Value>)
AlamofireCoreData提供了两个方便的方法来简化构建这个序列化器
- 一个自定义的
DataRequestSerializer
初始化器
public init<ParentValue>(
parent: DataResponseSerializer<ParentValue>,
transformer: @escaping (ResponseInfo, Result<ParentValue>) -> Result<Value>
)
其中响应通过parent
参数进行处理,然后通过transformer
闭包转换结果。
- 一个
DataRequest
类方法
public static func jsonTransformerSerializer(
options: JSONSerialization.ReadingOptions = .allowFragments,
transformer: @escaping ((ResponseInfo, Result<Any>) -> Result<Any>)
) -> DataResponseSerializer<Any>
其中响应被转换为JSON,然后通过transformer
闭包转换结果。
让我们看看第二种方法的例子。我们有一个这样的响应
{
"success": 1,
"data": { "id": 1, "name": "manue"}
}
我们需要一个执行两个任务的序列化器
- 检查
success
键以确定请求是否成功完成,如果不成功则发送错误 - 丢弃
success
参数,将data
的内容发送到序列化。
所以,我们可以创建这个序列化器
let jsonTransformer = DataRequest.jsonTransformerSerializer { (responseInfo, result) -> Result<Any> in
guard result.isSuccess else {
return result
}
let json = result.value as! [String: Any]
let success = json["success"] as! NSNumber
switch success.boolValue {
case true:
return Result.success(json["data"]!)
default:
// here we should create or own error and send it
return Result.failure(anError)
}
}
并这样调用请求
Alamofire.request(url).responseInsert(
jsonSerializer: jsonTransformer,
context: context,
type: User.self)
使用包装器
有时,我们的模型不是单独发送在服务器响应中的。相反,它们可能被包含在一个包含一些额外有用信息的更大的JSON中。例如,假设我们有一个用于我们的登录请求的响应,其中我们获取用户信息、访问令牌、令牌有效期以及朋友的列表
{
"info": {
"token": "THIS_IS_MY_TOKEN",
"validity": "2020-01-01"
},
"user": {
"id": "1",
"name": "manue",
},
"friends": [
{"id": 2, "name": "Ana"},
{"id": 3, "name": "Mila"}
]
}
我们需要不仅插入User
,还要插入token
、validity
和friends
。为此,我们需要创建一个新的类或结构,并采用Wrapper
协议。例如
struct LoginResponse: Wrapper {
var token: String!
var validity: Date?
var user: User!
var friends: Many<User>!
// required by protocol
init () {}
// provides info to convert the json
mutating func map(_ map: Map) {
token <- map["info.token"]
validity <- (map["info.validity"], dateTransformer)
user <- map["user"]
friends <- map["friends"]
}
}
map函数必须使用与示例相同的语法,使用<-
运算符。一些注意事项
- 如果变量是
NSManagedObject
、Many<NSManagedObject
、另一个Wrapper
或Many<Wrapper>
,则对象会被序列化和插入。 - 注意,集合必须是
Many
而不是Array
。如果您使用Array<User>
作为friends
类型,则对象不会被序列化或插入。 - 您可以添加转换器来更改JSON值的类型。例如,在示例中,JSON中的
validity
字段是String
类型,但我们需要一个Date
类型。我们传递一个函数dateTrasformer
,它将一个String
转换为Date
。
现在,我们可以像以前一样调用相同的方法,但这次用的期望类型是LoginResponse
Alamofire.request(loginURL)
.responseInsert(context: context, type: LoginResponse.self) { response in
switch response.result {
case let .success(response):
// The user and friends are already inserted in your context!
let user = response.user
let friends = response.friends
let validity = response.validity
let token = response.token
case .failure:
// handle error
}
}
根键路径
在某些情况下,我们需要映射到JSON的根级别的对象。例如,如果我们有一个实现了Wrapper
的Pagination
对象。
struct Pagination: Wrapper {
var total: Int = 0
var current: Int = 0
var previous: Int?
var next: Int?
// MARK: Wrapper protocol methods
required init() {}
mutating func map(map: Map) {
total <- map["total"]
current <- map["current"]
previous <- map["previous"]
next <- map["next"]
}
}
我们的响应如下
{
"total": 100,
"current": 3,
"previous": 2,
"next": 4,
"users": [
{"id": "1", "name": "manue"},
{"id": "2", "name": "ana"},
{"id": "3", "name": "lola"}
]
}
注意,分页不是在任意键下,而是在JSON的根处。在这种情况下,我们可以创建下一个对象
class UserListResponse: Wrapper {
var pagination: Pagination!
var users: Many<User>!
// MARK: Wrapper protocol methods
required init() {}
func map(map: Map) {
pagination <- map[.root] // Look that we use `.root` instead of a string
users <- map["users"]
}
}
--
联系
Manuel García-Estañ Martínez
@manueGE
许可
AlamofireCoreData 在 MIT 许可证 下可用。