I'm following this tutorial: https://www.raywenderlich.com/160517/mapkit-tutorial-getting-started
These questions are in regard to swift (whatever the latest version of xcode uses), JSON and PHP. The tutorial works as is, but I want to make several modifications. I've done everything else but I'm stuck on the following questions.
There are several differences between the tutorial code and what I'm trying to get the app to do. Mainly, the JSON format in the tutorial is different from what my PHP page is spitting out.
I have several questions.
1) How do I modify the code to:
a) use the JSON data from a URL, not from the PublicArt.json file as used in the tutorial, and
b) how do I modify the code from the tutorial to accept the JSON format I'm receiving from a PHP file on my server?
The above question is in reference to the following 2 piece of code (original code in the tutorial):
init?(json: [Any]) {
// 1
self.title = json[16] as? String ?? "No Title"
self.locationName = json[12] as! String
self.discipline = json[15] as! String
// 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()
}
}
and this code (original code in the tutorial):
func loadInitialData() {
// 1
guard let fileName = Bundle.main.path(forResource: "PublicArt", ofType: "json")
else { return }
let optionalData = try? Data(contentsOf: URL(fileURLWithPath: fileName))
guard
let data = optionalData,
// 2
let json = try? JSONSerialization.jsonObject(with: data),
// 3
let dictionary = json as? [String: Any],
// 4
let works = dictionary["data"] as? [[Any]]
else { return }
// 5
let validWorks = works.flatMap { Artwork(json: $0) }
artworks.append(contentsOf: validWorks)
}
The JSON format used in the tutorial is this:
[ 55, "8492E480-43E9-4683-927F-0E82F3E1A024", 55, 1340413921, "436621", 1340413921, "436621", "{\n}", "Sean Browne", "Gift of the Oahu Kanyaku Imin Centennial Committee", "1989", "Large than life-size bronze figure of King David Kalakaua mounted on a granite pedestal. Located at Waikiki Gateway Park.", "Waikiki Gateway Park", "http://hiculturearts.pastperfect-online.com/34250images/002/199103-3.JPG", "1991.03", "Sculpture", "King David Kalakaua", "Full", "21.283921", "-157.831661", [ null, "21.283921", "-157.831661", null, false ], null ]
The format from my PHP file that I want to use is this:
{"id":1,"placeid":"1","lat":"25.4432","long":"-153.2345","location_title":"Sample Location","location_subtitle":"Sample Subtitle","log_status":"success"}
{"id":2,"placeid":"2","lat":"25.4543","long":"-153.2345","location_title":"Sample Location 2","location_subtitle":"Sample Subtitle 2","log_status":"success"}
{"id":3,"placeid":"3","lat":"25.4632","long":"-153.2345","location_title":"Sample Location 3","location_subtitle":"Sample Subtitle 3","log_status":"success"}
The tutorial uses file PublicArt.json and I use url htttp//www.samplesite.com/json.php (not really the url I use but you get it, the url I actually use produces working JSON code)
2) Finally, the tutorial uses a callout accessory as an info button to open up the Maps app to give directions to a location. Instead, how can I use this button to create a segue to a different ViewController when the button is clicked? I think this is the part of the code from the tutorial the opens the Maps app:
let mapsButton = UIButton(frame: CGRect(origin: CGPoint.zero,
size: CGSize(width: 30, height: 30)))
mapsButton.setBackgroundImage(UIImage(named: "Maps-icon"), for: UIControlState())
rightCalloutAccessoryView = mapsButton
Thanks for your help. It's very appreciated!
Edit:
Ok, so here's my code for getting information from "my PHP file". In this case you don't even need var1 and var2, because json.php is made to spit out all the data anyway. With my code I can just us responseJSON["whatever"] to get values out of the response string. I'm confused about the formatting in the tutorial.
let url = "https://www.samplesite.com/json.php"
let var1 = self.textForm1.text!
let var2 = self.textForm2.text!
let request = NSMutableURLRequest(url: NSURL(string: url)! as URL)
request.httpMethod = "POST"
let postString = "var=\(var1)&var2=\(var2)"
print(postString)
request.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
guard error == nil && data != nil else {
print("error=\(String(describing: error))")
return
}
do {
if let responseJSON = try JSONSerialization.jsonObject(with: data!) as? [String:AnyObject]{
// If successful
if ((responseJSON["log_status"]!) as! String == "success") {
// do stuff here if log_status response is success
}
} else {
//If there is an error do stuff here
}
}
}
catch {
print("Error -> \(error)")
}
}
task.resume()
How do I modify the code from the tutorial to be able to parse the data in a similar way? This is the code from the tutorial:
func loadInitialData() {
// 1
guard let fileName = Bundle.main.path(forResource: "PublicArt", ofType: "json")
else { return }
let optionalData = try? Data(contentsOf: URL(fileURLWithPath: fileName))
guard
let data = optionalData,
// 2
let json = try? JSONSerialization.jsonObject(with: data),
// 3
let dictionary = json as? [String: Any],
// 4
let works = dictionary["data"] as? [[Any]]
else { return }
// 5
let validWorks = works.flatMap { Artwork(json: $0) }
artworks.append(contentsOf: validWorks)
}
Related
i have the following Json
USD {
"avg_12h" = "8252.96";
"avg_1h" = "8420.80";
"avg_24h" = "8253.11";
"avg_6h" = "8250.76";
rates = {
last = "8635.50";
};
"volume_btc" = "76.05988903";
}
where USD is a key found after searching in a json file, i want to access "avg_12h" value and assign it to a variable, what is the best way to do it.
import UIKit
/*URLSessionConfiguration.default
URLSessionConfiguration.ephemeral
URLSessionConfiguration.background(withIdentifier: <#T##String#>)
// create a URLSession instance
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)*/
/*create a URLSession instance*/
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
/*
The session.dataTask(with: url) method will perform a GET request to the url specified and its completion block
({ data, response, error in }) will be executed once response is received from the server.*/
let url = URL(string: "https://localbitcoins.com/bitcoinaverage/ticker-all-currencies")!
let task = session.dataTask(with: url) { data, response, error in
// ensure there is no error for this HTTP response
guard error == nil else {
print ("error: \(error!)")
return
}
// ensure there is data returned from this HTTP response
guard let content = data else {
print("No data")
return
}
/*JSONSerialization.jsonObject(with: content,
options: JSONSerialization.ReadingOptions.mutableContainers) as?
[String: Any] will parse the JSON data returned from web server into a dictionary*/
// serialise the data / NSData object into Dictionary [String : Any]
guard let json = (try? JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers)) as? [String: Any] else {
print("Not containing JSON")
return
}
let bolivares = "VES"
for (key, value) in json {
if key==bolivares {
print(value)
//ADD CODE TO ACCESS avg_12h and assign it to a value
}
}
}
// update UI using the response here
// execute the HTTP request
task.resume()
Assuming you are receiving the JSON as raw data and it hasn't been converted to an object yet, ou would want to do something like the following:
guard let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []) as! [String:[String]] else { return }
let usd = jsonObject["USD"]
let avg_12h = usd["avg_12h"]
But this will only work based on some assumptions I've made about the JSON you've provided. Is there a way you can link to a paste of the full JSON file?
Create two simple structs to hold your data (I didn't add all fields here)
struct PriceInfo {
let avg12h: String
let avg1h: String
let rates: [Rate]
}
struct Rate {
let last: String
}
then after converting json you can map it to a dictionary of [String: PriceInfo] where the key is the currency code
do {
if let json = try JSONSerialization.jsonObject(with: content) as? [String: Any] {
let prices: [String: PriceInfo] = json.mapValues {
let dict = $0 as? [String: Any]
let avg12h = dict?["avg_12h"] as? String ?? ""
let avg1h = dict?["avg_1h"] as? String ?? ""
let rates = dict?["rates"] as? [String: String] ?? [:]
return PriceInfo(avg12h: avg12h, avg1h: avg1h, rates: rates.compactMap { rate in Rate(last: rate.value) } )
}
}
} catch {
print(error)
return
}
Try to use CodingKey, it will be more clearer and JSONDecoder().decode method. I assume that you use any JsonViewer
I need help with combining data collected from firstVC, secondVC, and thirdVC and serializing those in the fourthVC.
This link helps with one VC but I have to send only ONE FILE of JSON DATA to the server.
How to create and send the json data to server using swift language
The other method is passing a dictionary array from firstVC, secondVC, and thirdVC to the fourthVC and from the fourthVC convert the dictionaries into JSON. But i don't know how to do that.
I used the format from the answer provided in the link above, but if you need additional info, I will gladly cooperate. Thanks!
PS. Please give me useful comments that will help in any way. I need the code and not feedbacks like doing my own research and such cause I have been stressing about this for nearly a month now.
This is the UserDefault keys
if let AC = UserDefaults.standard.value(forKey: "Acc") as? String {
labeltext.text = "\(AC)"
}
if let TY = UserDefaults.standard.value(forKey: "Taxyear") as? String {
taxtext.text = "\(TY)"
}
if let BB = UserDefaults.standard.value(forKey: "Bsb") as? String {
bsbtext.text = "\(BB)"
}
Here is my JSON code
#IBAction func save(_ sender: Any){
typealias JSONDictionary = [String:Any]
let parameters = ["BankAccountNumber": "Acc", "Tax Year": "Taxyear", "my-bsb": "Bsb"]
let url = URL(string: "https://server:port/")! //change the url
//create the session object
let session = URLSession.shared
//now create the URLRequest object using the url object
var request = URLRequest(url: url)
request.httpMethod = "POST" //set http method as POST
let valid = JSONSerialization.isValidJSONObject(parameters) // true
print (valid)
do {
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) // pass dictionary to nsdata object and set it as request body
} catch let error {
print(error)
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
// create dataTask using the session object to send data to the server
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard error == nil else {
return
}
guard let data = data else {
return
}
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
print(json)
// handle json...
}
} catch let error {
print(error.localizedDescription)
}
})
task.resume()
let alertMessage = UIAlertController(title: "Saved!", message: "We have recorded your information", preferredStyle: UIAlertControllerStyle.alert)
let action = UIAlertAction(title:"Okay", style: UIAlertActionStyle.default, handler: nil)
alertMessage.addAction(action)
self.present(alertMessage, animated: true, completion: nil)
}
I solved it by first storing them in a variable
var TITLE = UserDefaults.standard.value(forKey: "Title")
var GN = UserDefaults.standard.value(forKey: "GivenNames")
var LN = UserDefaults.standard.value(forKey: "LastName")
Then I placed them in a parameter and that's done. It was so obvious that I can't believe I didn't solve it sooner
#IBAction func save(_ sender: Any){
let parameters = ["Tax Year": TaxYear, "Title": TITLE, "first-name": GN, "sur-name": LN]
I build an little task in SWIFT-XCode where I am trying to fetch and print some JSON Data, but the console doesn´t show me any error´s or result. Maybe someone can help me with the problem?. Here´s my code:
let url = URL(string: "https://maps.googleapis.com/maps/api/directions/json?origin=\(lat),\(long)&destination=\(lat+auflong),\(long+auflat)&key=**************")
let task = URLSession.shared.dataTask(with: url!) { (data:Data?, response:URLResponse?, error:Error?) in
if let data = data {
do {
// Convert the data to JSON
let jsonSerialized = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any]
if let json = jsonSerialized, let url = json["url"], let explanation = json["explanation"] {
print(url)
print(explanation)
}
} catch let error as NSError {
print(error.localizedDescription)
}
} else if let error = error {
print(error.localizedDescription)
}
}
task.resume()
If you debug-step through the code, you see the result in the debug area: watch area.
In my test (using incorrect co-ordinates), when I put a breakpoint at the line starting `let jsonSerialized = '..., return 3 dictionary keys:
status
error_message
routes
But you were expecting url and explantion...
If you change the inner like:
let jsonSerialized = ...
if let json = jsonSerialized
{
print(json)
if let url = json["url"], let explanation = json["explanation"] {
print(url)
print(explanation)
}
}
} catch ...
you'll be able to print the returned json. (Replaced some code with ... for easy reading.)
My output:
["status": REQUEST_DENIED, "error_message": The provided API key is invalid., "routes": <__NSArray0 0x600000003910>(
)
]
I am working with Swift on Xcode and I try to parse a JSON file to retrieve some data about nearby stores.
My source code is the following:
import GooglePlaces
import SwiftyJSON
class Place {
let name: String
let coordinates: CLLocationCoordinate2D
init(diction:[String : Any])
{
let json = JSON(diction)
name = json["name"].stringValue //as! String
let lat = json["geometry"]["location"]["lat"].doubleValue as CLLocationDegrees
let long = json["geometry"]["location"]["lng"].doubleValue as CLLocationDegrees
coordinates = CLLocationCoordinate2DMake(lat, long)
}
}
class ViewController: UIViewController, MKMapViewDelegate, SceneLocationViewDelegate {
var urlString = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?"
urlString += "&location=51.507514,-0.073603"
urlString += "&radius=1500" //meters
urlString += "&name=Specsavers"
urlString += "&key=**************************"
guard let url = URL(string: urlString) else {return}
var places = [Place]()
var request = URLRequest(url:url)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
print("HEREurlSession")
if let content = data {
do {
let json = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print(json) // json results are printed fine here
if let results = json["results"] as? [[String : Any]] {
for place in results {
places.append(Place(diction: place))
}
}
else {
print("return")
}
}
catch{
}
}
}
task.resume()
let size = places.count
print("HERE: ", size)
}
The build is successful but the output is size = 0 which means that I do not retrieve the data and the variable places is empty.
I do not know if it is exactly relevant but I get the following warning: Cast from 'MDLMaterialProperty?!' to unrelated type '[[String : Any]]' always fails for the line if let results = json["results"] as? [[String : Any]] in my source code.
Why I do not parse the JSON file correctly and I do not retrieve the data the I want to?
URLSession.shared.dataTask(with:) is asynchronous. This means, it runs in the background. You are executing
let size = places.count
print("HERE: ", size)
while the dataTask is still working.
Instead, you should use your result in the completion handler:
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
print("HEREurlSession")
if let content = data {
do {
let json = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print(json)
if let results = json["results"] as? [[String : Any]] {
for place in results {
places.append(Place(diction: place))
}
}
else {
print("return")
}
}
catch{
}
}
// Use your result here
let size = places.count
useResultSize(size)
}
task.resume()
func useResultSize(_ size: Int) {
// Use your result here
print("HERE: ", size)
}
UPDATE
It seems, that you are missing what asynchronous execution actually means. Let me try to explain.
Lets mark the execution order in the code:
First, the red parts of your code are executed. Program execution starts at the top, then moves to the bottom red box and only after that (once the network request is finished) the green part is executed.
That means, that you can only use the result of the network request in the green part of the code. Outside of the green part, the result is not guaranteed to be available.
If you follow my initial advice, than everything should work. Please see the successful execution in my playground:
This question already has answers here:
Correctly Parsing JSON in Swift 3
(10 answers)
Closed 5 years ago.
I need to get my GPS location from mySQL by PHP in Swift 3. I tried to write the code for get data but it still not work, could you advise me?
JSON Data from PHP:
[{"id":"3752","latitude":"11.2222","longitude":"111.2222","Speed":"0.000000","Volt":"3.97","Percent":"87.000000","Dates":"2017-03-07 22:53:32"}]
Swift 3 code:
import UIKit
//-------- import google map library --------//
import GoogleMaps
import GooglePlaces
class ViewController: UIViewController , GMSMapViewDelegate {
var placesClient: GMSPlacesClient!
override func viewDidLoad() {
super.viewDidLoad()
var abc : String = String()
//-------- Google key for ios --------//
GMSServices.provideAPIKey("XXXXXXXXXX")
GMSPlacesClient.provideAPIKey("XXXXXXXXX")
//--------set URL --------//
let myUrl = URL(string: "http://www.myweb/service.php");
var request = URLRequest(url:myUrl!)
request.httpMethod = "POST"
let postString = "";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
if error != nil
{
print("error=\(error)")
return
}
// You can print out response object
print("response = \(response)")
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
// Now we can access value of latiutde
let latitude= parseJSON["latitude"] as? String //<---- Here , which i need latitude value
print("latitude = \(latitude)")
}
} catch {
print(error)
}
}
task.resume()
}
I tried to write the code but it show the errors on debug output
let responseString = String(data: data, encoding: .utf8 )
let str = String(data: data, encoding: .utf8 )
let data2 = str?.data(using: String.Encoding.utf8, allowLossyConversion: false)!
do {
let json = try JSONSerialization.jsonObject(with: data2!, options: []) as! [String: AnyObject]
if let names = json["latitude"] as? [String] {
print(names)
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
}
Error message
Could not cast value of type '__NSSingleObjectArrayI' (0x1065fad60) to
'NSDictionary' (0x1065fb288).
Try casting the json object to a Swift representation directly for a more 'Swifty' access of the underlying data. So you don't need to fuss around with NSNumber etc.
guard let json = JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [[String: String]] else { return }
guard json.count > 0 else { return }
guard let lattitude = json[0]["lattitude"] else { return }
print("Lattitude received: \(lattitude)")
If you are not sure you'll have a [String: String] object array, you can replace it with a [String: Any] in the cast, then all you need to do is check the type with an optional cast in reading the lattitude. You could add a chained optional then checking for isEmpty to check whether its the lattitude value you want or something went wrong.
I would also advice to pretty much never use ! in your code, try to rely more on optional chaining and guard statements.
Guard statement introduction
Note: a single line guard statement isn't very verbose and might make it very difficult to debug your application. Consider throwing errors or some more debug printing in the body of the guard statement.