I had this method here and it returned my data exactly as I needed it:
func getTestData() -> [Any]? { return [GradingData(lot: "lot", andColumns: "andColumns", SLAIssuedFinalGrading: true, SLAIssuedFinalGradingDate: "SLAIssuedFinalGradingDate", CityApprovalIssued: true, CityCommentReceived: false, GradingRepair: "GradingRepair", CurbStopRepair: "CurbStopRepair", SplashPadDownSpout: "SplashPadDownSpout", RYCBOtherRepairs: "RYCBOtherRepairs", Comments: "Comments", columnCamera: "", DepositReceived: false), GradingData(lot: "lot", andColumns: "andColumns2", SLAIssuedFinalGrading: false, SLAIssuedFinalGradingDate: "SLAIssuedFinalGradingDate", CityApprovalIssued: false, CityCommentReceived: false, GradingRepair: "GradingRepair", CurbStopRepair: "CurbStopRepair", SplashPadDownSpout: "SplashPadDownSpout", RYCBOtherRepairs: "RYCBOtherRepairs", Comments: "Comments", columnCamera: "", DepositReceived: false)] }
Now I am trying to do a call to an API and return the exact same structure with this:
func GetLandGradingData(_ community: String, completion: @escaping (_ result: [GradingData]) -> Void) { let escapedCommunity = community.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) let urlComponents = NSURLComponents(string: webservice + "?community=" + escapedCommunity!); urlComponents?.user = appDelegate.username; urlComponents?.password = appDelegate.password; let url = urlComponents?.url; let returnedData = [GradingData]() URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in if(error != nil){ completion(returnedData) }else{ do{ let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [GradingData] OperationQueue.main.addOperation({ completion(json) }) }catch let error as NSError{ print(error) completion(returnedData) } } }).resume() }
My problem is that is method is not returning the same structure as the getTestData method, attached are screenshots on what getTestData returns and what this api method call returns.
Here is my whole class:
@objcMembers class GradingData : NSObject { /** Define lot Column String */ var lot: String? /** Define Address Column String */ var address: String? /** Define SLA Issued Final Grading Column String */ var SLAIssuedFinalGrading = false /** Define SLA Issued Final Grading Date Column String */ var SLAIssuedFinalGradingDate: String? /** Define City Approval Issued Column String */ var CityApprovalIssued = false /** Define City Comment Received Column String */ var CityCommentReceived = false /** Define Grading Repair Column String */ var GradingRepair: String? /** Define Curb Stop Repair Column String */ var CurbStopRepair: String? /** Define Splash Pad or Down Spout Column String */ var SplashPadDownSpout: String? /** Define RYCB or Other Repairs Column String */ var RYCBOtherRepairs: String? /** Define Comments Column String */ var Comments: String? /** Define Camera Column String */ var columnCamera: String? /** Define Deposit Received Column String */ var DepositReceived = false /** Inital call to class */ init(lot: String?, andColumns address: String?, SLAIssuedFinalGrading: Bool?, SLAIssuedFinalGradingDate: String?, CityApprovalIssued: Bool?, CityCommentReceived: Bool?, GradingRepair: String?, CurbStopRepair: String?, SplashPadDownSpout: String?, RYCBOtherRepairs: String?, Comments: String?, columnCamera: String?, DepositReceived: Bool?) { super.init() //Set lot string self.lot = lot //Set Address Column string self.address = address //Set SLA Issued Final Grading Column string self.SLAIssuedFinalGrading = SLAIssuedFinalGrading! //Set SLA Issued Final Grading Date Column string self.SLAIssuedFinalGradingDate = SLAIssuedFinalGradingDate //Set City Approval Issued Column string self.CityApprovalIssued = CityApprovalIssued! //Set City Comment Received Column string self.CityCommentReceived = CityCommentReceived! //Set Grading Repair Column string self.GradingRepair = GradingRepair //Set Curb Stop Repair Column string self.CurbStopRepair = CurbStopRepair //Set Splash Pad or Down Spout Column string self.SplashPadDownSpout = SplashPadDownSpout //Set RYCB or Other Repairs Column string self.RYCBOtherRepairs = RYCBOtherRepairs //Set Comments Column string self.Comments = Comments //Set Camera Column string self.columnCamera = columnCamera //Set Deposit Received Column string self.DepositReceived = DepositReceived! } }
Here is my Data from the API:
<Reports> <CityApprovalIssued>false</CityApprovalIssued> <CityCommentReceived>false</CityCommentReceived> <Comments></Comments> <CurbStopRepair></CurbStopRepair> <DepositReceived>false</DepositReceived> <GradingRepair></GradingRepair> <RYCBOtherRepairs></RYCBOtherRepairs> <SLAIssuedFinalGrading>false</SLAIssuedFinalGrading> <SLAIssuedFinalGradingDate/> <SplashPadDownSpout></SplashPadDownSpout> <address>123 Fake Street</address> <lot>A0001</lot> </Reports> <Reports> <CityApprovalIssued>false</CityApprovalIssued> <CityCommentReceived>false</CityCommentReceived> <Comments></Comments> <CurbStopRepair></CurbStopRepair> <DepositReceived>false</DepositReceived> <GradingRepair></GradingRepair> <RYCBOtherRepairs></RYCBOtherRepairs> <SLAIssuedFinalGrading>false</SLAIssuedFinalGrading> <SLAIssuedFinalGradingDate/> <SplashPadDownSpout></SplashPadDownSpout> <address>125 Fake Street</address> <lot>A0002</lot> </Reports>
This is from an ASP.NET MVC API Controller: <ArrayOfReports xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/App.Models">
This is what I get when I do this:
let string = String(data: data!, encoding: .utf8) print(string as Any) Optional("[{\"lot\":\"A0001\",\"address\”:\”123 Fake Street\”,\”SLAIssuedFinalGrading\":false,\"SLAIssuedFinalGradingDate\":\"\",\"CityApprovalIssued\":false,\"CityCommentReceived\":false,\"GradingRepair\":\" \",\"CurbStopRepair\":\" \",\"SplashPadDownSpout\":\" \",\"RYCBOtherRepairs\":\" \",\"Comments\":\" \",\"DepositReceived\":false},{\"lot\":\"A0002\",\"address\":\"125 Fake Street\",\"SLAIssuedFinalGrading\":false,\"SLAIssuedFinalGradingDate\":\"\",\"CityApprovalIssued\":false,\"CityCommentReceived\":false,\"GradingRepair\":\" \",\"CurbStopRepair\":\" \",\"SplashPadDownSpout\":\" \",\"RYCBOtherRepairs\":\" \",\"Comments\":\" \",\"DepositReceived\":false}]")
4 Answers
Answers 1
First of all the response data is indeed JSON, the XML output is a bit confusing.
You probably don't need a class inherited from NSObject
, in most cases especially in iOS a struct is sufficient.
Except the key columnCamera
all keys exist in all records, so declare all properties as non-optional without a default value and columnCamera
as optional, and declare all properties as constants (let
) if they are not going to change.
An explicit init
method is not needed.
The CodingKeys
are added to translate most of the keys to lowercase.
struct GradingData : Decodable { private enum CodingKeys : String, CodingKey { case lot, address, SLAIssuedFinalGrading case SLAIssuedFinalGradingDate case cityApprovalIssued = "CityApprovalIssued" case cityCommentReceived = "CityCommentReceived" case gradingRepair = "GradingRepair" case curbStopRepair = "CurbStopRepair" case splashPadDownSpout = "SplashPadDownSpout" case RYCBOtherRepairs, comments = "Comments" case columnCamera, depositReceived = "DepositReceived" } let lot: String let address: String let SLAIssuedFinalGrading : Bool let SLAIssuedFinalGradingDate: String let cityApprovalIssued : Bool let cityCommentReceived : Bool let gradingRepair : String let curbStopRepair : String let splashPadDownSpout : String let RYCBOtherRepairs : String let comments : String let columnCamera : String? let depositReceived : Bool }
Then decode the JSON
.... var returnedData = [GradingData]() URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) in if error != nil { print(error!) completion(returnedData) } else { do { returnedData = try JSONDecoder().decode([GradingData].self, from: data!) DispatchQueue.main.async { completion(returnedData) } } catch { print(error) completion(returnedData) } } }).resume()
Answers 2
Just add another init
into your GradingData
model class like this:
convenience init(data: [String : Any]) { init(lot: data["lot"] as? String, andColumns: data["address"] as? String, SLAIssuedFinalGrading: data["SLAIssuedFinalGrading"] as? Bool, SLAIssuedFinalGradingDate: data["SLAIssuedFinalGradingDate"] as? String, CityApprovalIssued: data["CityApprovalIssued"] as? Bool, CityCommentReceived: data["CityCommentReceived"] as? Bool, GradingRepair: data["GradingRepair"] as? String, CurbStopRepair: data["CurbStopRepair"] as? String, SplashPadDownSpout: data["SplashPadDownSpout"] as? String, RYCBOtherRepairs: data["RYCBOtherRepairs"] as? String, Comments: data["Comments"] as? String, columnCamera: data["columnCamera"] as? String, DepositReceived: data["DepositReceived"] as? Bool) }
And in func GetLandGradingData(...)
method replace inside do { }
braces with these:
let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [[String : Any]] OperationQueue.main.addOperation({ let gradingDatas = json.map { GradingData(data: $0) } completion(gradingDatas) })
Answers 3
Just because you asked for an example (but did not provide your GradingData
which will make this really abstract):
struct GradingData : Codable { // here is the meat, but you did not show ... } ... let grades = JSONDecoder().decode(GradingData.self, from: data)
Swift is the first language I have come across which has nailed this. It really just boils down to implementing an interface (without actually doing anything interesting most of the time while still providing the possibility to do all the necessary things with minimal effort) and get a free JSON parser for your objects (not just some hash-junk dictionary).
(It is called "progress" :-)
Answers 4
Convert XML to dictionary by custom class or nsxmlparser. or if u have array of Grading then
var modelArr = [Grading]()
var model = Grading.init(fromDictionary dictionary: xmlParsedDic)
modelArr.append(model)
use this model class
import Foundation
class Grading : NSObject, NSCoding {
var cityApprovalIssued : Bool! var cityCommentReceived : Bool! var comments : String! var curbStopRepair : String! var depositReceived : Bool! var gradingRepair : String! var rYCBOtherRepairs : String! var sLAIssuedFinalGrading : Bool! var sLAIssuedFinalGradingDate : String! var splashPadDownSpout : String! var address : String! var lot : String! init(fromDictionary dictionary: [String:Any]){ cityApprovalIssued = dictionary["CityApprovalIssued"] as? Bool cityCommentReceived = dictionary["CityCommentReceived"] as? Bool comments = dictionary["Comments"] as? String curbStopRepair = dictionary["CurbStopRepair"] as? String depositReceived = dictionary["DepositReceived"] as? Bool gradingRepair = dictionary["GradingRepair"] as? String rYCBOtherRepairs = dictionary["RYCBOtherRepairs"] as? String sLAIssuedFinalGrading = dictionary["SLAIssuedFinalGrading"] as? Bool sLAIssuedFinalGradingDate = dictionary["SLAIssuedFinalGradingDate"] as? String splashPadDownSpout = dictionary["SplashPadDownSpout"] as? String address = dictionary["address"] as? String lot = dictionary["lot"] as? String } func toDictionary() -> [String:Any] { var dictionary = [String:Any]() if cityApprovalIssued != nil{ dictionary["CityApprovalIssued"] = cityApprovalIssued } if cityCommentReceived != nil{ dictionary["CityCommentReceived"] = cityCommentReceived } if comments != nil{ dictionary["Comments"] = comments } if curbStopRepair != nil{ dictionary["CurbStopRepair"] = curbStopRepair } if depositReceived != nil{ dictionary["DepositReceived"] = depositReceived } if gradingRepair != nil{ dictionary["GradingRepair"] = gradingRepair } if rYCBOtherRepairs != nil{ dictionary["RYCBOtherRepairs"] = rYCBOtherRepairs } if sLAIssuedFinalGrading != nil{ dictionary["SLAIssuedFinalGrading"] = sLAIssuedFinalGrading } if sLAIssuedFinalGradingDate != nil{ dictionary["SLAIssuedFinalGradingDate"] = sLAIssuedFinalGradingDate } if splashPadDownSpout != nil{ dictionary["SplashPadDownSpout"] = splashPadDownSpout } if address != nil{ dictionary["address"] = address } if lot != nil{ dictionary["lot"] = lot } return dictionary } @objc required init(coder aDecoder: NSCoder) { cityApprovalIssued = aDecoder.decodeObject(forKey: "CityApprovalIssued") as? Bool cityCommentReceived = aDecoder.decodeObject(forKey: "CityCommentReceived") as? Bool comments = aDecoder.decodeObject(forKey: "Comments") as? String curbStopRepair = aDecoder.decodeObject(forKey: "CurbStopRepair") as? String depositReceived = aDecoder.decodeObject(forKey: "DepositReceived") as? Bool gradingRepair = aDecoder.decodeObject(forKey: "GradingRepair") as? String rYCBOtherRepairs = aDecoder.decodeObject(forKey: "RYCBOtherRepairs") as? String sLAIssuedFinalGrading = aDecoder.decodeObject(forKey: "SLAIssuedFinalGrading") as? Bool sLAIssuedFinalGradingDate = aDecoder.decodeObject(forKey: "SLAIssuedFinalGradingDate") as? String splashPadDownSpout = aDecoder.decodeObject(forKey: "SplashPadDownSpout") as? String address = aDecoder.decodeObject(forKey: "address") as? String lot = aDecoder.decodeObject(forKey: "lot") as? String } @objc func encode(with aCoder: NSCoder) { if cityApprovalIssued != nil{ aCoder.encode(cityApprovalIssued, forKey: "CityApprovalIssued") } if cityCommentReceived != nil{ aCoder.encode(cityCommentReceived, forKey: "CityCommentReceived") } if comments != nil{ aCoder.encode(comments, forKey: "Comments") } if curbStopRepair != nil{ aCoder.encode(curbStopRepair, forKey: "CurbStopRepair") } if depositReceived != nil{ aCoder.encode(depositReceived, forKey: "DepositReceived") } if gradingRepair != nil{ aCoder.encode(gradingRepair, forKey: "GradingRepair") } if rYCBOtherRepairs != nil{ aCoder.encode(rYCBOtherRepairs, forKey: "RYCBOtherRepairs") } if sLAIssuedFinalGrading != nil{ aCoder.encode(sLAIssuedFinalGrading, forKey: "SLAIssuedFinalGrading") } if sLAIssuedFinalGradingDate != nil{ aCoder.encode(sLAIssuedFinalGradingDate, forKey: "SLAIssuedFinalGradingDate") } if splashPadDownSpout != nil{ aCoder.encode(splashPadDownSpout, forKey: "SplashPadDownSpout") } if address != nil{ aCoder.encode(address, forKey: "address") } if lot != nil{ aCoder.encode(lot, forKey: "lot") } }}
0 comments:
Post a Comment