Кодирование кодируемой структуры в POST-параметры Alamofire - Swift

Я пытаюсь подключить мое внешнее iOS-приложение (Swift) к бэкенду Django.

Я пытаюсь создать класс маршрутизатора, который обрабатывает любые запросы.

У меня есть такая структура:

struct APIAccountID: Codable {
    
    let username: String
    let password: String
}

Который можно закодировать.

Я пытаюсь передать данные этой структуры в POST-запросе Alamofire.

Итак, я пытаюсь получить кодировку объекта следующим образом:

var parameters: [String: AnyObject]? {
    switch self {
    case .fetchAccessToken(let accountID):
        if let data = try? JSONEncoder().encode(accountID) {
            return data
        } else {
            print("Error: encoding post data")
            return nil
        }
    default: break
    }
}

Который не работает, потому что Any или AnyObject не кодируются при использовании Alamofire URLEncodedFormParameterEncoder или JSONParameterEncoder.

Если я использую этот переключатель параметров:

var parameters: Data? {
        switch self {
        case .fetchAccessToken(let accountID):
            if let data = try? JSONEncoder().encode(accountID) {
                return data
            } else {
                print("[fetchAccessToken] Error: encoding post data")
            }
        default:
            break
        }
        return nil
    }

Я получаю эту ошибку, когда отправляю запрос:

{
    "non_field_errors" =     (
        "Invalid data. Expected a dictionary, but got str."
    );
}

AlamoFire URLFormEncodedParameterEncoder и JSONParameterEncoder могут принимать все, что соответствует Encodable, поэтому вам не нужно сначала использовать JSONEncoder; они позаботятся об этом.

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

Вы не можете сказать

var parameters: APIAccountID

потому что это не сработает для случая, когда параметры - это updatePortfolio или что-то еще.

И вы не можете просто сказать var parameters: Encodable?, потому что вам нужен конкретный тип.

Вам необходимо использовать стирание типов. Это техника, которая используется в Swift для того, чтобы позволить использовать в качестве параметра несвязанные типы. Существуют различные подходы, но общим является "боксирование", когда фактический тип хранится как свойство, и когда вы ссылаетесь на стертый тип, вы перенаправляете эту ссылку или вызов функции в боксированный или обернутый тип.

Итак, в этом случае вам нужна AnyEncodable - реализация стирания типа Encodable. К счастью, именно такая вещь доступна в репозитории FlightSchool - https://github.com/Flight-School/AnyCodable

После добавления этого пакета в ваш проект вы можете изменить parameters на

var parameters: AnyEncodable? {
    switch self {
        case .fetchAccessToken(let accountID):
            return AnyEncodable(accountID)
        case .updatePortfolio(let portfolio):
            return AnyEncodable(portfolio)
    }
}

Теперь наша переменная parameters имеет фиксированный тип AnyEncodable?, который соответствует Encodable, так что все довольны. Реализация требует, чтобы вы инстанцировали экземпляр AnyEncodable с вашим реальным Encodable

Update

Оказывается, что AnyEncodable на самом деле не может закодировать ни один кодируемый код! Я отправил PR, чтобы исправить это; Вот мой форк с этим изменением: https://github.com/paulw11/AnyCodable

Вернуться на верх