Вызов API в Swift | Кодементор

В настоящее время 99% приложений должны подключаться к серверу через API. Так что дело с нетворкингом весьма важно. Как нам с легкостью управлять вызовом API?
Я уверен, ваши ответы Alamofire. Ты прав. Alamofire настолько хорош, что это обязательная библиотека в Swift.
Alamofire, URLSession или любые другие библиотеки являются коннекторами к серверу. Но как их эффективно использовать — это отдельная история. Я говорю вам сегодня.

Это статическая структура, которую вы используете каждый день для взаимодействия с сервером. Взгляните на его содержание.

static private func getHeaders() -> [String: String]? {
    return [
        "Content-Type": "application/json",
        "access_token":  "your access token or api key"
    ]
}

Большинству запросов нужны заголовки, где вам нужно передать access_token или же api_key для авторизации ваших запросов. getHeaders сделает это. Добавьте сюда свои факторы авторизации, и они будут привязаны к вашим запросам, больше не нужно заботиться.

Что произойдет, если вы захотите настроить заголовок? Потом.

2. URL API

static private func getUrl(from api: String) -> URL? {
    let BASE_URL = ""
    let apiUrl = api.contains("http") ? api : BASE_URL + api
    return URL(string: apiUrl)
}

Вы не хотите повторять везде вы называете API, верно?
Положить ваши BASE_URL внутри getUrlтолько добавить /users/:id или же /transactions. Не повторяйтесь.

Нет "/" в конце BASE_URLи всегда запускайте API с "/"

Если вы хотите подключить другой BASE_URL, вы можете передать полный URL-адрес API, например https://custom_website.com/api/v2/users. Помните, что URL-адрес API должен начинаться с http или же https.

3. Запрос

Самая важная часть здесь.

  static func request(_ api: String,
                      method: HTTPMethod,
                      params: [String: Any]? = nil,
                      headers: [String: String]? = nil,
                      successJsonAction: ((_ result: AnyObject) -> Void)? = nil,
                      successDataAction: ((Data) -> Void)? = nil,
                      failAction: ((_ error: knError) -> Void)? = nil) {
    let finalHeaders = headers ?? getHeaders()
    let apiUrl = getUrl(from: api)
    connector.newRequest(withApi: apiUrl,
                         method: method,
                         params: params,
                         headers: finalHeaders,
                         successJsonAction: successJsonAction,
                         successDataAction: successDataAction,
                         failAction: failAction)
  }

Смотрим параметры.

  • api: Передайте здесь свой API, принимается только API или полный URL-адрес API. Я говорил вам во 2 части.

  • method: HTTPMethod: .get, .post, .put, .delete…

  • params: по умолчанию ноль. Вы можете игнорировать его, если параметры не нужно отправлять. В демо я его игнорирую.

  • headers: здесь вы можете добавить собственные заголовки. Если он равен нулю, будут присоединены заголовки по умолчанию.

  • successJsonAction, successDataAction: я поддерживаю 2 способа обработки ответа: вручную анализировать JSON Object и JSONDecoder.
    Мне нравится анализировать JSON вручную, я могу обрабатывать сложные объекты, объекты с несколькими вложенными уровнями, добавлять пользовательские свойства, которые я хочу.
    Но не могу этого отрицать, JSONDecoder действительно мощный. Простые объекты или результаты, которые вам нужны, все свойства, JSONDecoder будет лучшим выбором.

  • failAction: очевидно, вам нужно обработать ваш запрос в случае сбоя.

4. Разъем

static private var connector = AlamofireConnector()

Да, ты прав. Я поставил коннектор в качестве промежуточного программного обеспечения к библиотеке. Я полагаю, однажды Мэтт уберет Alamofire, и мы просто добавляем сюда другой коннектор. Это не повлияет на другой код.

Теперь мы используем Alamofire или любые библиотеки для подключения к нашему серверу.
В любом коннекторе есть 2 функции.

1. Беги

func run(withApi api: URL?,
          method: HTTPMethod,
          params: [String: Any]? = nil,
          headers: [String: String]? = nil,
          successJsonAction: ((_ result: AnyObject) -> Void)? = nil,
          successDataAction: ((Data) -> Void)? = nil,
          failAction: ((_ error: knError) -> Void)?) {
  guard let api = api else {
        failAction?(InvalidAPIError(api: "nil"))
        return
    }
    let encoding: ParameterEncoding = method == .get ?
        URLEncoding.queryString :
        JSONEncoding.default
    Alamofire.request(api, method: method,
                      parameters: params,
                      encoding: encoding,
                      headers: headers)
        .responseJSON { (returnData) in
            self.answer(response: returnData,
                             successJsonAction: successJsonAction,
                             successDataAction: successDataAction,
                             failAction: failAction)
    }
}

Вам необходимо обновить кодировку здесь, если ваш сервер принимает другую кодировку. За 6 лет работы с iOS мне только один раз пришлось его менять.

2. Ответ

private func answer(response: DataResponse<Any>,
                    successJsonAction: ((_ result: AnyObject) -> Void)? = nil,
                    successDataAction: ((Data) -> Void)? = nil,
                    failAction fail: ((_ error: knError) -> Void)?) {
    if let statusCode = response.response?.statusCode {
        if statusCode == 500 {
            return
        }
        // handle status code here: 401 -> show logout; 500 -> show error
    }

    if let error = response.result.error {
        let err = knError(code: "unknown", message: error.localizedDescription)
        fail?(err)
        return
    }

    guard let json = response.result.value as AnyObject?, let data = response.data else {
        // handle unknown error
        return
    }

    // handle special error convention from server
    // ...

    if let successDataAction = successDataAction {
        successDataAction(data)
        return
    }
    successJsonAction?(json)
}

Вам нужно обрабатывать ответы здесь. По проектам по разному. Если вы хотите отлаживать ответы, вам нужно поставить здесь точки останова.

Объект Error в ответ отсутствие информации. Поэтому я обычно создаю новый тип ошибки.

class knError {
    var code: String = "unknown"
    var message: String?
    var rawData: AnyObject?
    var displayMessage: String {
        return message ?? code
    }
    
    init() {}
    init(code: String, message: String? = nil, rawData: AnyObject? = nil) {
        self.code = code
        self.message = message
        self.rawData = rawData
    }
}

class InvalidAPIError: knError {
    private override init() {
        super.init()
    }
    
    private override convenience init(code: String, message: String? = nil, rawData: AnyObject? = nil) {
        self.init()
    }
    convenience init(api: String) {
        self.init()
        code = "404"
        message = "Invalid API Endpoint"
        rawData = api as AnyObject
    }
}

Можете добавить UnauthorizationError или же ForbiddenError отвечать вашим контроллерам. Это легче понять, чем ErrorПравильно?

Вот как я управляю нетворкингом в своих 10 проектах. Чувствуйте себя хорошо с этим и надеюсь, что это полезно для вас, ребята.
Пожалуйста, получите доступ к демо на моем https://github.com/nguyentruongky/CallingAPISwift.

Пожалуйста, не стесняйтесь делиться своими способами или комментариями здесь. Ждем ваших мнений.

Наслаждайтесь кодированием.

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *