Last active
August 12, 2018 18:56
-
-
Save ncreated/4ad3bb8a7a5d6d536708 to your computer and use it in GitHub Desktop.
A generic approach to parse XML REST API with `Alamofire` and `SWXMLHash`. Still in progress...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// APIService+Alamofire.swift | |
// | |
// Created by Maciek on 07.10.2015. | |
// Copyright © 2015 Code Latte. All rights reserved. | |
// | |
import SWXMLHash | |
import Alamofire | |
enum ModelError: ErrorType { | |
case BadXML(XMLIndexer) | |
} | |
public protocol XMLSerializable { | |
init(node: XMLIndexer) throws | |
} | |
extension Alamofire.Request { | |
public func responseObjectForKeyPath<T: XMLSerializable>(keyPath: String, completionHandler: (NSURLRequest?, NSHTTPURLResponse?, Result<T>) -> ()) -> Self { | |
let responseSerializer = GenericResponseSerializer<T> { request, response, data in | |
guard let data = data else { | |
let error = Error.errorWithCode(.DataSerializationFailed, failureReason: "Received empty response") | |
return .Failure(nil, error) | |
} | |
do { | |
let xml = SWXMLHash.parse(data) | |
let modelXML = try xml.byKeyPath(keyPath) | |
let model = try T(node: modelXML) | |
return .Success(model) | |
} catch let error { | |
return .Failure(data, error) | |
} | |
} | |
return response(responseSerializer: responseSerializer, completionHandler: completionHandler) | |
} | |
public func responseArrayForKeyPath<T: XMLSerializable>(keyPath: String, completionHandler: (NSURLRequest?, NSHTTPURLResponse?, Result<[T]>) -> ()) -> Self { | |
let responseSerializer = GenericResponseSerializer<[T]> { request, response, data in | |
guard let data = data else { | |
let error = Error.errorWithCode(.DataSerializationFailed, failureReason: "Received empty response") | |
return .Failure(nil, error) | |
} | |
do { | |
var models = [T]() | |
let xml = SWXMLHash.parse(data) | |
let collection = try xml.byKeyPath(keyPath) | |
// TODO: use .map | |
for modelXML in collection { | |
models.append(try T(node: modelXML)) | |
} | |
return .Success(models) | |
} catch let error { | |
return .Failure(data, error) | |
} | |
} | |
return response(responseSerializer: responseSerializer, completionHandler: completionHandler) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// APIService.swift | |
// | |
// Created by Maciek on 05.09.2015. | |
// Copyright © 2015 Code Latte. All rights reserved. | |
// | |
import Foundation | |
import SWXMLHash | |
import Alamofire | |
enum APIEndpoint { | |
case Museums | |
case Museum(id: String) | |
case Exhibitions(museumID: String) | |
case Exhibition(museumID: String, uri: String) | |
private var url: String { | |
return "http://<domain-here>\(relativeURLString)" | |
} | |
private var relativeURLString: String { | |
switch self { | |
case .Museums: return "/api/museum/pl.xml" | |
case .Museum(let id): return "/api/museum/\(id)/pl.xml" | |
case .Exhibitions(let museumID): return "/api/museum/\(museumID)/exhibition/pl.xml" | |
case .Exhibition(let museumID, let uri): return "/api/museum/\(museumID)/exhibition/\(uri)/pl.xml" | |
} | |
} | |
var request: Alamofire.Request { | |
switch self { | |
case .Museums: return Alamofire.request(.GET, url) | |
case .Museum: return Alamofire.request(.GET, url) | |
case .Exhibitions: return Alamofire.request(.GET, url) | |
case .Exhibition: return Alamofire.request(.GET, url) | |
} | |
} | |
var responseNodePath: String { | |
switch self { | |
case .Museums: return "collection.museum" | |
case .Museum: return "museum" | |
case .Exhibitions: return "collection.exhibition" | |
case .Exhibition: return "exhibition" | |
} | |
} | |
} | |
final class APIService { | |
// MARK: - API fetching | |
func fetchObjectFromEndpoint<T: XMLSerializable>(endpoint: APIEndpoint, callback: (Result<T>) -> ()) { | |
endpoint.request.validate().responseObjectForKeyPath(endpoint.responseNodePath) { (_, _, result: Result<T>) in | |
callback(result) | |
} | |
} | |
func fetchCollectionFromEndpoint<T: XMLSerializable>(endpoint: APIEndpoint, callback: (Result<[T]>) -> ()) { | |
endpoint.request.validate().responseArrayForKeyPath(endpoint.responseNodePath) { (_, _, result: Result<[T]>) in | |
callback(result) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Exhibition.swift | |
// | |
// Created by Maciek on 11.10.2015. | |
// Copyright © 2015 Code Latte. All rights reserved. | |
// | |
import Foundation | |
import SWXMLHash | |
struct Exhibition: XMLSerializable { | |
let uri: String | |
let name: String | |
let description: String? | |
init(node: XMLIndexer) throws { | |
guard let name = node["name"].element?.text, | |
uri = node["uri"].element?.text else { | |
throw ModelError.BadXML(node) | |
} | |
self.name = name | |
self.uri = uri | |
self.description = node["description"].element?.text | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Museum.swift | |
// | |
// Created by Maciek on 06.09.2015. | |
// Copyright © 2015 Code Latte. All rights reserved. | |
// | |
import Foundation | |
import CoreLocation | |
import SWXMLHash | |
struct Museum: XMLSerializable { | |
let beaconUUID: String | |
let name: String | |
let description: String? | |
let iconResourceURI: String? | |
let location: CLLocation? | |
init(node: XMLIndexer) throws { | |
guard let name = node["name"].element?.text, | |
beaconUUID = node["beaconUUID"].element?.text else { | |
throw ModelError.BadXML(node) | |
} | |
self.name = name | |
self.beaconUUID = beaconUUID | |
self.description = node["description"].element?.text | |
self.iconResourceURI = node["self.iconResourceURI"].element?.text | |
var location: CLLocation? = nil | |
if let latitudeString = node["latitude"].element?.text, | |
longitudeString = node["longitude"].element?.text { | |
let latitude = NSString(string: latitudeString).doubleValue | |
let longitude = NSString(string: longitudeString).doubleValue | |
location = CLLocation(latitude: latitude, longitude: longitude) | |
} | |
self.location = location | |
} | |
} |
Author
ncreated
commented
Oct 27, 2015
This code isn't compatible with Alamofire 3.0 isn't it?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment