好问题。让我们逐一分解。
URLRequestConvertible 在实际 API 中的正确用法是什么?
URLRequestConvertible 协议是确保给定对象可以创建有效NSURLRequest 的轻量级方法。实际上并没有一套严格的规则或指南来强制您以任何特定方式使用此协议。它只是一个方便的协议,允许其他对象存储正确创建NSURLRequest 所需的状态。更多关于 Alamofire 的信息可以在here找到。
我应该为每个端点创建一个路由器吗?
绝对不是。这将破坏使用Enum 的全部目的。 Swift Enum 对象非常强大,可以让你共享大量的公共状态,并打开真正不同的部分。能够使用如下简单的东西创建NSURLRequest 真的很强大!
let URLRequest: NSURLRequest = Router.ReadUser("cnoon")
我不明白为什么使用枚举来构建路由器?为什么我们不使用带有静态方法的类?
之所以使用枚举,是因为它是一种在通用接口下表达多个相关对象的更简洁的方式。所有方法在所有案例之间共享。如果您使用静态方法,则每种方法的每种情况都必须有一个静态方法。或者您必须在对象内使用 Obj-C 样式的枚举。这是我的意思的一个简单示例。
enum Router: URLRequestConvertible {
static let baseURLString = "http://example.com"
case CreateUser([String: AnyObject])
case ReadUser(String)
case UpdateUser(String, [String: AnyObject])
case DestroyUser(String)
var method: Alamofire.HTTPMethod {
switch self {
case .CreateUser:
return .post
case .ReadUser:
return .get
case .UpdateUser:
return .put
case .DestroyUser:
return .delete
}
}
var path: String {
switch self {
case .CreateUser:
return "/users"
case .ReadUser(let username):
return "/users/\(username)"
case .UpdateUser(let username, _):
return "/users/\(username)"
case .DestroyUser(let username):
return "/users/\(username)"
}
}
}
要获取任何不同端点的方法,您可以调用相同的方法,而无需传入任何参数来定义您要查找的端点类型,它已经由您选择的案例处理。
let createUserMethod = Router.CreateUser.method
let updateUserMethod = Router.UpdateUser.method
或者如果你想获取路径,相同类型的调用。
let updateUserPath = Router.UpdateUser.path
let destroyUserPath = Router.DestroyUser.path
现在让我们使用静态方法尝试相同的方法。
struct Router: URLRequestConvertible {
static let baseURLString = "http://example.com"
static var method: Method {
// how do I pick which endpoint?
}
static func methodForEndpoint(endpoint: String) -> Method {
// but then I have to pass in the endpoint each time
// what if I use the wrong key?
// possible solution...use an Obj-C style enum without functions?
// best solution, merge both concepts and bingo, Swift enums emerge
}
static var path: String {
// bummer...I have the same problem in this method too.
}
static func pathForEndpoint(endpoint: String) -> String {
// I guess I could pass the endpoint key again?
}
static var pathForCreateUser: String {
// I've got it, let's just create individual properties for each type
return "/create/user/path"
}
static var pathForUpdateUser: String {
// this is going to get really repetitive for each case for each method
return "/update/user/path"
}
// This approach gets sloppy pretty quickly
}
注意:如果你没有很多属性或函数来切换 case,那么 enum 不会比结构体有很多优势。它只是一种具有不同语法糖的替代方法。
枚举可以最大化状态和代码重用。关联的值还允许您执行一些非常强大的操作,例如对有些相似但具有令人难以置信的不同要求的对象进行分组……例如 NSURLRequest 创建。
为枚举案例构造参数以提高可读性的正确方法是什么? (不得不把这个混在一起)
这是一个了不起的问题。您已经列出了两种可能的选择。让我添加第三个可能会更好地满足您的需求。
case CreateUser(username: String, firstName: String, lastName: String, email: String)
case ReadUser(username: String)
case UpdateUser(username: String, firstName: String, lastName: String, email: String)
case DestroyUser(username: String)
如果您有关联的值,我认为为元组中的所有值添加显式名称会很有帮助。这确实有助于构建上下文。缺点是您必须像这样在 switch 语句中重新声明这些值。
static var method: String {
switch self {
case let CreateUser(username: username, firstName: firstName, lastName: lastName, email: email):
return "POST"
default:
return "GET"
}
}
虽然这为您提供了一个很好、一致的上下文,但它变得非常冗长。这些是您目前在 Swift 中的三个选项,哪个是正确的使用取决于您的用例。
更新
随着 ?? Alamofire 4.0 ?? 的发布,URLRequestConvertible 现在可以变得更加智能并且还可以投掷。我们在 Alamofire 中添加了对处理无效请求和通过响应处理程序生成合理错误的全面支持。这个新系统在我们的README中有详细记录。