Google geocoding - ios

Thanks for the tremendous helps, I was able to get lat and lng by using Google geocoding API with swift. If anyone encounters some problems, hope this helps!
here is the code.
var components = URLComponents(string: "https://maps.googleapis.com/maps/api/geocode/json")!
let key = URLQueryItem(name: "key", value: "YOUR_KEY")
let address = URLQueryItem(name: "address", value: TOKYO)
components.queryItems = [key, address]
let task = URLSession.shared.dataTask(with: components.url!) { data, response, error in
guard let data = data, let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200, error == nil else {
print(String(describing: response))
print(String(describing: error))
return
}
guard let json = try! JSONSerialization.jsonObject(with: data) as? [String: Any] else {
print("not JSON format expected")
print(String(data: data, encoding: .utf8) ?? "Not string?!?")
return
}
guard let results = json["results"] as? [[String: Any]],
let geometry = results[0]["geometry"] as? [String:AnyObject],
let location = geometry["location"] as? [String:Double],
let lat = location["lat"],
let lng = location["lng"],
let status = json["status"] as? String,
status == "OK"
else{return}

Check this out:
var components = URLComponents(string: "https://maps.googleapis.com/maps/api/geocode/json")!
let key = URLQueryItem(name: "key", value: "AIzaSyCbFUri87RMHBX42AsOuxDQANeDCCK8LkA") // use your key
let address = URLQueryItem(name: "address", value: "Tokyo")
components.queryItems = [key, address]
let task = URLSession.shared.dataTask(with: components.url!) { data, response, error in
guard let data = data, let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200, error == nil else {
print(String(describing: response))
print(String(describing: error))
return
}
guard let json = try! JSONSerialization.jsonObject(with: data) as? [String: Any] else {
print("not JSON format expected")
print(String(data: data, encoding: .utf8) ?? "Not string?!?")
return
}
guard let results = json["results"] as? [[String: Any]],
let status = json["status"] as? String,
status == "OK" else {
print("no results")
print(String(describing: json))
return
}
}
task.resume()

Take a look at,
stringByAddingPercentEscapesUsingEncoding
for ios 8 and prior.
from ios 9,
stringByAddingPercentEncodingWithAllowedCharacters
Your code should be like,
let jsonUrl = "https://maps.googleapis.com/maps/api/geocode/json?address=Tokyo&key=Mykey"
let yourFinalString = jsonUrl.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
guard URL(string:yourFinalString!) != nil else {
return print("error")
}
If your url string have some special characters then you have to escapes it by utf8 encoding!

For constructing URLs with query params I recommend to use URLComponents:
var constructor = URLComponents()
constructor.scheme = "https"
constructor.host = "maps.googleapis.com"
constructor.path = "/maps/api/geocode/json"
constructor.queryItems = [
URLQueryItem(name: "address", value: "Tokyo"),
URLQueryItem(name: "key", value: "MyKey")
]
constructor.url // https://maps.googleapis.com/maps/api/geocode/json?address=Tokyo&key=MyKey
or
var constructor = URLComponents(string: "https://maps.googleapis.com/maps/api/geocode/json")
constructor?.queryItems = [
URLQueryItem(name: "address", value: "Tokyo"),
URLQueryItem(name: "key", value: "MyKey")
]
constructor?.url // https://maps.googleapis.com/maps/api/geocode/json?address=Tokyo&key=MyKey
It will format and escape all the characters internally, using proper allowed characters list.

Related

Api is not showing serialized json output

I have created simple login screen attached with loginViewController.swift. Here are the urls
let login_url = "http://192.168.100.11:9000//users/authenticate"
let checksession_url = "http://192.168.100.11:9000//users/authenticate"
I have simple login api. The web service it is showing response on post man web service but it is not displaying serialized json output on Xcode. How to get serialize json from url?
The api is getting two parameters from username="andrews" and password="admin2"
func login_now(username:String, password:String){
let post_data: NSDictionary = NSMutableDictionary()
post_data.setValue(username, forKey: "username")
post_data.setValue(password, forKey: "password")
let url:URL = URL(string: login_url)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
var paramString = ""
for (key, value) in post_data {
paramString = paramString + (key as! String) + "=" + (value as! String) + "&"
}
let endIndex = paramString.index(paramString.endIndex, offsetBy: -1)
let truncated = paramString.substring(to: endIndex)
paramString=truncated
print(paramString) // This won't consist of last &
request.httpBody = paramString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
return
}
let json: Any?
do
{
json = try JSONSerialization.jsonObject(with: data!, options: [])
//////////////here json object is not formed to get json output
print("abc")
print(json)
}
catch
{
return
}
guard let server_response = json as? NSDictionary else
{
return
}
if let data_block = server_response["data"] as? NSDictionary
{
if let session_data = data_block["session"] as? String
{
self.login_session = session_data
let preferences = UserDefaults.standard
preferences.set(session_data, forKey: "session")
DispatchQueue.main.async(execute: self.LoginDone)
}
}
})
task.resume()
}
Here json object is not formed to get serialized json output.how to get the serialized json out put on Nslog? You can download the project from this link.

Working with JSON data retrieving into Swift data types

I'm trying to get data from a URL. It was successful. I can download and convert to a dictionary[String : Any] but response is in nested loops. I don't to how to retrieve. Can someone suggest how to get text and value in the response?
func getDataFromUrl() {
let url = URL(string: "https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&departure_time=1408046331&origins=37.407585,-122.145287&destinations=37.482890,-122.150235")
let request = NSMutableURLRequest(url: url!)
let session = URLSession.shared
request.httpMethod = "GET"
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: {(data, response, error) in
do {
let jsonData = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String: Any]
let destinationAddress = jsonData!["destination_addresses"]
print("Destination address \(String(describing: destinationAddress!))")
let origin_addresses = jsonData!["origin_addresses"]
print("Origin_addresses \(String(describing: origin_addresses!))")
let rows = jsonData!["rows"]
print("Rows \(String(describing: rows!))")
// Here I want to print text and value.
} catch {
// handle error
}
})
dataTask.resume()
}
The above answers work, but in my opinion the more swiftier approach is to use Codable.
class MyResponseType:Codable {
let destination_addresses:String
let rows:[MyCustomRowData]
}
class MyCustomRowData:Codable {
let elements:[MyCustomElementsData]
}
class MyCustomElementsData:Codable {
// properties here
}
Doing this, parsing the json is done like this:
let response = try? JSONDecoder().decode(MyResponseType.self, from: data)
Where the data variable is just the retrieved Data object from the request.
Initially you have to set up some boilerplate code to replicate your expected data format, but working with it is really worth it (and it makes it highly testable).
When the decode succeeds you have a perfectly typed object, it can also have optionals. It just wont decode if fields are missing or of the wrong type (which is a good thing).
Here is the way you can parse text and Value from response:
do{
if let jsonData = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String: Any] {
if let destinationAddress = jsonData["destination_addresses"] as? [String] {
print(destinationAddress) //["1 Hacker Way, Menlo Park, CA 94025, USA"]
}
if let origin_addresses = jsonData["origin_addresses"] as? [String] {
print(origin_addresses) //["3251 Hillview Ave, Palo Alto, CA 94304, USA"]
}
if let rows = jsonData["rows"] as? [[String: AnyObject]] {
if rows.indices.contains(0) {
if let elements = rows[0]["elements"] as? [[String: AnyObject]] {
for element in elements {
if let duration = element["duration"] as? [String: AnyObject] {
let text = duration["text"] as? String ?? ""
print(text) //17 mins
let value = duration["value"] as? Int ?? 0
print(value) //1010
}
if let distance = element["distance"] as? [String: AnyObject] {
let text = distance["text"] as? String ?? ""
print(text) //7.2 mi
let value = distance["value"] as? Int ?? 0
print(value) //11555
}
}
}
}
}
}
}catch{ //error handle
}
Use this code:
let rows = jsonData["rows"] as! Array
let element = rows[0] as! Dictionary
let elementArray = element.value(forKey: "elements")
let distance = elementArray[0].value(forKey: "distance")
let text = distance.value(forKey: "text")
print(text)
let value = distance.value(forKey: "value")
print(value)

URL parsing error with Arabic text

I am trying to translate Arabic to English using a web service. I get an error when constructing the URL. I've defined these cases:
enum MyErrors: Error {
case urlParsingError(String)
case nonDictonaryObjectReturned(Any)
}
And my code is:
func translateWord (text: String, ToLan:String) {
// the text is "مرحبا" and ToLan is "en"
do {
let jsonString = "https://translate.yandex.net/api/v1.5/tr.json/translate?key=apikey&text=\(text)&lang=\(ToLan)"
guard let url = URL(string: jsonString) else {
throw MyErrors.urlParsingError(jsonString)
}
let data = try Data(contentsOf: url, options: Data.ReadingOptions())
let jsonObject = try JSONSerialization.jsonObject(with: data,options: .allowFragments)
guard let dictionary = jsonObject as? [AnyHashable: Any] else {
throw MyErrors.nonDictonaryObjectReturned(jsonObject)
}
let result = dictionary["text"] as? [Any]
let translattedSTR = result?.first as? String
let encodedData = translattedSTR?.data(using: String.Encoding.utf8)!
print(encodedData!)
self.textresult.text = translattedSTR
} catch {
print("caught error \(error)")
}
but unfortunately, it prints
caught error urlParsingError(output url)
also, my the structure looks something like [this](https://translate.yandex.net/api/v1.5/tr.json/translate?key= apikey&text=مرحبا&lang=en)
You need to properly encode the values of the query parameters. One good solution is to use URLComponents to build your query.
Then these lines:
let jsonString = "https://translate.yandex.net/api/v1.5/tr.json/translate?key=myAPIkey&text=\(text)&lang=\(ToLan)"
guard let url = URL(string: jsonString) else {
throw MyErrors.urlParsingError(jsonString)
}
need to be replaced with:
let baseString = "https://translate.yandex.net/api/v1.5/tr.json/translate"
var comps = URLComponents(string: baseString)!
let keyQuery = URLQueryItem(name: "key", value: "myAPIKey")
let textQuery = URLQueryItem(name: "text", value: text)
let langQuery = URLQueryItem(name: "lang", value: ToLan)
comps.queryItems = [ keyQuery, textQuery, langQuery ]
guard let url = comps.url else {
throw MyErrors.urlParsingError("\(comps)")
}
The resulting url is now:
https://translate.yandex.net/api/v1.5/tr.json/translate?key=myAPIkey&text=%D9%85%D8%B1%D8%AD%D8%A8%D8%A7&lang=en
You have to encode your String to get a valid URL. You can do this by using String.addingPercentEncoding(withAllowedCharacters: ).
let jsonString = "https://translate.yandex.net/api/v1.5/tr.json/translate?key=trnsl.1.1.20170517T154730Z.927d87b76de60242.7a92e4612778a4838d40ab192df5297d2a1af4ed&text=\(text)&lang=\(ToLan)"
guard let encodedJsonString = jsonString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), let url = URL(string: encodedJsonString) else {
throw MyErrors.urlParsingError(jsonString)
}
The encoded URL becomes:
https://translate.yandex.net/api/v1.5/tr.json/translate?key=trnsl.1.1.20170517T154730Z.927d87b76de60242.7a92e4612778a4838d40ab192df5297d2a1af4ed&text=%D9%85%D8%B1%D8%AD%D8%A8%D8%A7&lang=en

How to parse a api for swift 3?

Have been researching on the parsing for quite a bit. With plethora of information avilable for JSON nothing seems to explain how to do in a sensible way to extract information with swift 3.
This is what got so far
func getBookDetails() {
let scriptUrl = "https://www.googleapis.com/books/v1/volumes?q=isbn:9781451648546" .
let myurl = URL(string:scriptUrl)
let request = NSMutableURLRequest(url: myurl!)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: myurl! ) { (data, response, error) in
if error != nil{
print("THIS ERROR",error!)
return
} else{
if let mydata = data{
do{
let myJson = try (JSONSerialization.jsonObject(with: mydata, options: JSONSerialization.ReadingOptions.mutableContainers)) as AnyObject
// print("this is the MY JSON",myJson) ---> prints out the json
if let dictonary = myJson["items"] as AnyObject? {
print("the DICTONARY",dictonary) // ----> OUTPUT
if let dictonaryAA = dictonary["accessInfo"] as AnyObject? {
print("the accessInfo",dictonaryAA)
}
}
} catch{
print("this is the in CATCH")
}
} //data
}
}
task.resume()
}
}
OUTPUT :
the DICTONARY (
{
accessInfo = {
accessViewStatus = SAMPLE;
country = US;
=============
RELEVANT DATA as in https://www.googleapis.com/books/v1/volumes?
q=isbn:9781451648546"
==========================
title = "Steve Jobs";
};
}
)
Just need to parse through the json data to get the name, author and title of the book with reference to isbn.
Know there should be a better way to do things that is easily understandable to someone new into the language
You can parse the api in two ways
Using URLSession:
let rawDataStr: NSString = "data={\"mobile\":\"9420....6\",\"password\":\"56147180..1\",\"page_no\":\"1\"}"
self.parsePostAPIWithParam(apiName: "get_posts", paramStr: rawDataStr){ ResDictionary in
// let statusVal = ResDictionary["status"] as? String
self.postsDict = (ResDictionary["posts"] as! NSArray!) as! [Any]
print("\n posts count:",self.postsDict.count)
}
func parsePostAPIWithParam(apiName:NSString, paramStr:NSString,callback: #escaping ((NSDictionary) -> ())) {
var convertedJsonDictResponse:NSDictionary!
let dataStr: NSString = paramStr
let postData = NSMutableData(data: dataStr.data(using: String.Encoding.utf8.rawValue)!)
let request = NSMutableURLRequest(url: NSURL(string: "http://13.12..205.248/get_posts/")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = nil
request.httpBody = postData as Data
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error as Any)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse as Any)
do{
if let convertedJsonIntoDict = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary {
convertedJsonDictResponse = convertedJsonIntoDict.object(forKey: apiName) as? NSDictionary
// callback for response
callback(convertedJsonDictResponse)
}
} catch let error as NSError {
print(error)
}
}
Using Alamofire
func AlamofirePOSTRequest() {
let urlString = "http://13.12..205.../get_posts/"
let para = ["data": "{\"mobile\":\"9420....6\",\"password\":\"56147180..1\",\"page_no\":\"1\"}"]
Alamofire.request(urlString, method: .post, parameters: para , headers: nil).responseJSON {
response in
switch response.result {
case .success:
print("response: ",response)
let swiftyJsonVar = JSON(response.result.value!)
if let resData = swiftyJsonVar["posts"].arrayObject {
self.postsDict = resData as! [[String:AnyObject]]
}
print("\n \n alomafire swiftyJsonVar: ",swiftyJsonVar)
break
case .failure(let error):
print(error)
}
}
}
})
dataTask.resume()
}
First of all, all JSON types are value types in Swift 3 so the most unspecified type is Any, not AnyObject.
Second of all, there are only two collection types in the JSON type set, dictionary ([String:Any]) and array ([Any], but in most cases [[String:Any]]). It's never just Any nor AnyObject.
Third of all, the given JSON does not contain a key name.
For convenience let's use a type alias for a JSON dictionary:
typealias JSONDictionary = [String:Any]
The root object is a dictionary, in the dictionary there is an array of dictionaries for key items. And pass no options, .mutableContainers is nonsense in Swift.
guard let myJson = try JSONSerialization.jsonObject(with: mydata) as? JSONDictionary,
let items = myJson["items"] as? [JSONDictionary] else { return }
Iterate through the array and extract the values for title and authors which is an array by the way. Both values are in another dictionary for key volumeInfo.
for item in items {
if let volumeInfo = item["volumeInfo"] as? JSONDictionary {
let title = volumeInfo["title"] as? String
let authors = volumeInfo["authors"] as? [String]
print(title ?? "no title", authors ?? "no authors")
The ISBN information is in an array for key industryIdentifiers
if let industryIdentifiers = volumeInfo["industryIdentifiers"] as? [JSONDictionary] {
for identifier in industryIdentifiers {
let type = identifier["type"] as! String
let isbn = identifier["identifier"] as! String
print(type, isbn)
}
}
}
}
You are doing wrong in this line
if let dictonaryAA = dictonary["accessInfo"] as AnyObject?
because dictonary here is an array not dictionary. It is array of dictionaries. So as to get first object from that array first use dictonary[0], then use accessInfo key from this.
I am attaching the code for your do block
do{
let myJson = try (JSONSerialization.jsonObject(with: mydata, options: JSONSerialization.ReadingOptions.mutableContainers)) as AnyObject
// print("this is the MY JSON",myJson) ---> prints out the json
if let array = myJson["items"] as AnyObject? {
print("the array",array) // ----> OUTPUT
let dict = array.object(at: 0) as AnyObject//Master Json
let accessInf = dict.object(forKey: "accessInfo") //Your access info json
print("the accessInfo",accessInf)
}
}
Hope this helps you.

Downloaded Json Contains Null value and app Crash ios swift [duplicate]

This question already has answers here:
What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean?
(16 answers)
Closed 5 years ago.
i am working an app in which data is downloaded from server in JSON format. but if Image value is nill then app crashed. i also set a default picture but cursor did not enter in else statement. Kindly guide me how i do my task correctly. Here is my code
func downloadUsersData(){
let email = UserDefaults.standard.value(forKey: "userEmail")
var urlString = "http://nexusvision.net/zeroone/selectuserbasic.php"
urlString.append("?")
urlString.append("id=\(email!)")
print("This is URL : \(urlString)")
let url = URL(string: urlString)
var request = URLRequest(url: url!)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if error != nil{
print(error?.localizedDescription)
}
let data = data
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
let responseString = String(data: data!, encoding: .utf8)
print("all Countary responseString = \(responseString)")
let responseData = responseString
do {
let jsonValue = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! NSDictionary
print("This is Json : \(jsonValue.value(forKey: "product"))")
if let profile = jsonValue.value(forKey: "product") as? NSArray
{
for profileData in profile{
if let dict = profileData as? NSDictionary{
if let firstName = dict.value(forKey: "firstname"){
print("Name is : \(firstName)")
self.defaults.set(firstName, forKey: "firstName")
}
if let lastName = dict.value(forKey: "lastname"){
print("Last Name : \(lastName)")
self.defaults.set(lastName, forKey: "lastName")
}
if let imageData = dict.value(forKey: "picture") {
print("This is Image Json: \(imageData)")
let convertToImage = imageData
let decodedData : NSData = NSData(base64Encoded: convertToImage as! String, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters)!
let decodedimage: UIImage = UIImage(data: decodedData as Data)!
print("Thats Decoded : \(decodedimage)")
self.profilePicImageShow.image = decodedimage
}
else{
self.profilePicImageShow.image = UIImage(named: "Empty")
}
}
}
}
}
catch let error as NSError {
print(error)
}
}.resume()
}
Technically, key "picture" is not nil, its empty string according to the JSON response. So when you execute the code, it will go into the if statement and finally crash at
let decodedimage: UIImage = UIImage(data: decodedData as Data)! as decodedData is nil.
Answer:
Modify the your if statement as below
if let imageData = dict.value(forKey: "picture"), (imageData as! String) != "" {
This should fix your crash problem.
Replace
if let imageData = dict.value(forKey: "picture") {
print("This is Image Json: \(imageData)")
let convertToImage = imageData
let decodedData : NSData = NSData(base64Encoded: convertToImage as! String, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters)!
let decodedimage: UIImage = UIImage(data: decodedData as Data)!
print("Thats Decoded : \(decodedimage)")
self.profilePicImageShow.image = decodedimage
} else {
self.profilePicImageShow.image = UIImage(named: "Empty")
}
with
let imageData = dict["picture"] as? NSData ?? UIImageJPEGRepresentation(UIImage(named: "Empty")!, 1.0)
self.profilePicImageShow.image = UIImage(data: imageData!)
Also, make sure that the key "picture" actually contains nil
You could add guard statements as follow:
guard let decodedData = NSData(base64Encoded: convertToImage as! String, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) else {
return
}
guard let decodedimage: UIImage = UIImage(data: decodedData as Data) else {
return
}
Since you are forcing an unwrap, if it fails to create an image from the data, your app will crash.
As a side note, you could use this and this, those are really great libs to work with HTTP Requests.
In my case I'll get URL from JSON and then download image with URL; So if url is nil then I'll add dummy image else I'll download Image from URL. Here is My Stuff :
var news = respone.result.value as! Dictionary<String,Any>
if let result:NSArray = news["stories"] as? NSArray
{
for i in 0..<result.count
{
let str = (((result[i] as! NSDictionary).value(forKey: "ximg") as! NSDictionary).value(forKey: "ipath") as? String)
if str == nil
{
self.news_url.append("nil")
}
else
{
self.news_url.append(str!)
}
}
}
self.tableview.reloadData()

Resources