I'm looking for a solution similar to these two questions
how to localize address result from reverseGeocodeLocation?
Reverse geocoding to return results only in English?
But I want to be able to get a non-localized version of address and locality.
In my add, when doing reverse geocoding on a non-latin localized device geocoding service returns CLPlacemark which already contains a localized version of address. For instance in Geneva, when using iPhone in Russian, I'm getting
"Площадь Пленпале" instead of "Place de Plainpalais"
which is probably cool but nonetheless confusing since street names aren't in cyrillic. Also, I'm unable to submit a localized version of the address to another API to find an itinerary. So is there a way to force a locale in reverseGeocodeLocation or briefly trick the OS into thinking locale language is set to something other?
Also, doing something like this just before the request doesn't seem to work, since it requires app restart
NSUserDefaults.standardUserDefaults().setObject("fr", forKey: "AppleLanguages"
NSUserDefaults.standardUserDefaults().synchronize()
It turns out modifying UserDefaults was not possible (at least I found no viable solution to do that), so I had to use external reverse geocoder to what I wanted (force locale). Here's one using Google API and Alamofire and SwiftyJSON, wrapped in with a nice completionHandler to get the address when geocoding is done
func getReversePositionFromGoogle(latitude:Double, longitude:Double, completionHandler: (JSON?, NSError?) -> ()) {
var parameters = ["latlng":"\(latitude), \(longitude)", "sensor": true, "language": "fr"]
Alamofire.request(.GET, "http://maps.google.com/maps/api/geocode/json", parameters: parameters as? [String : AnyObject])
.responseJSON(options: NSJSONReadingOptions.AllowFragments) { (request, response, responseObject, error) -> Void in
let json = JSON(responseObject!)
completionHandler(json, error)
}
}
I know this is old thread, by someone may be looking for updated solution like me.
Tested with Swift 4, iOS 11
You can force to get geocoding results in chosen locale by setting extra argument: preferredLocale: Locale.init(identifier: "en_US").
CLGeocoder().reverseGeocodeLocation(location, preferredLocale: Locale.init(identifier: "en_US"), completionHandler: {(placemarks, error) -> Void in
print(location)
if error != nil {
print("Reverse geocoder failed with error" + error!.localizedDescription)
return
}
if placemarks!.count > 0 {
let pm = placemarks![0]
print(pm.administrativeArea!, pm.locality!)
}
else {
print("Problem with the data received from geocoder")
}
})
Related
I am implementing ForwardGeocodeOptions in my iOS sample app using MAPBOX SDK. And I am making a call for getting Placemarks. But always getting 10 results only. I want to get minimum 30 results or more than that. How can I get ?
func getResults(){
let options = ForwardGeocodeOptions(query: "restaurant")
// options.allowedISOCountryCodes = ["IN"]
options.focalLocation = CLLocation(latitude: 17.4447496, longitude: 78.3136091)
options.allowedScopes = [.pointOfInterest,.all]
options.maximumResultCount = 50
let task = Geocoder.shared.geocode(options) {(placemarks, attribution, error) in
if let error = error {
NSLog("%#", error)
} else if let placemarks = placemarks, !placemarks.isEmpty {
print(" \(placemarks.count)")
}
}
task.resume()
}
Internally calling this API :-
https://api.mapbox.com/geocoding/v5/mapbox.places/restaurant.json?proximity=78.3817464,17.446809&types=all&limit=50&access_token=[access_token_here]
Per the API docs, 10 is the upper limit for a forward geocoding request. You’re free to make multiple requests with different parameters.
I am creating an iOS app using Swift that uses some web services to get some information. Specifically I am using the food2fork API to get some recipes. The problem that I'm having is that, if I am connected to the internet at my University, the web calls will always return errors, even though I know that I am connected to the internet on the phone. I believe that the error has something to do with how the network only will handle secure websites, but I'm not sure.
Am I not using NSURL correctly? Is there a better way that I should do it to ensure that my web calls will always return the data that the app needs? Here is the function:
func getRecipeByID(recipeId: String, sendTo: RecipeInfoViewController)
{
let theURLAsString = "http://food2fork.com/api/get?key=[MY KEY]&rId=" + recipeId
let theURL = NSURL(string: theURLAsString)
let theURLSession = NSURLSession.sharedSession()
let theJSONQuery = theURLSession.dataTaskWithURL(theURL!, completionHandler: {data, response, error -> Void in
if(error != nil)
{
print(error!)
}
do
{
let theJSONResult = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
if theJSONResult.count > 0
{
let theRecipeDictionary = theJSONResult["recipe"] as? NSDictionary
sendTo.setRecipeInfo(theRecipeDictionary!)
}
} catch let error as NSError {
print(error) //The function always gets here on certain networks
}
})
theJSONQuery.resume()
}
The error that is output at the print(error) line is:
Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start
with array or object and option to allow fragments not set."
UserInfo={NSDebugDescription=JSON text did not start with array or
object and option to allow fragments not set.}
If you're running iOS 9, you'll need to disable App Transport Security for that domain by adding keys to your Info.plist. Otherwise, you won't be able to make non-HTTPS connections.
I couldn't find a swift doc for Facebook SDK. I'm using the latest version and I managed to retrieve name, age, mail and profile picture of the user. I can't find out how to find the hometown though.
When I try this :
conn.addRequest(req, completionHandler: { (connection: FBSDKGraphRequestConnection!, result: AnyObject!, error: NSError!) -> Void in
if(error != nil){
println(error)
} else {
let loc = result.objectForKey("hometown") // loc is nil
}
})
conn.start()
I found nil. I know that ".objectForKey("hometown")" should return a "Page" and not a "String" but I don't know how to handle a page.
Thanks for your help!
Cheers
EDIT : (thanks to Tobi)
I needed to add the user_location in login permissions and then, to find the location, I used :
let obj = result.objectForKey("location")
let location = obj.objectForKey("name") as! String
You need to make sure that the User Access Token you're using contains the user_hometown permission:
https://developers.facebook.com/docs/facebook-login/permissions#reference-user_hometown
Also, make sure that you really want the hometown and not the location (current city) of the user. The latter would be the user_location permission:
https://developers.facebook.com/docs/facebook-login/permissions#reference-user_location
in my app I'm using JSON and I made a session recently so if I would like to make some http request to get data for a specific user, the user must log in before (also used by http request).
in the safari when I entering the url's of login and then the url of receive data, it does that as needed.
but in my app, I first call login and then the url for getting data, but it's probably starting a new session in every url request which leads me to get an error and not receive the data.
my url request function is:
static func urlRequest (adress: String, sessionEnded: (NSDictionary->Void)?){
println(adress)
var urli = NSURL(string: adress)
var request = NSURLRequest(URL: urli!)
var rVal = "";
self.task = NSURLSession.sharedSession().dataTaskWithURL(urli!) {(data, response, error) in
var parseError: NSError?
let parsedObject: AnyObject? = NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions.AllowFragments,
error:&parseError)
let po = parsedObject as NSDictionary
if let a = sessionEnded{
sessionEnded!(po)
}
}
task!.resume()
}
thanks in advance!!
You have shared only half of the puzzle with us, the client code. We can't comment on why the app isn't working with a clearer picture of what the server API. For example, once you "log in", how do subsequent queries confirm that the request is coming from valid session. Furthermore, you report that "every url request which leads me to get an error". Well, what error do you receive? You have to be far more specific regarding the precise errors/crashes you are receiving. BTW, are you logging on to some service with a well-defined API or are you writing that code yourself, too?
Having said that, I might suggest a few refinements to this method:
The sessionEnded (which I've renamed completionHandler to conform to informal standard naming conventions), probably should return an optional NSError object, too, so the caller can detect if there was an error.
Your unwrapping of the sessionEnded completion handler can be simplified to use ?.
When you parse the object, you should feel free to perform the optional cast, too.
You probably want to detect a network error (in which case data would be nil) and return the network NSError object.
Minor point, but I'd probably also rename the function to conform to Cocoa naming conventions, using a verb to start the name. Perhaps something like performURLRequest.
This is your call, but I'd be inclined to have the method return the NSURLSessionTask, so that the caller could use that task object if it wanted to (e.g. save the task object so that it could cancel it later if it wanted to).
Thus, that yields something like:
func performURLRequest (address: String, completionHandler: ((NSDictionary!, NSError!) -> Void)?) -> NSURLSessionTask {
let url = NSURL(string: address)
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, error) in
if data == nil {
sessionEnded?(nil, error)
} else {
var parseError: NSError?
let parsedObject = NSJSONSerialization.JSONObjectWithData(data, options: nil, error:&parseError) as? NSDictionary
completionHandler?(parsedObject, parseError)
}
}
task.resume()
return task
}
And you'd invoke it like:
performURLRequest("http://www.example.com/some/path") { responseDictionary, error in
if responseDictionary == nil {
// handle error, e.g.
println(error)
return
}
// use `responseDictionary` here
}
Outside of asking the user to input their name, is there any way to get it off the device?
I tried this library, which attempts to extract the name from [UIDevice currentDevice] name], but that doesn't work in a lot of situations:
https://github.com/tiboll/TLLNameFromDevice
Is the user's name present in the phonebook or anywhere else that we have access to in iOS 6?
Well you could go through all the contacts in the AddressBook and see if any of them are marked with the owner flag.
Just be aware that doing this will popup the "this app wants access to the address book" message. Also Apple isn't very keen on these kind of things. In the app review guide it is specified that an app can not use personal information without the user's permission.
You could use Square's solution:
Get the device's name (e.g. "John Smith's iPhone").
Go through the contacts on the phone and look for a contact named "John Smith".
JBDeviceOwner and ABGetMe will both do this for you.
You could use CloudKit. Following a snippet in Swift (ignoring errors):
let container = CKContainer.defaultContainer()
container.fetchUserRecordIDWithCompletionHandler(
{
(recordID, error) in
container.requestApplicationPermission(
.PermissionUserDiscoverability,
{
(status, error2) in
if (status == CKApplicationPermissionStatus.Granted)
{
container.discoverUserInfoWithUserRecordID(
recordID,
completionHandler:
{
(info, error3) in
println("\(info.firstName) \(info.lastName)")
}
)
}
}
)
}
)
The above code was based on the code at http://www.snip2code.com/Snippet/109633/CloudKit-User-Info
to save folks time. in swift4:
let container = CKContainer.default()
container.fetchUserRecordID(
completionHandler: {
(recordID, error) in
guard let recordID = recordID else {
return
}
container.requestApplicationPermission(
.userDiscoverability,
completionHandler: {
(status, error2) in
if (status == CKContainer_Application_PermissionStatus.granted)
{
if #available(iOS 10.0, *) {
container.discoverUserIdentity(withUserRecordID:
recordID,
completionHandler:
{
(info, error3) in
guard let info = info else {
return
}
print("\(info.firstName) \(info.lastName)")
}
)
}
}
}
)
}
)
however: CKUserIdentity no longer exposes either first or last name
So this answer no longer works.
You can use:
NSLog(#"user == %#",[[[NSHost currentHost] names] objectAtIndex:0]);
I did receive compiler warnings that the methods +currentHost and -names were not found. Given the warning, I’m not sure of Apple’s intention to make this available (or not) as a publicly accessible API, however, everything seemed to work as expected without the need to include any additional header files or linking in additional libraries/frameworks.
Edit 1:
You may also take a look at this Link
Edit 2:
If you have integrated your app with Facebook you can easily retrieve the user info, see Facebook Fetch User Data
For SWIFT you can use
NSUserName() returns the logon name of the current user.
func NSUserName() -> String