AlamofireCoreData 2.0.0

AlamofireCoreData 2.0.0

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布最新发布2018年9月
SPM支持 SPM

Manuel García-Estañ 维护。



 
依赖项
Alamofire~> 4.7
Groot~> 3.0
 

  • 作者
  • Manuel García-Estañ

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 项目 了解具体方法。

插入单个对象

假设我们有一个名为 UserNSManagedObject 子类。我们还有一个 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,还要插入tokenvalidityfriends。为此,我们需要创建一个新的类或结构,并采用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函数必须使用与示例相同的语法,使用<-运算符。一些注意事项

  • 如果变量是NSManagedObjectMany<NSManagedObject、另一个WrapperMany<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的根级别的对象。例如,如果我们有一个实现了WrapperPagination对象。

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 许可证 下可用。