I'm new to REST API and am having some trouble. I'm trying to call data from an array that's nested within a dictionary. While I've been able to pull data from other variables fine, this is giving me some problems. For reference, the URL I need is labeled "front default," inside the "sprites" array. Furthermore, the URL is that of an image, and I am using Alamofire Image to display images. I will include the code I've used thus far to call other variables, along with a screenshot of the API's structure.
Screenshot of API hierarchy
import Foundation
import Alamofire
import AlamofireImage
import SwiftyJSON
import Siesta
class PokeInfo {
var name: String
var id: Int
var abilities: [String]
var types: [String]
// converts dictionary into string
init?(jsonDictionary: [String: Any]) {
//guard used to check if value entered in search bar matches a name in API data
guard let name = jsonDictionary["name"] as? String,
let id = jsonDictionary["id"] as? Int,
//abilities data nested. First must be set to string
let abilities = jsonDictionary["abilities"] as? [[String: Any]],
let types = jsonDictionary["types"] as? [[String: Any]]
else {
return nil
};
var abilityNames: [String] = []
var typeNames: [String] = []
//targets nested data, going inside dictionary to find attributes of that element
for abilityDictionary in abilities {
guard let ability = abilityDictionary["ability"] as? [String: Any], let name = ability["name"] as? String else { break }
abilityNames.append(name)
};
for typeDictionary in types {
guard let type = typeDictionary["type"] as? [String: Any], let name = type["name"] as? String else { break }
typeNames.append(name)
}
self.name = name
self.id = id
self.abilities = abilityNames
self.types = typeNames
}
}
As of Swift 4 you should be using Codable for JSON which is now part of the language. Here is a Playground example:
import PlaygroundSupport
import UIKit
class PokeInfo: Codable {
var name: String
var id: Int
var abilities: [String]
var types: [String]
}
let pokeString = """
{
"name": "test",
"id": 1,
"abilities": ["a", "b", "c"],
"types": ["x", "y", "z"]
}
"""
let pokeData = pokeString.data(using: .utf8)!
let decoder = JSONDecoder()
let pokeInfo = try! decoder.decode(PokeInfo.self, from: pokeData)
print(pokeInfo.abilities)
the output is:
["a", "b", "c"]
Related
I am sending a request to my vapor 1.5 server via Alamofire with body of type [String:Any] where Any type is a dictionary of String:String
When the request is received on the server I convert it to JSON type
guard let reqJson = request.json else {return}
How can I loop through this JSON object and convert it to an array of [String:Any]
The body of the request I send from the client app looks like this:
["DHSKL3920JFLSKXFgs":
["title": “some title”,
"body": “some body”,
"DHSKL3920JFLSKXFgs": "DHSKL3920JFLSKXFgs",
"unreadMessagesCount": 3],
"PKF993AVG59gkCM":
["title": “some title”,
"body": “some body”,
"DHSKL39": "DHSKL39",
"unreadMessagesCount": 3]]
You can use swift4 Codable and shrink your code to 4-5 lines. Documentation
If I understood you correctly. Maybe the following will help.
//This method uses high order function map
func convert(json:[String:Any]) -> [[String: Any]] {
let requiredObjects = json.map { $0.value as! [String:Any] } //force unwrapping
return requiredObjects
}
//This method uses simple loop
func convert(json:[String:Any]) -> [[String: Any]] {
var requiredObjects = [[String:Any]]()
for (key, value) in json.enumerated() {
requiredObjects.append([value.key : value.value])
}
return requiredObjects
}
struct DataFromClientSendNotifications {
let title: String
let body: String
let sound: String
let badge: String
let fcmToken: String
let unreadMessagesCount: String
}
guard let reqJson = request.json else {
throw Abort.custom(status: .badRequest, message: message)
}
for obj in reqJson.object! {
print("new obj is \(obj)")
let objKey = obj.key
let objValue = obj.value.object
print("objectValue here is \(objValue)")
let title = objValue?["title"]?.string
let body = objValue?["body"]?.string
let unreadMessagesCount = objValue?["unreadMessagesCount"]?.string
let sound = objValue?["sound"]?.string
let badge = objValue?["badge"]?.string
let fcmToken = objValue?["objValue"]?.string
let itemCompleted = DataFromClientSendNotifications(title: title!, body: body!, sound: sound!, badge: badge!, fcmToken: fcmToken!, unreadMessagesCount: unreadMessagesCount!)
print("itemCompleted is \(itemCompleted)")
//now you can do whatever you want with itemCompleted
}
Hi guys I don't know why the array Places returns weird values like 0x6080004b3aa0 instead of displaying my title, coordinate and subtitle out of my JSON url. Thanks for your Help!
import MapKit
#objc class Place: NSObject {
var title: String?
var coordinate: CLLocationCoordinate2D
var subtitle: String?
init(title:String,subtitle:String, coordinate:CLLocationCoordinate2D){
self.title = title
self.coordinate = coordinate
self.subtitle = subtitle
}
static func getPlaces() -> [Place] {
guard let url = NSURL(string: "https://script.googleusercontent.com/macros/echo?user_content_key=Z-LfTMdhgAg_6SRd-iMucSyWu-LFBQO8MLxJZ6DPcL05Rtr3joCCypWD2l46qaegSpVpVINc1DLl5inoDOgGx3p3ANpY1AkGOJmA1Yb3SEsKFZqtv3DaNYcMrmhZHmUMWojr9NvTBuBLhyHCd5hHa1ZsYSbt7G4nMhEEDL32U4DxjO7V7yvmJPXJTBuCiTGh3rUPjpYM_V0PJJG7TIaKp4bydEiKBUZP6fpOyGJIhkmEGneM7ZIlWloTVbXmkjs15vHn8T7HCelqi-5f3gf3-sKiW3k6MDkf31SIMZH6H4k&lib=MbpKbbfePtAVndrs259dhPT7ROjQYJ8yx") else { return [] }
let request = NSMutableURLRequest(url: url as URL!)
var places = [Place]()
let task = URLSession.shared.dataTask(with: request as URLRequest) {data,response,error in
guard error == nil && data != nil else {
print ("Error:",error)
return
}
let httpStatus = response as? HTTPURLResponse
if httpStatus?.statusCode == 200
{ if data?.count != 0
{
let responseString = try! JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! NSDictionary
let contacts = responseString["Sheet1"] as? [AnyObject]
for contact in contacts!{
var places = [Place]()
let title = contact["name"] as! String
let subtitle = contact["description"] as? String
let latitude = contact["latitude"] as? Double ?? 0, longitude = contact["longitude"] as? Double ?? 0
let place = Place(title:title,subtitle:subtitle!,coordinate: CLLocationCoordinate2DMake(latitude, longitude))
places.append(place)
print(latitude)
print(place)
}
}
else {
print("No data got from url")
}
} else {
print("error httpsatus code is:", httpStatus!.statusCode)
}
}
task.resume()
return places as [Place]
}
}
I think the problem is this:
let place = Place(title:title,subtitle:subtitle!,coordinate: CLLocationCoordinate2DMake(latitude, longitude))
When I print(place) it returns the weird results
When you make a class that subclasses from NSObject you're creating a object that is backed by an Objective-c class -- which in some circumstances can be really useful (most common use is when you want to take your object and archive it as a blob of binary data).
I'm guessing that in your case, you probably don't want/need to subclass NSObject.
Here's a simplified example to show what's happening:
Here's a class backed by NSObject:
#objc class ObjCPlace: NSObject {
let name: String
init(name: String) {
self.name = name
}
}
If you create an instance of this object and try to print contents - like you've found, you get the objects location in memory:
let testObjcPlace = ObjCPlace(name: "New York")
print(testObjcPlace)
// prints:
// <__lldb_expr_310.ObjCPlace: 0x600000055060>
On alternative to using print could be to use dump that provides a more detailed look at your object:
let testObjcPlace = ObjCPlace(name: "New York")
dump(testObjcPlace)
// Prints:
// ▿ <__lldb_expr_310.ObjCPlace: 0x600000055060> #0
// - super: NSObject
// - name: "New York"
But instead of making an NSObject subclass, you probably just want to make a Swift class (or in this example a struct take a look at this question and answers for explanations of the differences)
struct Place {
let name: String
init(name: String) {
self.name = name
}
}
Because this object isn't an Objective-c object you can use print and get the internal properties printed as well:
let testPlace = Place(name: "New York")
print(testPlace)
// Prints:
// Place(name: "New York")
p/s welcome to StackOverflow!
I have a JSON result from Alamofire and SwiftyJSON and am trying to create a dictionary from it to create an array
JSON Result
JSON: [
{
"p_589b6a49a0bfd" : {
"path" : "\/uploads\/588fa43eba3c9\/588fa43eba3c9_1486580297.jpg",
"likes" : "0",
"userid" : "588fa43eba3c9",
"caption" : "Bae",
"comments" : "0",
"date" : "1486580297"
}
},
{
"p_589b7f1c540f1" : {
"path" : "\/uploads\/588fa43eba3c9\/588fa43eba3c9_1486585628.jpg",
"likes" : "0",
"userid" : "588fa43eba3c9",
"caption" : "Hot stuff bitch ",
"comments" : "0",
"date" : "1486585628"
}
}
]
Request/Response
Alamofire.request(BASE_URL + "index.php/feed/build", method: .get, headers: headers).responseJSON { response in
switch response.result {
case .success(let value):
let json = JSON(value)
print("JSON: \(json)")
case .failure(let error):
print(error)
}
}
I have then set up a simple class called 'FeedPost' which will store each of the elements in JSON response (this is the function in the FeedPost class)
init(postid: String, postData: Dictionary<String, AnyObject>) {
self._postid = postid
if let caption = postData["caption"] as? String {
self._caption = caption
}
if let path = postData["path"] as? String {
self._path = path
}
if let likes = postData["likes"] as? Int {
self._likes = likes
}
if let comments = postData["comments"] as? Int {
self._comments = comments
}
if let userid = postData["userid"] as? String {
self._userid = userid
}
if let date = postData["date"] as? String {
self._date = date
}
}
I need to somehow cycle through the JSON to create a dictionary to pass to FeedPost then add each FeedPost to another array called Posts during the request. The string that starts with 'p_' I want to use as the postid
With Alamofire response you have used SwiftyJSON and with your FeedPost init you are using swift native dictionary. So I'm suggesting you to either work with SwiftyJSON or with swift's native type. Since you have already added init with dictionary I'm answering your answer with native type.
Alamofire.request(BASE_URL + "index.php/feed/build", method: .get, headers: headers).responseJSON { response in
switch response.result {
case .success(let value):
If let dic = value as? [String: Any],
let array = DIC["JSON"] as? [[String: Any]] {
for item in array {
for (key, value) in item {
If let subDic = value as? [String: Any] {
let obj = FeedPost(postid: key, postData: subDic)
}
}
}
}
print("JSON: \(json)")
case .failure(let error):
print(error)
}
}
Note: Correct Dictionary notation for JSON in swift 3 is [String: Any] not [String: AnyObject] so change your init parameter postData's type to [String: Any].
Below is the code which can be used for your case, This code is copied from playgrounds.
import UIKit
typealias JSONDictionary = [String: AnyObject]
class Post {
let id: String
let userId: String?
let date: Double?
let caption: String?
let comments: Double?
let likes: Double?
let path: String?
init?(with dictionary: JSONDictionary) {
guard let postId = dictionary.keys.first, let postInfo = dictionary[postId] as? JSONDictionary else { return nil }
self.id = postId
self.userId = postInfo["userid"] as? String
self.date = postInfo["date"] as? Double
self.caption = postInfo["caption"] as? String
self.comments = postInfo["comments"] as? Double
self.likes = postInfo["likes"] as? Double
self.path = postInfo["path"] as? String
}
}
Parsing JSON array will be like this.
case .success(let value):
let jsonArray = value["JSON"] as? [JSONDictionary]
let posts = jsonArray?.flatMap(Post.init(with:))
print("Posts \(posts)"
case .failure: break
I have tried this using a local JSON file in a Playgrounds & code was something like this.
let url = Bundle.main.url(forResource: "data", withExtension: "json")
let data = try! Data(contentsOf: url!)
let jsonArray = try! JSONSerialization.jsonObject(with: data , options: .allowFragments) as? [JSONDictionary]
let posts = jsonArray?.flatMap(Post.init(with:))
i have the following object class:
class NewsItem: NSObject {
var storyCategory: String?
var titleText: String?
var paragraph1: String?
var paragraph2: String?
var featureImage: String?
var secondImage: String?
var storyDate: String?
var majorReference: String?
var fact: String?
var actualFeatureImage: UIImage? // Using these two to pass the image from home to story view
var actualSecondImage: UIImage?
var referencesArray = [AnyObject]()
...
init?(dictionary: [String: AnyObject]) {
guard
let storyCategory = dictionary["category"] as? String,
let titleText = dictionary["title"] as? String,
let paragraph1 = dictionary["paragraph1"] as? String,
let paragraph2 = dictionary["paragraph2"] as? String,
let featureImage = dictionary["headerImage"] as? String,
let storyDate = dictionary["date"] as? String,
let majorReference = dictionary["majorReference"] as? String,
let secondImage = dictionary["secondImage"] as? String
else {
return nil
}
self.storyCategory = storyCategory
self.titleText = titleText
self.paragraph2 = paragraph2
self.paragraph1 = paragraph1
self.featureImage = featureImage
self.storyDate = storyDate
self.majorReference = majorReference
self.fact = dictionary["fact"] as? String //if there's a fact in the dict, it will be assigned and if not there'll be nil
self.secondImage = secondImage
let referenceObject = dictionary["reference"] as? [[String: AnyObject]]
for object in referenceObject! {
self.referencesArray.append(object)
//print(object)
}
//bellow is a snippet from the method that's serializing the JSON data
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
if let newsArticles = json["stories"] as? [[String: AnyObject]] {
for article in newsArticles {
let newsArticle = NewsItem(dictionary: article)
newsItems.append(newsArticle!)
}
}
} catch {
print("error in the news items delivery: \(error)")
let displayError = EHPlainAlert(title: "hmmm...", message: "\(error)", type: ViewAlertError)
displayError.show()
}
}
in the backend of the app, i have JSON objects and one of the values of each of the objects is as follows:
... "reference" : [
{
"refName": "CNN",
"refURL": "http://edition.cnn.com/2016/07/19/football/brexit-effect-on-english-premier-league/index.html"
},
{
"refName": "Telegraph",
"refURL": "http://www.telegraph.co.uk/football/2016/06/21/what-would-brexit-mean-for-the-premier-league/"
}
], ...
i don't know if i have the JSON wrong o' something but what i intend to have is an array of references, each reference having a reference name(refName) and the url of the reference (refURL).
my question is, what would be the best method to have the JSON object above be added to the referencesArray in my object class. Would it be better to use a dictionary instead of an array? if so, how should i re-write my code?
if you need any more details about the code, ask and ye shall receive an update. Thanks!
EDIT
Here's the JSON in full :
{
"stories" : [
{
"title" : "English Premier League vs BREXIT: Who will win?",
"headerImage" : "http://i2.cdn.turner.com/cnnnext/dam/assets/160117112140-rooney-goal-exlarge-169.jpg",
"category" : "Sports",
"paragraph1" : "Paragraph text",
"paragraph2" : "More text goes here",
"date" : "21st July",
"majorReference" : "CNN",
"reference" : [
{
"refName": "CNN",
"refURL": "http://edition.cnn.com/2016/07/19/football/brexit-effect-on-english-premier-league/index.html"
},
{
"refName": "Telegraph",
"refURL": "http://www.telegraph.co.uk/football/2016/06/21/what-would-brexit-mean-for-the-premier-league/"
}
],
"secondImage" : "http://www.telegraph.co.uk/content/dam/football/2016/06/14/02-leicester-afp-xlarge_trans++Wy_u4a9GUNQgLIY2EGV3qvLCN329DeTLuwi-bwi35Bo.jpg",
"fact" : "Talent will go missing"
}, ...
Thats just one story... after the comma, is another story.
First, define a data model for your reference:
struct NewsItemReference {
var refName: String
var refURL: NSURL
}
Then modify your NewsItem class as below:
class NewsItem: NSObject {
var referencesArray = [NewsItemReference]()
init?(dictionary: [String: AnyObject]) {
...
if let references = dictionary["reference"] as? [[String: AnyObject]] {
for object in references {
guard let refName = object["refName"] as? String,
let refURLString = object["refURL"] as? String,
let refURL = NSURL(string: refURLString) else {
continue
}
self.referencesArray.append(NewsItemReference(refName: refName, refURL: refURL))
}
}
}
If you see yourself quickly outgrowing this model (the code is too verbose), take a look at some JSON-to-object mapping framework like ObjectMapper.
i need to save with NSUserDefaults an array that i get from jSON, the problem is it save only the first string and not all the array. So if the array is like NewYork,London,Rome .. it save only NewYork. I use it for a picker view.
This is the code:
EDIT
For save the Array from jSON:
if let jsonData = NSJSONSerialization.JSONObjectWithData(urlData!, options: nil, error: &error) as? [String:AnyObject] { // dictionary
if let locationsArray = jsonData["locations"] as? [[String:AnyObject]] { // array of dictionaries
for locationDictionary in locationsArray { // we loop in the array of dictionaries
if let location = locationDictionary["location_name"] as? String { // finally, access the dictionary like you were trying to do
// println(location)
var locationSave: Void = save.setObject(location, forKey: "Location")
}
}
}
}
}
and for request the Array:
var Location = save.objectForKey("Location")!
var pickerviewFields = Location
return pickerviewFields.count
Thanks in advance!
You can only save an NSArray, if the Array is a Swift Array, you will need to convert it. Also, NSArray and NSDictionary objects, their contents must be property list objects.
Here's how you would convert the Array:
var MyArray = ["a", "b", "c"]
var MyNSArray: NSArray
MyNSArray = MyArray as NSArray
println("\(MyNSArray)")
Prints: (a,b,c)
I have a small example with some sample JSON:
var myJSONString: NSString = "{ \"locations\" : [ { \"location_name\" : \"A\" }, { \"location_name\" : \"B\" }, { \"location_name\" : \"C\" }, { \"location_name\" : \"D\" } ] }"
var urlData: NSData? = NSData()
var error: NSError?
var save = NSUserDefaults.standardUserDefaults()
urlData = myJSONString.dataUsingEncoding(NSUTF8StringEncoding)
if let jsonData = NSJSONSerialization.JSONObjectWithData(urlData!, options: nil, error: &error) as? NSDictionary { // dictionary
if let locationsArray = jsonData["locations"] as? NSArray { // array of dictionaries
for locationDictionary in locationsArray { // we loop in the array of dictionaries
if let location = locationDictionary["location_name"] as? NSString {
println(location)
}
}
NSUserDefaults.standardUserDefaults().setObject(locationsArray, forKey: "locationArray")
}
}
println(save.dictionaryRepresentation())
You can try this:
Writing
let locationArray = ["London", "NewYork", "Rome"]
let locationData = NSKeyedArchiver.archivedDataWithRootObject(locationArray)
NSUserDefaults.standardUserDefaults().setObject(locationData, forKey: "Location")
NSUserDefaults.standardUserDefaults().synchronize()
Reading
let locationData = NSUserDefaults.standardUserDefaults().objectForKey("Location") as? NSData
if let locationData = locationData {
let locationArray = NSKeyedUnarchiver.unarchiveObjectWithData(locationData) as? [String]
if let locationArray = locationArray {
println(locationArray)
}
}