How to use swiftyjson to parse json data to foundation object and loop through data - swift

So I'm trying to get his raw json data and use it to ultimately be viewed in a table(so one table cell would be --> Emirates - $1588.77)
Problem: Having trouble parsing the JSON data.. alamofire apparently does it automatically? but im completely confused with the data types. I keep getting weird errors like 'doesnt have a member named subscript" (I've also got swiftyjson installed but aa non-swiftyjson solution should work as well.
Code:
request(qpxRequest).responseJSON { (request, response, json, error) -> Void in
if response != nil {
//println(response!)
}
if json != nil {
// 1. parse the JSON data into a Foundation object
// 2. Grab the data from the foundation object (so its can be looped though in a table)
}
{
trips = {
data = {
carrier = (
{
name = "Cathay Pacific Airways Ltd.";
},
{
name = Emirates;
},
{
name = "Ethiopian Airlines Enterprise";
},
{
name = "Qantas Airways Ltd.";
},
{
name = "South African Airways";
}
);
};
tripOption = (
{
saleTotal = "AUD1537.22";
},
{
saleTotal = "AUD1588.77";
},
{
saleTotal = "AUD1857.42";
},
{
saleTotal = "AUD1857.42";
},
{
saleTotal = "AUD1922.42";
}
);
};
}
-------- Edit.
Using this model.
class FlightDataModel {
var carrier: String
var price: String
init(carrier: String?, price: String?) {
self.carrier = carrier!
self.price = price!
}
}
How woudl I use your solution to add it to an array of FlightDataModel class
This my my attempt..
var arrayOfFlights : [FlightDataModel] = [FlightDataModel]()
if let tripOptions = trips["tripOption"] as? [[String:AnyObject]] {
for (index, tripOption) in enumerate(tripOptions) {
//println("\(index): " + (tripOption["saleTotal"]! as String))
self.arrayOfFlights[index].carrier = tripOption["saleTotal"]! as String
println("\(self.arrayOfFlights[index].carrier)")
}

Alamofire can do it, but you have to dig into your JSON structure. :)
Like this, using Alamofire's responseJSON method:
Alamofire.request(.GET, YOUR_URL, parameters: nil, encoding: .URL).responseJSON(options: NSJSONReadingOptions.allZeros) { (request, response, json, error) -> Void in
if let myJSON = json as? [String:AnyObject] {
if let trips = myJSON["trips"] as? [String:AnyObject] {
if let data = trips["data"] as? [String:AnyObject] {
if let carriers = data["carrier"] as? [[String:String]] {
for (index, carrierName) in enumerate(carriers) {
println("\(index): " + carrierName["name"]!)
}
}
}
if let tripOptions = trips["tripOption"] as? [[String:AnyObject]] {
for (index, tripOption) in enumerate(tripOptions) {
println("\(index): " + (tripOption["saleTotal"]! as! String))
}
}
}
}
}
Output:
0: Cathay Pacific Airways Ltd.
1: Emirates
...
0: AUD1537.22
1: AUD1588.77
...
It's a bit easier with SwiftyJSON indeed. And for diversity's sake, we'll use Alamofire's responseString method this time:
Alamofire.request(.GET, YOUR_URL, parameters: nil, encoding: .URL).responseString(encoding: NSUTF8StringEncoding, completionHandler: {(request: NSURLRequest, response: NSHTTPURLResponse?, responseBody: String?, error: NSError?) -> Void in
if let dataFromString = responseBody!.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) {
let json = JSON(data: dataFromString)
let carriers = json["trips"]["data"]["carrier"].array
for (index, carrier) in enumerate(carriers!) {
println("\(index):" + carrier["name"].string!)
}
let tripOption = json["trips"]["tripOption"].array
for (index, option) in enumerate(tripOption!) {
println("\(index):" + option["saleTotal"].string!)
}
}
})
Output:
0: Cathay Pacific Airways Ltd.
1: Emirates
...
0: AUD1537.22
1: AUD1588.77
...
Note: I've used enumerate as an example for how getting the index of the content at the same time you get the content.

Related

Parse responseJSON to ObjectMapper

I'm currently making a migration from Android to iOS, better said Java to Swift, I got a generic response in JSON, but I'm not able to use it as an object and show it in the storyboard. I'm really new to Swift so I've been stuck for a while.
I've tried ObjectMapper and also JSON decode with no result at all.
I declared this response as I used in Java(Android)
class ResponseObjectMapper<T,R>: Mappable where T: Mappable,R:Mappable{
var data:T?
var message:String!
var error:R?
required init?(_ map: Map) {
self.mapping(map: map)
}
func mapping(map: Map) {
data <- map["data"]
message <- map["message"]
error <- map["error"]
}
}
class UserMapper :Mappable{
var email:String?
var fullName:String?
var id:CLong?
var phoneNumber:String?
var token:CLong?
required init?(_ map: Map) {
}
func mapping(map: Map) {
email <- map["email"]
fullName <- map["fullName"]
id <- map["id"]
phoneNumber <- map["phoneNumber"]
token <- map["token"]
phoneNumber <- map["phoneNumber"]
}
}
In my Android project I use the Gson dependency and I was able to use my JSON as an object
class ErrorMapper:Mappable{
var message:String?
var code:Int?
required init?(_ map: Map) {
}
func mapping(map: Map) {
message <- map["message"]
code <- map["code"]
}
}
This is the Alamofire that gave me the JSON.
func login(params: [String:Any]){Alamofire.request
("http://192.168.0.192:8081/SpringBoot/user/login", method: .post,
parameters: params,encoding: JSONEncoding.default, headers:
headers).responseJSON {
response in
switch response.result {
case .success:
let response = Mapper<ResponseObjectMapper<UserMapper,ErrorMapper>>.map(JSONString: response.data)
break
case .failure(let error):
print(error)
}
}
}
If I print the response with print(response) I got
SUCCESS: {
data = {
email = "vpozo#montran.com";
fullName = "Victor Pozo";
id = 6;
phoneNumber = 099963212;
token = 6;
};
error = "<null>";
message = SUCCESS;
}
and if I use this code I can got a result with key and value but I don't know how to use it as an object
if let result = response.result.value {
let responseDict = result as! [String : Any]
print(responseDict["data"])
}
console:
Optional({
email = "vpozo#gmail.com";
fullName = "Victor Pozo";
id = 6;
phoneNumber = 099963212;
token = 6;
})
I would like to use it in an Object, like user.token in a View Controller, probably I'm really confused, trying to map with generic attributes.
Type 'ResponseObjectMapper<UserMapper, ErrorMapper>' does not conform to protocol 'BaseMappable'
First of all you will need a Network Manager which uses Alamofire to make all your requests. I have made generalized one that looks something like this. You can modify it as you want.
import Foundation
import Alamofire
import SwiftyJSON
class NetworkHandler: NSObject {
let publicURLHeaders : HTTPHeaders = [
"Content-type" : "application/json"
]
let privateURLHeaders : HTTPHeaders = [
"Content-type" : "application/json",
"Authorization" : ""
]
enum RequestType {
case publicURL
case privateURL
}
func createNetworkRequestWithJSON(urlString : String , prametres : [String : Any], type : RequestType, completion:#escaping(JSON) -> Void) {
let internetIsReachable = NetworkReachabilityManager()?.isReachable ?? false
if !internetIsReachable {
AlertViewManager.sharedInstance.showAlertFromWindow(title: "", message: "No internet connectivity.")
} else {
switch type {
case .publicURL :
commonRequest(urlString: baseURL+urlString, parameters: prametres, completion: completion, headers: publicURLHeaders)
break
case .privateURL:
commonRequest(urlString: baseURL+urlString, parameters: prametres, completion: completion, headers: privateURLHeaders)
break
}
}
}
func commonRequest(urlString : String, parameters : [String : Any], completion : #escaping (JSON) -> Void , headers : HTTPHeaders){
print("urlString:"+urlString)
print("headers:")
print(headers)
print("parameters:")
print(parameters)
let url = NSURL(string: urlString)
var request = URLRequest(url: url! as URL)
request.httpMethod = "POST"
request.httpHeaders = headers
request.timeoutInterval = 10
let data = try! JSONSerialization.data(withJSONObject: parameters, options: JSONSerialization.WritingOptions.prettyPrinted)
let json = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
if let json = json {
print("parameters:")
print(json)
}
request.httpBody = json!.data(using: String.Encoding.utf8.rawValue)
let alamoRequest = AF.request(request as URLRequestConvertible)
alamoRequest.validate(statusCode: 200..<300)
alamoRequest.responseJSON{ response in
print(response.response?.statusCode as Any )
if let status = response.response?.statusCode {
switch(status){
case 201:
print("example success")
SwiftLoader.hide()
case 200 :
if let json = response.value {
let jsonObject = JSON(json)
completion(jsonObject)
}
default:
SwiftLoader.hide()
print("error with response status: \(status)")
}
}else{
let jsonObject = JSON()
completion(jsonObject)
SwiftLoader.hide()
}
}
}
}
After this when ever you need to make a request you can use this function. This will take in parameters if any needed and once the request is complete it will execute a call back function in which you can handle the response. The response here will be of SWIFTYJSON format.
func makeNetworkRequest(){
let networkHandler = NetworkHandler()
var parameters : [String:String] = [:]
parameters["email"] = usernameTextField.text
parameters["pass"] = passwordTextField.text
networkHandler.createNetworkRequestWithJSON(urlString: "http://192.168.0.192:8081/SpringBoot/user/login", prametres: parameters, type: .publicURL, completion: self.handleResponseForRequest)
}
func handleResponseForRequest(response: JSON){
if let message = response["message"].string{
if message == "SUCCESS"{
if let email = response["data"]["email"].string{
//Do something with email.
}
if let fullName = response["data"]["fullName"].string{
//Do something with fullName.
}
if let id = response["data"]["id"].int{
//Do something with id.
}
if let phoneNumber = response["data"]["phoneNumber"].int64{
//Do something with phoneNumber.
}
if let token = response["data"]["token"].int{
//Do something with token.
}
}else{
//Error
}
}
}
Hope this helps. Let me know if you get stuck anywhere.

how to pass the API parameter and parameter is in array

How to pass array parameter
Parameter
[
{
"id": 0,
"followerId": 1030,
"followingId": 1033,
"followerName": "string",
"followingName": "string",
"createdDate": "string",
"message": "string"
}
] //how to solve this array
API Function
class func postFollowers(params:[String: Any],success:#escaping([FollowingDataProvider]) -> Void, failure:#escaping (String) -> Void){
var request = RequestObject()
request = Services.servicePostForFollower(param: params)
APIManager.Singleton.sharedInstance.callWebServiceWithRequest(rqst: request, withResponse: { (response) in
if (response?.isValid)!
{
//success()
print(response?.object as! JSON)
success(self.followingJSONarser(responseObject: response?.object as! JSON));
//followingJSONarser(responseObject: response?.object as! JSON)
}
else
{
failure((response?.error?.description)!)
}
}, withError: {
(error) in
failure((error?.description)!)
})
}
Parsing
static func followingJSONarser(responseObject:JSON) -> [FollowingDataProvider]{
var dataProvider = [FollowingDataProvider]()
let jsonDataa = responseObject["data"]
print(jsonDataa)
let newJSON = jsonDataa["data"].arrayValue
print(newJSON)
for item in newJSON{
print(item)
dataProvider.append(FollowingDataProvider(id: item["userId"].intValue, followerId: item["userId"].intValue, followingId: item["followingId"].intValue, followerName: item["userName"].stringValue, followingName: item["followingName"].stringValue, createdDate: item["createdDate"].stringValue, message: item["message"].stringValue))
}
return dataProvider
}`
You can try to combine SwiftyJson with Codable
struct Root: Codable {
let id, followerID, followingID: Int
let followerName, followingName, createdDate, message: String
enum CodingKeys: String, CodingKey {
case id
case followerID = "followerId"
case followingID = "followingId"
case followerName, followingName, createdDate, message
}
}
if let con = response?.object as? JSON {
do {
let itemData = try con.rawData()
let res = try JSONDecoder().decode([Root].self, from: itemData)
print(res)
catch {
print(error)
}
}
Also avoid force-unwraps with json
response?.object as! JSON
Following your code you're trying to parse data from API, you can use SwiftyJSON with Alamofire to create an HTTP request (post,get,put,delete,etc)
You should use arrayObject instead of arrayValue
Your code missing the right definition to data parsing
static func followingJSONarser(responseObject:JSON) -> [FollowingDataProvider]{
var dataProvider = [FollowingDataProvider]()
var itemClass = [ItemClass]()
let jsonDataa = responseObject["data"] as! Dictionary
let newJSON = jsonDataa["data"].arrayObject as! Array
Now create a dataModel class to cast data to it
class ItemsClass:NSObject{
var id:Int = 0
var followerId:Int = 0
var followingId:Int = 0
var followerName:String = ""
var followingName:String = ""
var createdDate:String = ""
var message:String = ""
init(data:JSON) {
self.id = data["userId"].intValue
self.followerId = data["userId"].intValue
self.followingId = data["followingId"].intValue
self.followerName = data["userName"].stringValue
self.followingName = data["followingName"].stringValue
self.createdDate = data["createdDate"].stringValue
self.message = data["message"].stringValue
}
}
for item in newJSON{
dataProvider.append(itemClass)
}
return dataProvider
}`

Migration to Swift 2 "Use of unresolved identifier '***'"

I'm upgrading my project from Swift 1.2 to Swift 2.
I use this occasion to upgrade lot of the lib that I use, in particular Alamofire.
But now I got this error on many of my request:
Use of unresolved identifier 'notifTypeJSON'
Here is the code of one of the func:
func getNotifications(failure failure: (NSError) -> (), success: ([Notification]) -> ()) {
Alamofire.request(Router.Notifications)
.validate()
.responseJSON { response in
if let error = response.result.error {
failure(error)
} else {
var json = JSON(response.data!)
let status = json["error"].intValue
if status != 0 {
failure(self.createError(status))
} else {
var notifications = [Notification]()
let notificationsList = json["notification"]
for (index: String, notifTypeJSON: JSON) in notificationsList {
if let notifJSON = notifTypeJSON[NotificationTypes.Generic.rawValue].dictionaryObject {
notifications.append(GenericNotification(json: notifJSON))
}
else if let notifJSON = notifTypeJSON[NotificationTypes.Follow.rawValue].dictionaryObject {
notifications.append(FollowNotification(json: notifJSON))
}
else if let notifJSON = notifTypeJSON[NotificationTypes.Comment.rawValue].dictionaryObject {
notifications.append(CommentNotification(json: notifJSON))
}
else if let notifJSON = notifTypeJSON[NotificationTypes.Like.rawValue].dictionaryObject {
notifications.append(LikeNotification(json: notifJSON))
}
}
DDLogInfo("SeetyAPI getNotifications() success")
success(notifications)
}
}
}
}
In Swift 2 the way we loop over a dictionary with a tuple has changed: the types have to be in a separate tuple.
Example Before:
for (key:String, value:Int) in xxx {
Example After:
for (key, value):(String, Int) in xxx {
So for you, you would need to replace this:
for (index: String, notifTypeJSON: JSON) in notificationsList {
With this:
for (index, notifTypeJSON):(String, JSON) in notificationsList {

Limit objects that are returned from a Deployd query in Swift

I'm using Deployd as back-end for my Swift application. This is how I'm querying the JSON data currently. I need to limit the amount of objects that are returned from a query. I'll implement the pagination part myself. I just want to know that how can I embed the below mentioned $limit method in the query. Any advice would be greatly appreciated.
http://docs.deployd.com/docs/collections/reference/querying-collections.html#s-$limit-1416
From Deployd's Docs:
The $limit command allows you to limit the amount of objects that are
returned from a query. This is commonly used for paging, along with
$skip.
// Return the top 10 scores {
$sort: {score: -1},
$limit: 10 }
import Foundation
class ObjectHandler {
var greetings: [initTable] = []
init(filename: String) {
//filter data
let fileP = NSURL(string: "http://localhost:2403/users/me")
let jsonD = NSData(contentsOfURL:fileP!)
let jso = JSON(data: jsonD!, options: NSJSONReadingOptions.AllowFragments, error: nil)
var id = jso["id"]
let filePath = NSURL(string: "http://localhost:2403/postedjob")
let jsonData = NSData(contentsOfURL:filePath!)
let json = JSON(data: jsonData!, options: NSJSONReadingOptions.AllowFragments, error: nil)
for (key: String, subJson: JSON) in json {
var language:String?, link: String?, description:String?, greetingText: String?
for (key1, value:JSON) in subJson {
switch key1 {
case "briefDes": language = value.string
case "skill": link = value.string
case "userId": description = value.string
case "id": greetingText = value.string
default: break
}
}
let greeting = initTable(language: language, link: link, description: description, greetingText: greetingText)
self.greetings.append(greeting)
self.greetings = self.greetings.filter { $0.description == "\(id)"}
}
}
func getGreetingsAsAnyObjects() -> [String: [AnyObject]]{
return [SelectJobConstant.GreetingOBJHandlerSectionKey: greetings.map { $0 as AnyObject }]
} }
So this is how I made it to work :)
Alamofire.request(.GET, "http://localhost:2403/postedjob", parameters: ["$limit": 2, "$sort": ["userId":"-1"]])
.responseJSON { _, _, JSON, _ in
println(JSON)
}

how to deserialize a collection from an alamofire request

this is a pretty basic question of the syntax for deserializing responsejson collection in alamofire and swift
I have a GET service that returns an array of user objects e.g.
[
{"uname": "bob"},
{"uname": "jane"}
]
and here is my request
Alamofire.request(.GET, url , encoding:.JSON)
.responseJSON(options: .MutableContainers, completionHandler:{ (request, response, JSON, error) -> Void in
let result = JSON as? [Dictionary<String,String>]
if (response!.statusCode == 200 && error == nil){
//how should I deserialize the result here to return [String]
callback(success: true, errorMsg: nil, friends:friends)
}
else{
callback(success: false,errorMsg:error?.localizedDescription,friends:nil)
}
})
My question is how to deserialize the result, and am I correct to assume that the json result is a [Dictionary] and in order for me to change it to a [String] result, should I map it?
If I use the map syntax below I have a few questions
let friends = result?.map {
return $0["uname"] as String?
}
With the optional return value can I get it to return a [String] instead of a [String?] in a graceful way - isn't there a way to let map return only non null values somehow in a concise syntax?
Also is there a better syntax for map that lets me name the parameters instead of using $0?
I use Alamofire with SwiftyJSON to do the job and deserialise array of JSON objects to native objects. Here is an example of how I do it:
Alamofire.request(request)
.responseJSON(options: nil) { (request, response, json, error) -> Void in
if let requestError = error as NSError?{
callback!(nil, nil, requestError)
}
if let httpResponse = response as NSHTTPURLResponse?{
if(httpResponse.statusCode != 200){
callback!(nil, httpResponse, nil)
} else{
let jsonResult = JSON(json!) as JSON
var concerts = self.deserializeConcerts(jsonResult)
callback!(concerts, nil, nil)
}
}
}
Deserialize concerts looks like this:
class private func deserializeConcerts(concerts: SwiftyJSON.JSON) -> [ConcertModel]{
let concertsArray = concerts.object as! NSMutableArray
var concertObjs = [ConcertModel]()
for concert in concertsArray{
let concertObj = ConcertModel(concert: concert as! NSDictionary)
concertObjs.append(concertObj)
}
return concertObjs
}
And finally, ConcertModel initialiser just maps the values of the NSDictionary to the properties of the ConcertModel object.
Updated implementation for Alamofire 4.3
Alamofire.request(url, method: .get)
.responseJSON { response in
if response.data != nil {
let json = JSON(data: response.data!)
let name = json["data"][0]["name"].string
if name != nil {
print(name!)
}
}
}
That will parse this JSON input:
{
data: [
{ name: 'John' },
{ name: 'Dave' }
]
}