How to initialize multiple dictionary in json in 1 NSObject using SwiftyJson - ios

My API response is look like this
{
"error": false,
"id": "6",
"user_id": 7,
"users": [
{
"user_id": 1,
"username": "spiderman"
},
{
"user_id": 7,
"username": "wonderwoman"
}
],
"info": [
{
"id": 471,
"message": "abc",
"age": 10,
}
]
}
I know how to initialize the value of id,user_id and error in NSOject. But I dont know how can I initialize the array of users and info in the same NSObject class.
Now I initialize the JSON like this
import UIKit
import SwiftyJSON
class MyItem: NSObject {
var userId : Int
var error : Bool
var id : Int
init?(dict: [String :JSON]) {
self.id = dict["id"]?.int ?? 0
self.error = dict["error"]?.bool ?? false
self.userId = dict["userId"]?.int ?? 0
}
}
Now the problem is I don't know how to initialize data inside the users and info dictionary.How should I arrange it and how can I use it in other class
Kindly give an example.

Use as below,
Root Class :-
import Foundation
import SwiftyJSON
class RootClass : NSObject, NSCoding{
var error : Bool!
var id : String!
var info : [Info]!
var userId : Int!
var users : [User]!
/**
* Instantiate the instance using the passed json values to set the properties values
*/
init(fromJson json: JSON!){
if json.isEmpty{
return
}
error = json["error"].boolValue
id = json["id"].stringValue
info = [Info]()
let infoArray = json["info"].arrayValue
for infoJson in infoArray{
let value = Info(fromJson: infoJson)
info.append(value)
}
userId = json["user_id"].intValue
users = [User]()
let usersArray = json["users"].arrayValue
for usersJson in usersArray{
let value = User(fromJson: usersJson)
users.append(value)
}
}
/**
* Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
*/
func toDictionary() -> [String:Any]
{
let dictionary = [String:Any]()
if error != nil{
dictionary["error"] = error
}
if id != nil{
dictionary["id"] = id
}
if info != nil{
var dictionaryElements = [[String:Any]]()
for infoElement in info {
dictionaryElements.append(infoElement.toDictionary())
}
dictionary["info"] = dictionaryElements
}
if userId != nil{
dictionary["user_id"] = userId
}
if users != nil{
var dictionaryElements = [[String:Any]]()
for usersElement in users {
dictionaryElements.append(usersElement.toDictionary())
}
dictionary["users"] = dictionaryElements
}
return dictionary
}
/**
* NSCoding required initializer.
* Fills the data from the passed decoder
*/
#objc required init(coder aDecoder: NSCoder)
{
error = aDecoder.decodeObject(forKey: "error") as? Bool
id = aDecoder.decodeObject(forKey: "id") as? String
info = aDecoder.decodeObject(forKey: "info") as? [Info]
userId = aDecoder.decodeObject(forKey: "user_id") as? Int
users = aDecoder.decodeObject(forKey: "users") as? [User]
}
/**
* NSCoding required method.
* Encodes mode properties into the decoder
*/
func encode(with aCoder: NSCoder)
{
if error != nil{
aCoder.encode(error, forKey: "error")
}
if id != nil{
aCoder.encode(id, forKey: "id")
}
if info != nil{
aCoder.encode(info, forKey: "info")
}
if userId != nil{
aCoder.encode(userId, forKey: "user_id")
}
if users != nil{
aCoder.encode(users, forKey: "users")
}
}
}
User Class :-
import Foundation
import SwiftyJSON
class User : NSObject, NSCoding{
var userId : Int!
var username : String!
/**
* Instantiate the instance using the passed json values to set the properties values
*/
init(fromJson json: JSON!){
if json.isEmpty{
return
}
userId = json["user_id"].intValue
username = json["username"].stringValue
}
/**
* Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
*/
func toDictionary() -> [String:Any]
{
let dictionary = [String:Any]()
if userId != nil{
dictionary["user_id"] = userId
}
if username != nil{
dictionary["username"] = username
}
return dictionary
}
/**
* NSCoding required initializer.
* Fills the data from the passed decoder
*/
#objc required init(coder aDecoder: NSCoder)
{
userId = aDecoder.decodeObject(forKey: "user_id") as? Int
username = aDecoder.decodeObject(forKey: "username") as? String
}
/**
* NSCoding required method.
* Encodes mode properties into the decoder
*/
func encode(with aCoder: NSCoder)
{
if userId != nil{
aCoder.encode(userId, forKey: "user_id")
}
if username != nil{
aCoder.encode(username, forKey: "username")
}
}
}
Info Class :-
import Foundation
import SwiftyJSON
class Info : NSObject, NSCoding{
var age : Int!
var id : Int!
var message : String!
/**
* Instantiate the instance using the passed json values to set the properties values
*/
init(fromJson json: JSON!){
if json.isEmpty{
return
}
age = json["age"].intValue
id = json["id"].intValue
message = json["message"].stringValue
}
/**
* Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
*/
func toDictionary() -> [String:Any]
{
let dictionary = [String:Any]()
if age != nil{
dictionary["age"] = age
}
if id != nil{
dictionary["id"] = id
}
if message != nil{
dictionary["message"] = message
}
return dictionary
}
/**
* NSCoding required initializer.
* Fills the data from the passed decoder
*/
#objc required init(coder aDecoder: NSCoder)
{
age = aDecoder.decodeObject(forKey: "age") as? Int
id = aDecoder.decodeObject(forKey: "id") as? Int
message = aDecoder.decodeObject(forKey: "message") as? String
}
/**
* NSCoding required method.
* Encodes mode properties into the decoder
*/
func encode(with aCoder: NSCoder)
{
if age != nil{
aCoder.encode(age, forKey: "age")
}
if id != nil{
aCoder.encode(id, forKey: "id")
}
if message != nil{
aCoder.encode(message, forKey: "message")
}
}
}

The best way to do this is to create 2 different classes for user and info as follows:
class MyItem : NSObject {
var error : Bool!
var id : String!
var info : [Info]!
var userId : Int!
var users : [User]!
init(fromJson json: JSON!){
if json == nil{
return
}
error = json["error"].boolValue
id = json["id"].stringValue
info = [Info]()
let infoArray = json["info"].arrayValue
for infoJson in infoArray{
let value = Info(fromJson: infoJson)
info.append(value)
}
userId = json["user_id"].intValue
users = [User]()
let usersArray = json["users"].arrayValue
for usersJson in usersArray{
let value = User(fromJson: usersJson)
users.append(value)
}
}
}
class User : NSObject {
var userId : Int!
var username : String!
init(fromJson json: JSON!){
if json == nil{
return
}
userId = json["user_id"].intValue
username = json["username"].stringValue
}
}
class Info : NSObject {
var age : Int!
var id : Int!
var message : String!
init(fromJson json: JSON!){
if json == nil{
return
}
age = json["age"].intValue
id = json["id"].intValue
message = json["message"].stringValue
}
}
By doing this you would be able to directly access the value of user and info like for eg: MyItem.users[index].userId

Do Like this,
class MyItem: NSObject {
var userId : Int
var error : Bool
var id : Int
var users : [[String:Any]] = []
var info : [[String:Any]] = []
init?(dict: [String :JSON]) {
self.id = dict["id"]?.int ?? 0
self.error = dict["error"]?.bool ?? false
self.userId = dict["userId"]?.int ?? 0
self.users = dict["users"] ?? []
self.info = dict["info"] ?? []
}
}

No offense to the developers of SwiftyJSON, it is a great library, but in Swift 4 to decode JSON SwiftyJSON became obsolete.
With the Decodable protocol you are able to decode the JSON without any extra code.
Create one struct including the two substructs, the coding keys are only necessary to map snake_case to camelCase.
struct MyItem: Decodable {
private enum CodingKeys: String, CodingKey { case userId = "user_id", error, id, users, info}
let userId : Int
let error : Bool
let id : String
let users : [User]
let info : [Info]
struct User : Decodable {
private enum CodingKeys: String, CodingKey { case userId = "user_id", username}
let userId : Int
let username : String
}
struct Info : Decodable {
let message : String
let id, age : Int
}
}
Now decode the JSON
let jsonString = """
{
"error": false,
"id": "6",
"user_id": 7,
"users": [{"user_id": 1, "username": "spiderman"},{"user_id": 7,"username": "wonderwoman"}],
"info": [{"id": 471,"message": "abc", "age": 10}]
}
"""
do {
let data = Data(jsonString.utf8)
let decoder = JSONDecoder()
let result = try JSONDecoder().decode(MyItem.self, from: data)
for user in result.users {
print(user.userId, user.username)
}
} catch { print(error) }

Related

How to add Values to NSObject Model Variables in Swift?

I have created separate NSObject class called ProfileModel
like below:
class ProfileModel : NSObject, NSCoding{
var userId : String!
var phone : String!
var firstName : String!
var email : String!
var profileImageUrl : String!
var userAddresses : [ProfileModelUserAddress]!
// Instantiate the instance using the passed dictionary values to set the properties values
init(fromDictionary dictionary: [String:Any]){
userId = dictionary["userId"] as? String
phone = dictionary["phone"] as? String
firstName = dictionary["firstName"] as? String
email = dictionary["email"] as? String
profileImageUrl = dictionary["profileImageUrl"] as? String
}
/**
* Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
*/
func toDictionary() -> [String:Any]
{
var dictionary = [String:Any]()
if userId != nil{
dictionary["userId"] = userId
}
if phone != nil{
dictionary["phone"] = phone
}
if firstName != nil{
dictionary["firstName"] = firstName
}
if email != nil{
dictionary["email"] = email
}
if profileImageUrl != nil{
dictionary["profileImageUrl"] = profileImageUrl
}
return dictionary
}
/**
* NSCoding required initializer.
* Fills the data from the passed decoder
*/
#objc required init(coder aDecoder: NSCoder)
{
userId = aDecoder.decodeObject(forKey: "userId") as? String
userType = aDecoder.decodeObject(forKey: "userType") as? String
phone = aDecoder.decodeObject(forKey: "phone") as? String
firstName = aDecoder.decodeObject(forKey: "firstName") as? String
email = aDecoder.decodeObject(forKey: "email") as? String
profileImageUrl = aDecoder.decodeObject(forKey: "profileImageUrl") as? String
}
/**
* NSCoding required method.
* Encodes mode properties into the decoder
*/
#objc func encode(with aCoder: NSCoder)
{
if userId != nil{
aCoder.encode(userId, forKey: "userId")
}
if phone != nil{
aCoder.encode(phone, forKey: "phone")
}
if firstName != nil{
aCoder.encode(firstName, forKey: "firstName")
}
if email != nil{
aCoder.encode(email, forKey: "email")
}
if profileImageUrl != nil{
aCoder.encode(profileImageUrl, forKey: "profileImageUrl")
}
}
}
In RegistrationViewController I adding firstName value which i need to show in ProfileViewController How ?
In RegistrationViewController i am adding firstName and phone values which i need in ProfileViewController:
class RegistrationViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var firstNameTextField: FloatingTextField!
var userModel : ProfileModel?
override func viewDidLoad() {
let userID: String=jsonObj?["userId"] as? String ?? ""
self.userModel?.firstName = self.firstNameTextField.text
self.userModel?.phone = phoneTextField.text
}
}
This is ProfileViewController here in name and number i am not getting firstName and phone values why?:
class ProfileViewController: UIViewController {
#IBOutlet var name: UILabel!
#IBOutlet var number: UILabel!
var userModel : ProfileModel?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
name.text = userModel?.firstName
number.text = userModel?.phone
}
}
PLease help me with code.
You cannot set firstName or phone to the userModal which is nil. First you should create an instance, and then you can pass it through your controllers. We should change code step by step:
class ProfileModel {
var userId : String?
var phone : String?
var firstName : String?
var email : String?
var profileImageUrl : String?
var userAddresses : [ProfileModelUserAddress]?
init() {}
}
Second, you need to reach ProfileModel instance from both of your ViewController classes. For this, you can create a singleton class:
class ProfileManager {
static var shared = ProfileManager()
var userModel: ProfileModel?
private init() {}
}
Then you can reach it from both of your ViewControllers:
class RegistrationViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var firstNameTextField: FloatingTextField!
override func viewDidLoad() {
super.viewDidLoad()
let userModel = ProfileModel()
userModel.firstName = self.firstNameTextField.text
ProfileManager.shared.userModel = userModel
}
}
Other VC:
class ProfileViewController: UIViewController {
#IBOutlet var name: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
if let userModel = ProfileManager.shared.userModel,
let firstName = userModel.firstName {
name.text = firstName
}
}
}
Modify it as you wanted.

JSONSerialization parsing issue:

I am experiencing an error while parsing a json
"hoursOfOperation" : {
"sat" : {
"enabled" : true,
"schedule" : [
{
"time" : {
"close" : 1020,
"open" : 600
}
}
]
},
"fri" : {
"enabled" : true,
"schedule" : [
{
"time" : {
"close" : 1260,
"open" : 660
}
}
]
},
"sun" : {
"enabled" : true,
"schedule" : [
{
"time" : {
"close" : 1020,
"open" : 600
}
}
]
},
"mon" : {
"enabled" : true,
"schedule" : [
{
"time" : {
"close" : 1260,
"open" : 960
}
}
]
},
"tue" : {
"enabled" : true,
"schedule" : [
{
"time" : {
"close" : 1260,
"open" : 660
}
}
]
},
"wed" : {
"enabled" : true,
"schedule" : [
{
"time" : {
"close" : 1260,
"open" : 660
}
}
]
},
"thu" : {
"enabled" : true,
"schedule" : [
{
"time" : {
"close" : 1260,
"open" : 660
}
}
]
}
}
This is JSON shared. I'm using JSONSerialization for parsing the same. Code is as below:
struct HoursOfOperation{
var sun : hoursOfOperationData?
var mon : hoursOfOperationData?
var tue : hoursOfOperationData?
var wed : hoursOfOperationData?
var thu : hoursOfOperationData?
var fri : hoursOfOperationData?
var sat : hoursOfOperationData?
init(_ info: AnyObject) {
let s = String(describing: info)
let data = s.data(using: String.Encoding.utf8, allowLossyConversion: false)!
do {
let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String: AnyObject]
if let sun = json["sun"]{
self.sun = hoursOfOperationData.init(sun)
}
if let sat = json["sat"]{
self.sat = hoursOfOperationData.init(sat)
}
if let fri = json["fri"]{
self.fri = hoursOfOperationData.init(fri)
}
if let thu = json["thu"] {
self.thu = hoursOfOperationData.init(thu)
}
if let wed = json["wed"]{
self.wed = hoursOfOperationData.init(wed)
}
if let tue = json["tue"]{
self.tue = hoursOfOperationData.init(tue)
}
if let mon = json["mon"] {
self.mon = hoursOfOperationData.init(mon)
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
}
}
//hoursOfOperationData
struct hoursOfOperationData{
var enabled : AnyObject?
var schedule : [scheduleData]?
init(_ info: AnyObject) {
let s = String(describing: info)
let data = s.data(using: String.Encoding.utf8, allowLossyConversion: false)!
do {
let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String: AnyObject]
if let enabled = json["enabled"]{
self.enabled = enabled as AnyObject
}
if let schedule = json["schedule"] as? NSArray{
for dic in schedule{
schedule.adding(scheduleData.init(dic as AnyObject))
}
self.schedule = schedule as? [scheduleData]
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
}
}
//scheduleData
struct scheduleData{
var time : scheduleDataForLocation?
init(_ info: AnyObject) {
let s = String(describing: info)
let data = s.data(using: String.Encoding.utf8, allowLossyConversion: false)!
do {
let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String: AnyObject]
if let time = json["time"]{
self.time = scheduleDataForLocation.init(time)
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
}
}
//scheduleDataForLocation
struct scheduleDataForLocation{
var openTime : AnyObject?
var closeTime : AnyObject?
init(_ info: AnyObject) {
let s = String(describing: info)
let data = s.data(using: String.Encoding.utf8, allowLossyConversion: false)!
do {
let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String: AnyObject]
if let open = json["open"]{
self.openTime = open as AnyObject
}
if let close = json["close"]{
self.closeTime = close as AnyObject
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
}
}
While Preparing this model Im unable to parse the json and getting error message
' Error Domain=NSCocoaErrorDomain Code=3840 "No string key for value in object around character 6." UserInfo={NSDebugDescription=No string key for value in object around character 6.} '
Please suggest a correct way to handle the same. I have visited many stack overflow questions and answers but none answers my question. Any help would be appreciable.
You can directly use below model class, reference : http://www.jsoncafe.com/
class OperationModel : NSObject, NSCoding{
var hoursOfOperation : HoursOfOperation!
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
init(fromDictionary dictionary: [String:Any]){
if let hoursOfOperationData = dictionary["hoursOfOperation"] as? [String:Any]{
hoursOfOperation = HoursOfOperation(fromDictionary: hoursOfOperationData)
}
}
/**
* Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
*/
func toDictionary() -> [String:Any]
{
var dictionary = [String:Any]()
if hoursOfOperation != nil{
dictionary["hoursOfOperation"] = hoursOfOperation.toDictionary()
}
return dictionary
}
/**
* NSCoding required initializer.
* Fills the data from the passed decoder
*/
#objc required init(coder aDecoder: NSCoder)
{
hoursOfOperation = aDecoder.decodeObject(forKey: "hoursOfOperation") as? HoursOfOperation
}
/**
* NSCoding required method.
* Encodes mode properties into the decoder
*/
#objc func encode(with aCoder: NSCoder)
{
if hoursOfOperation != nil{
aCoder.encode(hoursOfOperation, forKey: "hoursOfOperation")
}
}
}
class Schedule : NSObject, NSCoding{
var time : Time!
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
init(fromDictionary dictionary: [String:Any]){
if let timeData = dictionary["time"] as? [String:Any]{
time = Time(fromDictionary: timeData)
}
}
/**
* Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
*/
func toDictionary() -> [String:Any]
{
var dictionary = [String:Any]()
if time != nil{
dictionary["time"] = time.toDictionary()
}
return dictionary
}
/**
* NSCoding required initializer.
* Fills the data from the passed decoder
*/
#objc required init(coder aDecoder: NSCoder)
{
time = aDecoder.decodeObject(forKey: "time") as? Time
}
/**
* NSCoding required method.
* Encodes mode properties into the decoder
*/
#objc func encode(with aCoder: NSCoder)
{
if time != nil{
aCoder.encode(time, forKey: "time")
}
}
}
class HoursOfOperation : NSObject, NSCoding{
var fri : Day!
var mon : Day!
var sat : Day!
var sun : Day!
var thu : Day!
var tue : Day!
var wed : Day!
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
init(fromDictionary dictionary: [String:Any]){
if let friData = dictionary["fri"] as? [String:Any]{
fri = Day(fromDictionary: friData)
}
if let monData = dictionary["mon"] as? [String:Any]{
mon = Day(fromDictionary: monData)
}
if let satData = dictionary["sat"] as? [String:Any]{
sat = Day(fromDictionary: satData)
}
if let sunData = dictionary["sun"] as? [String:Any]{
sun = Day(fromDictionary: sunData)
}
if let thuData = dictionary["thu"] as? [String:Any]{
thu = Day(fromDictionary: thuData)
}
if let tueData = dictionary["tue"] as? [String:Any]{
tue = Day(fromDictionary: tueData)
}
if let wedData = dictionary["wed"] as? [String:Any]{
wed = Day(fromDictionary: wedData)
}
}
/**
* Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
*/
func toDictionary() -> [String:Any]
{
var dictionary = [String:Any]()
if fri != nil{
dictionary["fri"] = fri.toDictionary()
}
if mon != nil{
dictionary["mon"] = mon.toDictionary()
}
if sat != nil{
dictionary["sat"] = sat.toDictionary()
}
if sun != nil{
dictionary["sun"] = sun.toDictionary()
}
if thu != nil{
dictionary["thu"] = thu.toDictionary()
}
if tue != nil{
dictionary["tue"] = tue.toDictionary()
}
if wed != nil{
dictionary["wed"] = wed.toDictionary()
}
return dictionary
}
/**
* NSCoding required initializer.
* Fills the data from the passed decoder
*/
#objc required init(coder aDecoder: NSCoder)
{
fri = aDecoder.decodeObject(forKey: "fri") as? Day
mon = aDecoder.decodeObject(forKey: "mon") as? Day
sat = aDecoder.decodeObject(forKey: "sat") as? Day
sun = aDecoder.decodeObject(forKey: "sun") as? Day
thu = aDecoder.decodeObject(forKey: "thu") as? Day
tue = aDecoder.decodeObject(forKey: "tue") as? Day
wed = aDecoder.decodeObject(forKey: "wed") as? Day
}
/**
* NSCoding required method.
* Encodes mode properties into the decoder
*/
#objc func encode(with aCoder: NSCoder)
{
if fri != nil{
aCoder.encode(fri, forKey: "fri")
}
if mon != nil{
aCoder.encode(mon, forKey: "mon")
}
if sat != nil{
aCoder.encode(sat, forKey: "sat")
}
if sun != nil{
aCoder.encode(sun, forKey: "sun")
}
if thu != nil{
aCoder.encode(thu, forKey: "thu")
}
if tue != nil{
aCoder.encode(tue, forKey: "tue")
}
if wed != nil{
aCoder.encode(wed, forKey: "wed")
}
}
}
class Time : NSObject, NSCoding{
var close : Int!
var open : Int!
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
init(fromDictionary dictionary: [String:Any]){
close = dictionary["close"] as? Int
open = dictionary["open"] as? Int
}
/**
* Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
*/
func toDictionary() -> [String:Any]
{
var dictionary = [String:Any]()
if close != nil{
dictionary["close"] = close
}
if open != nil{
dictionary["open"] = open
}
return dictionary
}
/**
* NSCoding required initializer.
* Fills the data from the passed decoder
*/
#objc required init(coder aDecoder: NSCoder)
{
close = aDecoder.decodeObject(forKey: "close") as? Int
open = aDecoder.decodeObject(forKey: "open") as? Int
}
/**
* NSCoding required method.
* Encodes mode properties into the decoder
*/
#objc func encode(with aCoder: NSCoder)
{
if close != nil{
aCoder.encode(close, forKey: "close")
}
if open != nil{
aCoder.encode(open, forKey: "open")
}
}
}
class Day : NSObject, NSCoding{
var enabled : Bool!
var schedule : [Schedule]!
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
init(fromDictionary dictionary: [String:Any]){
enabled = dictionary["enabled"] as? Bool
schedule = [Schedule]()
if let scheduleArray = dictionary["schedule"] as? [[String:Any]]{
for dic in scheduleArray{
let value = Schedule(fromDictionary: dic)
schedule.append(value)
}
}
}
/**
* Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
*/
func toDictionary() -> [String:Any]
{
var dictionary = [String:Any]()
if enabled != nil{
dictionary["enabled"] = enabled
}
if schedule != nil{
var dictionaryElements = [[String:Any]]()
for scheduleElement in schedule {
dictionaryElements.append(scheduleElement.toDictionary())
}
dictionary["schedule"] = dictionaryElements
}
return dictionary
}
/**
* NSCoding required initializer.
* Fills the data from the passed decoder
*/
#objc required init(coder aDecoder: NSCoder)
{
enabled = aDecoder.decodeObject(forKey: "enabled") as? Bool
schedule = aDecoder.decodeObject(forKey: "schedule") as? [Schedule]
}
/**
* NSCoding required method.
* Encodes mode properties into the decoder
*/
#objc func encode(with aCoder: NSCoder)
{
if enabled != nil{
aCoder.encode(enabled, forKey: "enabled")
}
if schedule != nil{
aCoder.encode(schedule, forKey: "schedule")
}
}
}
Use :
let jsonData = try Data(contentsOf: url)
let json = try JSONSerialization.jsonObject(with: jsonData) as! [String : Any]
let arrOper = OperationModel(fromDictionary: json)
Output:
I have found some problem if you JSON which is related with Missing opening brace or square bracket { }
so first correct that and then your JSON will be:
{
"hoursOfOperation" : {
"sat" : {
"enabled" : true,
"schedule" : [
{
"time" : {
"close" : 1020,
"open" : 600
}
}
]
},
"fri" : {
"enabled" : true,
"schedule" : [
{
"time" : {
"close" : 1260,
"open" : 660
}
}
]
},
"sun" : {
"enabled" : true,
"schedule" : [
{
"time" : {
"close" : 1020,
"open" : 600
}
}
]
},
"mon" : {
"enabled" : true,
"schedule" : [
{
"time" : {
"close" : 1260,
"open" : 960
}
}
]
},
"tue" : {
"enabled" : true,
"schedule" : [
{
"time" : {
"close" : 1260,
"open" : 660
}
}
]
},
"wed" : {
"enabled" : true,
"schedule" : [
{
"time" : {
"close" : 1260,
"open" : 660
}
}
]
},
"thu" : {
"enabled" : true,
"schedule" : [
{
"time" : {
"close" : 1260,
"open" : 660
}
}
]
}
}
}
Now you can create your Codable protocol with THIS site and code it will look like:
import Foundation
struct ScheduleData: Codable {
let hoursOfOperation: HoursOfOperation
}
struct HoursOfOperation: Codable {
let sat, fri, sun, mon: Fri
let tue, wed, thu: Fri
}
struct Fri: Codable {
let enabled: Bool
let schedule: [Schedule]
}
struct Schedule: Codable {
let time: Time
}
struct Time: Codable {
let close, timeOpen: Int
enum CodingKeys: String, CodingKey {
case close
case timeOpen = "open"
}
}
Uses :
let scheduleData = try? JSONDecoder().decode(ScheduleData.self, from: response.data!)
let close_time = scheduleData?.hoursOfOperation.sat.schedule[0].time.close
Print(close_time)
Output :
1020

How to parse JSON using Alamofire 4.7 in Swift 4

I know this question was asked before, but answers were in Swift 3 and using older versions of Alamofire.
Problem: Can't figure out how to retrieve data from JSON response, mainly api_key.
Here is code for my response:
Alamofire.request(serverLink!, headers: headers).responseJSON{ response in
if response.value != nil {
//Some code to get api_key
print(response)
} else {
print("error")
}
When I print(response) I get the following:
SUCCESS: {
user = {
"api_key" = 9a13f31770b80767a57d753961acbd3a18eb1370;
"created_on" = "2010-09-30T12:57:42Z";
firstname = Paul;
id = 4;
"last_login_on" = "2018-03-27T10:15:10+03:00";
lastname = Smith;
login = admin;
mail = "admin#demo.com";
status = 1;
};
}
What I need to get is
"api_key" = 9a13f31770b80767a57d753961acbd3a18eb1370;
It could be in form of array, dict or just string containing:
9a13f31770b807...
Could someone please explain to me how to get(decode) it from this request?
EDIT
print(response.result.value):
RESPONSE: Optional({ user = { "api_key" = 9a13f31770b80767a57d753961acbd3a18eb1370; "created_on" = "2010-09-30T12:57:42Z"; firstname = Paul; id = 4; "last_login_on" = "2018-03-27T11:10:25+03:00"; lastname = Smith; login = admin; mail = "admin#demo.com"; status = 1; }; })
As per the docs, this is how you access the serialised JSON response:
if let json = response.result.value as? [String: Any] {
print("JSON: \(json)") // serialized json response
}
To access api_key you just need to unwrap the success and user dictionaries first and then you can access the api_key property in the user dictionary.
guard let user = json["user"] as? [String: Any],
let apiKey = user["api_key"] as? String else {
print("Failed to parse JSON")
return
}
print(apiKey)
When you get the response you can. use Mapper class of ObjectMapper library.
Create Model Class
import Foundation
import ObjectMapper
public final class LoginModel: Mappable, NSCoding {
// MARK: Declaration for string constants to be used to decode and also serialize.
private struct SerializationKeys {
static let status = "status"
static let login = "login"
static let firstname = "firstname"
static let id = "id"
static let lastname = "lastname"
static let mail = "mail"
static let apiKey = "api_key"
static let createdOn = "created_on"
static let lastLoginOn = "last_login_on"
}
// MARK: Properties
public var status: Int?
public var login: String?
public var firstname: String?
public var id: Int?
public var lastname: String?
public var mail: String?
public var apiKey: String?
public var createdOn: String?
public var lastLoginOn: String?
// MARK: ObjectMapper Initializers
/// Map a JSON object to this class using ObjectMapper.
///
/// - parameter map: A mapping from ObjectMapper.
public required init?(map: Map){
}
/// Map a JSON object to this class using ObjectMapper.
///
/// - parameter map: A mapping from ObjectMapper.
public func mapping(map: Map) {
status <- map[SerializationKeys.status]
login <- map[SerializationKeys.login]
firstname <- map[SerializationKeys.firstname]
id <- map[SerializationKeys.id]
lastname <- map[SerializationKeys.lastname]
mail <- map[SerializationKeys.mail]
apiKey <- map[SerializationKeys.apiKey]
createdOn <- map[SerializationKeys.createdOn]
lastLoginOn <- map[SerializationKeys.lastLoginOn]
}
/// Generates description of the object in the form of a NSDictionary.
///
/// - returns: A Key value pair containing all valid values in the object.
public func dictionaryRepresentation() -> [String: Any] {
var dictionary: [String: Any] = [:]
if let value = status { dictionary[SerializationKeys.status] = value }
if let value = login { dictionary[SerializationKeys.login] = value }
if let value = firstname { dictionary[SerializationKeys.firstname] = value }
if let value = id { dictionary[SerializationKeys.id] = value }
if let value = lastname { dictionary[SerializationKeys.lastname] = value }
if let value = mail { dictionary[SerializationKeys.mail] = value }
if let value = apiKey { dictionary[SerializationKeys.apiKey] = value }
if let value = createdOn { dictionary[SerializationKeys.createdOn] = value }
if let value = lastLoginOn { dictionary[SerializationKeys.lastLoginOn] = value }
return dictionary
}
// MARK: NSCoding Protocol
required public init(coder aDecoder: NSCoder) {
self.status = aDecoder.decodeObject(forKey: SerializationKeys.status) as? Int
self.login = aDecoder.decodeObject(forKey: SerializationKeys.login) as? String
self.firstname = aDecoder.decodeObject(forKey: SerializationKeys.firstname) as? String
self.id = aDecoder.decodeObject(forKey: SerializationKeys.id) as? Int
self.lastname = aDecoder.decodeObject(forKey: SerializationKeys.lastname) as? String
self.mail = aDecoder.decodeObject(forKey: SerializationKeys.mail) as? String
self.apiKey = aDecoder.decodeObject(forKey: SerializationKeys.apiKey) as? String
self.createdOn = aDecoder.decodeObject(forKey: SerializationKeys.createdOn) as? String
self.lastLoginOn = aDecoder.decodeObject(forKey: SerializationKeys.lastLoginOn) as? String
}
public func encode(with aCoder: NSCoder) {
aCoder.encode(status, forKey: SerializationKeys.status)
aCoder.encode(login, forKey: SerializationKeys.login)
aCoder.encode(firstname, forKey: SerializationKeys.firstname)
aCoder.encode(id, forKey: SerializationKeys.id)
aCoder.encode(lastname, forKey: SerializationKeys.lastname)
aCoder.encode(mail, forKey: SerializationKeys.mail)
aCoder.encode(apiKey, forKey: SerializationKeys.apiKey)
aCoder.encode(createdOn, forKey: SerializationKeys.createdOn)
aCoder.encode(lastLoginOn, forKey: SerializationKeys.lastLoginOn)
}
}
User this model class to map your response...
Alamofire.request(serverLink!, headers: headers).responseJSON{ response in
if let responseData = Mapper< LoginModel>().map(JSONObject: response.result.value) {
print(responseData.apiKey)
} else {
print("Fail to map data")
}
}

Saving Array of Struct with NSKeyedArchiver and Userdefault in which object conform to NSCoding

As a Protocol oriented programming concept, I have created my model with Struct.
I want to save Array of "Struct" into Userdefault. But I am having a problem in encode/decode of the array of this model.
Here is my model Struct
struct Room {
let name : String
let id : String
let booked : Bool
}
Here I created a extension like this
extension Room {
func decode() -> Room? {
let userClassObject = NSKeyedUnarchiver.unarchiveObject(withFile: RoomClass.path()) as? RoomClass
return userClassObject?.room
}
func encode() {
let personClassObject = RoomClass(room: self)
NSKeyedArchiver.archiveRootObject(personClassObject, toFile: RoomClass.path())
}
class RoomClass: NSObject, NSCoding {
var room : Room?
init(room: Room) {
self.room = room
super.init()
}
class func path() -> String {
let documentsPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first
let path = documentsPath?.appending(("/Room"))
return path!
}
func encode(with aCoder: NSCoder) {
aCoder.encode(room!.name, forKey: "name")
aCoder.encode(room!.id, forKey: "Id")
aCoder.encode(room!.booked, forKey: "booked")
}
required init?(coder aDecoder: NSCoder) {
let _name = aDecoder.decodeObject(forKey: "name") as? String
let _id = aDecoder.decodeObject(forKey: "Id") as? String
let _booked = aDecoder.decodeBool(forKey: "booked")
room = Room(name: _name!, id: _id!, booked: _booked)
super.init()
}
}
}
When I am trying to save arrRoomList(a Array of Room objects) like this
self.saveRooms(arrayRooms: arrRoomList)
I got this error
[_SwiftValue encodeWithCoder:]: unrecognized selector sent to instance
I have also tried to encode each object first and then try to save them in default, then also it gives an error.
Can anyone please guide me how to encode/decode the array of Struct in Userdefaults in a proper way without converting it into Dictionary?
you can setup the struct to use NSKeyedArchiver directly like this:
struct Room {
let name : String
let id : String
let booked : Bool
}
extension Room {
func encode() -> Data {
let data = NSMutableData()
let archiver = NSKeyedArchiver(forWritingWith: data)
archiver.encode(name, forKey: "name")
archiver.encode(id, forKey: "id")
archiver.encode(booked, forKey: "booked")
archiver.finishEncoding()
return data as Data
}
init?(data: Data) {
let unarchiver = NSKeyedUnarchiver(forReadingWith: data)
defer {
unarchiver.finishDecoding()
}
guard let name = unarchiver.decodeObject(forKey: "name") as? String else { return nil }
guard let id = unarchiver.decodeObject(forKey: "id") as? String else { return nil }
booked = unarchiver.decodeBool(forKey: "booked")
self.name = name
self.id = id
}
}
to use with UserDefaults, call like this:
// to encode to data and save to user defaults
let room = Room(name: "asdf", id: "123", booked: true)
UserDefaults.standard.set(room.encode(), forKey: "room")
// to retrieve from user defaults
if let data = UserDefaults.standard.object(forKey: "room") as? Data {
let room = Room(data: data)
}
Can save/retrieve an array of rooms like this:
func saveRooms(arrayRooms: [Room]) {
let roomsData = arrayRooms.map { $0.encode() }
UserDefaults.standard.set(roomsData, forKey: "rooms")
}
func getRooms() -> [Room]? {
guard let roomsData = UserDefaults.standard.object(forKey: "rooms") as? [Data] else { return nil }
return roomsData.flatMap { return Room(data: $0) }
}
// save 2 rooms to user defaults
let roomA = Room(name: "A", id: "123", booked: true)
let roomB = Room(name: "B", id: "asdf", booked: false)
saveRooms(arrayRooms: [roomA, roomB])
// get the rooms
print(getRooms())
You can try Model like
class CardModel: NSObject
{
let name : String
let id : String
let booked : Bool
override init()
{
self.name = ""
self.id = ""
self.booked = false
}
required init(coder aDecoder: NSCoder)
{
self.name = aDecoder.decodeObject(forKey: "name") as! String
self.id = aDecoder.decodeObject(forKey: "id") as! String
self.booked = aDecoder.decodeObject(forKey: "booked") as! Bool
}
func encodeWithCoder(_ aCoder: NSCoder)
{
aCoder.encode(name, forKey: "name")
aCoder.encode(id, forKey: "id")
aCoder.encode(booked, forKey: "booked")
}
}
Use by Creating CardModel model Object
let objCardModel = CardModel()
objCardModel.name = "Shrikant"
objCardModel.id = "8"
objCardModel.booked = true
Access by object
let userName = objCardModel.name

how to use updateValue to add an object in Swift

I have a User Struct that I'm casting to Json to be able to get into NSUserDefaults...
import Foundation
struct User {
var name = ""
var stores: [Store] = []
init?(json: [String: AnyObject]) {
if let name = json["name"] as? String,
storesJSON = json["stores"] as? [[String: AnyObject]]
{
self.name = name
self.stores = storesJSON.map { Store(json: $0)! }
} else {
return nil
}
}
init() { }
func toJSON() -> [String: AnyObject] {
return [
"name": name,
"stores": stores.map { $0.toJSON() }
]
}
}
and I am using a Data Manager class (Singleton) to add a new User. But I can't figure out what to pass into updateValue in my addPerson function below? Alternatively is there another way to get this object into NSUserDefaults?
import Foundation
class DataManager {
static let sharedInstance = DataManager()
var users = [String : User]()
init() {
let userDefaults = NSUserDefaults.standardUserDefaults()
if let var userFromDefaults = userDefaults.objectForKey("users") as? [String : User] {
users = userFromDefaults
}
else {
// add default values later
}
}
var userList: [String] {
var list: [String] = []
for userName in users.keys {
list.append(userName)
}
list.sort(<)
return list
}
func addPerson(newUserName: String) {
users.updateValue(User(), forKey: newUserName)
// saveData()
}
You should change your interface of the addPerson function, use addPerson(newUser: User) instead of using addPerson(newUserName: String) as #iosDev82 said:
// Because your addPerson function needs two parameters: a name and a user object
func addPerson(newUser: User) {
users.updateValue(newUser, forKey: newUser.name)
// saveData()
}
so you can:
let newName = textField.text.capitalizedString
let newUser = User(["name": newName, "stores" : []])
DataManager.sharedInstance.addPerson(newUser)
I think you already know how to create a User object. And that is what you should pass as an argument to your following function. Something like this.
var aUser = User(["name": textField.text. capitalizedString])
DataManager.sharedInstance.addPerson(aUser)
func addPerson(newUser: User) {
users[newUser.name] = newUser
// saveData()
}

Resources