Шаблон синглтона и правильное использование URL-запроса Alamofire

Решение Flexbox

Гибкая компоновка предпочтительна для естественной высоты заголовка и нижнего колонтитула. Это гибкое решение проверено в современных браузерах и фактически работает :) в IE11.

См. JS Fiddle .

HTML


  
...
...
...

CSS

html {
  height: 100%;
}

body {
  height: 100%;
  min-height: 100vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  margin: 0;
  display: flex;
  flex-direction: column;
}

main {
  flex-grow: 1;
  flex-shrink: 0;
}

header,
footer {
  flex: none;
}

17
задан Community 23 May 2017 в 12:10
поделиться

1 ответ

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

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

Это отличный вопрос, и, к сожалению, нет единого идеального ответа. Несомненно, есть несколько способов расширить шаблон Router для размещения нескольких типов объектов. Первый вариант - добавить больше дел для поддержки другого типа объекта. Тем не менее, это становится довольно быстро, когда вы получаете более 6 или 7 случаев. Ваши операторы switch просто начинают выходить из-под контроля. Поэтому я бы не рекомендовал такой подход.

Другим способом решения проблемы является введение дженериков в Router.

RouterObject Protocol

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

Обновления маршрутизатора 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
    }
}

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

32
ответ дан cnoon 23 May 2017 в 12:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: