Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

enter image description here

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?

like image 908
Mehmet Avatar asked Jun 18 '15 11:06

Mehmet


People also ask

What is Alamofire Swift?

Alamofire is a Swift-based, HTTP networking library. It provides an elegant interface on top of Apple's Foundation networking stack that simplifies common networking tasks. Its features include chainable request/response methods, JSON and Codable decoding, authentication and more.

What is the advantage of Alamofire?

Alamofire makes developing networking layers easier, faster and much cleaner. Another great benefit of using it is that it can be studied and its code is available. This can help programmers because it's a well-written framework.


4 Answers

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.

like image 181
tsaiid Avatar answered Sep 28 '22 12:09

tsaiid


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)

like image 45
Alessandro Ornano Avatar answered Sep 24 '22 12:09

Alessandro Ornano


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
    }
  }
}
like image 35
irkinosor Avatar answered Sep 25 '22 12:09

irkinosor


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.

like image 30
gcharita Avatar answered Sep 26 '22 12:09

gcharita