iOS swift Alamofire parsing, creating generic class to parse json data - ios

I have this JSON data to parse :
{
"data": {
"user_name": "JohP llDoe",
"email_address": "tozto#gmail.com"
},
"result": {
"code": 0
}
}
The data type change from a web-service to another, but the general structure is always the same.
So I create this general Class using EVReflection to parse the Data :
class MyResponse<T>: EVObject {
var data : T?
var result : Result?
}
class Result: EVObject {
var message : String = ""
var code : Int = 0
}
and for the Data :
class Preaccount : EVObject {
public var user_name : String = ""
public var email_address : String = ""
}
And to call the web-service I created this method :
Alamofire.request(url, method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers)
.responseObject { (response: DataResponse<MyResponse<Preaccount>>) in
if response.result.isSuccess {
print(response.result.value!)
}
if response.result.isFailure {
let error : Error = response.result.error!
}
}
And now I'am having a bad access :
Is there a way to create a generic method and pass the parsed model in parameters.

you can try this
class MyResponse: EVObject {
var data : Preaccount?
var result : Result?
}
and to process response data
Alamofire.request(url, method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers)
.responseObject { (response: DataResponse) in
if response.result.isSuccess {
print(response.result.value!)
var myResponse = MyResponse(json:response.result.value!)
}
.......
}

Finally here is my solution for the problem :
struct MyResponse<T: Decodable>: Decodable {
var data : T?
var result : Result?
}
struct Result: Decodable {
let message : String?
let code : Int
let form_errors : [String: [String]]?
}
struct Preaccount : Decodable {
let user_name : String
let email_address : String
}
And to call the web service :
func postRequest<T: Decodable>(url: String,
params : [String: Any]?,
returnType : T.Type)
{
Alamofire.request(url, method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers)
.responseDecodableObject { (response: DataResponse<FindoorResponse<T>>) in
//...
}
}
And here is an example to call the method :
AlamofireHelper.shared().postRequest(url: CHECK_USERNAME_URL, params: parameters, returnType: Preaccount.self)

I'd recommend using Decodable instead:
struct MyResponse<T: Decodable>: Decodable {
let data: T?
let result: Result?
}
struct Result: Decodable {
var message: String?
var code: Int
}
struct Preaccount: Decodable {
let user_name: String
let email_address: String
}
let data = """
{
"data": {
"user_name": "JohP llDoe",
"email_address": "tozto#gmail.com"
},
"result": {
"code": 0
}
}
""".data(using: .utf8)!
let result = try? JSONDecoder().decode(MyResponse<Preaccount>.self, from: data)

Related

How to parse JSON dictionary using SwiftyJSON and Alamofire

I have a problem with my alamofire.request. I tried to to decode JSON response with Struct using SwiftyJSON.
. But my model data is getting nil.
here is my API response
{
"userDetails" :
{ "id":2,
"roleID":1,
"successFlag":1
},
"settingID" : "20"
}
Model class
import Foundation
import SwiftyJSON
struct User {
var settingID : String?
var userdetails : UserDetails?
init(json : JSON?) {
self.settingID = json?["settingID"].string
if let value = json?["userDetails"].dictionaryObject {
let new = UserDetails(json: JSON(value))
self.userdetails = new
}
}
}
struct UserDetails {
var id : Int?
var roleID : Int?
var successFlag : Int?
init(json : JSON?) {
self.id = json?["id"].int
self.roleID = json?["roleID"].int
self.successFlag = json?["successFlag"].int
}
}
My code for Data fetching using Alamofire and SwiftyJSON
import Alamofire
import SwiftyJSON
var userData : [User] = []
func fetchData() {
Alamofire.request(base_url + APIManager.loginApi, method: .post, parameters: params, encoding: URLEncoding.queryString, headers: nil).responseJSON { (resp) in
switch resp.result {
case .success :
print(resp.result)
do {
let myResult = try JSON(data: resp.data!)
print(myResult)
myResult.dictionaryValue.forEach({(user) in
let newUser = User(json: JSON(user))
self.userData.append(newUser)
print(self.userData)
})
}catch {
print(error)
}
break
case .failure :
break
}
}
}
But if i print self.userData , getting nill response.
Have you any idea why I can't decode my struct with my JSON data?.
Thanks a lot for your help
Try using Codable instead. It is easier to create a Codable model and is Apple recommended.
struct Root: Decodable {
let userDetails: User
let settingID: String
}
struct User: Decodable {
let id: Int
let roleID: Int
let successFlag: Int
}
Parse the data like,
do {
let response = try JSONDecoder().decode(Root.self, from: data)
print(response)
} catch {
print(error)
}
Change your response code like below
switch response.result {
case .success(let value):
let response = JSON(value)
print("Response JSON: \(response)")
let newUser = User(json: response)
self.userData.append(newUser)
print(self.userData)
case .failure(let error):
print(error)
break
}

Parameters can't be passed to a url using Alamofire

I have defined the parameters to pass to rapidAPI using Alamofire, but I've got an error.
I followed everything they said in the API documentation. If I put the full URL into a string it works fine, but when I pass as a parameter it doesn't
import UIKit
import Alamofire
import SwiftyJSON
class ViewController: UIViewController {
let CURRENT_BR_LEAGUE_URL = "https://api-football-v1.p.rapidapi.com/v2/leagues/country/"
override func viewDidLoad() {
super.viewDidLoad()
let params = ["country_name" : "england", "season" : "2018"]
getLeague(url: CURRENT_BR_LEAGUE_URL, parameters: params)
}
func getHeaders() -> [String : String] {
let headers = [
"X-RapidAPI-Host": "api-football-v1.p.rapidapi.com",
"X-RapidAPI-Key": "xxxxxxxxxxxxxxxxxxx"
]
return headers
}
func getLeague (url : String, parameters : [String : String]) {
Alamofire.request(url, method: .get, parameters: parameters, encoding: URLEncoding(destination: .queryString), headers: getHeaders()).responseJSON {
response in
if response.result.isSuccess {
let leagueJSON : JSON = JSON(response.result.value!)
print(leagueJSON)
print()
}
else {
}
}
}
}
It throws a "wrong country" error. If I use full URL
let CURRENT_BR_LEAGUE_URL = "https://api-football-v1.p.rapidapi.com/v2/leagues/country/england/2018" it works fine.
If I use "https://api-football-v1.p.rapidapi.com/v2/leagues/country/"
and set the parameters
let params = ["country_name" : "england", "season" : "2018"]
it does not work
In Your Code, you are making a get request. So you have to pass the params bind with your base URL. Please try the below code hopefully it will work for you.
class ViewController: UIViewController {
let CURRENT_BR_LEAGUE_URL = "https://api-football-v1.p.rapidapi.com/v2/leagues/country/%#/%#"
override func viewDidLoad() {
super.viewDidLoad()
let params = ["country_name" : "england", "season" : "2018"]
let urlString = String(format: CURRENT_BR_LEAGUE_URL, params["country_name"],params["season"])
getLeague(url: urlString, parameters: nil)
}
func getHeaders() -> [String : String] {
let headers = [
"X-RapidAPI-Host": "api-football-v1.p.rapidapi.com",
"X-RapidAPI-Key": "xxxxxxxxxxxxxxxxxxx"
]
return headers
}
func getLeague (url : String, parameters : [String : String]?) {
Alamofire.request(url, method: .get, parameters: parameters, encoding: URLEncoding(destination: .queryString), headers: getHeaders()).responseJSON {
response in
if response.result.isSuccess {
let leagueJSON : JSON = JSON(response.result.value!)
print(leagueJSON)
print()
}
else {
}
}
}
}
As I looked now into RapidApi documentation for API-Football, you don't need the parameters. You just need to construct the URL from different parts and then make a GET request.
class ViewController: UIViewController {
let MAIN_URL = "https://api-football-v1.p.rapidapi.com/v2/leagues/country/"
override func viewDidLoad() {
super.viewDidLoad()
getLeague(for: "england", year: 2018)
}
func getHeaders() -> [String : String] {
let headers = [
"X-RapidAPI-Host": "api-football-v1.p.rapidapi.com",
"X-RapidAPI-Key": "xxxxxxxxxxxxxxxxxxx"
]
return headers
}
func getLeague (for country : String, year: Int) {
let url = MAIN_URL + country + "/\(year)/"
Alamofire.request(url, method: .get, parameters: nil, encoding: URLEncoding.default, headers: getHeaders()).responseJSON {
response in
if response.result.isSuccess {
let leagueJSON : JSON = JSON(response.result.value!)
print(leagueJSON)
print()
}
else {
}
}
}
}

Getting JSON by using Alamofire and decode - Swift 4

I have an API and I also want to get request.
But I try to using JSONDecoder to convert data type and I failed.
I don't know how to decode this Json like following data.
I want to take json["response"] contents setting my User struct.
Have any suggestion to me?
Thanks.
Error Domain=NSCocoaErrorDomain Code=4865 "No value associated with key id ("id")." UserInfo={NSCodingPath=(
), NSDebugDescription=No value associated with key id ("id").}
This is JSON Data:
{
"status": "success",
"response": {
"id": "1130f1e48b608f79c5f350dd",
"name": "Jack",
},
"errors": ""
}
enum RestfulAPI:String {
case userInfo = "http://www.mocky.io/v2/5a796fb92e00002a009a5a49"
func get(parameters:[String : Any]? = nil,success:#escaping (_ response : Data)->(), failure : #escaping (_ error : Error)->()){
let url = self.rawValue
Alamofire.request(url, method: .get, parameters: parameters, encoding: JSONEncoding.default, headers: nil).responseJSON { (response) in
switch response.result {
case .success:
if let data = response.data {
success(data)
}
case .failure(let error):
failure(error)
}
}
}
}
struct User: Decodable {
let id: String
let name: String
}
usage:
RestfulAPI.userInfo.get(success: { data in
do {
let user = try JSONDecoder().decode(User.self, from: data)
print("==) ", user.id, user.name)
}catch let error as NSError{
print(error)
}
}) { (error) in
print(error)
}
The key id is not on the level where you expect it. That's what the error message states.
The keys id and name are in the (sub)dictionary for key response.
So you need an umbrella struct
struct Root : Decodable {
let status: String
let response: User
}
struct User: Decodable {
let id: String
let name: String
}
Then decode
let root = try JSONDecoder().decode(Root.self, from: data)
let user = root.response

Alamofire response object mapping

I am an android developer new to swift 3 programming, I am using Alamofire for making api calls and to avoid tedious json paring I am using AlamofireObjectMapper library.
I have a ApiController which has a function to make api calls below is the code for that:
public static func makePostRequest<T: Mappable>(url: String, params: Parameters, networkProtocol: NetworkProtocol, responseClass: T){
let headers = getHeaders()
networkProtocol.showProgress()
Alamofire.request(url, method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers)
.validate()
.responseData{ response in
let json = response.result.value
var jsonString = String(data: json!, encoding: String.Encoding.utf8)
let responseObject = responseClass(JSONString: jsonString!)
switch(response.result){
case .success(_):
networkProtocol.hideProgress()
networkProtocol.onResponse(response: response)
break
case .failure(_):
networkProtocol.hideProgress()
networkProtocol.onErrorResponse(response: response)
break
}
}
The Json response template I am getting from server is:
{
"some_int": 10,
"some_array":[...]
}
Below is my model class:
import ObjectMapper
class BaseResponse: Mappable {
var some_int: Int?
var some_array: [Array_of_objects]?
required init?(map: Map) {
some_int <- map["some_int"]
some_array <- map["some_array"]
}
func mapping(map: Map) {
}
}
And below is the function to class make the api call:
public static func callSomeApi(params: Parameters, networkProtocol: NetworkProtocol){
ApiHelper.makePostRequest(url: AppConstants.URLs.API_NAME, params: params, networkProtocol: networkProtocol, responseClass: BaseResponse)
}
Now the error is in the below line
let responseObject = responseClass(JSONString: jsonString!)
I am not able to understand how to convert jsonString into the responseClass generic object which I am accepting from View controller
Someone please help me resolve this, stuck on this issue for quite a while now.
You can use AlamofireMapper:
With json:
{
"page":1,
"per_page":3,
"total":12,
"total_pages":4,
"data":[
{
"id":1,
"first_name":"George",
"last_name":"Bluth",
"avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/calebogden/128.jpg"
},
{
"id":2,
"first_name":"Janet",
"last_name":"Weaver",
"avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"
},
{
"id":3,
"first_name":"Emma",
"last_name":"Wong",
"avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/olegpogodaev/128.jpg"
}
]
}
Swift class:
class UserResponse: Decodable {
var page: Int!
var per_page: Int!
var total: Int!
var total_pages: Int!
var data: [User]?
}
class User: Decodable {
var id: Double!
var first_name: String!
var last_name: String!
var avatar: String!
}
Request with alamofire
let url1 = "https://raw.githubusercontent.com/sua8051/AlamofireMapper/master/user1.json"
Alamofire.request(url1, method: .get
, parameters: nil, encoding: URLEncoding.default, headers: nil).responseObject { (response: DataResponse<UserResponse>) in
switch response.result {
case let .success(data):
dump(data)
case let .failure(error):
dump(error)
}
}
Link: https://github.com/sua8051/AlamofireMapper
Generic Response Object Serialization using Swift 4 Codable
If you don't want to use another dependency like ObjectMapper you can do the following way but you may have to make some chagnes.
Following is a typical model which we use to deserialize JSON data with generics using Alamofire. There is plenty of examples and excellent documentation on Alamofire.
struct User: ResponseObjectSerializable, ResponseCollectionSerializable, CustomStringConvertible {
let username: String
let name: String
var description: String {
return "User: { username: \(username), name: \(name) }"
}
init?(response: HTTPURLResponse, representation: Any) {
guard
let username = response.url?.lastPathComponent,
let representation = representation as? [String: Any],
let name = representation["name"] as? String
else { return nil }
self.username = username
self.name = name
}
}
Using Codable protocol introduced in Swift 4
typealias Codable = Decodable & Encodable
The first step in this direction is to add helper functions that
will do half of the work in deserialization JSON data and handle
errors. Using Swift extensions we add functions to decode incoming
JSON into our model struct/class that we will write afterward.
let decoder = JSONDecoder()
let responseObject = try? decoder.decode(T.self, from: jsonData)
The decoder (1) is an object that decodes instances of a data type from JSON objects.
Helper functions
extension DataRequest{
/// #Returns - DataRequest
/// completionHandler handles JSON Object T
#discardableResult func responseObject<T: Decodable> (
queue: DispatchQueue? = nil ,
completionHandler: #escaping (DataResponse<T>) -> Void ) -> Self{
let responseSerializer = DataResponseSerializer<T> { request, response, data, error in
guard error == nil else {return .failure(BackendError.network(error: error!))}
let result = DataRequest.serializeResponseData(response: response, data: data, error: error)
guard case let .success(jsonData) = result else{
return .failure(BackendError.jsonSerialization(error: result.error!))
}
// (1)- Json Decoder. Decodes the data object into expected type T
// throws error when failes
let decoder = JSONDecoder()
guard let responseObject = try? decoder.decode(T.self, from: jsonData)else{
return .failure(BackendError.objectSerialization(reason: "JSON object could not be serialized \(String(data: jsonData, encoding: .utf8)!)"))
}
return .success(responseObject)
}
return response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler)
}
/// #Returns - DataRequest
/// completionHandler handles JSON Array [T]
#discardableResult func responseCollection<T: Decodable>(
queue: DispatchQueue? = nil, completionHandler: #escaping (DataResponse<[T]>) -> Void
) -> Self{
let responseSerializer = DataResponseSerializer<[T]>{ request, response, data, error in
guard error == nil else {return .failure(BackendError.network(error: error!))}
let result = DataRequest.serializeResponseData(response: response, data: data, error: error)
guard case let .success(jsonData) = result else{
return .failure(BackendError.jsonSerialization(error: result.error!))
}
let decoder = JSONDecoder()
guard let responseArray = try? decoder.decode([T].self, from: jsonData)else{
return .failure(BackendError.objectSerialization(reason: "JSON array could not be serialized \(String(data: jsonData, encoding: .utf8)!)"))
}
return .success(responseArray)
}
return response(responseSerializer: responseSerializer, completionHandler: completionHandler)
}
}
Second, I earlier mentioned “using Swift 4 Codable” but if all we
want is to decode JSON from the server, we only need is a model
struct/class that conforms to protocol Decodable. (If you have the
same structure you want to upload you can use Codable to handle both
decoding and encoding) So, now our User model struct now looks like
this.
struct User: Decodable, CustomStringConvertible {
let username: String
let name: String
/// This is the key part
/// If parameters and variable name differ
/// you can specify custom key for mapping "eg. 'user_name'"
enum CodingKeys: String, CodingKey {
case username = "user_name"
case name
}
var description: String {
return "User: { username: \(username), name: \(name) }"
}
}
Lastly, our function call to API looks like.
Alamofire.request(Router.readUser("mattt"))).responseObject{ (response: DataResponse<User>) in
// Process userResponse, of type DataResponse<User>:
if let user = response.value {
print("User: { username: \(user.username), name: \(user.name) }")
}
}
For more complex (nested) JSON, the logic remains the same and only modifications you need in model struct/class is that all structs/classes must conform to Decodable protocol and Swift takes care of everything else.
You can use SwiftyJSON: https://cocoapods.org/pods/SwiftyJSON
Here is some example code could help you:
Alamofire.request(endpointURL, method: .get, parameters: params, encoding: URLEncoding.default, headers: nil).validate().responseJSON()
{
(response) in
if response.result.isFailure
{
print("ERROR! Reverse geocoding failed!")
}
else if let value = response.result.value
{
var country: String? = nil
var county: String? = nil
var city: String? = nil
var town: String? = nil
var village: String? = nil
print("data: \(value)")
let json = JSON(value)
print("json: \(json)")
country = json["address"]["country"].string
county = json["address"]["county"].string
city = json["address"]["city"].string
town = json["address"]["town"].string
village = json["address"]["village"].string
}
else
{
print("Cannot get response result value!")
}
}
Please let you know the code has been simplified (lot of line has been removed) and pasted here from my actual project, so this code is not tested, maybe contains typos or something like that, but the logic is visible
For Object Mapping you need follow this with AlamofireObjectMapper .
//Declare this before ViewLoad
var BaseResponse: Array<BaseResponse>?
// After you receive response from API lets say "data"
if let jsonData = data as? String {
self.BaseResponse = Mapper< BaseResponse >().mapArray(JSONString: jsonData)
}

Handling XML data with Alamofire in Swift

I started to use cocoapods with my current ios project. I need to use SOAP to get content with easy way for my ios project. I have googled it and Alamofire pod is great for me. Because I am using Swift programming language.
I have inited easily this pod. But my web services return me XML result. And I want to serialisation to array this XML result. But I can't.
When I call my web service with a browser I get this kind of result
Alamofire response method is like this:
Alamofire.request(.GET, "http://my-web-service-domain.com", parameters: nil)
.response { (request, response, data, error) in
println(request)
println(response)
println(error)
}
When I run this method I see this output on the terminal:
<NSMutableURLRequest: 0x170010a30> { URL: http://my-web-service-domain.com }
Optional(<NSHTTPURLResponse: 0x1704276c0> { URL: http://my-web-service-domain.com } { status code: 200, headers {
"Cache-Control" = "private, max-age=0";
"Content-Length" = 1020;
"Content-Type" = "text/xml; charset=utf-8";
Date = "Thu, 18 Jun 2015 10:57:07 GMT";
Server = "Microsoft-IIS/7.5";
"X-AspNet-Version" = "2.0.50727";
"X-Powered-By" = "ASP.NET";
} })
nil
I want to get result to an array which see on browser to show my storyboard.
Can anybody help me how to serialise this data with Alamofire framework or Swift language?
If I did not misunderstand your description, I think you would like to get the XML data and parse it, right? Regarding to this, you may handle with wrong variables in the response callback. You should println(data) to check the XML document.
For parsing XML data, you could consider SWXMLHash. The Alamofire request could look like:
Alamofire.request(.GET, "http://my-web-service-domain.com", parameters: nil)
.response { (request, response, data, error) in
println(data) // if you want to check XML data in debug window.
var xml = SWXMLHash.parse(data!)
println(xml["UserDTO"]["FilmID"].element?.text) // output the FilmID element.
}
Further information about XML management, please check SWXMLHash.
Alamofire 4.x - Swift 3.x:
(please note that in this example I've used URLEncoding.default instead of URLEncoding.xml because the xml parameter exclude the possibility to pass parameters and headers, so default is more confortable.)
let url = "https://httpbin.org/get"
let parameters: Parameters = ["foo": "bar"]
let headers: HTTPHeaders = [
"Authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
"Accept": "application/json"
]
Alamofire.request(url, method: .get, parameters: parameters, encoding: URLEncoding.default, headers: headers)
.responseString { response in
print(" - API url: \(String(describing: response.request!))") // original url request
var statusCode = response.response?.statusCode
switch response.result {
case .success:
print("status code is: \(String(describing: statusCode))")
if let string = response.result.value {
print("XML: \(string)")
}
case .failure(let error):
statusCode = error._code // statusCode private
print("status code is: \(String(describing: statusCode))")
print(error)
}
}
Alamofire 3.0 october 2015 and Xcode 7 according to the 3.0.0-beta.3 README and the Alamofire 3.0 Migration Guide.
For me the correct syntax is:
Alamofire.request(.GET, url, parameters: params, encoding: ParameterEncoding.URL).responsePropertyList { response in
if let error = response.result.error {
print("Error: \(error)")
// parsing the data to an array
} else if let array = response.result.value as? [[String: String]] {
if array.isEmpty {
print("No data")
} else {
//Do whatever you want to do with the array here
}
}
}
If you want a good XML parser, please take a look to SWXMLHash.
An example could be:
let xml = SWXMLHash.parse(string)
Using Alamofire 3.0 current version as of Sept 2015 and Xcode 7.
The implementation bellow has the advantage of not using an additional external library such as SWXMLHash
Alamofire.request(.GET, urlString, encoding: .PropertyList(.XMLFormat_v1_0, 0)).responsePropertyList { request, response, result in
//Note that result have two properties: error and value as of Alamofire 3.0, check the migration guide for more info
if let error = result.error {
print("Error: \(error)")
// parsing the data to an array
} else if let array = result.value as? [[String: String]] {
if array.isEmpty {
print("No data")
} else {
//Do whatever you want to do with the array here
}
}
}
If you want to map the XML to swift objects, you may also consider XMLMapper. (uses the same technique as the ObjectMapper)
Create your model by implementing XMLMappable protocol:
class UserDTO: XMLMappable {
var nodeName: String!
var extensionData: String?
var canChangeDeviceConfig: BooleanAtttribute?
var canChangeDriverConfig: BooleanAtttribute?
var canChangeFleetConfig: BooleanAtttribute?
var canChangeGeofenceConfig: BooleanAtttribute?
var canSaveHistory: BooleanAtttribute?
var canViewReport: BooleanAtttribute?
var canWatchHistory: BooleanAtttribute?
var deliverDailyReportByEmail: BooleanAtttribute?
var deliverDailyReportBySms: BooleanAtttribute?
var email: String?
var firm: String?
var firmId: Int?
var firstName: String?
var id: Int?
var isActive: Bool?
var isAdmin: Bool?
var lastName: String?
var phone: String?
var recivesDailyReport: BooleanAtttribute?
var userName: String?
required init(map: XMLMap) {
}
func mapping(map: XMLMap) {
extensionData <- map["ExtensionData"]
canChangeDeviceConfig <- map["CanChangeDeviceConfig"]
canChangeDriverConfig <- map["CanChangeDriverConfig"]
canChangeFleetConfig <- map["CanChangeFleetConfig"]
canChangeGeofenceConfig <- map["CanChangeGeofenceConfig"]
canSaveHistory <- map["CanSaveHistory"]
canViewReport <- map["CanViewReport"]
canWatchHistory <- map["CanWatchHistory"]
deliverDailyReportByEmail <- map["DeliverDailyReportByEmail"]
deliverDailyReportBySms <- map["DeliverDailyReportBySms"]
email <- map["Email"]
firm <- map["Firm"]
firmId <- map["FirmId"]
firstName <- map["FirstName"]
id <- map["Id"]
isActive <- (map["IsActive"], BooleanTransformeType(trueValue: "true", falseValue: "false"))
isAdmin <- (map["IsAdmin"], BooleanTransformeType(trueValue: "true", falseValue: "false"))
lastName <- map["LastName"]
phone <- map["Phone"]
recivesDailyReport <- map["RecivesDailyReport"]
userName <- map["UserName"]
}
}
class BooleanAtttribute: XMLMappable {
var nodeName: String!
var booleanValue: Bool?
required init(map: XMLMap) {
}
func mapping(map: XMLMap) {
booleanValue <- (map.attributes["xsi:nil"], BooleanTransformeType(trueValue: "true", falseValue: "false"))
}
}
class Firm: XMLMappable {
var nodeName: String!
var extensionData: String?
var firmTypeId: Int?
var id: Int?
var name: String?
var parentFirmId: Int?
required init(map: XMLMap) {
}
func mapping(map: XMLMap) {
extensionData <- map["ExtensionData"]
firmTypeId <- map["FirmTypeId"]
id <- map["Id"]
name <- map["Name"]
parentFirmId <- map["ParentFirmId"]
}
}
class BooleanTransformeType<T: Equatable>: XMLTransformType {
typealias Object = Bool
typealias XML = T
private var trueValue: T
private var falseValue: T
init(trueValue: T, falseValue: T) {
self.trueValue = trueValue
self.falseValue = falseValue
}
func transformFromXML(_ value: Any?) -> Bool? {
if let value = value as? T {
return value == trueValue
}
return nil
}
func transformToXML(_ value: Bool?) -> T? {
if value == true {
return trueValue
}
return falseValue
}
}
And use the XMLMapper class to map the XML string into the model objects:
let userDTO = XMLMapper<UserDTO>().map(XMLString: xmlString)
Altenatively you can use Requests subspec and the responseXMLObject(completionHandler:) function to map the response directly into the model objects:
Alamofire.request("http://my-web-service-domain.com", method: .get).responseXMLObject { (response: DataResponse<UserDTO>) in
let userDTO = response.result.value
print(userDTO?.id ?? "nil")
}
I hope this is useful.
I had a really unique issue where the server returned an XML that has a JSON as a string. Hope it will help someone.
Basically the XML looked like this:
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">{"Response":{"Status":"success","Result_Count":"1","Error_Description":"","Result":{"Login_result":{"user_id":"1","user_type":"1","user_name":"h4cked","user_locked":"False","user_locked_message":""}}}}</string>
As you can see the actual JSON is the {"Response":....
The solution is based only on Alamofire 4.4.
What you need to do is this:
Use the .responsePropertyList
Check for error
Convert the value to Data
Serialize to JSON object
Cast to Dictionary [String : Any]
Here it is:
Alamofire.request(NetworkAPIPaths.pathForLogin(),
method: .get,
parameters: [APIParameters.userName.rawValue : "",
APIParameters.password.rawValue : ""]).responsePropertyList
{ (response : DataResponse<Any>) in
if let error = response.result.error
{
// Error...
}
else if let jsonFullString = response.result.value as? String
{
if let jsonStringAsData = jsonFullString.data(using: .utf8)
{
do
{
let jsonGhost = try JSONSerialization.jsonObject(with: jsonStringAsData, options: [])
if let actualJSON = jsonGhost as? [String : Any]
{
// actualJSON is ready to be parsed :)
}
}
catch
{
print (error.localizedDescription)
}
}
}
tsaiid's answer in Swift 3 and Alamofire 4:
Alamofire.request("http://my-web-service-domain.com", parameters: nil) //Alamofire defaults to GET requests
.response { response in
if let data = response.data {
println(data) // if you want to check XML data in debug window.
var xml = SWXMLHash.parse(data)
println(xml["UserDTO"]["FilmID"].element?.text) // output the FilmID element.
}
}
If you need to use different decoders (JSON, URL, XML) with Alamofire, the best and simplest way I found was using XMLCoder.
Alamofire 5
Swift 5
(It will probably work on older versions)
On the Alamofire's response decodable method, you just need to use XMLDecoder()
#discardableResult
public func responseDecodable<T: Decodable>(of type: T.Type = T.self,
...
decoder: DataDecoder = JSONDecoder(),
...
completionHandler: #escaping (AFDataResponse<T>) -> Void) -> Self {
Something like this:
import XMLCoder
...
dataRequest
.validate()
.responseDecodable(of: T.self, decoder: XMLDecoder()) { (response: DataResponse<T, AFError>) in
And your models just need to conform the Codable protocol.
import Foundation
class ReportModel: Codable {
var connections: ConnectionModel?
var notifications: NotificationsModel?
enum CodingKeys: String, CodingKey {
case connections
case notifications
}
}
I created a GIST with a complete structure of how you can decode XML using Alamofire and I added some examples with nested objects and XML attributes.
https://gist.github.com/Enriquecm/bedf024d8a8f519eb1185fef14adcec3

Resources