Which is the best way to create json objects in swift? - ios

I'm trying to create json objects in swift with dictionaries, but the code is getting longer and longer. Is there any way to reduce the lines of for example this code? You can see that both dictionaries are the same except the "id" field
If it's possible without using external libraries
var product: [String: String] = [
"chemical_id":String(activity.chemical!.id),
"plague_id":String(activity.plague!.id),
"justification":activity.justification!,
"amount":activity.amount!,
"amount_scope":activity.amount_scope!,
"security_time":activity.security_time!,
"effectiveness": activity.effectiveness!
]
if activity.id != -1 {
product = [
"id":String(activity.id),
"chemical_id":String(activity.chemical!.id),
"plague_id":String(activity.plague!.id),
"justification":activity.justification!,
"amount":activity.amount!,
"amount_scope":activity.amount_scope!,
"security_time":activity.security_time!,
"effectiveness": activity.effectiveness!
]
}

Just add it on the condition
if activity.id != -1 {
product["id"] = String(activity.id)
}

Related

How to map a json that I don't know the number of the parameters

Hello I have a json the returns me some parameters as variables.
It has Parameter1, Parameter2, Parameter3 etc..
I don't know how many parameters will it give me. It's not a list it's just different variables in the json.
Which is the best way to map a json like that? I use Object Mapper
For Example:
First Time the json is
{
"MyObject": {
"Parameter1": "p1",
"Parameter2": "p2",
"Parameter3": "p3",
"Parameter4": "p4"
}
}
And a second time the json is
{
"MyObject": {
"Parameter1": "p1",
"Parameter2": "p2",
"Parameter3": "p3"
}
}
You can try this.
let keyvalue = parentDict.value(forKey: "MyObject") as! NSDictionary
var lastValue = Keyvalue.allValues
var lastKey = Keyvalue.allKeys
for Keyname in Keyvalue.allKeys
{
print("Keyname %#",Keyname)
print("Value %#",Keyvalue.value(forKey:Keyname))
}
the first step to parse any JSON to make it reusable is to create your Model class or struct accordingly.
Create a class called MyObject as same as your json dictionary
Create a let/var property parameters: [String]?. It's optional as API isn't reliable and maybe wont send anything at all.
See the example below how I parse the json object below.
class MyObject {
let parameters: [String]?
// it's failable because maybe json different at runtime & couldn't parse
init?(json: [String:AnyObject]) {
var key = "Parameter"
var parms = [String]()
for i in 0..<json.count {
guard let item = json["\(key)\(i+1)"] as? String else { continue }
params.append(item)
}
self.parameters = params
}
}
Now you can access the parameters array with index.
Well this could be refactored and you can get the idea how you will handle this with that library.

Get a value from a dictionary given the key of a different field

I'm using the third-party library SwiftAddressBook to work with Contacts in my app. I want to iterate through contacts in my device and extract a few specific values. Namely the twitter and facebook usernames if they exist.
SwiftAddressBook has an object called SwiftAddressBookPerson which represents a single contact. And SwiftAddressBookPerson object has a property called socialProfiles which contains an array of dictionaries. Each dictionary contains info about available social media account such as the username, url etc. Printing the value of socialProfiles looks like this.
[
SwiftAddressBook.MultivalueEntry<Swift.Dictionary<SwiftAddressBook.SwiftAddressBookSocialProfileProperty, Swift.String>>(value: [
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.url: "http://twitter.com/isuru",
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.username: "isuru",
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.service: "twitter"],
label: nil, id: 0),
SwiftAddressBook.MultivalueEntry<Swift.Dictionary<SwiftAddressBook.SwiftAddressBookSocialProfileProperty, Swift.String>>(value: [
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.url: "http://www.facebook.com/isuru",
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.username: "isuru",
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.service: "facebook"],
label: Optional("facebook"), id: 1)
]
I cleaned it up a little by doing this socialProfiles.map { $0.map { $0.value } } which outputs the following.
[
[
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.url: "http://twitter.com/isuru",
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.username: "isuru",
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.service: "twitter"
],
[
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.url: "http://www.facebook.com/isuru",
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.username: "isuru",
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.service: "facebook"
]
]
What I want now is given the service's name (twitter, facebook), retrieve the username used for that service.
How do I parse through the dictionary and get the value of a different field?
Given that this is an array, I'd use a first { $0.service == serviceName }.
Alternatively, you could loop over the entire array ahead of time to create a dictionary with key as serviceName, and value as the profile property. The advantage of this approach is subsequent lookups would be much faster.
I have named your array of swift address book dictionaries, profiles. The forced unwrapping in the flatMap call is safe because both key and value are tested for non-nil in the previous filter call.
The result is a single dictionary containing username values for the services listed in your interestingServices array.
let service = SwiftAddressBook.SwiftAddressBookSocialProfileProperty.service
let username = SwiftAddressBook.SwiftAddressBookSocialProfileProperty.username
let interestingServices = ["facebook", "twitter"]
let usernames = profiles
.filter {
guard
let service = $0[service],
$0[username] != nil
else {
return false
}
return interestingServices.contains(service)
}
.flatMap { profile in
return [profile[service]!, profile[username]!]
}

How to read-write json file without disturbing the sequences of its key-value pairs in iOS?

I am serializing a json file whose key-value pairs should not be shuffled when writing to a new file after editing. Even if I do not edit it still goes shuffles the pairs.
I just need the same sequence of the key-value pairs in the new file(written file) as it was in the previous file that I read.
Here is the sample json
[
{
"StudentName":"Amit",
"StudentId":"1"
},
{
"StudentName":"Lalit",
"StudentId":"2"
},
{
"StudentName":"Ram",
"StudentId":"3"
},
{
"StudentName":"Shyam",
"StudentId":"4"
}
]
What I am getting after writing the jsonObject to a new file is:
[
{
"StudentName":"Lalit",
"StudentId":"2"
},
{
"StudentName":"Ram",
"StudentId":"3"
},
{
"StudentName":"Shyam",
"StudentId":"4"
},
{
"StudentName":"Amit",
"StudentId":"1"
}
]
I have two code files, since I am working on command line tool.
1)main.swift
import Foundation
var behavioralJsonObject : AnyObject
var newBehavioralDataObject: NSData = NSData()
let fileManager = NSFileManager.defaultManager()
var path = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first
var stringPath: String = (path?.path)!
var behavioralFilePath: String = stringPath.stringByAppendingString("/BehavioralFile.json")
var newBehavioralFilePath : String = stringPath.stringByAppendingString("/BehavioralFileNew.json")
behavioralJsonObject = MyJsonParser().jsonObject(withFilePath: behavioralFilePath)
print(behavioralJsonObject.description)
newBehavioralDataObject = try NSJSONSerialization.dataWithJSONObject(behavioralJsonObject, options: .PrettyPrinted)
newBehavioralDataObject.writeToFile(newBehavioralFilePath, atomically: true)
2) MyJsonParser.swift
import Foundation
class MyJsonParser: NSObject {
func jsonObject(withFilePath path:String)-> AnyObject{
let dataObject = NSData.init(contentsOfFile: path)
var jsonObject: AnyObject = []
do{
jsonObject = try NSJSONSerialization.JSONObjectWithData(dataObject!, options: .AllowFragments)
}
catch{
print("Serialization error : \(error)")
}
return jsonObject;
}
}
Has anybody already faced this problem or has a solution for this?
Please re-check your code. If your code is correct, you are parsing a JSON array, converting it back to data, and don't write the array elements in the correct order. If that is what really happens, then the only thing you can do is send a bug report to Apple and let them fix it. Array elements have a defined order; if that is changed by the act of reading and writing the array then there is a serious bug somewhere.
It's different with dictionaries. The key/value pairs in a dictionary are unordered. You can't find out in which order they were parsed, and the order in which they are written is undefined. So if your first array element was written as
{
"StudentId":"1"
"StudentName":"Amit",
},
that would be totally to be expected and completely correct. Changing the order of array elements however is a bug. Note that if you actually read a dictionary and printed out the array of values of the dictionary, that would again be in undefined order.

How to push data with multiple types in Firebase?

I want to push an array that has strings, numbers and date. Do I have to update individually or is there another way I can accomplish this?
Example:
var categories : [String] = self.parseCategories()
var standbyDataStrings = [
"firstName":firstName,
"lastName":lastName,
"categories": categories,
"time_stamp":self.date.timeIntervalSince1970
]
var standbyDataNums = [
"radius":nf.numberFromString(self.helpRadiusLabel.text!),
"duration":nf.numberFromString(self.helpDurationLabel.text!)
]
standbyUserRef.updateChildValues(standbyDataStrings)
standbyUserRef.updateChildValues(standbyDataNums) // this gives me a error "string is not identical to NSObject"
Combining standByDataStrings and standbyDataNums gives me an error.
Or is there a way to retrieve a string from Firebase and using it as an int. It gets stored as a String with the quotations.
The Firebase API expects an NSDictionary for updateChildValues. A NSDictionary can't contain nil values. A normal Swift dictionary on the other hand can contain nil values.
The return type of numberFromString is NSNumber?, so Swift infers that the dictionary might contain nil and so it can't be passed to updateChildValues. By explicitly forcing non-nil values with ! you can make this code compile:
var standbyDataNums = [
"radius":nf.numberFromString(self.helpRadiusLabel.text!)!,
"duration":nf.numberFromString(self.helpDurationLabel.text!)!
]
standbyUserRef.updateChildValues(standbyDataNums) // now type checks

Looping an array of NSDictionaries in swift

Ok, trying to catch the last train to learn Swift, I have seen similar questions but I am not getting them to solve my issue.
I have an NSDictionary called entries, and one of the values, corresponding to key "TYPES" is an NSArray of NSDictionaries. I am trying to loop over this latter NSDictionary and retrieve an integer value for the key "TID", I am doing:
for dict in entries["TYPES"] as NSDictionary {
let tid : Int = typeDict["TID"]
}
But I am receiving as error: (key: AnyObject, value: AnyObject) does not have a member named 'subscript'
I understand this is due to entries["TYPES"] being anyObject! and comes from Xcode 6 beta 6, where a large number of Foundation APIs have been audited for optional conformance and hence need unwrapping but I have tried my best to unwrap without success, the compiler is always complaining a different thing. Someone knows how to do this?
If this is a sample of your dictionary:
var entries: NSDictionary = [
"TYPES": [
[],
["TPD": 2],
["TID": 4]
] as NSArray
]
you have to:
retrieve the element identified by the TYPES key, and attempt to cast as NSArray
loop through all elements of the array
attempt a cast of each element as NSDictionary
check for the TID key existence, and read its value
if the value is not nil, the search is over
This is the code:
var tid: Int?
if let types = entries["TYPES"] as? NSArray {
for type in types {
if let dict = types.lastObject as? NSDictionary {
tid = dict["TID"] as? Int
if tid != nil {
break
}
}
}
}
Running the code in a playground with the sample data, the output I see is {Some 4}.
However I would keep #Zaph's advice into account and model your data in a different way, using structs and/or classes

Resources