Joe Sene Joe Sene - 10 days ago 5
Swift Question

Handle XML parsed response in swift

Here is the code where I've sent a SOAP message to my Web Service, with my Integration key, Username and Password. I was able to get the response and parse it all the way down to foundCharacters.

Now I need to store both elements found inside the parsed response, so I can use them later for another request.

I've been searching for tutorials all over, but I cant quiet understand those tutorials, because most of them are about XML files store locally and not from a real WebService.

class LoginCentralViewController: UIViewController, XMLParserDelegate, NSURLConnectionDelegate {

var chaveWS = ChaveWebService().chave()
var mutableData:NSMutableData = NSMutableData()
var currentElement:NSString = ""

@IBAction func btnAcessarACTION(_ sender: Any) {
let soapMessage = "<soapenv:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:log='LoginCentral'><soapenv:Header/><soapenv:Body><log:LoginCentral soapenv:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'><Autenticacao xsi:type='urn:Autenticacao' xmlns:urn='urn:RouterBoxMobile'><ChaveIntegracao xsi:type='xsd:string'>\(chaveWS)</ChaveIntegracao></Autenticacao><DadosLoginCentral xsi:type='urn:DadosLoginCentral' xmlns:urn='urn:RouterBoxMobile'><Usuario xsi:type='xsd:string'>wagner</Usuario><Senha xsi:type='xsd:string'>mudar123</Senha></DadosLoginCentral></log:LoginCentral></soapenv:Body></soapenv:Envelope>"

let urlString = "https://example.com?wsdl"

let url = NSURL (string: urlString)

let theRequest = NSMutableURLRequest(url: url! as URL)

let msgLength = soapMessage.characters.count

theRequest.addValue("text/xml; charset=utf-8", forHTTPHeaderField: "Content-Type")
theRequest.addValue(String(msgLength), forHTTPHeaderField: "Content-Length")
theRequest.httpMethod = "POST"
theRequest.httpBody = soapMessage.data(using: String.Encoding.utf8, allowLossyConversion: false)

let connection = NSURLConnection(request: theRequest as URLRequest, delegate: self, startImmediately: true)
connection!.start()

if (connection != nil) {
var mutableData : Void = NSMutableData.initialize()
}
print("passou")

}


override func viewDidLoad() {
super.viewDidLoad()

}
func connection(_ connection: NSURLConnection!, didReceiveResponse response: URLResponse!) {
mutableData.length = 0;
print("passou aqui tbm")
}

func connection(_ connection: NSURLConnection!, didReceiveData data: NSData!) {
mutableData.append(data as Data)

}


func connectionDidFinishLoading(_ connection: NSURLConnection!) {
let response = NSString(data: mutableData as Data, encoding: String.Encoding.utf8.rawValue)

let xmlParser = XMLParser(data: mutableData as Data)
xmlParser.delegate = self
xmlParser.parse()
xmlParser.shouldResolveExternalEntities = true
//print(response)

}

//XMLParser
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
currentElement = elementName as NSString
//print(elementName)
}

func parser(_ parser: XMLParser, foundCharacters string: String) {
if currentElement == "LoginCentralResponse" {
print(currentElement, string)
}
print(currentElement, string)
}
}


Here is the parsed response that I need to store and reutilize:

ClientCode : 8
Permissions : 70,73,77,75,71,72

Answer

I see that you want to store credentials within your app. There is no need to store such file inside XML by writing the file. You can use Keychain for storing such sensitive data and you can receive anytime you want it back from the Keychain for further more HTTP requests. It's secure and safe.

Here is the keychain library that I used

https://github.com/marketplacer/keychain-swift

And one another suggestion is you don't need to parse XML so difficult like that,try use this.

https://github.com/drmohundro/SWXMLHash

Your SOAP Web service code seems out of date. It's rare web service that we don't use it most of day and rare documentations. Now, people are turning to REST. And you are using NSURLConnection which was deprecated in iOS 8. So, let's start using to URLSession. And I will use Delegate Pattern here. I have made my answer very simple for you to understand. And you can change anything you want to handle the response.

So, I have two Swift classes. One is ViewController.swift and another is SOAPService.swift.

Here is how we gonna handle SOAPService using delegate pattern.

import Foundation
import SWXMLHash

// At here you define your constants at global variables, or you can use structs
public let verify_token = "VerifyToken"
public let WSDL_URL = "https://api.example.com/services?wsdl"
public let BASE_URL = "https://api.example.com/"
public let VERIFY_TOKEN = BASE_URL + "VerifyToken"

// Creating protocol for data transfering
protocol SOAPServiceProtocol{
    func didSuccessRequest(results : String, requestName : String)
    func didFailRequest(err : String, requestName : String)
}

// I have extended the URLSessionDelegate and URLSessionTaskDelegate for passing TLS, so you might not needed until you handle HTTPS
class SOAPService : NSObject, URLSessionDelegate, URLSessionTaskDelegate{

// Here the initialization of delegate pattern to transfer data between classes
var delegate : SOAPServiceProtocol
init(delegate : SOAPServiceProtocol){
    self.delegate=delegate
}

func post(wsdlURL : String, soapAction : String, soapMessage : String, serviceName : String, method : String){

    // Here your request configurations
    var request = URLRequest(url: URL(string: wsdlURL)!)
    let msgLength  = String(soapMessage.characters.count)

    // Configure your soap message here
    let data = soapMessage.data(using: String.Encoding.utf8, allowLossyConversion: false)

    // Setting HTTP Header,Body and Method
    request.httpMethod = method
    request.addValue("text/xml; charset=utf-8", forHTTPHeaderField: "Content-Type")
    request.addValue(msgLength, forHTTPHeaderField: "Content-Length")
    request.addValue(soapAction, forHTTPHeaderField: "SOAPAction")
    request.httpBody = data

    // URLSession configuration such as TIME OUT,etc
    let urlconfig = URLSessionConfiguration.default
    urlconfig.timeoutIntervalForRequest = 15
    urlconfig.timeoutIntervalForResource = 15

    // Initiating URLSession before making a request, I will use default here
    var session = URLSession.shared
    session = URLSession(configuration: urlconfig, delegate: nil, delegateQueue: nil)

    // Start HTTP Request
    let task = session.dataTask(with: request) {
        data, response, error in

        if error != nil {
            // If error include,return fail
            self.delegate.didFailRequest(err: "Request failed", requestName: serviceName)
            return
        }

        guard let datastring = String(data: data!, encoding:String.Encoding(rawValue: String.Encoding.utf8.rawValue)) else{
            return self.delegate.didFailRequest(err: "No Data", requestName: verify_token)
        }

        let xml = SWXMLHash.parse(datastring)

        guard let xmlResult : String = xml["soap:Envelope"]["soap:Body"]["\(serviceName)Response"]["\(serviceName)Result"].element?.text else{
            print("XML is NIL")
            self.delegate.didFailRequest(err: "XML is NIL", requestName: verify_token)
            return
        }

        // when parsing complete return the parse result
        self.delegate.didSuccessRequest(results: xmlResult, requestName: verify_token)

    }
    task.resume()

}

// Start Writing Your SOAP Services and messages HERE
func doVerify(userName : String, password : String, methodName : String){
    let soapMessage = String(format:"<?xml version=\"1.0\" encoding=\"UTF-8\"?><SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:ns1=\"https://api.example.com/\"><SOAP-ENV:Body><ns1:VerifyToken><ns1:UserName>%@</ns1:UserName><ns1:Password>%@</ns1:Password></ns1:VerifyToken></SOAP-ENV:Body></SOAP-ENV:Envelope>",userName,password)

    post(wsdlURL: WSDL_URL, soapAction: VERIFY_TOKEN, soapMessage: soapMessage, serviceName: verify_token, method: "POST")
}

}

So, that is how we gonna handle SOAP web services using URLSession.

So, how do we get the response data from ViewController?

It's easy. We just implement the Protocol methods here.

import UIKit

class ViewController: UIViewController, SOAPServiceProtocol{

var soapService : SOAPService?

override func viewDidLoad() {
    super.viewDidLoad()

    // You need to initialize the SOAP Service to call SOAP Web Service.
    soapService = SOAPService(delegate: self)

    // Do any additional setup after loading the view, typically from a nib.
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

@IBAction func doVerify(sender : AnyObject){
    // Here I started HTTP Request
    soapService?.doVerify(userName: "Thiha6245", password: "dsadafwa", methodName: verify_token)
}

// Here you implement the results which success
func didSuccessRequest(results : String, requestName: String) {
    print("Results : \(results)")
    switch requestName {
    case verify_token:
        // do handling xml parsing from result to model object and get data from model
        break
    default :
        break
    }
}

// Here you implement the failure
func didFailRequest(err: String, requestName: String) {
    print("Error : \(err)")
    switch requestName {
    case verify_token:
        // do error handling here // Request TIME OUT,Internet connection error or data error,etc
        break
    default :
        break
    }
}

}

I hope it helps you. Since you are beginner at this, I suggest to read the documentations about NSURLSession for further configuration and read how to parse XML using the SWXMLHash that I mentioned. Good Luck!