Use Alomofire for get data from server in Xcode 9 - json

I'm beginner in iOS programming but I work in android so now I stuck in a problem in iOS.
I know my question is general but I really need your help!
in android for connect to server we do like below:
Call<String> myList = service.Contact_List("");
myList.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
try{
ArrayList<Contact> contactArrayList = new ArrayList<>();
JSONArray jsonArray = new JSONArray(response.body());
for(int i=0 ; i<jsonArray.length() ; i++)
contactArrayList.add(gson.fromJson(jsonArray.getJSONObject(i).toString(), Contact.class));
}catch (Exception e) {
Log.d("Catch","Error")
}finally {
}
}
#Override
public void onFailure(Call<String> call, Throwable t) {
Log.d("Failure","Error")
}
});
and in xcode I do like below :
let url = URL(string: "http://api.example.com/Contact-List")
Alamofire.request(url!, method: HTTPMethod.post, parameters: param, encoding: URLEncoding.default, headers: nil).responseJSON { (response) in
print("response.request")
print(response.request as Any) // original URL request
print("response.response")
print(response.response as Any) // URL response
print("response.result.value")
}
and my result is something like this:
[{"Id":1,"Name":"Mary","TelNumber":"09111111"},{"Id":2,"Name":"Sarah","TelNumber":"09222222"},
{"Id":3,"Name":"Ben","TelNumber":"09333333"}]
now my question is that how can I do like this code in Xcode(swift 3 ):
for(int i=0 ; i<jsonArray.length() ; i++)
contactArrayList.add(gson.fromJson(jsonArray.getJSONObject(i).toString(), Contact.class));
in addition, I use ASP.net in server side.
I am really sorry about my long and ambiguous question!
thanks for any suggestions.

First create NSObject swift file :
then add code
class demo {
var ID: String
var Name: String
init(ID: String, Name: String) {
self.ID = ID
self.Name = Name
}
}
Try like this way :
let data = response.result.value
if data != nil {
self.presentWindow.hideToastActivity()
if let response = data as? [[String: AnyObject]] {
for detail_data in response {
let Id = detail_data["Id"] as? String ?? ""
let Name = detail_data["Name"] as? String ?? ""
let demoObj = demo(ID: ID, Name: Name
self.demoObjects.append(demoObj)
}
}
}
Updated Answer
for converting string response to JSON
Sample code will be something like this. Don't forget to handle unwrap stuff
let data1 = "[{\"Id\": 1,\"Name\": \"Mary\",\"TelNumber\": \"09111111\"},{\"Id\": 2,\"Name\": \"Sarah\",\"TelNumber\": \"09222222\"}]" //your JSON From API Response
let data = data1.data(using: .utf8)
do {
let array = try JSONSerialization.jsonObject(with: data!) as! [[String : Any]]
for detail_data in array {
let Id = detail_data["Id"] as? Int ?? 00
let Name = detail_data["Name"] as? String ?? ""
print("Id:",Id)
print("Name:",Name)
print("****")
}
} catch {
print("Exception occured \(error))")
}

Related

SwiftUI decoding JSON from API

I know there are already some articles regarding this issue, but I could not find anything related to my JSON.
This is how my JSON likes like:
{
"message": {
"affenpinscher": [],
"african": [],
"airedale": [],
"akita": [],
"appenzeller": [],
"australian": [
"shepherd"
],
"basenji": []
},
"status: "succes"
}
So, if I understand it correctly it is dictionary because it starts with {, but what are the things inside the "message"?
This is my Dog.swift class where I am re-writing the JSON, but I am not sure if it is correct:
class Dog: Decodable, Identifiable {
var message: Message?
var status: String?
}
struct Message: Decodable {
var affenpinscher: [String:[String]]?
var african: [String]?
var airedale: [String]?
var akita: [String]?
var appenzeller: [String]?
var australian: [String]?
var basenji: [String]?
}
As you can see in the first value I was trying to play with data types, but no success.
I am decoding and parsing JSON here:
class ContentModel: ObservableObject {
#Published var dogs = Message()
init() {
getDogs()
}
func getDogs(){
// Create URL
let urlString = Constants.apiUrl
let url = URL(string: urlString)
if let url = url {
// Create URL request
var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 10)
request.httpMethod = "GET"
// Get URLSession
let session = URLSession.shared
// Create Data Task
let dataTask = session.dataTask(with: request) { (data, response, error) in
// Check that there is not an error
if error == nil {
do {
// Parse JSON
let decoder = JSONDecoder()
let result = try decoder.decode(Dog.self, from: data!)
print(result)
// Assign result to the dogs property
DispatchQueue.main.async {
self.dogs = result.message!
}
} catch {
print(error)
}
}
}
// Start the Data Task
dataTask.resume()
}
}
}
And here I would love to iterate through it eventually, which I also have no idea how to do it:
struct ContentView: View {
#EnvironmentObject var model: ContentModel
var body: some View {
NavigationView {
ScrollView {
LazyVStack {
if model.dogs != nil {
// ForEach(Array(model.dogs.keys), id: \.self) { d in
// Text(d)
// }
}
}
.navigationTitle("All Dogs")
}
}
}
}
What can I try next to resolve this?
First of all don't use classes for a JSON model and to conform to Identifiable you have to add an id property and CodingKeys if there is no key id in the JSON.
My suggestion is to map the unhandy [String: [String]] dictionary to an array of an extra struct
I renamed Dog as Response and named the extra struct Dog
struct Dog {
let name : String
let types : [String]
}
struct Response: Decodable, Identifiable {
private enum CodingKeys: String, CodingKey { case message, status }
let id = UUID()
let dogs: [Dog]
let status: String
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
status = try container.decode(String.self, forKey: .status)
let message = try container.decode([String:[String]].self, forKey: .message)
dogs = message.map(Dog.init).sorted{$0.name < $1.name}
}
}
In the model declare
#Published var dogs = [Dog]()
and decode
let result = try decoder.decode(Response.self, from: data!)
print(result)
// Assign result to the dogs property
DispatchQueue.main.async {
self.dogs = result.dogs
}
The dogs array can be displayed seamlessly in a List
PS: Actually appenzeller is supposed to be
"appenzeller": ["sennenhund"],
or correctly in English
"appenzell": ["mountain dog"],
😉😉😉
Using the native Swift approach that #vadian answered is a much lighter weight solution, but if you work with JSON often I'd recommend using SwiftyJSON.
You can parse the URL data task response into a Swifty json object like so:
import SwiftyJSON
guard let data = data, let json = try? JSON(data: data) else {
return
}
// Make sure the json fetch was successful
if json["status"].stringValue != "success" {
return
}
Then you can access the message object safely without the verbosity of using Decodable. Here the message is parsed into an array of dog structs:
struct Dog {
let name : String
let types : [String]
}
var dogs: [Dog] = []
/// Load the docs into an array
for (name, typesJson) in json["message"].dictionaryValue {
dogs.append(Dog(name: name, types: typesJson.arrayValue.map { $0.stringValue }))
}
print("dogs", dogs)

Get user ID from JWT (JSON web token)

I am using the plugin to authenticate WordPress using api-rest : JWT Authentication for WP REST API
From the request to the server I get the following answer:
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvbWlob3N0Lm9yZ1wvcHJ1ZWJhcyIsImlhdCI6MTU1MzcyNDM4MSwibmJmIjoxNTUzNzI0MzgxLCJleHAiOjE1NTQzMjkxODEsImRhdGEiOnsidXNlciI6eyJpZCI6IjIifX19.rgi5Q2c8RCoHRp-lJiJN8xQaOavn9T_q8cmf8v1-57o",
"user_email": "abc#test.com",
"user_nicename": "test",
"user_display_name": "Test"
}
So far everything works fine, but I need to know the user ID.
I have read that the token is coded in base64 and within this is the ID. Trying to decode, I see if the ID that I need is there.
In swift with this function I decode the token, but I can not get the dictionary ID.
func decode(_ token: String) -> [String: AnyObject]? {
let string = token.components(separatedBy: ".")
let toDecode = string[1] as String
var stringtoDecode: String = toDecode.replacingOccurrences(of: "-", with: "+") // 62nd char of encoding
stringtoDecode = stringtoDecode.replacingOccurrences(of: "_", with: "/") // 63rd char of encoding
switch (stringtoDecode.utf16.count % 4) {
case 2: stringtoDecode = "\(stringtoDecode)=="
case 3: stringtoDecode = "\(stringtoDecode)="
default: // nothing to do stringtoDecode can stay the same
print("")
}
let dataToDecode = Data(base64Encoded: stringtoDecode, options: [])
let base64DecodedString = NSString(data: dataToDecode!, encoding: String.Encoding.utf8.rawValue)
var values: [String: AnyObject]?
if let string = base64DecodedString {
if let data = string.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: true) {
values = try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String : AnyObject]
}
}
return values
}
The dictionary that returns this function is:
["iss": https://myhost.me/test, "exp": 1554235730, "nbf": 1553630930, "iat": 1553630930, "data": {
user = {
id = 2;
};
}]
How do I get the ID from this dictionary?
Your code is pretty unswifty.
Basically don't use NS... classes in Swift if there is a native equivalent and a JSON dictionary is always value type ([String:Any]).
I recommend to add an Error enum, make the function can throw, decode the serialized token with Decodable and return the Token instance on success
struct Token : Decodable {
let data : UserData
struct UserData : Decodable {
let user : User
struct User : Decodable {
let id : String
}
}
}
You are encouraged to keep the parameter label in the method declaration
enum TokenError : Error {
case invalidJWTFormat, invalidBase64EncodedData
}
func decode(token: String) throws -> Token {
let components = token.components(separatedBy: ".")
guard components.count == 3 else { throw TokenError.invalidJWTFormat }
var decodedString = components[1]
.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
while decodedString.utf16.count % 4 != 0 {
decodedString += "="
}
guard let decodedData = Data(base64Encoded: decodedString) else { throw TokenError.invalidBase64EncodedData }
return try JSONDecoder().decode(Token.self, from: decodedData)
}
and call it
do {
let userID = try decode(token: "eyJ0eXAi.....").data.user.id
} catch { print(error) }

Swift add key to local JSON for saving checkmarks

Im trying to save checkmarks in my application. But cause im paring my data from an api.. I don't know how I can add like the key "checked". The thing is the JSON gets downloaded once a Week, adding new content. Is there a way to still save my checkmarks?
struct Base : Codable {
let expireDate : String
let Week : [Weeks]
}
struct Weeks : Codable {
let name : String
let items : [Items]
}
struct Items : Codable {
let Icon: String
let text : String
}
In my RootTableView I have the array Weeks, and I would like to add checkmarks to the child tableView Items.
Thanks in advance
UPDATE:
//
// Download JSON
//
enum Result<Value> {
case success(Value)
case failure(Error)
}
func getItems(for userId: Int, completion: ((Result<Base>) -> Void)?) {
var urlComponents = URLComponents()
urlComponents.scheme = "https"
urlComponents.host = "api.jsonbin.io"
print(NSLocale.preferredLanguages[0])
let preferredLanguage = NSLocale.preferredLanguages[0]
if preferredLanguage.starts(with: "de"){
urlComponents.path = "/b/xyz"
}
else
{
urlComponents.path = "/xyz"
}
let userIdItem = URLQueryItem(name: "userId", value: "\(userId)")
urlComponents.queryItems = [userIdItem]
guard let url = urlComponents.url else { fatalError("Could not create URL from components") }
var request = URLRequest(url: url)
request.httpMethod = "GET"
let config = URLSessionConfiguration.default
config.httpAdditionalHeaders = [
"secret-key": "xyzzy"
]
let session = URLSession(configuration: config)
let task = session.dataTask(with: request) { (responseData, response, responseError) in
DispatchQueue.main.async {
if let error = responseError {
completion?(.failure(error))
} else if let jsonDataItems = responseData {
let decoder = JSONDecoder()
do {
let items = try decoder.decode(Base.self, from: jsonDataItems)
completion?(.success(items))
} catch {
completion?(.failure(error))
}
} else {
let error = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey : "Data was not retrieved from request"]) as Error
completion?(.failure(error))
}
}
}
task.resume()
}
func loadJson() {
getItems(for: 1) { (result) in
switch result {
case .success(let item):
self.saveItemsToDisk(items: item)
self.defaults.set(item.expireDate, forKey: "LastUpdateItems")
case .failure(let error):
fatalError("error: \(error.localizedDescription)")
}
self.getItemesFromDisk()
}
}
//
// Save Json Local
//
func getDocumentsURL() -> URL {
if let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
return url
} else {
fatalError("Could not retrieve documents directory")
}
}
func saveItemsToDisk(items: Base) {
// 1. Create a URL for documents-directory/items.json
let url = getDocumentsURL().appendingPathComponent("items.json")
// 2. Endcode our [Item] data to JSON Data
let encoder = JSONEncoder()
do {
let data = try encoder.encode(items)
// 3. Write this data to the url specified in step 1
try data.write(to: url, options: [])
} catch {
fatalError(error.localizedDescription)
}
}
func getItmesFromDisk(){
// 1. Create a url for documents-directory/items.json
let url = getDocumentsURL().appendingPathComponent("items.json")
let decoder = JSONDecoder()
do {
// 2. Retrieve the data on the file in this path (if there is any)
let data = try Data(contentsOf: url)
// 3. Decode an array of items from this Data
let items = try decoder.decode(Base.self, from: data)
itemsDisk = items
} catch {
}
}
I would create a wrapper class (or struct) for Items, say MyItem, that contains the original Items object and the checkmark property.
class MyItem {
let item: Items
var checkmark: Bool
//more properties...?
init(withItem item: Items {
this.item = item
this.checkmark = false
}
func isEqual(otherItem item: Items) -> Bool {
return this.item == item
}
}
The isEqual is used to check if there already exists an MyItem object for a downloaded Items object or if a new should be created. isEqual assumes that you change the Items struct to implement the Equatable protocol.
You probably also need to replace Weeks but here you don't need to include the original Weeks object.
class MyWeek {
let name: String
let items: [MyItem]
}

Swifty Json getting unknown but long way works fine?

I'm attempting to use SwiftyJson to pull some JSON data.
What's unusual is the "println(json)" says "unknowon" while if I pull the JSON data the regular way it works just fine -- the "println(pop)" says medium, as expected.
Below is the code I'm using. I started cutting out parts until I got to "println(json)" and then decided to try and handle it manually to see if it's SwiftyJson or me.
Any suggestions? I'm fairly new to iOS programming so I'm assuming I'm being silly in some form or another.
var ghostlandsJsonUrl: NSURL = NSURL(string: "http://us.battle.net/api/wow/realm/status?realm=Ghostlands")!
var jsonData: NSData!
var request: NSURLRequest = NSURLRequest(URL: ghostlandsJsonUrl)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task : NSURLSessionDataTask = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
jsonData = data
if(jsonData != nil) {
let json = JSON(jsonData)
println(json)
} else {
println("jsonData: nil value... net down again?")
}
let jsonObject : AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil)
if let statuses = jsonObject as? NSDictionary{
if let realms = statuses["realms"] as? NSArray{
if let realm = realms[0] as? NSDictionary{
if let pop = realm["population"] as? NSString{
println(pop)
}
}
}
}
});
task.resume()
Looking at SwiftyJSON source code I can see that JSON is a simple struct. It implements the Printable protocol. Which give support to the print methods.
public var description: String {
if let string = self.rawString(options:.PrettyPrinted) {
return string
} else {
return "unknown"
}
}
Which means that for a reason or another the rawString method returns nil.
public func rawString(encoding: UInt = NSUTF8StringEncoding, options opt: NSJSONWritingOptions = .PrettyPrinted) -> String? {
switch self.type {
case .Array, .Dictionary:
if let data = self.rawData(options: opt) {
return NSString(data: data, encoding: encoding)
} else {
return nil
}
case .String:
return (self.object as String)
case .Number:
return (self.object as NSNumber).stringValue
case .Bool:
return (self.object as Bool).description
case .Null:
return "null"
default:
return nil
}
}
As you are fairly new to iOS development, I will tell you that the constructor doesn't expect a NSData object.
Here is the source:
public var object: AnyObject {
get {
return _object
}
set {
_object = newValue
switch newValue {
case let number as NSNumber:
if number.isBool {
_type = .Bool
} else {
_type = .Number
}
case let string as NSString:
_type = .String
case let null as NSNull:
_type = .Null
case let array as [AnyObject]:
_type = .Array
case let dictionary as [String : AnyObject]:
_type = .Dictionary
default:
_type = .Unknown
_object = NSNull()
_error = NSError(domain: ErrorDomain, code: ErrorUnsupportedType, userInfo: [NSLocalizedDescriptionKey: "It is a unsupported type"])
}
}
}
So you should pass it the unserialized NSData as it:
if let jsonData = data {
//jsonData can't be nil with this kind of if
let jsonObject : AnyObject! = NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers, error: nil)
let json = JSON(jsonObject)
println(json)
//...
The constructor of JSON does the serialisation. Below is the constructor code from SwiftyJSON git repo where you can directly pass the NSData.
public init(data:NSData, options opt: NSJSONReadingOptions = .AllowFragments, error: NSErrorPointer = nil) {
do {
let object: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: opt)
self.init(object)
} catch let aError as NSError {
if error != nil {
error.memory = aError
}
self.init(NSNull())
}
}
In simple, you can directly use the data returned in the completion handler of NSURLSession data task as below in your code.
let json = JSON(data: jsonData)

Deserialize JSON / NSDictionary to Swift objects

Is there a way to properly deserialize a JSON response to Swift objects resp. using DTOs as containers for fixed JSON APIs?
Something similar to http://james.newtonking.com/json or something like this example from Java
User user = jsonResponse.readEntity(User.class);
whereby jsonResponse.toString() is something like
{
"name": "myUser",
"email": "user#example.com",
"password": "passwordHash"
}
SWIFT 4 Update
Since you give a very simple JSON object the code prepared for to handle that model. If you need more complicated JSON models you need to improve this sample.
Your Custom Object
class Person : NSObject {
var name : String = ""
var email : String = ""
var password : String = ""
init(JSONString: String) {
super.init()
var error : NSError?
let JSONData = JSONString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
let JSONDictionary: Dictionary = NSJSONSerialization.JSONObjectWithData(JSONData, options: nil, error: &error) as NSDictionary
// Loop
for (key, value) in JSONDictionary {
let keyName = key as String
let keyValue: String = value as String
// If property exists
if (self.respondsToSelector(NSSelectorFromString(keyName))) {
self.setValue(keyValue, forKey: keyName)
}
}
// Or you can do it with using
// self.setValuesForKeysWithDictionary(JSONDictionary)
// instead of loop method above
}
}
And this is how you invoke your custom class with JSON string.
override func viewDidLoad() {
super.viewDidLoad()
let jsonString = "{ \"name\":\"myUser\", \"email\":\"user#example.com\", \"password\":\"passwordHash\" }"
var aPerson : Person = Person(JSONString: jsonString)
println(aPerson.name) // Output is "myUser"
}
I recommend that you use code generation (http://www.json4swift.com) to create native models out of the json response, this will save your time of parsing by hand and reduce the risk of errors due to mistaken keys, all elements will be accessible by model properties, this will be purely native and the models will make more sense rather checking the keys.
Your conversion will be as simple as:
let userObject = UserClass(userDictionary)
print(userObject!.name)
Swift 2: I really like the previous post of Mohacs! To make it more object oriented, i wrote a matching Extension:
extension NSObject{
convenience init(jsonStr:String) {
self.init()
if let jsonData = jsonStr.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
{
do {
let json = try NSJSONSerialization.JSONObjectWithData(jsonData, options: []) as! [String: AnyObject]
// Loop
for (key, value) in json {
let keyName = key as String
let keyValue: String = value as! String
// If property exists
if (self.respondsToSelector(NSSelectorFromString(keyName))) {
self.setValue(keyValue, forKey: keyName)
}
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
}
else
{
print("json is of wrong format!")
}
}
}
custom classes:
class Person : NSObject {
var name : String?
var email : String?
var password : String?
}
class Address : NSObject {
var city : String?
var zip : String?
}
invoking custom classes with JSON string:
var jsonString = "{ \"name\":\"myUser\", \"email\":\"user#example.com\", \"password\":\"passwordHash\" }"
let aPerson = Person(jsonStr: jsonString)
print(aPerson.name!) // Output is "myUser"
jsonString = "{ \"city\":\"Berlin\", \"zip\":\"12345\" }"
let aAddress = Address(jsonStr: jsonString)
print(aAddress.city!) // Output is "Berlin"
Yet another JSON handler I wrote:
https://github.com/dankogai/swift-json
With it you can go like this:
let obj:[String:AnyObject] = [
"array": [JSON.null, false, 0, "", [], [:]],
"object":[
"null": JSON.null,
"bool": true,
"int": 42,
"double": 3.141592653589793,
"string": "a α\t弾\n𪚲",
"array": [],
"object": [:]
],
"url":"http://blog.livedoor.com/dankogai/"
]
let json = JSON(obj)
json.toString()
json["object"]["null"].asNull // NSNull()
json["object"]["bool"].asBool // true
json["object"]["int"].asInt // 42
json["object"]["double"].asDouble // 3.141592653589793
json["object"]["string"].asString // "a α\t弾\n𪚲"
json["array"][0].asNull // NSNull()
json["array"][1].asBool // false
json["array"][2].asInt // 0
json["array"][3].asString // ""
As you see no !? needed between subscripts.
In addition to that you can apply your own schema like this:
//// schema by subclassing
class MyJSON : JSON {
override init(_ obj:AnyObject){ super.init(obj) }
override init(_ json:JSON) { super.init(json) }
var null :NSNull? { return self["null"].asNull }
var bool :Bool? { return self["bool"].asBool }
var int :Int? { return self["int"].asInt }
var double:Double? { return self["double"].asDouble }
var string:String? { return self["string"].asString }
var url: String? { return self["url"].asString }
var array :MyJSON { return MyJSON(self["array"]) }
var object:MyJSON { return MyJSON(self["object"]) }
}
let myjson = MyJSON(obj)
myjson.object.null // NSNull?
myjson.object.bool // Bool?
myjson.object.int // Int?
myjson.object.double // Double?
myjson.object.string // String?
myjson.url // String?
There's a great example by Apple for deserializing JSON with Swift 2.0
The trick is to use the guard keyword and chain the assignments like so:
init?(attributes: [String : AnyObject]) {
guard let name = attributes["name"] as? String,
let coordinates = attributes["coordinates"] as? [String: Double],
let latitude = coordinates["lat"],
let longitude = coordinates["lng"],
else {
return nil
}
self.name = name
self.coordinates = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
}
I personally prefer native parsing vs any 3rd party, as it is transparent and magic-less. (and bug less?)
Using quicktype, I generated your model and serialization helpers from your sample:
import Foundation
struct User: Codable {
let name: String
let email: String
let password: String
}
extension User {
static func from(json: String, using encoding: String.Encoding = .utf8) -> OtherUser? {
guard let data = json.data(using: encoding) else { return nil }
return OtherUser.from(data: data)
}
static func from(data: Data) -> OtherUser? {
let decoder = JSONDecoder()
return try? decoder.decode(OtherUser.self, from: data)
}
var jsonData: Data? {
let encoder = JSONEncoder()
return try? encoder.encode(self)
}
var jsonString: String? {
guard let data = self.jsonData else { return nil }
return String(data: data, encoding: .utf8)
}
}
Then parse User values like this:
let user = User.from(json: """{
"name": "myUser",
"email": "user#example.com",
"password": "passwordHash"
}""")!
I wrote this small open-source library recently that lets you quickly and easily deserialize dictionaries into Swift objects: https://github.com/isair/JSONHelper
Using it, deserializing data becomes as easy as this:
var myInstance = MyClass(data: jsonDictionary)
or
myInstance <-- jsonDictionary
And models need to look only like this:
struct SomeObjectType: Deserializable {
var someProperty: Int?
var someOtherProperty: AnotherObjectType?
var yetAnotherProperty: [YetAnotherObjectType]?
init(data: [String: AnyObject]) {
someProperty <-- data["some_key"]
someOtherProperty <-- data["some_other_key"]
yetAnotherProperty <-- data["yet_another_key"]
}
}
Which, in your case, would be:
struct Person: Deserializable {
var name: String?
var email: String?
var password: String?
init(data: [String: AnyObject]) {
name <-- data["name"]
email <-- data["email"]
password <-- data["password"]
}
}
If you would like parse from and to json without the need to manually map keys and fields, then you could also use EVReflection. You can then use code like:
var user:User = User(json:jsonString)
or
var jsonString:String = user.toJsonString()
The only thing you need to do is to use EVObject as your data objects base class.
See the GitHub page for more detailed sample code
I am expanding upon Mohacs and Peter Kreinz's excellent answers just a bit to cover the array of like objects case where each object contains a mixture of valid JSON data types. If the JSON data one is parsing is an array of like objects containing a mixture of JSON data types, the do loop for parsing the JSON data becomes this.
// Array of parsed objects
var parsedObjects = [ParsedObject]()
do {
let json = try NSJSONSerialization.JSONObjectWithData(jsonData, options: []) as [Dictionary<String, AnyObject>]
// Loop through objects
for dict in json {
// ParsedObject is a single instance of an object inside the JSON data
// Its properties are a mixture of String, Int, Double and Bool
let parsedObject = ParsedObject()
// Loop through key/values in object parsed from JSON
for (key, value) in json {
// If property exists, set the value
if (parsedObject.respondsToSelector(NSSelectorFromString(keyName))) {
// setValue can handle AnyObject when assigning property value
parsedObject.setValue(keyValue, forKey: keyName)
}
}
parsedObjects.append(parsedObject)
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
This way lets you get the user from a URL. It's parse the NSData to a NSDictionary and then to your NSObject.
let urlS = "http://api.localhost:3000/"
func getUser(username: Strung) -> User {
var user = User()
let url = NSURL(string: "\(urlS)\(username)")
if let data = NSData(contentsOfURL: url!) {
setKeysAndValues(user, dictionary: parseData(data))
}
return user
}
func setKeysAndValues (object : AnyObject, dictionary : NSDictionary) -> AnyObject {
for (key, value) in dictionary {
if let key = key as? String, let value = value as? String {
if (object.respondsToSelector(NSSelectorFromString(key))) {
object.setValue(value, forKey: key)
}
}
}
return object
}
func parseData (data : NSData) -> NSDictionary {
var error: NSError?
return NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error) as! NSDictionary
}
In Swift 4, You can use the Decoding, CodingKey protocols to deserialize the JSON response:
Create the class which confirm the decodable protocol
class UserInfo: Decodable
Create members of the class
var name: String
var email: String
var password: String
Create JSON key enum which inherits from CodingKey
enum UserInfoCodingKey: String, CodingKey {
case name
case password
case emailId
}
Implement init
required init(from decoder: Decoder) throws
The whole class look like :
Call Decoder
// jsonData is JSON response and we get the userInfo object
let userInfo = try JsonDecoder().decode(UserInfo.self, from: jsonData)
You do this by using NSJSONSerialization. Where data is your JSON.
First wrap it in an if statement to provide some error handling capablity
if let data = data,
json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String: AnyObject] {
// Do stuff
} else {
// Do stuff
print("No Data :/")
}
then assign them:
let email = json["email"] as? String
let name = json["name"] as? String
let password = json["password"] as? String
Now, This will show you the result:
print("Found User iname: \(name) with email: \(email) and pass \(password)")
Taken from this Swift Parse JSON tutorial. You should check out the tutorial as it goes a lot more in depth and covers better error handling.