单例模式和正确使用 Alamofire 的 URLRequestConvertible

Singleton pattern and proper use of Alamofire's URLRequestConvertible

这是一个由两部分组成的问题,第一部分类似于此处的这个问题:。但我需要更多帮助!

1) 我是否创建一个枚举路由器来为我的模型层中的每个模型实现 URLRequestConvertible?

alamofire github 页面提供了我在此处复制的路由器示例:

  enum Router: URLRequestConvertible {
    static let baseURLString = "http://example.com"
    static var OAuthToken: String?

    case CreateUser([String: AnyObject])
    case ReadUser(String)
    case UpdateUser(String, [String: AnyObject])
    case DestroyUser(String)

    var method: Alamofire.Method {
        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)"
        }
    }

    // MARK: URLRequestConvertible

    var URLRequest: NSURLRequest {
        let URL = NSURL(string: Router.baseURLString)!
        let mutableURLRequest = NSMutableURLRequest(URL: URL.URLByAppendingPathComponent(path))
        mutableURLRequest.HTTPMethod = method.rawValue

        if let token = Router.OAuthToken {
            mutableURLRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
        }

        switch self {
        case .CreateUser(let parameters):
            return Alamofire.ParameterEncoding.JSON.encode(mutableURLRequest, parameters: parameters).0
        case .UpdateUser(_, let parameters):
            return Alamofire.ParameterEncoding.URL.encode(mutableURLRequest, parameters: parameters).0
        default:
            return mutableURLRequest
        }
    }
}

当我看到这个时(我是 swift 的新手所以请耐心等待 >_<)我看到了对用户 object 的操作;他们正在创建一个用户,更新一个用户等等...所以,如果我在我的模型层中有模型 object 的人、公司、位置,我会为每个模型 object 创建一个路由器吗?

2) 当与 API 大量交互时,我习惯于创建一个 "network manager" 单例来抽象出网络层并保存 headers 和它的 baseurl API。 alamofire 有一个 "Manager" 描述在这里:

Top-level convenience methods like Alamofire.request use a shared instance of Alamofire.Manager, which is configured with the default NSURLSessionConfiguration. As such, the following two statements are equivalent:

Alamofire.request(.GET, "http://httpbin.org/get")

let manager = Alamofire.Manager.sharedInstance
manager.request(NSURLRequest(URL: NSURL(string: "http://httpbin.org/get")))

这个管理器是我应该用作我的单身人士吗?如果是这样,我如何在经理上设置 baseurl?另外,如果我使用这个管理器,它是否/可以与上面显示的路由器构造一起工作(每个模型 object 设置它的 baseurl 和 NSURLRquest)?如果可以,您能提供一个简单的例子吗?

我是 Alamofire 库的新手 swift。所以,我知道我的理解有很多漏洞,但我只是想尽我所能去理解!任何信息都有帮助。谢谢

这些都是一些非常好的问题。让我尝试依次回答每个问题。

Do I create an enum router which implements URLRequestConvertible for each model in my model layer?

这是一个很好的问题,遗憾的是没有一个完美的答案。当然有一些方法可以扩展 Router 模式以适应多种对象类型。第一个选项是添加更多案例以支持另一种对象类型。但是,当您收到超过 6 或 7 个案例时,这很快就会变得棘手。您的 switch 语句刚刚开始失控。因此,我不推荐这种方法。

解决该问题的另一种方法是将泛型引入 Router

RouterObject 协议

protocol RouterObject {
    func createObjectPath() -> String
    func readObjectPath(identifier: String) -> String
    func updateObjectPath(identifier: String) -> String
    func destroyObjectPath(identifier: String) -> String
}

模型对象

struct User: RouterObject {
    let rootPath = "/users"

    func createObjectPath() -> String { return rootPath }
    func readObjectPath(identifier: String) -> String { return "\(rootPath)/\(identifier)" }
    func updateObjectPath(identifier: String) -> String { return "\(rootPath)/\(identifier)" }
    func destroyObjectPath(identifier: String) -> String { return "\(rootPath)/\(identifier)" }
}

struct Company: RouterObject {
    let rootPath = "/companies"

    func createObjectPath() -> String { return rootPath }
    func readObjectPath(identifier: String) -> String { return "\(rootPath)/\(identifier)" }
    func updateObjectPath(identifier: String) -> String { return "\(rootPath)/\(identifier)" }
    func destroyObjectPath(identifier: String) -> String { return "\(rootPath)/\(identifier)" }
}

struct Location: RouterObject {
    let rootPath = "/locations"

    func createObjectPath() -> String { return rootPath }
    func readObjectPath(identifier: String) -> String { return "\(rootPath)/\(identifier)" }
    func updateObjectPath(identifier: String) -> String { return "\(rootPath)/\(identifier)" }
    func destroyObjectPath(identifier: String) -> String { return "\(rootPath)/\(identifier)" }
}

路由器

let baseURLString = "http://example.com"
var OAuthToken: String?

enum Router<T where T: RouterObject>: URLRequestConvertible {
    case CreateObject(T, [String: AnyObject])
    case ReadObject(T, String)
    case UpdateObject(T, String, [String: AnyObject])
    case DestroyObject(T, String)

    var method: Alamofire.Method {
        switch self {
        case .CreateObject:
            return .POST
        case .ReadObject:
            return .GET
        case .UpdateObject:
            return .PUT
        case .DestroyObject:
            return .DELETE
        }
    }

    var path: String {
        switch self {
        case .CreateObject(let object, _):
            return object.createObjectPath()
        case .ReadObject(let object, let identifier):
            return object.readObjectPath(identifier)
        case .UpdateObject(let object, let identifier, _):
            return object.updateObjectPath(identifier)
        case .DestroyObject(let object, let identifier):
            return object.destroyObjectPath(identifier)
        }
    }

    // MARK: URLRequestConvertible

    var URLRequest: NSMutableURLRequest {
        let URL = NSURL(string: baseURLString)!
        let mutableURLRequest = NSMutableURLRequest(URL: URL.URLByAppendingPathComponent(path))
        mutableURLRequest.HTTPMethod = method.rawValue

        if let token = OAuthToken {
            mutableURLRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
        }

        switch self {
        case .CreateObject(_, let parameters):
            return Alamofire.ParameterEncoding.JSON.encode(mutableURLRequest, parameters: parameters).0
        case .UpdateObject(_, _, let parameters):
            return Alamofire.ParameterEncoding.URL.encode(mutableURLRequest, parameters: parameters).0
        default:
            return mutableURLRequest
        }
    }
}

示例用法

func exampleUsage() {
    let URLRequest = Router.CreateObject(Location(), ["address": "1234 Road of Awesomeness"]).URLRequest
    Alamofire.request(URLRequest)
        .response { request, response, data, error in
            print(request)
            print(response)
            print(data)
            print(error)
    }
}

现在您必须在这里做出一些权衡。首先,您的模型对象需要符合 RouterObject 协议。否则 Router 不知道该路径使用什么。此外,您需要确保所有路径都可以使用单个 identifier 构建。如果不能,则此设计可能行不通。最后一个问题是您不能将 baseURLOAuthToken 直接存储在 Router 枚举中。不幸的是,通用枚举尚不支持静态和存储属性。

无论如何,这肯定是避免为每个模型对象创建 Router 的有效方法。

Should the Alamofire.Manager.sharedInstance be used as my singleton NetworkManager instance?

当然可以这样使用。这实际上取决于您的用例以及您如何设计网络访问。它还取决于您需要多少不同类型的会话。如果您需要后台会话和默认会话,那么您可能仍然需要包含每个自定义 Manager 实例的 NetworkManager 的概念。但是,如果您只是使用默认会话访问网络,那么 sharedInstance 可能就足够了。

How could the baseURL of the Alamofire singleton be used in conjunction with the Router pattern?

好问题...下面的代码是如何完成的一个示例。

Alamofire 管理器扩展

extension Manager {
    static let baseURLString = "http://example.com"
    static var OAuthToken: String?
}

路由器 URLRequestConvertible 更新

var URLRequest: NSMutableURLRequest {
    let URL = NSURL(string: Alamofire.Manager.baseURLString)!
    let mutableURLRequest = NSMutableURLRequest(URL: URL.URLByAppendingPathComponent(path))
    mutableURLRequest.HTTPMethod = method.rawValue

    if let token = Alamofire.Manager.OAuthToken {
        mutableURLRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
    }

    switch self {
    case .CreateObject(_, let parameters):
        return Alamofire.ParameterEncoding.JSON.encode(mutableURLRequest, parameters: parameters).0
    case .UpdateObject(_, _, let parameters):
        return Alamofire.ParameterEncoding.URL.encode(mutableURLRequest, parameters: parameters).0
    default:
        return mutableURLRequest
    }
}

希望这有助于阐明一些问题。祝你好运!