Parsing JSON Data - ios

I want to parse this JSON :http://jsonplaceholder.typicode.com/users
I have a problem for find JSON structure.
I am trying JSON with this structure that is work well, but I don't sure of this is better way or is not!
what is the best way for parsing this JSON array to post instance?
there is my code:
func profileFromJSONData(data : NSData) -> ProfileResult {
do{
let jsonObject : NSArray!
= try NSJSONSerialization.JSONObjectWithData(data, options: []) as! NSArray
for profileJSON in jsonObject {
if let profile = profileFromJsonObject(profileJSON as! NSDictionary) {
finalProfile.append(profile)
}
}
return .Success(finalProfile)
}
catch let error {
return .Failure(error)
}
}
func profileFromJsonObject(json: NSDictionary) -> UserProfile?{
guard let
id = json["id"] as? Int,
name = json["name"] as? String,
userName = json["username"] as? String,
email = json["email"] as? String,
address = json["address"] as? NSDictionary,
phone = json["phone"] as? String,
website = json["website"] as? String,
company = json["company"] as? NSDictionary
else {
return nil
}
let obj = UserProfile(id: id, name: name, userName: userName, email: email, address: address, phone: phone, website: website, company: company)
return obj
}

Here is what apple suggestion when Working with JSON in Swift,
and you can improve your code to one line by using flatMap
change from:
for profileJSON in jsonObject {
if let profile = profileFromJsonObject(profileJSON) {
finalProfile.append(profile)
}
}
to:
finalProfile += jsonObject.flatMap(profileFromJsonObject)
That's the same.
Deal with address:
struct Address {
var street: String
var city: String
...
init?(json: [String: AnyObject]){...}
}
extension Address: CustomStringConvertible {
var description: String {
return "street: \(street), city: \(city)"
}
}
func profileFromJsonObject(json: [String: AnyObject]) -> UserProfile? {
guard let
...
addressJson = json["address"] as? [String: AnyObject]
address = Address(json: addressJson),
...
else {
return nil
}
let obj = UserProfile(id: id, name: name, userName: userName, email: email, address: address, phone: phone, website: website, company: company)
print(address) // output: "street: Kulas Light, city: Gwenborough"
}

Related

Reduce compact map time Swift

I am using compact map to get models (NSObject) from a json array [[String: Any]].
I made some tests and seems that it takes some time to get my objects from json array. It's about 1 sec for 300 objects in json array.
The problem is that i am dealing with a big app data and i can have thousands of objects in json array.
class ModelObject: NSObject {
var data: Data?
var json: [String: Any]?
init(withJSON json: [String: Any]?) {
data = json?.toData
self.json = json
}
init(withId id: String? = nil,
withType type: String? = nil,
withStatus status: Int? = nil,
withName name: String? = nil) {
super.init()
updateContent(withId: id,
withType: type,
withStatus: status,
withName: name)
}
func updateContent(withJSON json: [String: Any]? = nil,
withId id: String? = nil,
withType type: String? = nil,
withStatus status: Int? = nil,
withName name: String? = nil) {
var json = self.json ?? [String: Any]()
if let id = id {
json["_id"] = id
}
if let type = type {
json["type"] = type
}
if let status = status {
json["status"] = status
}
if let name = name {
json["name"] = name
}
}
}
extension Dictionary {
var toData: Data? {
return try? JSONSerialization.data(withJSONObject: self, options: [])
}
}
It's there any other possibility to reduce time or to get it instantly?
This is my code:
return fileJSONArray.compactMap { ModelObject(withJSON: $0) }

Display lastName and name in application after retrieve value key in token Swift 4

I come back with my problem and I still find no answer
according to the previous solutions I had to develop the function to retrieve information (lastname, name) on the token
func loadMemberProfil(completion: ((_ sub : [String: AnyObject]) -> Void)!) {
// get API profile and Bearer token
let token = HPWSLoginManager.shared().saveSuccessResponse.token
let url = URL(string: "http://51.38.36.76:40/api/v1/profile")
var request = URLRequest(url: url!)
request.httpMethod = "GET"
request.addValue("Bearer \(token!)", forHTTPHeaderField: "Authorization")
//get information in token
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else { return }
do {
let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as! [String: AnyObject]
let sub = json["sub"] as! [String: AnyObject]
if completion != nil{
completion(sub)
}
} catch {
print("error")
}
}.resume()
}
And in this function (that I call in viewDidLoad()) I want to display the name and lastname but nothing is displayed when I run the emulator
func initTableViewModel() {
self.loadMemberProfil { (sub) in
let headerMenu = HPMenuHeaderViewModel.init(firstName: "\(sub["firstname"])", lastName: "\(sub["lastname"])")
let tableViewModel = HPMenuViewModel(titlePage: "Menu", array: arrayCellModel, headerMenuViewModel: headerMenu)
self.tableViewModel = tableViewModel
}
}
but i recover the values in the console please hep me
You are fetching but not reloading the data. You might want to read the documentation to understand the problem.
After you initTableViewModel you need to use the code below to reload the data. Note that the DispatchQueue.main.async is for doing that in the main thread.
DispatchQueue.main.async {
self.tableView.reloadData()
}
in HPMenuHeaderViewModel class
class HPMenuHeaderViewModel: NSObject {
var imageName: UIImage?
var firstName: String?
var lastName: String?
var numberString: String?
var name: String {
return String.safe(firstName) + " " + String.safe(lastName)
}
var nameInitials: String {
let arrayName = self.name.components(separatedBy: " ")
return arrayName.reduce("") { $0.safePrefix(1) + $1.safePrefix(1)}
}
#objc init(firstName: String, lastName: String, numberString: String) {
super.init()
self.firstName = firstName
self.lastName = lastName
self.numberString = numberString
}
}
in HPMenuViewModel class
class HPMenuViewModel: NSObject {
var titlePage: String?
var accountTableViewCellModel: [HPMenuTableViewCellModel]?
var headerMenuViewModel: HPMenuHeaderViewModel?
init(titlePage: String, array accountTableViewCellModel: [HPMenuTableViewCellModel]?, headerMenuViewModel: HPMenuHeaderViewModel ) {
super.init()
self.titlePage = titlePage
self.accountTableViewCellModel = accountTableViewCellModel
self.headerMenuViewModel = headerMenuViewModel
}
}
I use MVVM architecture

JSON data in http body contains special characters in my simple case

I have a very simple model struct Student which only has two properties firstName and lastName:
struct Student {
let firstName: String
let lastName: String
init(_ firstName: String, _ lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
// Convert to json data
func toData() -> Data? {
var json = [String: Any]()
let mirror = Mirror(reflecting: self)
for child in mirror.children {
if let key = child.label?.trimmingCharacters(in: .whitespacesAndNewlines) {
json[key] = child.value
}
}
do {
return try JSONSerialization.data(withJSONObject: json, options: [])
} catch {
print("\(error.localizedDescription)")
}
return nil
}
}
As you see above, I created an toData() function which I use to convert model object to JSON data for my HTTP request body.
I create Student instance by:
let student = Student("John", "Smith")
I got Json Data by:
let jsonData = student.toData()
later I set to my URLRequest body by:
request.httpBody = jsonData!
However, backend team always see :
{\"firstName\":\"John\", \"lastName\":\"Smith\"}
But backend expect:
{"firstName":"John", "lastName":"Smith"}
I am sure it is not backend problem. Looks like something needs to improve in my toData() function. But I don't know what to do. Could someone help me?
Try this :
if let jsonString:String = String(data: jsonData!, encoding: String.Encoding.utf8) {
request.httpBody = jsonString.data(using: String.Encoding.utf8)
}
You can get rid of the extra backslashes by converting your struct to a dictionary manually.
I have tested below method using a dummy rest server (rest-server-dummy from npm) and there are no extra backslashes around the "" characters.
struct Student {
let firstName: String
let lastName: String
init(_ firstName: String, _ lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
// Convert to json data
func toData() -> Data? {
var json = [String: Any]()
json["firstName"] = firstName
json["lastname"] = lastName
do {
return try JSONSerialization.data(withJSONObject: json, options: [])
} catch {
print("\(error.localizedDescription)")
}
return nil
}
}
I used this method to send the data to the dummy server running on localhost:
var request = URLRequest(url:URL(string: "http://localhost:8080/username")!)
request.httpMethod = "POST"
request.httpBody = student.toData()
URLSession.shared.dataTask(with: request, completionHandler: { data, response, error in
data
response
error
}).resume()
The contents of the output from the server log:
{
"lastname": "Smith",
"firstName": "John"
}

How to return a JSON string embedded in a dictionary using Alamofire/ ObjectMapper?

I'm having issues trying to access a string inside of a JSON dictionary, Here is the JSON data, the string I'm trying to access is the "link" value :
channels = (
{
social = {
facebook = {
"facebook_id" = 47360808996;
link = "https://www.facebook.com/CBS";
};
twitter = {
link = "https://twitter.com/CBS";
"twitter_id" = 97739866;
};
};
}
);
Here is the custom objects I created which keeps giving me an error
" Type '(key: String, value: AnyObject)' has no subscript members"
class SocialInfo: NSObject, Mappable {
var facebook: String?
var facebookDict: String?
required init?(map: Map) {
}
func mapping(map: Map) {
///transforms string to dictionary [string: anyobject] - only used for specific JSON data name: (facebook) - (link)
let facebookTransform = TransformOf<String, [String: AnyObject]> (fromJSON: { (value: [String: AnyObject]?) -> String? in
if let value = value {
for dict in value {
if let social = dict["social"] as? NSDictionary {
if let fb = social["facebook"] as? [String: AnyObject]{
if let fbLink = fb["link"] as? String{
return fbLink
}
}
}
}
}
return nil
}, toJSON: { (value: String?) -> [String: AnyObject]? in
return nil
})
facebookDict <- (map["channels"], facebookTransform)
facebook <- map["link"]
}
}
Here is my JSON call function to get info:
func getSocialInfo (_ completion: (([SocialInfo]) -> Void)?) {
Alamofire.request("\(baseURL)/\(apiKey)/show/950", method: .get, encoding: JSONEncoding.default).responseArray { (response: DataResponse<[SocialInfo]>) in
if let channelsResponse = response.result.value {
completion?(channelsResponse)
}
}
}
For reference I recreated this from a working project using NSURLSession the below code works:
if let channels = jsonResult["channels"] as? [[String:AnyObject]], !channels.isEmpty {
let channel = channels[0] // now the compiler knows it's [String:AnyObject]
let social = channel["social"] as? NSDictionary
let facebookDict = social!["facebook"] as? [String:AnyObject]
let facebook = nullToNil(facebookDict!["link"]) as? String ?? "N/A"
let twitterDict = social!["twitter"] as? [String:AnyObject]
let twitter = nullToNil(twitterDict!["link"]) as? String ?? "N/A"
I will assume you use Swift 3, as Swift 2 is deprecated.
You should avoid NSDictionary and AnyObject when parsing JSON in Swift 3. Simply use [String: Any] everywhere instead.

JSON Structure for parsing JSON data

I want to parse this JSON,at the top level incoming JSON is an array,how can access to information of dictionary in array?
[
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "Sincere#april.biz",
"address": {
"street": "Kulas Light",
"suite": "Apt. 556",
"city": "Gwenborough",
"zipcode": "92998-3874",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
},
"phone": "1-770-736-8031 x56442",
"website": "hildegard.org",
"company": {
"name": "Romaguera-Crona",
"catchPhrase": "Multi-layered client-server neural-net",
"bs": "harness real-time e-markets"
}
}
]
that is my code but I don't know how detect dictionary data.
func profileFromJSONData(data : NSData) -> ProfileResult {
do{
let jsonObject : [[String:AnyObject]]
= try NSJSONSerialization.JSONObjectWithData(data, options: []) as! [[String:AnyObject]]
for profileJSON in jsonObject {
if let profile = profileFromJsonObject(profileJSON) {
finalProfile.append(profile)
}
}
return .Success(finalProfile)
}
catch let error {
return .Failure(error)
}
}
it is my profileFromJsonObject method for parse JSON into profile instance
func profileFromJsonObject(json: [String:AnyObject]) -> UserProfile?{
guard let
id = json["id"] as? Int,
name = json["name"] as? String,
userName = json["username"] as? String,
email = json["email"] as? String,
address = json["address"] as? NSDictionary,
phone = json["phone"] as? String,
website = json["website"] as? String,
company = json["company"] as? NSDictionary
else {
return nil
}
let obj = UserProfile(id: id, name: name, userName: userName, email: email, address: address, phone: phone, website: website, company: company)
return obj
}
I tried your JSON by taking it in local file, and I am able to parse to the model object in following way.
You just put your remote JSON in my code, i hope it will work
func getTheLocalJSON()
{
let filePath = NSBundle.mainBundle().pathForResource("test", ofType: "json");
let data = NSData.init(contentsOfFile: filePath!);
var json : NSArray!
do{
json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.AllowFragments) as! NSArray;
}catch{
}
print("json \(json)");
let dictResponse = json.objectAtIndex(0) as! NSDictionary;
let objUser = profileFromJsonObject(dictResponse)! as UserProfile;
print("user : \(objUser)");
//Code to access company name and show it as screen title
self.title = (objUser.company)!["name"] as? String;
lblCompanyname.text = (objUser.company)!["name"] as? String;
lblCatchPhrase.text = (objUser.company)!["catchPhrase"] as? String;
lblbs.text = (objUser.company)!["bs"] as? String;
}
func profileFromJsonObject(json: NSDictionary) -> UserProfile?{
guard let
id = json["id"] as? Int,
name = json["name"] as? String,
userName = json["username"] as? String,
email = json["email"] as? String,
address = json["address"] as? NSDictionary,
phone = json["phone"] as? String,
website = json["website"] as? String,
company = json["company"] as? NSDictionary
else {
return nil
}
let obj = UserProfile(id: id, name: name, userName: userName, email: email, address: address, phone: phone, website: website, company: company)
return obj
}
Output :

Resources