I am trying to get data that has been encoded as json in a php script.
My code:
func getJson(completion: #escaping (Array<CarPark>) -> Void) {
activityIndicatior.center = self.view.center
activityIndicatior.hidesWhenStopped = true
activityIndicatior.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
view.addSubview(activityIndicatior)
self.activityIndicatior.startAnimating()
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil
{
print("ERROR")
}
else
{
if let content = data
{
do{
let myJson = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
for index in 0..<myJson.count {
if let entry = myJson[index] as? NSDictionary{
let name = entry["Name"] as! String
let longitude = Double(entry["Longitude"] as! String)
let latitude = Double(entry["Latitude"] as! String)
let quiet = Int(entry["Quiet"] as! String)
let moderate = Int(entry["Moderate"] as! String)
let busy = Int(entry["Busy"] as! String)
let coordinate = CLLocationCoordinate2D( latitude: latitude!, longitude: longitude!)
print("coordinate lat is : \(coordinate.latitude)")
print("coordinate long is : \(coordinate.longitude)")
print("coordinate full is: \(coordinate)")
let tempPark = CarPark(name: name, latitude: latitude!, longitude: longitude!, quiet: quiet!, moderate: moderate!, busy: busy!, coordinate: coordinate, level: "Nil")
let level = tempPark.calcAvailability()
tempPark.level = level
print("Availability is \(tempPark.level)")
self.tempCarParks.append(tempPark)
// print("amount of parks: \(self.carParks.count)")
print("name of parks in array: \(self.tempCarParks[index].name)")
print("Availability is \(tempPark.level)")
}
}
completion(self.tempCarParks)
}
catch
{
print("Error")
}
}
}
}
task.resume()
}
I am getting an error that says 'Ambiguous use of subscript' at the line:
if let entry = myJson[index] as? NSDictionary{
How can I fix this?
Since you know myJson is an array why do you cast the object to unspecified AnyObject?
That causes the error, the compiler needs to know the concrete types of all subscripted objects. Help the compiler then the compiler helps you:
let myJson = try JSONSerialization.jsonObject(with: content) as! [[String:Any]]
for entry in myJson { // don't use old-fashioned index based loops
let name = entry["Name"] as! String
...
.mutableContainers is completely useless in Swift.
Related
First of all, my question may have already been asked but I search on many tutorial and forums and I can't make it works because I'm french and not very good in English.
I try to read JSON with my app but It doesn't work. The only thing printed is "Foundation.JSONDecoder"
This is my SWIFT 4 code :
func getSpots(){
guard let downloadURL = URL(string: "http://dronespot.fr/getSpot.php") else { return }
URLSession.shared.dataTask(with: downloadURL) { data, urlResponse, error in
guard let data = data, error == nil, urlResponse != nil else {
print("Oops Call for Help")
return
}
print("downloaded")
do
{
let decoder = JSONDecoder()
//let rates = try decoder.decode([Artwork].self, from: data)
print(decoder)
} catch {
print("Error after loading")
}
}.resume()
}
Artwork.swift :
init?(json: [Any]) {
// 1
self.title = json[16] as? String ?? "No Title"
self.locationName = json[12] as! String
self.id = 3
self.img = "tamere"
// 2
if let latitude = Double(json[18] as! String),
let longitude = Double(json[19] as! String) {
self.coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
} else {
self.coordinate = CLLocationCoordinate2D()
}
}
Expected JSON :
http://dronespot.fr/getSpot.php
Thanks for your help
Since you want to use JSONDecoder your approach is completely wrong (apart from the issue that you commented out the actual line to decode the JSON).
Create Artwork as a struct adopting Decodable, declare the keys in the JSON as properties and add a lazy instantiated variable to build the coordinate:
struct Artwork : Decodable {
let nom : String
let description : String
let id: String
let img1 : String
let latitude : String
let longitude : String
// there are much more keys
lazy var coordinate : CLLocationCoordinate2D = {
return CLLocationCoordinate2D(latitude: Double(latitude) ?? 0.0, longitude: Double(longitude) ?? 0.0)
}()
}
Now decode the array of Artwork
do {
let decoder = JSONDecoder()
let rates = try decoder.decode([Artwork].self, from: data)
print(rates)
} catch {
print("Error after loading", error)
}
I have a Swift struct like this.
func parseData() {
fetchedProduct = []
let url = "http://www.koulourades.gr/api/products/get/?category=proionta"
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "GET"
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) { (data, response, error) in
if (error != nil) {
print("Error")
}
else{
do {
let fetchedData = try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as! NSArray
for eachFetchedProduct in fetchedData {
let eachProduct = eachFetchedProduct as! [String : Any]
let title = eachProduct ["ProductTitle"] as! String
let price = eachProduct ["ProductPrice"] as! Double
self.fetchedProduct.append(Product(title: title, price: price))
}
print(self.fetchedProduct)
print(self.fetchedProduct.count)
} catch {
print("Error 2")
}
}
}
task.resume()
}
class Product {
var title: String
var price: Double
init(title: String, price: Double) {
self.title = title
self.price = price
}
}
From an API, I get the following JSON response.(it's in Greek language and there are more properties i just write some of these)
[
{
"ProductTitle":"\u03a0\u03c1\u03b1\u03bb\u03af\u03bd\u03b1 OREO",
"ProductSummary":"\u03bc\u03c0\u03b9\u03c3\u03ba\u03bf\u03c4\u03bf",
"ProductPrice":"1.50",
"ProductPriceGross":"1.86",
"ProductPriceWithDiscount":"0.00",
"ProductImage":"koulouri_tyri_gemisto_philadelphia_galopoula_ntomata[1].jpg",
},
...
]
but I want to take only the ProductPrice and the ProductTitle
Swift tells me this: Could not cast value of type 'NSTaggedPointerString' (0x1098a1b90) to 'NSNumber' (0x108eaa320).
And, when I try to take only the ProductTitle which is String tells me this:
fatal error: unexpectedly found nil while unwrapping an Optional value
What did I do wrong?
The ProductPrice value in your JSON is a string, not a double and that is what the exception is telling you; you can't force downcast a string to a double.
You need to convert it via the Double initialiser
From the information you have posted, you should not get an exception when accessing ProductTitle, although force unwrapping/downcasting is best avoided. Check carefully for typing errors in your key names.
A "safer" approach (that also avoids NSArray which is best practice in Swift):
if let fetchedData = try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as? [[String:Any]] {
for eachProduct in fetchedData {
if let title = eachProduct ["ProductTitle"] as? String,
let price = eachProduct ["ProductPrice"] as? String,
let doublePrice = Double(price) {
self.fetchedProduct.append(Product(title: title, price: doublePrice))
}
}
}
I am trying to get information from a json result and append and access certain key values. However, I am getting the error "Type '[String:Any]' has no subscript members.
let json = try? JSONSerialization.jsonObject(with: data!, options: []) as! [String: Any]
for case let studentsInfo in json["results"] {
if let studentInfo = StudentResults(json: result) {
let name = firstName + " " + lastName
StudentsResults.append(name)
}
This is also the struct that I have placed in an extension.
struct StudentResults{
let firstName: String
let lastName: String
let latitude: Double
let longitude: Double
let mapString:String
let mediaURL: String
let objectID:String
let uniqueKey: String
let updatedAt: String
}
convenience init?(json: [String: Any]) {
guard let firstName = json["firstName"] as? String,
let lastName = json["lastName"] as? String
else {
return nil
}
return nil
This is also my GitHub page for this project if you would like to take a look at it
https://github.com/SteveBurgos95/UdacityMapProject
It appears to me that the problem is with this line of code:
let json = try? JSONSerialization.jsonObject(with: data!, options: []) as! [String: Any]
Whilst you are force casting it here as! [String: Any], the try? is giving you an optional value.
You have two options. You can be a crazy person and change try? to try!, or you can take the safer approach and wrap the code like so:
do {
guard let json = try JSONSerialization.jsonObject(with: data!, options: []) as [String: Any] else { return }
…
} catch {
// HANDLE ERROR
}
I've looked around but I don't find an answer to fix this error that has been bugging me. I tried adding a "as! NSMutableArray" but that gave me another error. Any ideas on how to fix it? I converted my project from Objective-C to Swift, so hopefully the code is all good I had 20+ errors now I'm down to 3 errors. Thank you.
Error Message:
'jsonObject' produces 'Any', not the expected contextual result type 'NSMutableArray'
Code for retrieving data from server
// Retrieving Data from Server
func retrieveData() {
let getDataURL = "http://ip/example.org/json.php"
let url: NSURL = NSURL(string: getDataURL)!
do {
let data: NSData = try NSData(contentsOf: url as URL)
jsonArray = JSONSerialization.jsonObject(with: data, options: nil)
}
catch {
print("Error: (data: contentsOf: url)")
}
// Setting up dataArray
var dataArray: NSMutableArray = []
// Looping through jsonArray
for i in 0..<jsonArray.count {
// Create Data Object
let dID: String = (jsonArray[i] as AnyObject).object(forKey: "id") as! String
let dName: String = (jsonArray[i] as AnyObject).object(forKey: "dataName") as! String
let dStatus1: String = (jsonArray[i] as AnyObject).object(forKey: "dataStatus1") as! String
let dStatus2: String = (jsonArray[i] as AnyObject).object(forKey: "dataStatus2") as! String
let dURL: String = (jsonArray[i] as AnyObject).object(forKey: "dataURL") as! String
// Add Data Objects to Data Array
dataArray.add(Data(dataName: dName, andDataStatus1: dStatus1, andDataStatus2: dStatus2, andDataURL: dURL, andDataID: dID))
}
self.myTableView.reloadData()
}
The jsonObject function will return a value of type Any but the jsonArray's type of NSMutableArray. And this function will throw an error if something is wrong, put a try keyword before it. In my experience, let change the type of jsonArray to array of dictionary, so you will extract data with ease.
do {
let data: Data = try Data(contentsOf: url as URL)
let jsonArray: [[String: AnyObject]] = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! [[String: AnyObject]]
print("json: \(jsonArray)")
for dict in jsonArray {
let dataName = dict["dataName"] as! String
print("dataName: \(dataName)")
}
}
catch {
print("Error: (data: contentsOf: url)")
}
I am trying to to populate a table by filling an array from JSON data. I am using the code below but keep getting the error:
Type 'Any' has no subscript members
on the following lines of code:
self.tableData.append(jsonResult[i]["title"]! as! String)
self.tableImages.append(jsonResult[i]["image"]! as! String)
self.tableDesc.append(jsonResult[i]["description"]! as! String)
self.tableValidity.append(jsonResult[i]["validity"]! as! String)
My code:
let str3 = Int(str2!)!
let url = NSURL(string: "https://www.*****.php")!
let task = URLSession.shared.dataTask(with: url as URL) { (data, response, error) -> Void in
if let urlContent = data {
do {
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers)
print(str3)
var i = 0
while i < str3 {
print(jsonResult[i]["title"]! as!String)
print(jsonResult[i]["image"]! as! String)
self.tableData.append(jsonResult[i]["title"]! as! String)
self.tableImages.append(jsonResult[i]["image"]! as! String)
self.tableDesc.append(jsonResult[i]["description"]! as! String)
self.tableValidity.append(jsonResult[i]["validity"]! as! String)
i = i + 1
}
} catch {
print("JSON serialization failed")
}
} else {
print("ERROR FOUND HERE")
}
DispatchQueue.main.async(execute: { () -> Void in
self.tableView.reloadData()
})
}
task.resume()
The compiler doesn't know the type of jsonResult, you have to tell what it is, for example with optional binding like this:
if let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: []) as? [[String:AnyObject]] {
}
Here I downcast the JSON as an array of dictionaries. Use your loop inside this if let and it should work.
The compiler doesn't know the type the JSON object, you need to cast it to the actual type
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options:[]) as! [[String:Any]]
Mutable containers are not needed at all.
The C-while loop with incrementing a counter is very ugly. Don't use that in Swift. And you are using too many exclamation marks
for item in jsonResult {
print(item["title"] as! String)
print(item["image"] as! String)
self.tableData.append(item["title"] as! String)
self.tableImages.append(item["image"] as! String)
self.tableDesc.append(item["description"] as! String)
self.tableValidity.append(item["validity"] as! String)
}
PS: And multiple arrays as data source is also clumsy and error-prone. In an object oriented language a custom struct or class is preferable