Подтвердить что ты не робот

Образец Singleton и правильное использование Alamofire URLRequestConvertible

Это вопрос из двух частей: первый похож на этот вопрос: Правильное использование URL-адреса Alamofire URLRequestConvertible. Но мне нужна дополнительная помощь!

1) Создаю ли я маршрутизатор enum, который реализует 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
        }
    }
}

Когда я смотрю на это (я новичок в быстрой, поэтому, пожалуйста, несите меня > _ <) Я вижу операции над пользовательским объектом; они создают пользователя, обновляют пользователя и т.д. Итак, если бы у меня были лица модели, компания, местоположение на моем уровне модели, я бы создал маршрутизатор для каждого объекта модели?

2) При интенсивном взаимодействии с API я привык создавать одноэлементный "сетевой менеджер", чтобы абстрагироваться от сетевого уровня и хранить заголовки и baseurl для этого API. Здесь описывается "Менеджер":

Удобные методы верхнего уровня, такие как Alamofire.request, используют общий экземпляр Alamofire.Manager, который настроен по умолчанию NSURLSessionConfiguration. Таким образом, следующие два утверждения эквивалентны:

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

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

Этот менеджер, что я должен использовать как мой синглтон? Если да, то каким образом я устанавливаю baseurl в менеджере? Кроме того, если я использую этот менеджер, он/она может работать вместе с конструкцией маршрутизатора, показанной выше (с каждым объектом модели, устанавливающим его baseurl и NSURLRquest)? Если это так, вы можете привести простой пример?

Я новичок в библиотеке Alamofire и быстро. Итак, я знаю, что в моем понимании много дыр, но я просто пытаюсь понять лучшее, что могу! Любая информация помогает. Спасибо.

4b9b3361

Ответ 1

Это очень хорошие вопросы. Позвольте мне попытаться ответить на каждый по очереди.

Я создаю маршрутизатор enum, который реализует URLRequestConvertible для каждой модели в моем уровне модели?

Это большой вопрос, и, к сожалению, нет однозначного ответа. Конечно, есть несколько способов расширить шаблон 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. Если они не могут, этот проект может не сработать. Последняя проблема заключается в том, что вы не можете хранить baseURL или OAuthToken непосредственно внутри перечисления Router. К сожалению, статические и хранимые свойства еще не поддерживаются в общих перечислениях.

Независимо от того, это, безусловно, было бы правильным способом избежать создания Router для каждого объекта модели.

Должен ли Alamofire.Manager.sharedInstance использоваться как мой одиночный экземпляр NetworkManager?

Это, безусловно, можно было бы использовать таким образом. Это действительно зависит от вашего варианта использования и того, как вы разработали свой сетевой доступ. Это также зависит от того, сколько разных типов сеансов вам нужно. Если вам нужны фоновые сеансы и сеансы по умолчанию, вам, вероятно, по-прежнему нужна концепция NetworkManager, которая содержит каждый пользовательский экземпляр Manager. Однако, если вы просто нажмете на сеть с сеансом по умолчанию, то, вероятно, будет sharedInstance.

Как можно использовать baseURL одиночного синтаксиса Alamofire в сочетании с шаблоном Router?

Хороший вопрос... приведенный ниже код является одним из примеров того, как это можно сделать.

Расширение Alamofire Manager

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

Router URLRequestConvertible Updates

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
    }
}

Надеюсь, это поможет пролить свет. Удачи!