How to create JSON from Model in ios Swift 3 - ios

I have created model class for the user as below.
public class SignUpUser {
public var fullName : String?
public var id : Int?
public var city : String?
public var email : String?
public var address : String?
public var lastName : String?
public var countryCode : String?
public var firstName : String?
public var zipCode : Int?
public var contactNumber : Int?
public var sex : String?
public var dob : String?
public var signupType : String?
public var verified : String?
public var emailTokenExpiration : String?
public var updatedAt : String?
public var createdAt : String?
/**
Returns an array of models based on given dictionary.
Sample usage:
let user_list = User.modelsFromDictionaryArray(someDictionaryArrayFromJSON)
- parameter array: NSArray from JSON dictionary.
- returns: Array of User Instances.
*/
public class func modelsFromDictionaryArray(array:NSArray) -> [SignUpUser]
{
var models:[SignUpUser] = []
for item in array
{
models.append(SignUpUser(dictionary: item as! NSDictionary)!)
}
return models
}
/**
Constructs the object based on the given dictionary.
Sample usage:
let user = User(someDictionaryFromJSON)
- parameter dictionary: NSDictionary from JSON.
- returns: User Instance.
*/
init?() {}
required public init?(dictionary: NSDictionary) {
fullName = dictionary["fullName"] as? String
id = dictionary["id"] as? Int
city = dictionary["city"] as? String
email = dictionary["email"] as? String
address = dictionary["address"] as? String
lastName = dictionary["lastName"] as? String
countryCode = dictionary["countryCode"] as? String
firstName = dictionary["firstName"] as? String
zipCode = dictionary["zipCode"] as? Int
contactNumber = dictionary["contactNumber"] as? Int
sex = dictionary["sex"] as? String
dob = dictionary["dob"] as? String
signupType = dictionary["signupType"] as? String
verified = dictionary["verified"] as? String
emailTokenExpiration = dictionary["emailTokenExpiration"] as? String
updatedAt = dictionary["updatedAt"] as? String
createdAt = dictionary["createdAt"] as? String
}
/**
Returns the dictionary representation for the current instance.
- returns: NSDictionary.
*/
public func dictionaryRepresentation() -> NSDictionary {
let dictionary = NSMutableDictionary()
dictionary.setValue(self.fullName, forKey: "fullName")
dictionary.setValue(self.id, forKey: "id")
dictionary.setValue(self.city, forKey: "city")
dictionary.setValue(self.email, forKey: "email")
dictionary.setValue(self.address, forKey: "address")
dictionary.setValue(self.lastName, forKey: "lastName")
dictionary.setValue(self.countryCode, forKey: "countryCode")
dictionary.setValue(self.firstName, forKey: "firstName")
dictionary.setValue(self.zipCode, forKey: "zipCode")
dictionary.setValue(self.contactNumber, forKey: "contactNumber")
dictionary.setValue(self.sex, forKey: "sex")
dictionary.setValue(self.dob, forKey: "dob")
dictionary.setValue(self.signupType, forKey: "signupType")
dictionary.setValue(self.verified, forKey: "verified")
dictionary.setValue(self.emailTokenExpiration, forKey: "emailTokenExpiration")
dictionary.setValue(self.updatedAt, forKey: "updatedAt")
dictionary.setValue(self.createdAt, forKey: "createdAt")
return dictionary
}
}
I am trying to conver the object to JSON with following way but getting error saying "invalid top-level type in json write"
let signUpuser = SignUpUser()
signUpuser?.fullName = "Teswt"
signUpuser?.id = 1
signUpuser?.city = "Test"
signUpuser?.email = "Test"
signUpuser?.address = "Test"
signUpuser?.lastName = "Test"
signUpuser?.countryCode = "Test"
signUpuser?.firstName = "Test"
signUpuser?.zipCode = 380004
signUpuser?.contactNumber = 12345
signUpuser?.sex = "Test"
signUpuser?.dob = "Test"
signUpuser?.signupType = "Test"
signUpuser?.verified = "Test"
signUpuser?.emailTokenExpiration = "Test"
signUpuser?.updatedAt = "Test"
signUpuser?.createdAt = "Test"
if let jsonData = try? JSONSerialization.data(withJSONObject: signUpuser, options: []) {
let theJSONText = String(data: jsonData, encoding: .utf8)
AppLog.debug(tag: TAG, msg: theJSONText!)
}
In Android using Google's gson library we can easily convert JSON to Object and vice versa but in iOS it seems bit difficult.
I also tried to wrap the SignupUser object inside other class object but no luck.
"Wrapping inside other class..."
let wrapperObject = JSONServerRequest(data: signUpuser)
if let jsonData = try? JSONSerialization.data(withJSONObject: wrapperObject, options: []) {
let theJSONText = String(data: jsonData, encoding: .utf8)
AppLog.debug(tag: TAG, msg: theJSONText!)
}
I don't wish to do this with Dictionary as I have to write the keys every time, I prefer to work with objects so If anyone has any clue please kindly guide me.

Add this method in your model class
func toDictionary() -> [String : Any] {
var dictionary = [String:Any]()
let otherSelf = Mirror(reflecting: self)
for child in otherSelf.children {
if let key = child.label {
dictionary[key] = child.value
}
}
print("USER_DICTIONARY :: \(dictionary.description)")
return dictionary
}
This will give the dictionary representation of your model class
so you can access like youclassObj.toDictionary()
and follow as per requirement : Convert Dictionary to JSON in Swift

Related

swift 3.0 How can I access `AnyHashable` types in `Any` in Swift 3?

I'm using sqlite file to get the diaryEntriesTeacher from the authorId. it generates the following object of authorId when I print the variable authorId is nil
Code :-
func applySelectQuery() {
checkDataBaseFile()
objFMDB = FMDatabase(path: fullPathOfDB)
objFMDB.open()
objFMDB.beginTransaction()
do {
let results = try objFMDB.executeQuery("select * from diaryEntriesTeacher", values: nil)
while results.next() {
let totalCount = results.resultDictionary
let authorId = totalCount?["authorId"]!
print("authorId",authorId)
}
}
catch {
print(error.localizedDescription)
}
print(fullPathOfDB)
self.objFMDB.commit()
self.objFMDB.close()
}
output
This is how you access Dictionary of [AnyHashable : Any]
var dict : Dictionary = Dictionary<AnyHashable,Any>()
dict["name"] = "sandeep"
let myName : String = dict["name"] as? String ?? ""
In your case
let authorId = totalCount?["authorId"] as? String ?? ""
We need to convert the property we are trying to access to AnyHashable before using it.
In your case :
do {
let results = try objFMDB.executeQuery("select * from diaryEntriesTeacher", values: nil)
while results.next() {
let totalCount = results.resultDictionary
let authorId = totalCount?[AnyHashable("authorId")]!
print("authorId",authorId)
}
This is Swift. Use strong types and fast enumeration. Dictionary<AnyHashable,Any> is the generic type of a dictionary and can be cast to <String,Any> as all keys seem to be String.
do
if let results = try objFMDB.executeQuery("select * from diaryEntriesTeacher", values: nil) as? [[String:Any]]
for item in results {
let authorId = item["authorId"] as? String
let studentName = item["studentName"] as? String
print("authorId", authorId ?? "n/a")
print("studentName", studentName ?? "n/a")
}
}
....

How to work with Firebase without allowing optional values

I'm new to iOS development and I understand that allowing optional values when an object is initialized is not a 'good citizen' technique. That being said, I've read that it is good practice to always have values set, like this:
class Item{
var name: String
var color: String
init(name: String, color: String) {
self.name = name
self.color = color
}
}
This looks nice and tidy but how can I do something like that working with Firebase? Look what I've got so far:
private func loadPosts(){
databaseHandle = ref.child("users/\(self.user.uid)/posts").observe(.value, with:{(snapshot) in
var newPosts = [Post]()
for itemSnapShot in snapshot.children {
let post = Post(snapshot: itemSnapShot as! FIRDataSnapshot)
newPosts.append(post!)
}
self.posts = newPosts
self.tableView.reloadData()
})
}
This guy is placed in my PostsViewController where I have my table view. This is my model:
class Post {
var ref: FIRDatabaseReference?
var title: String?
var answer: String?
var contentUrl: String?
var photoUrl: String?
var createdAt: String?
var feeling: String?
var kind: String?
var text: String?
var uid: String?
var measurements: Dictionary<String, String>?
//MARK: Initialization
init?(snapshot: FIRDataSnapshot){
ref = snapshot.ref
let data = snapshot.value as! Dictionary<String, Any>
title = data["title"]! as? String
answer = data["answer"] as? String
contentUrl = data["content_url"] as? String
photoUrl = data["photo_url"] as? String
createdAt = data["created_at"] as? String
feeling = data["feeling"] as? String
kind = data["kind"] as? String
text = data["text"] as? String
uid = data["uid"] as? String
measurements = data["measurements"] as? Dictionary<String, String>
}
}
I don't know exactly why but those question marks doesn't feel quite right and now and then I get some nil pointer error, which I think I should be able to avoid by using the 'good citizen' technique.
So, does anybody know how can I use Firebase following Swift best practices?
Either you wish to allow the properties of your Post class to be nil or you don't.
If you do, that's fine. The code you posted allows any of them to be nil. You just need to safely access each property every time you need it.
If you don't, then don't make them optional. Then in your init you need to ensure none of the properties are set to nil by giving each a default if there is no value in the snapshot.
class Post {
var ref: FIRDatabaseReference
var title: String
var answer: String
var contentUrl: String
var photoUrl: String
var createdAt: String
var feeling: String
var kind: String
var text: String
var uid: String
var measurements: [String : String]
//MARK: Initialization
init?(snapshot: FIRDataSnapshot) {
if let data = snapshot.value as? [String : Any] {
self.ref = snapshot.ref
title = data["title"] as? String ?? ""
answer = data["answer"] as? String ?? ""
contentUrl = data["content_url"] as? String ?? ""
photoUrl = data["photo_url"] as? String ?? ""
createdAt = data["created_at"] as? String ?? ""
feeling = data["feeling"] as? String ?? ""
kind = data["kind"] as? String ?? ""
text = data["text"] as? String ?? ""
uid = data["uid"] as? String ?? ""
measurements = data["measurements"] as? [String : String] ?? [:]
} else {
return nil
}
}
}
Note how this ensures there is a proper snapshot. Note how a default value is set to each property if there is no value in the snapshot. Obviously you can assign any default you wish. I use the empty string as an example.
Even if you want to allow the properties to be nil, you should at least update your code to check for a valid snapshot like in the code above.
Of course you can have a combination where some properties can't be nil and some can. That's up to your needs.
First it is fine for you to have optionals in your data model, as long as you assign value to it later on in the future.
I would recommend to use ObserveSingleEvent() and you should make use of completion handler to make it easy. If you don't know completion handler: Link
I recommend:
• not to put database ref in your class model, and instead of using Dictionary<String, String>? just use [String: AnyObject]?
• make your post array public so that it can be accessed into the tableview.
Here's example:
class func getPosts(uid: String, _ completion: #escaping (_ posts: [Post]?, _ error: Error?) -> Void) {
//update inside users node
var posts = [Post]()
Firebase.databaseRef.child("users").child(uid).child("posts").observeSingleEvent(of: FIRDataEventType.value, with: { (dataSnapshot) in
guard let postsDictionary = dataSnapshot.value as? [String: AnyObject] else {
completion(nil, nil)
return
}
let n = postsDictionary.count
for postDictionary in postsDictionary {
let post = Post()
post.userID = uid
if let content = postDictionary.value["content"] as? String {
post.content = content
}
if let imageURL = postDictionary.value["imageURL"] as? String {
post.imageURL = imageURL
}
if let timeStamp = postDictionary.key as String! {
if let date = timeStamp.convertToDate() {
post.timeStamp = date
}
post.postIdentifier = timeStamp
}
posts.append(post)
if posts.count == n {
// Sort the array by the newest post
let sortedPosts = posts.sorted(by: { $0.timeStamp.compare($1.timeStamp) == .orderedDescending })
completion(sortedPosts, nil)
}
}
}) { (error) in
completion(nil, error)
}
}
Assigning to tableview be like:
getPosts(uid: Current.user.userID!) { (posts, error) in
guard error == nil else {
print(error.debugDescription)
return
}
cell.label.text = posts[indexPath.item].content

swift3 parsing with model issue

Hello I am trying to parse through this json file: http://pastebin.com/TCdkJnvZ
Here is the class I made of the information I want to parse out:
public class Recipe: NSObject {
var recipeID : NSNumber?
var categoryName : String?
var ingredients : [Int : Ingredients]?
var nutrition : [Nutrition]?
var imageName : String?
var instructions : [Int : String]?
}
class Ingredients : NSObject {
var id : NSNumber?
var name : String?
var quantity: NSNumber?
var unit : String?
}
class Nutrition : NSObject {
var serving : String?
var calories : NSNumber?
var fat : String?
var carbs : NSNumber?
}
This image is the current issue.. I am really not sure what I am doing wrong here.. so if I can get any help on fixing my logic/issue it would be appreciated.
func parseToJSON(data: Any) {
// add meals to here
var recipes : [Recipe]
// single meals here
var meals : Recipe
do {
if let json = try JSONSerialization.jsonObject(with: data as! Data) as? [String: Any],
meals.recipeID == json["recipeID"] as! NSNumber? ,
meals.imageName == json["ImageURL"] as! String?,
//meals.instructions == meals.parseInstructions(instructions: (json["Instructions"] as! String)),
meals.categoryName == "Meals" ,
let ingredients = json["Ingredients"] as! [[String: Any]]? {
for items in ingredients {
var i : Int = 0
var groceryItems : Ingredients
groceryItems.id = items["IngredientID"] as? NSNumber
groceryItems.name = items["Name"] as? String
groceryItems.quantity = items["Quantity"] as? NSNumber
groceryItems.unit = items["Unit"] as? String
meals.ingredients?[i] = groceryItems
}
};
let nutritionInfo = json["NutritionInfo"] as! [[String: Any]]? {
for items in nutritionInfo {
var nutrition : Nutrition
nutrition.serving = items["SingularYieldUnit"] as? String
nutrition.calories = items["TotalCalories"] as? NSNumber
nutrition.fat = items["TotalFat"] as? String
nutrition.carbs = items["TotalCarbs"] as NSNumber
meals.nutrition = nutrition
}
};
}
catch{
}
}
It looks like you have a variety of syntax errors, but the compiler can only show one issue at a time. I've cleaned up the code slightly for you, which should push you in the right direction. I can't completely fix it because I don't know what your exact intentions are.
Here is the updated parseToJSON function:
func parseToJSON(data: Any) {
let meals = Recipe()
do {
if let json = try JSONSerialization.jsonObject(with: data as! Data) as? [String: Any] {
meals.recipeID == json["recipeID"] as! NSNumber?
meals.imageName == json["ImageURL"] as! String?
//meals.instructions == meals.parseInstructions(instructions: (json["Instructions"] as! String)),
meals.categoryName == "Meals"
if let ingredients = json["Ingredients"] as! [[String: Any]]? {
for items in ingredients {
let groceryItems = Ingredients()
groceryItems.id = items["IngredientID"] as? NSNumber
groceryItems.name = items["Name"] as? String
groceryItems.quantity = items["Quantity"] as? NSNumber
groceryItems.unit = items["Unit"] as? String
meals.ingredients?.append(groceryItems)
}
}
if let nutritionInfo = json["NutritionInfo"] as! [[String: Any]]? {
for items in nutritionInfo {
let nutrition = Nutrition()
nutrition.serving = items["SingularYieldUnit"] as? String
nutrition.calories = items["TotalCalories"] as? NSNumber
nutrition.fat = items["TotalFat"] as? String
nutrition.carbs = items["TotalCarbs"] as? NSNumber
meals.nutrition?.append(nutrition)
}
}
}
}
catch{
}
}
I also changed the Recipe object's ingredients property to:
var ingredients : [Ingredients]?
The main issue was that a lot of your code was inside of an if let expression and your indentation was off so you couldn't as easily tell.

Making a Dictionary in Swift

I have an object taken from Parse and I want to save its columns into a Dictionary or something else (if it's better).
I want to have a Dictionary like this: ["name" : "Mike", "lastname" : "vorisis", "id" : "advsas"]
Below is the code I use to take my results:
func queryEvents() {
let query = PFQuery(className: "eventController")
query.limit = 1000
query.includeKey("idEvent")
query.includeKey("eventType")
query.includeKey("idEvent.idMagazi")
query.findObjectsInBackgroundWithBlock { (objects, error)-> Void in
if let objects = objects {
for object in objects {
var post = object["idEvent"] as? PFObject
var post2 = post!["idMagazi"]
print("retrieved related post: \(post2["name"]!)")
}
}
}
}
Something else (if it's better) is a custom class.
Change the type of idMagazi to the real type.
class Event {
let post : PFObject
let name : String
let idMagazi : String
init(object : PFObject) {
self.post = object
self.name = object["name"] as! String
self.idMagazi = object["idMagazi"] as! String
}
}
And use it
...
if let objects = objects as? [PFObject] {
var events = [Event]()
for object in objects {
let post = Event(object: object)
events.append(post)
print("retrieved related post: \(post.name)")
}
}
...
In Swift Dictionary<T,V> is equivalent to [T: V]. Type is inferred if not explicitly declared.
Empty dictionary creation (all equivalent):
var dict1: Dictionary<String, String> = [:]
var dict2: [String: String] = [:]
var dict3 = Dictionary<String, String>()
var dict4 = [String: String]()
Dictionary with values (all equivalent):
var dict5 = ["Foo": "Bar", "Foo1": "Bar1", "Foo2": "Bar2"]
var dict6: Dictionary<String, String> = ["Foo": "Bar", "Foo1": "Bar1", "Foo2": "Bar2"]
var dict7: [String: String] = ["Foo": "Bar", "Foo1": "Bar1", "Foo2": "Bar2"]
Add values to an existing dictionary:
dict["Foo"] = "Bar"
In your specific scenario, you could use this:
let dict = ["name" : name, "lastname" : lastname , "id" : id]
where name, lastname and id are String variables.
Update based on your own answer:
Having this struct:
struct Event {
var nameEvent: String
var nameMagazi: String
}
You can use this approach, that avoid having an external index and uses an array instead of a dictionary for storing the results.
var events: [Event]?
guard let objects = objects else { return }
events = objects.map { object in
let post = object["idEvent"] as? PFObject
let post2 = post!["idMagazi"] as? PFObject
let nameEvent = post!["name"] as! String
let idEvent = post?.objectId
let nameMagazi = post2!["name"] as! String
return Event(nameEvent: nameEvent , nameMagazi: nameMagazi)
}
I finally found it out how can i do it.
I use a struct with what I want like this:
var userDictionary = [Int : Event]()
struct Event {
var nameEvent: String
var nameMagazi: String
}
And then i use this:
if let objects = objects {
for object in objects {
let post = object["idEvent"] as? PFObject
let post2 = post!["idMagazi"] as? PFObject
let nameEvent = post!["name"] as! String
let idEvent = post?.objectId
let nameMagazi = post2!["name"] as! String
self.events[self.i] = Event(nameEvent: nameEvent , nameMagazi: nameMagazi)
self.i += 1
}
print(self.events[1]!.nameEvent)
}
Thank you all for your answers!
Create dictionary like this:
var dict = ["name" : "Mike", "lastname" : "vorisis" , "id" : "advsas"]
OR
var dict = Dictionary<String,String>
dict.setValue("Mike", forKey: "name")
A dictionary can be made using this line:
var dictionaryArray: [Dictionary<String,String,Int>] = []
Then values can be added by appending them to the dictionary.
dictionaryArray.append(["name" : "Mike", "lastname" : "vorisis" , "id" : "advsas"])
Hope this helps.

Swift: Parsing Array of object

I have a Contact object which can have and array of Address objects.
class Contact {
var firstName: String = ""
var lastName: String = ""
var middleName: String = ""
var id: Int = -1
var addresses: Array<Address> = []
How would I initialize each Address object while fetching it from json dictionary?
init(json: Dictionary<String, AnyObject>) {
if let line1 = json["streetLine"] as? String {
self.streetLine1 = line1
}
if let city = json["city"] as? String {
self.city = city
}
if let state = json["state"] as? String {
self.state = state
}
if let zip = json["zip"] as? Int {
self.zip = zip
}
Tried doing like this:
if let addressMap = json["addresses"] as? Array<Dictionary<String, AnyObject?>> {
for address as? Address in addressMap {
addresses.append(address)
}
}
Try this:
if let dictionaryArray = json["addresses"] as? Array<Dictionary<String, Anyobject?>>
for address in dictionaryArray {
var address = Address(json: address)
addresses.append(address)
}
}
So you get your list of Dictionarys from your json, loop through them and then use your init function to inject the data into your class.
Try this
addresses = (json["addresses"] as? Array<Dictionary<String, AnyObject?>>)?.map{Address(json: $0)}

Resources