Кодирование кодируемой структуры в 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