initialise Codable result with var - swift4

I need to initialise the result of JSONDecoder in a var object defined outside the API Call.
apiService.GETAPI(url: apiStr, completion: {(success, result) in
if(success) {
let apiResponse = result.value as! NSDictionary
let data = apiResponse.value(forKey: "data") as! NSDictionary
do {
let profileData = try JSONSerialization.data(withJSONObject: data.value(forKey: "profile"), options: .prettyPrinted)
print(profileData)
let profile = try JSONDecoder().decode(Profile.self, from: profileData)
print(profile.name)
}
catch {
print("json error: \(error.localizedDescription)")
}
}
completion(success)
})
But I am unable to do so. This is my Profile Codable struct
struct Profile : Codable {
var id : Int
var name : String
var member_id : Int
var category_id : Int
var membership_id : Int
var information : String
var city : String
var love_count : Int
var vendor_price : String
var locality_name : String
var phone : [String]
var address : [Address]?
var status : Int?
var managed_by_wmg : Int?
}
How to do it. I need it to be var since I need to perform operation and access it later in the other code.

Related

Bittrex API - JSON Struct in Swift

I'm trying to call Bittrex api in my sample iOS application.
I'm trying to read JSON from here.
https://bittrex.com/api/v1.1/public/getmarketsummaries
But I am getting this error: Expected to decode Array but found a dictionary instead.", underlyingError: nil)
According to the Google search result, JSON Struct is incorrect.
Where could I have made a mistake?
Here is my JSON Struct;
struct MarketSummaries : Decodable{
let success : Bool?
let message : String?
let result : [SummaryResult]?
}
struct SummaryResult : Decodable{
let marketName : String?
let high : Double?
let low : Double?
let volume : Double?
let last : Double?
let baseVolume : Double?
let timeStamp : String?
let bid : Double?
let ask : Double?
let openBuyOrders : Int?
let openSellOrders : Int?
let prevDay : Double?
let created : String?
private enum CodingKeys : String, CodingKey {
case marketName = "MarketName", high = "High", low = "Low", volume = "Volume",
last = "Last", baseVolume = "BaseVolume", timeStamp = "TimeStamp", bid = "Bid",
ask = "Ask", openBuyOrders = "OpenBuyOrders", openSellOrders = "OpenSellOrders",
prevDay = "PrevDay", created = "Created"
}
}
And Here is my JSON Struct;
let url = URL(string: "https://bittrex.com/api/v1.1/public/getmarketsummaries")
let session = URLSession.shared
let task = session.dataTask(with: url!) { (data, response, error) in
if error != nil {}
else
{
if (data != nil)
{
do
{
let coins = try JSONDecoder().decode([MarketSummaries].self, from: data!)
DispatchQueue.main.async {
self.market = coins
self.table.reloadData()
}
}
catch
{
print(error)
}
}
}
}
task.resume()
You can use this Result class to retrieve your Json data... I think this will Help you.
import foundation
public class Result {
public var marketName : String?
public var high : Double?
public var low : Double?
public var volume : Double?
public var last : Double?
public var baseVolume : Double?
public var timeStamp : String?
public var bid : Double?
public var ask : Double?
public var openBuyOrders : Int?
public var openSellOrders : Int?
public var prevDay : Double?
public var created : String?
/** Returns an array of models based on given dictionary.
Sample usage:
let result_list = Result.modelsFromDictionaryArray(someDictionaryArrayFromJSON)
- parameter array: NSArray from JSON dictionary.
- returns: Array of Result Instances.
*/
public class func modelsFromDictionaryArray(array:NSArray) -> [Result]
{
var models:[Result] = []
for item in array
{
models.append(Result(dictionary: item as! NSDictionary)!)
}
return models
}
/**
Constructs the object based on the given dictionary.
Sample usage:
let result = Result(someDictionaryFromJSON)
- parameter dictionary: NSDictionary from JSON.
- returns: Result Instance.
*/
required public init?(dictionary: NSDictionary) {
marketName = dictionary["MarketName"] as? String
high = dictionary["High"] as? Double
low = dictionary["Low"] as? Double
volume = dictionary["Volume"] as? Double
last = dictionary["Last"] as? Double
baseVolume = dictionary["BaseVolume"] as? Double
timeStamp = dictionary["TimeStamp"] as? String
bid = dictionary["Bid"] as? Double
ask = dictionary["Ask"] as? Double
openBuyOrders = dictionary["OpenBuyOrders"] as? Int
openSellOrders = dictionary["OpenSellOrders"] as? Int
prevDay = dictionary["PrevDay"] as? Double
created = dictionary["Created"] as? String
}
/**
Returns the dictionary representation for the current instance.
- returns: NSDictionary.
*/
public func dictionaryRepresentation() -> NSDictionary {
let dictionary = NSMutableDictionary()
dictionary.setValue(self.marketName, forKey: "MarketName")
dictionary.setValue(self.high, forKey: "High")
dictionary.setValue(self.low, forKey: "Low")
dictionary.setValue(self.volume, forKey: "Volume")
dictionary.setValue(self.last, forKey: "Last")
dictionary.setValue(self.baseVolume, forKey: "BaseVolume")
dictionary.setValue(self.timeStamp, forKey: "TimeStamp")
dictionary.setValue(self.bid, forKey: "Bid")
dictionary.setValue(self.ask, forKey: "Ask")
dictionary.setValue(self.openBuyOrders, forKey: "OpenBuyOrders")
dictionary.setValue(self.openSellOrders, forKey: "OpenSellOrders")
dictionary.setValue(self.prevDay, forKey: "PrevDay")
dictionary.setValue(self.created, forKey: "Created")
return dictionary
}
}
I found my own mistake. I read MarketSummaries like array while reading data from JSON. But it's not an array.
Bad line:
let coins = try JSONDecoder().decode([MarketSummaries].self, from: data!)
Corrected line
let coins = try JSONDecoder().decode(MarketSummaries.self, from: data!)

Deserialising JSON from Alamofire - Swift

I'm receiving some json via an Alamofire GET request in the following format.
{
"result" : {
"id" : 3456543
},
"error" : 0,
"success" : "True"
}
Is there a straightforward way of iterating over this and placing the content into a class, so that I might have something like...
class Issues {
var success: String?
var error: Int?
var resultId: Int?
}
You should consider using SwiftyJSON with Alamofire. There is a short example here.
But, to answer your question, for Swift 2 you can do it like this:
Alamofire.request(.GET, "http://mywebservice/getstuff", parameters: nil)
.responseJSON { response in
if let json = response.result.value {
let issue:Issues = Issues()
issue.error = json["error"] as! Int
issue.success = json["success"]
// and so on...
}
}
use this as your class:
//
// Issues.swift
//
import Foundation
class Issues:NSObject
{
var success:String?
var error:Int?
var resultId:Int?
}
You need to tweak this for your particular Model, but this is the process I generally take with parsing JSON.
Model:
class Category : NSObject {
// Model Objects
var categoryId: NSNumber!
var categoryDesc: String!
var inAppId: String!
var categoryUnlocked: NSNumber!
override init() {
self.categoryId = 0
self.categoryDesc = ""
self.inAppId = ""
self.categoryUnlocked = 0
}
convenience init(categoryDictionary: [String : AnyObject]) {
self.init()
self.categoryId = NSNumber(int: categoryDictionary["CategoryID"]!.intValue)
self.categoryDesc = categoryDictionary["Category_Desc"] as? String ?? ""
self.inAppId = categoryDictionary["InAppID"] as? String ?? ""
self.categoryUnlocked = categoryDictionary["CategoryUnlocked"] as? NSNumber ?? 0
}
}
With the JSON I use the array.map function:
var categoryList = [Category]()
if let catArray = data["categories"] as? NSArray {
categoryList = catArray.map({Category(categoryDictionary: $0 as! [String : AnyObject])})
CoreDataService.instance.saveCategories(categoryList)
}
This way is tidy and readable for future developers and easily manageable within the specific Model.

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]
...
}

Swift 4 download Swedens radio channels

I got the same error when I try to get previous and song that play right now for every channel. I think something is wrong in my struct Root and playlist because data is not in the correct format.I am using Swift 4 and trying to learn JSON, but its hard. Previous struct is same as Song struct.
How do I fix my error?
My code looks now:
*struct Root : Decodable {
let copyright : String
let channels : [Channel]
let pagination : Pagination
//let playlists : [Playlist]
}
struct Channel : Decodable {
let image : String
let imagetemplate : String
let color : String
let tagline : String?
let siteurl : String
let id : Int
let url : URL?
let statkey : String?
let scheduleurl : String
let channeltype : String
let name : String
}
struct Pagination : Decodable {
let page, size, totalhits, totalpages : Int
let nextpage : URL
}
struct Playlist : Decodable{
let id : Int
let name :String
let prev : [previoussong]
let song : [Song]
}
struct Song : Decodable {
let title : String
let description : String
let artist : String
let composer :String
let conductor : String
let albumname : String
let recordlabel : String
let lyricist : String
let producer : String
let starttimeutc : String
let stopttimeutc : String
}
var tests = [Channel]()
var pl = [Playlist]()
func downloadStations(){
let test2 = "http://api.sr.se/api/v2/channels?format=json"
let play = "http://api.sr.se/api/v2/playlists/rightnow?format=json"
let url = URL(string: test2) /// play instead of test2
URLSession.shared.dataTask(with: url!){ (data , response, error) in
do{
let root = try JSONDecoder().decode(Root.self, from: data!)
self.tests = root.channels
//self.pl = root.playlists
for eachStations in self.tests {
DispatchQueue.main.async {
self.tableV.reloadData()
}
}
} catch {
print(error.localizedDescription)
}
}.resume()*
It doesn't return the array of channels. It returns an object:
{
"copyright": "somestring",
"pagination": {},
"channels": []
}
So, copyright is not a part of channel and you have an object with field channels.
Please read the JSON carefully. In the root object there is the key copyright and a key channels which contains the channel array.
You need to add an umbrella struct for the root object.
Please name the channel struct in singular form and with starting uppercase letter (Channel). Further some keys in the struct are optional.
struct Root : Decodable {
let copyright : String
let channels : [Channel]
let pagination : Pagination
}
struct Channel : Decodable {
let image : String
let imagetemplate : String
let color : String
let tagline : String?
let siteurl : String
let id : Int
let url : URL?
let statkey : String?
let scheduleurl : String
let channeltype : String
let name : String
}
struct Pagination : Decodable {
let page, size, totalhits, totalpages : Int
let nextpage : URL
}
var tests = [Channel]()
let root = try JSONDecoder().decode(Root.self, from: data!)
self.tests = root.channels
I got the same error when I try to get previous and song that play right now for every channel. How do I fix this?
My code looks now:
struct Root : Decodable {
let copyright : String
let channels : [Channel]
let pagination : Pagination
//let playlists : [Playlist]
}
struct Channel : Decodable {
let image : String
let imagetemplate : String
let color : String
let tagline : String?
let siteurl : String
let id : Int
let url : URL?
let statkey : String?
let scheduleurl : String
let channeltype : String
let name : String
}
struct Pagination : Decodable {
let page, size, totalhits, totalpages : Int
let nextpage : URL
}
struct Playlist : Decodable{
let id : Int
let name :String
let prev : [previoussong]
let song : [Song]
}
struct previoussong : Decodable {
let title : String
let description : String
let artist : String
let composer :String
let conductor : String
let albumname : String
let recordlabel : String
let lyricist : String
let producer : String
let starttimeutc : String
let stopttimeutc : String
}
struct Song : Decodable {
let title : String
let description : String
let artist : String
let composer :String
let conductor : String
let albumname : String
let recordlabel : String
let lyricist : String
let producer : String
let starttimeutc : String
let stopttimeutc : String
}
var tests = [Channel]()
var pl = [Playlist]()
var stationsName:String?
#IBOutlet weak var tableV: UITableView!
#IBOutlet weak var image: UIImageView!
#IBOutlet weak var statLabel: UILabel!
#IBOutlet weak var songLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
tableV.dataSource = self
downloadStations()
}
func downloadStations(){
let test2 = "http://api.sr.se/api/v2/channels?format=json"
let play = "http://api.sr.se/api/v2/playlists/rightnow?format=json"
let url = URL(string: test2) /// play instead of test2
URLSession.shared.dataTask(with: url!){ (data , response, error) in
do{
let root = try JSONDecoder().decode(Root.self, from: data!)
self.tests = root.channels
//self.pl = root.playlists
for eachStations in self.tests {
self.stationsName = eachStations.name
print(" " + self.stationsName!)
DispatchQueue.main.async {
self.tableV.reloadData()
}
}
} catch {
print(error.localizedDescription)
}
}.resume()

setValuesForKeys function throws an error?

Hi i got a json file the link is here JSON
what i am trying to do is "setValuesForKeys" in my appCatagory class but it throws an error: "Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key type."
i got all the necessary properties in appCatagory class and the Json output is also correct but i just can't set the values for the keys .Please help:
import UIKit
class AppCategory:NSObject{
var name:String?
var apps:[App]?
var type:String?
override func setValue(_ value: Any?, forKey key: String) {
//this if statement below isn't run even though i fetch apps key
if key == "apps" {
apps = [App]()
for dict in value as! [[String: AnyObject]] {
let app = App()
app.setValuesForKeys(dict)
apps?.append(app)
}
} else {
super.setValue(value, forKey: key)
}
}
static func fetchFeaturedApps(){
let urlString = "https://api.letsbuildthatapp.com/appstore/featured"
URLSession.shared.dataTask(with: URL(string:urlString)!) { (data, response, error)-> Void in
if error != nil{
print(error!)
return
}
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String:Any]
var appCategories = [AppCategory]()
for dict in (json!["categories"] as? [[String:Any]])! {
let appCategory = AppCategory()
appCategory.setValuesForKeys(dict)
appCategories.append(appCategory)
}
DispatchQueue.main.async(execute: { () -> Void in
completionHandler(appCategories)
})
}catch let err{
print(err)
}
}.resume()
}
static func sampleAppCategories() ->[AppCategory]{
let bestNewAppsCategory = AppCategory()
bestNewAppsCategory.name = "Best New Apps"
var apps = [App]()
let frozenApp = App()
frozenApp.name = "Disney Build It : Frozen"
frozenApp.imageName = "Frozen"
frozenApp.category = "Entertainment"
frozenApp.price = NSNumber(value: 3.99)
apps.append(frozenApp)
bestNewAppsCategory.apps = apps
//Create a new category and name
let bestNewGamesCategory = AppCategory()
bestNewGamesCategory.name = "Best New Games"
var bestNewGamesApps = [App]()
let telepaintApp = App()
telepaintApp.name = "Telepaint"
telepaintApp.category = "Games"
telepaintApp.imageName = "Telepaint"
telepaintApp.price = NSNumber(value: 2.99)
bestNewGamesApps.append(telepaintApp)
bestNewGamesCategory.apps = bestNewGamesApps
return [bestNewAppsCategory , bestNewGamesCategory]
}
}
class App: NSObject {
var id: NSNumber?
var name: String?
var category: String?
var imageName:String?
var price: NSNumber?

Resources