Skip to content

Commit

Permalink
feat: Use EncodableDictionary for request params
Browse files Browse the repository at this point in the history
  • Loading branch information
PhilippeWeidmann committed Jan 30, 2025
1 parent 60156ce commit a791611
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import Alamofire
import Foundation

/// Wrapping HTTP POST and GET parameters in this type
public typealias Parameters = [String: any Any & Sendable]
public typealias Parameters = [String: any Encodable & Sendable]

/// Wrapping the body of an HTTP Request with common types
public enum RequestBody {
Expand Down Expand Up @@ -67,7 +67,7 @@ struct BodyDataEncoding: ParameterEncoding {
}

func encode(_ urlRequest: URLRequestConvertible,
with parameters: Parameters?) throws -> URLRequest {
with parameters: Alamofire.Parameters?) throws -> URLRequest {
var request = try urlRequest.asURLRequest()
request.httpBody = data
return request
Expand Down
6 changes: 2 additions & 4 deletions Sources/InfomaniakCore/Networking/ApiFetcher+Dispatch.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,7 @@ extension ApiFetcher: RequestDispatchable {
let method = requestable.method.alamofireMethod
switch body {
case .POSTParameters(let parameters):
request = try authenticatedRequest(endpoint,
method: method,
parameters: parameters)
request = authenticatedRequest(endpoint, method: method, parameters: parameters)
case .requestBody(let data):
let headers: HTTPHeaders = [Self.contentType: Self.octetStream]
request = authenticatedSession.request(endpoint.url,
Expand All @@ -65,7 +63,7 @@ extension ApiFetcher: RequestDispatchable {
encoding: BodyDataEncoding(data: data),
headers: headers)
case .none:
request = try authenticatedRequest(endpoint,
request = authenticatedRequest(endpoint,
method: method,
parameters: nil)
}
Expand Down
33 changes: 25 additions & 8 deletions Sources/InfomaniakCore/Networking/ApiFetcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ open class ApiFetcher {
public var authenticatedSession: Session!

private let decoder: JSONDecoder
private let bodyEncoder: any ParameterEncoder
private let bodyEncoder: JSONEncoder

private let jsonParameterEncoder: JSONParameterEncoder

public var currentToken: ApiToken? {
get {
Expand All @@ -62,7 +64,7 @@ open class ApiFetcher {

public init(
decoder: JSONDecoder? = nil,
bodyEncoder: any ParameterEncoder = JSONParameterEncoder.convertToSnakeCase
bodyEncoder: JSONEncoder? = nil
) {
if let decoder {
self.decoder = decoder
Expand All @@ -72,7 +74,19 @@ open class ApiFetcher {
defaultDecoder.keyDecodingStrategy = .convertFromSnakeCase
self.decoder = defaultDecoder
}
self.bodyEncoder = bodyEncoder
if let bodyEncoder {
self.bodyEncoder = bodyEncoder
jsonParameterEncoder = JSONParameterEncoder(encoder: bodyEncoder)
} else {
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
encoder.dateEncodingStrategy = .custom { date, encoder in
var container = encoder.singleValueContainer()
try container.encode(Int(date.timeIntervalSince1970))
}
self.bodyEncoder = encoder
jsonParameterEncoder = JSONParameterEncoder(encoder: encoder)
}
}

/// Creates a new authenticated session for the given token.
Expand Down Expand Up @@ -121,12 +135,15 @@ open class ApiFetcher {

public func authenticatedRequest(_ endpoint: Endpoint,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
parameters: [String: Encodable]? = nil,
overrideEncoder: ParameterEncoder? = nil,
headers: HTTPHeaders? = nil,
requestModifier: RequestModifier? = nil) throws -> DataRequest {
guard let encodableParameters = parameters as? Encodable else {
throw ErrorDomain.invalidJsonInBody
requestModifier: RequestModifier? = nil) -> DataRequest {
let encodableParameters: EncodableDictionary?
if let parameters {
encodableParameters = EncodableDictionary(parameters)
} else {
encodableParameters = nil
}

return authenticatedRequest(
Expand All @@ -145,7 +162,7 @@ open class ApiFetcher {
overrideEncoder: ParameterEncoder? = nil,
headers: HTTPHeaders? = nil,
requestModifier: RequestModifier? = nil) -> DataRequest {
let encoder = overrideEncoder ?? bodyEncoder
let encoder = overrideEncoder ?? jsonParameterEncoder
return authenticatedSession
.request(
endpoint.url,
Expand Down
47 changes: 47 additions & 0 deletions Sources/InfomaniakCore/Networking/EncodableDictionary.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
Infomaniak Core - iOS
Copyright (C) 2025 Infomaniak Network SA

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import Foundation

struct EncodableDictionary: Encodable {
private let dictionary: [String: Encodable]

init(_ dictionary: [String: Encodable]) {
self.dictionary = dictionary
}

func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
for (key, value) in dictionary {
try container.encode(value, forKey: CodingKeys(stringValue: key))
}
}

private struct CodingKeys: CodingKey {
var stringValue: String
var intValue: Int? { return nil }

init(stringValue: String) {
self.stringValue = stringValue
}

init?(intValue: Int) {
return nil
}
}
}

0 comments on commit a791611

Please sign in to comment.