Reputation: 3676
Before you tell me to use Decodable
for all the models, and you would be right, I am trying to fix a project in the time the client has allocated. Changing all models to Decodable
, and all the places where the models get used, would take a number of days.
The project intercepts calls, turns them to SwiftyJSON
and returns custom error messages from the API.
The issue is that Alamofire 5 doesn't accept a closure as a DataRequest
anymore. So I need another way of intercepting the response, checking for these custom error messages, and then returning SwiftyJSON
to the caller. All models have an init that creates a SwiftyJSON
model.
What models look like - simplified:
final public class SomeImage: ResponseCollectionSerializable {
public static func collection(json: JSON) -> [SomeImage] {
var images = [SomeImage]()
for (_, subJson):(String, JSON) in json["children"] {
if let image = SomeImage(json: subJson) {
images.append(image)
}
}
return images
}
init?(json: JSON) {
guard let id = json["attributes"]["id"].string, let name = json["name"].string else {
return nil
}
imageURL = json["attributes"]["imageURL"].string
super.init(withId: id, name: name)
}
// Attributes
let imageURL: String?
}
Protocol on collection models:
public protocol ResponseCollectionSerializable {
static func collection(json: JSON) -> [Self]
}
Problem area:
extension Alamofire.DataRequest {
public func responseCollection<T: ResponseCollectionSerializable>(completionHandler: @escaping (DataResponse<[T], AFError>) -> Void) -> Self {
let responseSerializer = DataResponseSerializer<[T]> { request, response, data, error in
// error: Trailing closure passed to parameter of type 'DataPreprocessor' that does not accept a closure
guard error == nil else {
return .failure(error!)
}
let JSONSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments)
let result = JSONSerializer.serializeResponse(request, response, data, error)
switch result {
case .success(let value):
if response != nil {
let json = JSON(value)
return .success(T.collection(json: json))
} else {
let failureReason = "Response collection could not be serialized"
let userInfo = [NSLocalizedFailureReasonErrorKey: failureReason]
let error = NSError(domain: Bundle.main.bundleIdentifier!, code: MyErrorCode.JSONSerializationFailed.rawValue, userInfo: userInfo)
return .failure(error) // custom Error
}
case .failure(let error):
return .failure(error) // AFError
}
return response(responseSerializer: responseSerializer, completionHandler: completionHandler)
}
}
In simple terms, my question is how in Alamofire 5 can I run this intercepting code on all calls and return SwiftyJSON?
Upvotes: 0
Views: 1005
Reputation: 12770
If you want to create a custom response serializer you need to conform to DataResponseSerializerProtocol
(or ResponseSerializer
if you support downloads as well). Here's an example from our documentation:
struct CommaDelimitedSerializer: ResponseSerializer {
func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> [String] {
// Call the existing StringResponseSerializer to get many behaviors automatically.
let string = try StringResponseSerializer().serialize(request: request,
response: response,
data: data,
error: error)
return Array(string.split(separator: ","))
}
}
Then you would use it with response(using:)
or create your own extension to DataReqeuest
to create the responseCollection
method you had before.
Before doing any of this I would instead switch to using Decodable
and our responseDecodable
method. There's no reason to write any of this code yourself just for JSON parsing.
Upvotes: 1