building objects from json response using NSJSONSerialization - json

Using swift 1.2, xcode 6.3 and IOS 8, Im trying to build an object from a json response using NSJSONSerialization class.
the json response is:
[{
"_id" : "5470def9e0c0be27780121d7",
"imageUrl" : "https:\/\/s3-eu-west-1.amazonaws.com\/myapi-static\/clubs\/5470def9e0c0be27780121d7_180.png",
"name" : "Mondo",
"hasVip" : false,
"location" : {
"city" : "Madrid"
}
}, {
"_id" : "540b2ff281b30f3504a1c72f",
"imageUrl" : "https:\/\/s3-eu-west-1.amazonaws.com\/myapi-static\/clubs\/540b2ff281b30f3504a1c72f_180.png",
"name" : "Teatro Kapital",
"hasVip" : false,
"location" : {
"address" : "Atocha, 125",
"city" : "Madrid"
}
}, {
"_id" : "540cd44581b30f3504a1c73b",
"imageUrl" : "https:\/\/s3-eu-west-1.amazonaws.com\/myapi-static\/clubs\/540cd44581b30f3504a1c73b_180.png",
"name" : "Charada",
"hasVip" : false,
"location" : {
"address" : "La Bola, 13",
"city" : "Madrid"
}
}]
the object class (Club.swift) with the NSJSONSerialization.JSONObjectWithData implementation is:
class Club: NSObject {
var id: String = ""
var name: String = ""
var imageUrl: String = ""
var hasVip: Bool = false
var desc: String = ""
var location: [Location] = []
init(JSONString: String) {
super.init()
var error : NSError?
let JSONData = JSONString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
let JSONDictionary: NSDictionary = NSJSONSerialization.JSONObjectWithData(JSONData!, options: nil, error: &error) as! NSDictionary
self.setValuesForKeysWithDictionary(JSONDictionary as [NSObject : AnyObject])
}
}
and finally the ApiClient class is
class ApiClient {
func getList(completionHandler: ([JSON]) -> ()) {
let URL = NSURL(string: "https://myapi.com/v1/clubs")
let mutableURLRequest = NSMutableURLRequest(URL: URL!)
mutableURLRequest.setValue("Content-Type", forHTTPHeaderField: "application/json")
mutableURLRequest.HTTPMethod = "GET"
mutableURLRequest.setValue("Bearer R01.iNsG3xjv/r1LDkhkGOANPv53xqUFDkPM0en5LIDxx875fBjdUZLn1jtUlKVJqVjsNwDe1Oqu2WuzjpaYbiWWhw==", forHTTPHeaderField: "Authorization")
let manager = Alamofire.Manager.sharedInstance
let request = manager.request(mutableURLRequest)
request.responseJSON { (request, response, json , error) in
if (json != nil){
var jsonObj = JSON(json!)
if let data = jsonObj["hits"].arrayValue as [JSON]?{
var aClub : Club = Club(JSONString: data)
println(aClub.name)
completionHandler(data)
}
}
}
}
}
but the problem is when I try to println(aClub.name) the error is
"cannot invoke initializer for type'Club' with an argument list of type (JSONString [JSON])"
I dont know, how could I use NSJSONSerialization class with a complex JSON response.

The jsonObj would appear to be a SwiftyJSON object, or something like that, which one uses in lieu of NSJSONSerialization, not in conjunction with it. The data variable is an array of JSON objects (i.e. it's a [JSON]), not a string.
But you're using Alamofire's responseJSON method, which does the JSON parsing for you. So you don't need to use either NSJSONSerialization or SwiftyJSON. It's already parsed it into an array of dictionaries.
If you want an array of Club objects, you could do can just iterate through this array, building Club objects from the dictionaries:
class ApiClient {
func getList(completionHandler: ([Club]?, NSError?) -> ()) {
let URL = NSURL(string: "https://myapi.com/v1/clubs")
let mutableURLRequest = NSMutableURLRequest(URL: URL!)
mutableURLRequest.setValue("Content-Type", forHTTPHeaderField: "application/json")
mutableURLRequest.HTTPMethod = "GET"
mutableURLRequest.setValue("Bearer R01.iNsG3xjv/r1LDkhkGOANPv53xqUFDkPM0en5LIDxx875fBjdUZLn1jtUlKVJqVjsNwDe1Oqu2WuzjpaYbiWWhw==", forHTTPHeaderField: "Authorization")
let manager = Alamofire.Manager.sharedInstance
let request = manager.request(mutableURLRequest)
request.responseJSON { (request, response, json, error) in
var clubs = [Club]()
if let arrayOfDictionaries = json as? [[String: AnyObject]] {
for dictionary in arrayOfDictionaries {
clubs.append(Club(dictionary: dictionary))
}
completionHandler(clubs, nil)
} else {
completionHandler(nil, error)
}
}
}
}
You obviously have to change Club to handle the dictionary object:
class Club {
var id: String!
var name: String!
var imageUrl: String!
var hasVippler: Bool!
var location: [String: String]!
init(dictionary: [String: AnyObject]) {
id = dictionary["_id"] as? String
name = dictionary["name"] as? String
imageUrl = dictionary["imageUrl"] as? String
hasVippler = dictionary["hasVip"] as? Bool
location = dictionary["location"] as? [String: String]
}
}
Finally, your table view controller could call the API:
let apiClient = ApiClient()
var clubs: [Club]!
override func viewDidLoad() {
super.viewDidLoad()
apiClient.getList() { clubs, error in
if clubs != nil {
self.clubs = clubs
self.tableView.reloadData()
} else {
println(error)
}
}
}

Related

Swift 4 Json Parse

I need to throw this json data into the "arrayCategory" array above. How can I do it ?
"Elektronik" and "Hobi" titles are not fixed. It is variable.
Json Data
{
"Elektronik": [
{
"kategoriisim": "Akıllı Saatler",
"uyevarmi": "1"
},
{
"kategoriisim": "Anakart",
"uyevarmi": "1"
} ],
"Hobi": [
{
"kategoriisim": "Drone Multikopter",
"uyevarmi": "1"
}
]
}
Struct and Array . I need to fill in the "dimensionalArray" array
ExpandableNames "baslik" = The future of "Elektronik" data in json data
ExpandableNames "kategori" = The sub-elements of the "Elektronik" circuit will come
struct ExpandableNames {
let baslik : String
let kategori: [cellKategorilerData]
}
struct cellKategorilerData {
var kategoriisim : String?
var uyevarmi : String?
}
var dimensionalArray = [ExpandableNames]()
var arrayKategoriData = [cellKategorilerData]()
-
static func jsonSonucGetir(servlet:String,parametre:String) {
let connectString = Connect.ConnectInfo
var request = URLRequest(url: URL(string:connectString.conString + "/" + servlet)!)
request.httpMethod = "POST"
var postString = parametre
postString = postString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if error != nil
{
print("error" , error)
}
if let urlContent = data
{
do
{
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
???
}
catch
{
print("server hatasi")
}
}
}
task.resume()
}
If the keys are dynamic I recommend to decode the object as dictionary ([String:[CellKategorilerData]])
let jsonString = """
{
"Elektronik": [
{"kategoriisim": "Akıllı Saatler", "uyevarmi": "1"},
{"kategoriisim": "Anakart", "uyevarmi": "1"}
],
"Hobi": [
{"kategoriisim": "Drone Multikopter", "uyevarmi": "1"}
]
}
"""
struct CellKategorilerData : Decodable {
let kategoriisim : String
let uyevarmi : String
}
do {
let data = Data(jsonString.utf8)
let result = try JSONDecoder().decode([String:[CellKategorilerData]].self, from: data)
for (key, value) in result {
print("key", key, "value", value)
}
} catch { print(error) }

Parsing JSON in Swift and accessing values?

I have successfully parsed JSON for:
birthday = "04/10/1986";
id = 202038339983;
location = {
city = Jupiter;
country = "United States";
state = FL;
};
My question is when part of the JSON is:
submissions = {
data = (
{
"created_time" = "2018-02-16T05:11:56+0000";
id = "131448394823824_167398094382256";
viewer = "Any random string and/or emojis";
},
{
"created_time" = "2018-02-14T23:36:41+0000";
id = "809809871824_8908987486899";
message = "vday \Ud83d\Udda4\U2665\Ufe0f";
});}
How am I supposed to access created_time, id, viewer, and message?
I have been able to print the whole submissions JSON response to the console with this code :
guard let jsonD = responseFromServer as? [String : Any] else {return}
let subs1 = (jsonD["submissions"] as? [String : Any])
let accessSubs1 = theSubs1
guard let parsedPost = theSubs1 else {
return
}
My console will display:
["data": <__NSArrayI 0x6040001a86c0>(
{
"created_time" = "2018-02-16T05:11:56+0000";
id = "131448394823824_167398094382256";
viewer = "Any random string and/or emojis";
},
{
"created_time" = "2018-02-14T23:36:41+0000";
id = "809809871824_8908987486899";
message = "vday \Ud83d\Udda4\U2665\Ufe0f";
})]
My question is how should I parse the JSON so I can access the created_time inside submissions?
Here is the HTTP Request:
struct XClass: RequestProtocol {
var Path = "/User"
var parameters: [String : Any]? = ["stuff": "id, birthday, location, submissions"]
var aToken = aToken.current
var httpMethod: RequestHTTPMethod = .GET
var apiVersion: APIVersion = .defaultVersion
struct Response: ResponseProtocol {
var id = String()
var birthday = String()
var city = String()
var state = String()
var country = String()
var viewSubs = [String : Any]()
init(XResponse: Any?) {
guard let jsonD = XResponse as? [String : Any] else {return}
id = (jsonD["id"] as? String)!
birthday = (jsonD["birthday"] as? String)!
let XArr = (jsonD["location"] as? [String : String])
city = XArr!["city"]!
country = XArr!["country"]!
state = XArr!["state"]!
let subs1 = (jsonD["submissions"] as? [String : Any])
let accessSubs1 = theSubs1
guard let parsedPost = theSubs1 else {
return
}
viewSubs = theSubs1
}}}
func getXData(){
let connection = RequestConnection()
connection.add(XClass()) { response, result in
switch result {
case .success(let response):
print("Request Succeeded: \(response)\n\n\n")
case .failed(let error):
print("Request Failed: \(error)")
}}
connection.start()
}
Create a struct
struct Data: Decodable {
var created_time : String
var id : String
var viewer : String
}
call to the api url from URLSession
guard let url = URL(string: "your api url")
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error.localizedDescription)
} else {
guard let data = data else {return}
var data: [Data]() = JSONDecoder().decode(Data.self, data)
for dat in data{
print(dat.created_time)
print(dat.id)
print(dat.viewer)
}
}
If you are not using Decodable from Swift 4, or still in Swift 3,
then you can specify that the data in "submissions" is an array of dictionaries (double brackets) then you can iterate that.
Change
let subs1 = (jsonD["submissions"] as? [String : Any])
To
let subs1 = (jsonD["submissions"] as? [[String : Any]])
for sub in subs1 {
let time = sub["created_time "] as? [String : Any]
...
}

Parsing JSON data from alamofire into Array with Dictionary

I'm trying to parse JSON data from alamorefire as follows.
import UIKit
import Alamofire
import SwiftyJSON
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request(.GET, "https://api.mynexttrainschedule.net/")
.responseJSON { response in
guard let object = response.result.value else {
print("Oh, no!!!")
return
}
let json = JSON(object);print(json)
let schedule = json[0]["schedule"]
}
}
}
If I print json, I have a data structure like the following (stated concisely).
[
{
"schedule" : [
{"departureTime" : "05:09", "destination" : "Boston", "trainType" : "Express"},
{"departureTime" : "05:19", "destination" : "Portland", "trainType" : "Rapid"},
{"departureTime" : "05:29", "destination" : "Boston", "trainType" : "Express""}
],
"station" : "Grand Central",
"direction" : "North"
},
{
"schedule" : [
{"departureTime" : "05:11","destination" : "Washington, "trainType" : "Express""},
{"departureTime" : "05:23","destination" : "Baltimore, "trainType" : "Express""},
{"departureTime" : "05:35","destination" : "Richmond, "trainType" : "Local""}
],
"station" : "Grand Central",
"direction" : "South"
}
]
Now, how can I save the schedule array with a dictionary (departureTime, destination...) through or not through SwiftyJSON?
Thanks.
UPDATE
The following is my own solution.
import Alamofire
import SwiftyJSON
class ViewController: UIViewController {
var scheduleArray = [Dictionary<String,String>]()
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request(.GET, "https://api.mynexttrainschedule.net/")
.responseJSON { response in
guard let object = response.result.value else {
print("Oh, no!!!")
return
}
let json = JSON(object)
if let jArray = json.array {
if let westHolidayArray = jArray[0]["schedule"].array {
for train in westHolidayArray {
if let time = train["departureTime"].string,
let dest = train["destination"].string,
let type = train["trainType"].string {
let dict = ["time":time, "dest":dest, "type": type]
self.scheduleArray.append(d)
}
}
}
}
}
}
}
First of all you should create a class that is your model of Schedule like this
class Schedule: NSObject {
var departureTime: String
var destination: String
var trainType: String
init(jsonDic : NSDictionary) {
self.departureTime = jsonDic["departureTime"] != nil ? jsonDic["departureTime"] as! String! : nil
self.destination = jsonDic["destination"] != nil ? jsonDic["destination"] as! String! : nil
self.trainType = jsonDic["trainType"] != nil ? jsonDic["trainType"] as! String : nil
}
}
And in your view controller your going to need an array of the Schedule object and after you could parse your Json you do it like this:
class ScheduleController: UIViewController {
// The two object use to show the spinner loading
var loadingView: UIView = UIView()
var spinner = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
// Array of your objects
var arrSchedule: [Schedule] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.getInfoSchedule()
}
func getInfoSchedule() {
showActivityIndicator()
Alamofire.request("https://api.mynexttrainschedule.net/", method: .get, parameters: nil, encoding: URLEncoding.default, headers: nil).responseJSON {
response in
self.hideActivityIndicator()
switch response.result {
case .success:
if let objJson = response.result.value as! NSArray? {
for element in objJson {
let data = element as! NSDictionary
if let arraySchedule = data["schedule"] as! NSArray? {
for objSchedule in arraySchedule {
self.arrSchedule.append(Schedule(jsonDic: objSchedule as! NSDictionary))
}
}
}
}
case .failure(let error):
print("Error: \(error)")
}
}
}
//Those two method serves to show a spinner when the request is in execution
func showActivityIndicator() {
DispatchQueue.main.async {
self.loadingView = UIView()
self.loadingView.frame = CGRect(x: 0.0, y: 0.0, width: self.view.frame.width, height: self.view.frame.height)
self.loadingView.center = self.view.center
self.loadingView.backgroundColor = UIColor(rgba: "#111111")
self.loadingView.alpha = 0.9
self.loadingView.clipsToBounds = true
self.spinner = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
self.spinner.frame = CGRect(x: 0.0, y: 0.0, width: 80.0, height: 80.0)
self.spinner.center = CGPoint(x:self.loadingView.bounds.size.width / 2, y:self.loadingView.bounds.size.height / 2)
self.loadingView.addSubview(self.spinner)
self.view.addSubview(self.loadingView)
self.spinner.startAnimating()
}
}
func hideActivityIndicator() {
DispatchQueue.main.async {
self.spinner.stopAnimating()
self.loadingView.removeFromSuperview()
}
}
}
Maybe is not the more efficient way to do it, but it worked for me. I'm using swift3 with xcode 8.1.
Hope it helps !
Basically what you have is an array of schedules. You can map it using ObjectMapper. Install its pod and just create a new Swift file. and Write this
import ObjectMapper
class TrainSchedules: Mappable {
var mySchedules: [Schedules]
required init?(_ map: Map) {
mySchedules = []
}
func mapping(map: Map) {
mySchedules <- map["schedule"]
}
}
class Schedules: Mappable {
var departureTime: String
var destination: String
var trainType: String
required init?(_ map: Map) {
departureTime = ""
destination = ""
trainType = ""
}
func mapping(map: Map) {
departureTime <- map["departureTime"]
destination <- map["destination"]
trainType <- map["trainType"]
}
}
Now you can use it like
if let data = Mapper<TrainSchedules>().map(json){
// now data is an array containt=g all the schedules
// access departureTimelike below
print(data[0].departureTime)
}
I hope it helps, Letme know if you find any difficulty.
Alamofire.request("YOUR_URL", method:.post, parameters:params, encoding:URLEncoding.default, headers: nil).responseJSON { response in
switch(response.result)
{
case .success(_):
if response.result.value != nil
{
let dict :NSDictionary = response.result.value! as! NSDictionary
print(dict)
let status = dict.value(forKey: "status")as! String
print(status)
if(status=="1")
{
self.array_placeRequestId=((dict.value(forKeyPath: "result.request_id") as! NSArray).mutableCopy() as! NSMutableArray)
}
else
{
print("Something Missed")
}
}
break
case .failure(_):
print(response.result.error)
break
}
}

Swift 3: NSArray element failed to match the Swift Array Element type

I am trying to parse JSON in swift 3 below is my JSON file. And try to get in a array of class which i have declared. But getting error: fatal error: NSArray element failed to match the Swift Array Element type.
{
"Headers": [
{
"headerName": "Home",
"sortByNo" : 1,
"headerImageName": "header0",
"viewCotrollerName": "InitialViewController"
},
{
"headerName": "About",
"sortByNo" : 2,
"headerImageName": "header1",
"viewCotrollerName": ""
},
{
"headerName": "Timing",
"sortByNo" : 3,
"headerImageName": "header3",
"viewCotrollerName": "TimingViewController"
}
]
}
// Class Type
class JsonObjectClass {
var headerName = ""
var sortByNo = ""
var headerImageName = ""
var viewControllerName = ""
}
var array = [JsonObjectClass]() // my array of class type
//JSON Parsing Code
func parseLocalFile() {
let url = Bundle.main.url(forResource: "HeaderFileD", withExtension: "json")
let data = NSData(contentsOf: url!)
do {
let jsonData = try JSONSerialization.jsonObject(with: data! as Data, options: .mutableContainers) as! NSDictionary
array = jsonData.object(forKey: "Headers") as! [JsonObjectClass]
// I am getting error here "fatal error: NSArray element failed to match the Swift Array Element type"
for arr in array {
print(arr)
}
} catch {
}
}
You cannot assign an array or dictionary to a custom class directly.
You need to map the array by creating instances of your class.
I changed the class to a struct to get the member-wise initializer. By the way, the value for key sortByNo is an Int
struct JsonObjectClass {
var headerName = ""
var sortByNo = 0
var headerImageName = ""
var viewControllerName = ""
}
var array = [JsonObjectClass]() // my array of class type
//JSON Parsing Code
func parseLocalFile() {
guard let url = Bundle.main.url(forResource: "HeaderFileD", withExtension: "json") else { return }
do {
let data = try Data(contentsOf: url)
let jsonData = try JSONSerialization.jsonObject(with: data, options: []) as! [String:Any]
let jsonArray = jsonData["Headers"] as! [[String:Any]]
array = jsonArray.map { JsonObjectClass(headerName: $0["headerName"] as! String,
sortByNo: $0["sortByNo"] as! Int,
headerImageName: $0["headerImageName"] as! String,
viewControllerName: $0["viewCotrollerName"] as! String)}
for arr in array {
print(arr)
}
} catch let error as NSError {
print(error)
}
}
PS: Consider the typo viewControllerName vs viewCotrollerName

Remote json parsing in Swift

[
-{
valid:"2",
invalid: "1",
pending: "2"
},
-{
valid:"0",
invalid: "1",
pending: "0"
},
-{
valid:"2",
invalid: "1",
pending: "2"
}
]
I am trying to parse this remote json and populate the data into an array.
I am struggling for hours trying to find out why my code isn't working,the array always ends up being empty. can somebody please tell me what am i doing wrong ?
var arrayreports : [Report] = []
var report = Report()
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let host = appDelegate.host
if(Reachability.isConnectedToNetwork()){
let postEndpoint: String = host+"/api/reportbyworkflow/7"
let session = NSURLSession.sharedSession()
let url = NSURL(string: postEndpoint)!
session.dataTaskWithURL(url, completionHandler: { ( data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
guard let realResponse = response as? NSHTTPURLResponse where
realResponse.statusCode == 201 else {
print("Bad thing happened")
return
}
do {
if let ipString = NSString(data:data!, encoding: NSUTF8StringEncoding) {
let jsonDictionary:AnyObject! = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
let json = jsonDictionary as? Array<AnyObject>
for index in 0...json!.count-1 {
let contact : AnyObject? = json![index]
print(contact)
let collection = contact! as! Dictionary<String, AnyObject>
let valid = collection["valid"] as! String
let invalid = collection["invalid"] as! String
let pending = collection["pending"] as! String
report!.valid = Double(Int(valid)!)
report!.invalid = Double(Int(invalid)!)
report!.pending = Double(Int(pending)!)
arrayreports.append(report!)
}
}}
catch {
print("bad things happened")
}
}).resume()
}
If your json is really the one you copied here, it is not valid ( check on jsonvalidator.com ).
So it is normal than your serialization returns an empty array