I ran into some code for a location manager class and I noticed that there is a variable that holds a closure.
var locationInfoCallBack: ((_ info: LocationInformation) -> ())!
I can't seem to access the underlying arguments from the variable, which would be preferred. Is it possible to retrieve the LocationInformation from the above variable?
My understanding is that by passing in the info parameter like this self.locationInfoCallBack(info) the 'start' function will re-run:
In my experience it is unnecessary to recall locationManager.requestAlwaysAuthorization() or locationManager.startUpdatingLocation()
Here's the code:
class Location: NSObject, CLLocationManagerDelegate {
static let shared = Location()
let locationManager : CLLocationManager
var locationInfoCallBack: ((_ info: LocationInformation) -> ())!
override private init() {
locationManager = CLLocationManager()
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.distanceFilter = kCLLocationAccuracyNearestTenMeters
super.init()
locationManager.delegate = self
}
func start(completion: #escaping(_ info: LocationInformation) -> Void) {
self.locationInfoCallBack = completion
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
func stop() {
locationManager.stopUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let mostRecentLocation = locations.last else {
return
}
let info = LocationInformation()
info.latitude = mostRecentLocation.coordinate.latitude
info.longitude = mostRecentLocation.coordinate.longitude
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(mostRecentLocation) { (placemarks, error) in
guard let placemarks = placemarks, let placemark = placemarks.first else { return }
if let city = placemark.locality,
let state = placemark.administrativeArea,
let zip = placemark.postalCode,
let locationName = placemark.name,
let thoroughfare = placemark.thoroughfare,
let country = placemark.country {
info.city = city
info.state = state
info.zip = zip
info.address = locationName + ", " + (thoroughfare as String)
info.country = country
}
self.locationInfoCallBack(info)
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
locationManager.stopUpdatingLocation()
}
}
class LocationInformation {
var city: String?
var address: String?
var latitude: CLLocationDegrees?
var longitude: CLLocationDegrees?
var zip: String?
var state: String?
var country: String?
init(city: String? = "", address: String? = "", latitude: CLLocationDegrees? = Double(0.0), longitude: CLLocationDegrees? = Double(0.0), zip: String? = "", state: String? = "", country: String? = "") {
self.city = city
self.address = address
self.latitude = latitude
self.longitude = longitude
self.zip = zip
self.state = state
self.country = country
}
}
If the underlying argument is inaccessible my current hypothesis is the variable 'locationInfoCallBack' could be used to check whether the 'start' function has completed. If this is the case why not use a simple boolean for the same result?
I want it to be available app wide
In that case, a more suitable design would be not to use a closure. Replace the closure with:
var currentLocationInfo: LocationInformation?
The parameter to start can be removed:
func start() {
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
And in didUpdateLocation, replace the call self.locationInfoCallBack(info) with a simple assignment to currentLocationInfo:
self.currentLocationInfo = info
Now you can access from anywhere:
Location.shared.currentLocationInfo
This will give you the location that you got, the last time that didUpdateLocation is called, and you successfully reverse-geocoded that.
Note that this could be nil, because you might be accessing it before the first time that you successfully. reverse-geocoded a location.
You can access the parameter when the closure is running from the code of the closure, and its value will be whatever the caller passed. It’s the same as with a function. Unless the function (or closure) is running, the parameters don’t exist.
Related
I am working in Swift trying to update an organization struct that will need to hold a latitude and longitude. I created a mutating function in the struct that will update the latitude and longitude based on the organization organization struct’s address. I got it to work, but the issue is that when I call the mutating function, I need to manually enter the variable name with the .latitude and .longitude. Is there a way that I can pass the variable struct’s name automatically and reference the .latitude and .longitude without calling the specific variable name with it so I can make it more usable? I included an example below with my code. Thanks for your help!
import UIKit
import PlaygroundSupport
import CoreLocation
PlaygroundPage.current.needsIndefiniteExecution = true
struct organization {
var name: String
var address: String
var latitude: CLLocationDegrees = 0 //default setting for latitude
var longitude: CLLocationDegrees = 0 //default setting for longitude
mutating func getCoordinateFrom(completion: #escaping(_ coordinate: CLLocationCoordinate2D?, _ error: Error?) -> () ) {
CLGeocoder().geocodeAddressString(address) { placemarks, error in
completion(placemarks?.first?.location?.coordinate, error)
}
}
}
struct Coordinates {
var latitude: Double
var longitude: Double
}
//create an wildernessLodge variable of type organization
var wildernessLodge = organization(name: "Disney's Wilderness Lodge", address: "901 Timberline Dr, Orlando, FL 32830")
wildernessLodge.getCoordinateFrom { coordinate, error in
guard let coordinate = coordinate, error == nil else { return }
wildernessLodge.latitude = coordinate.latitude
wildernessLodge.longitude = coordinate.longitude
print("update 1 \(wildernessLodge)")
}
I'm a bit confused by your code. Why is getCoordinateFrom marked as mutating? Perhaps you meant to write something like this.
mutating func getCoordinatesFromAddress() {
CLGeocoder().geocodeAddressString(address) { placemarks, error in
guard let coordinate = placemarks?.first?.location?.coordinate, error == nil else { return }
self.latitude = coordinate.latitude
self.longitude = coordinate.longitude
}
}
Now this function is mutating (it modifies self), and
wildernessLodge.getCoordinateFrom { coordinate, error in ... }
can be replaced with
wildernessLodge.getCoordinatesFromAddress()
The only reason to leave the getCoordinateFrom method is if, somewhere in your code, you intend to get coordinates from an address but not update the coordinates in the struct. I can't imagine a good reason to do that, so I would recommend replacing the getCoordinateFrom method with something else.
Alternatively, if you generally intend to set the coordinates right after creating a value of this type, you might want to consider something like this.
init(name: String, address: String) {
self.name = name
self.address = address
CLGeocoder().geocodeAddressString(address) { placemarks, error in
guard let coordinate = placemarks?.first?.location?.coordinate, error == nil else { return }
self.latitude = coordinate.latitude
self.longitude = coordinate.longitude
}
}
or
init(name: String, address: String) {
self.name = name
self.address = address
self.getCoordinatesFromAddress()
}
Then, you could create an organization using organization(name: name, address: address) and the coordinates would automatically be set correctly.
If neither of these are satisfactory, maybe you should create two different structs to capture the behavior you want.
struct Organization {
var name: String
var address: String
func withCoordinates(completion: #escaping(_ coordinate: CLLocationCoordinate2D?, _ error: Error?) -> () ) {
CLGeocoder().geocodeAddressString(address) { placemarks, error in
completion(placemarks?.first?.location?.coordinate, error)
}
}
}
struct OrganizationWithCoordinates {
var name: String
var address: String
var latitude: CLLocationDegrees
var longitude: CLLocationDegrees
init(from organization: Organization) {
self.name = organization.name
self.address = organization.address
organization.withCoordinates { coordinate, error in
guard let coordinate = coordinate, error == nil else { return }
self.latitude = coordinate.latitude
self.longitude = coordinate.longitude
}
}
}
I would prefer an approach like this, but I like having lots of types.
Finally, as noted in the comments, if you are really just concerned with brevity, you can replace
var latitude: CLLocationDegrees
var longitude: CLLocationDegrees
with
var coordinates: CLLocationCoordinate2D
var latitude: CLLocationDegrees { coordinates.latitude }
var longitude: CLLocationDegrees { coordinates.longitude }
and then replace
wildernessLodge.latitude = coordinate.latitude
wildernessLodge.longitude = coordinate.longitude
with
wildernessLodge.coordinates = coordinate
In fact, you should feel free to combine any of these approaches.
Edit: As pointed out, these solutions do not work as-is. The fundamental tension is trying to work with CLGeocoder's async method synchronously. One solution is to use a class instead of a struct. The other approach is to use a modification of the withCoordinate method above:
struct Organization {
var name: String
var address: String
func withCoordinate(callback: #escaping (OrganizationWithCoordinate?, Error?) -> Void) {
CLGeocoder().geocodeAddressString(self.address) { placemarks, error in
if let coordinate = placemarks?.first?.location?.coordinate, error == nil {
let orgWithCoord = OrganizationWithCoordinate(name: self.name, address: self.address, latitude: coordinate.latitude, longitude: coordinate.latitude)
callback(orgWithCoord, nil)
} else {
callback(nil, error)
}
}
}
}
struct OrganizationWithCoordinate {
var name: String
var address: String
var latitude: CLLocationDegrees
var longitude: CLLocationDegrees
}
Organization(name: "Disney's Wilderness Lodge", address: "901 Timberline Dr, Orlando, FL 32830").withCoordinate { orgWithCoord, error in
guard let orgWithCoord = orgWithCoord, error == nil else {
print("Error")
return
}
print(orgWithCoord)
}
This embraces the async nature of CLGeocoder.
Another solution could be to force CLGeocoder to be synchronous using DispatchSemaphore as follows. I don't think these work correctly in Playgrounds, but this should work in an actual app.
struct Organization {
var name: String
var address: String
var latitude: CLLocationDegrees
var longitude: CLLocationDegrees
init(name: String, address: String) throws {
self.name = name
self.address = address
var tempCoordinate: CLLocationCoordinate2D?
var tempError: Error?
let sema = DispatchSemaphore(value: 0)
CLGeocoder().geocodeAddressString(address) { placemarks, error in
tempCoordinate = placemarks?.first?.location?.coordinate
tempError = error
sema.signal()
}
// Warning: Will lock if called on DispatchQueue.main
sema.wait()
if let error = tempError {
throw error
}
guard let coordinate = tempCoordinate else {
throw NSError(domain: "Replace me", code: -1, userInfo: nil)
}
self.longitude = coordinate.longitude
self.latitude = coordinate.latitude
}
}
// Somewhere in your app
let queue = DispatchQueue(label: "Some queue")
queue.async {
let wildernessLodge = try! Organization(name: "Disney's Wilderness Lodge", address: "901 Timberline Dr, Orlando, FL 32830")
DispatchQueue.main.async {
print(wildernessLodge)
}
}
Here, a new queue to do Organization related work is created to avoid locking up the main queue. This method creates the least clunky-looking code in my opinion, but probably is not the most performant option. The location APIs are async for a reason.
Trying to get name of a city, while having latitude and longitude.
Inside a model class Location, I'm using reverseGeocodeLocation(location: , completionHandler: ) func that comes with CLGeocoder (part of CoreLocation).
func getLocationName() {
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: currentLatitude, longitude: currentLongitude)
geoCoder.reverseGeocodeLocation(location, completionHandler: { placemarks, error in
guard let addressDict = placemarks?[0].addressDictionary else {
return
}
if let city = addressDict["City"] as? String {
self.currentCity = city
print(city)
}
if let zip = addressDict["ZIP"] as? String {
print(zip)
}
if let country = addressDict["Country"] as? String {
print(country)
}
})
}
However, in ViewController, after running the getLocationName(), the location.currentCity is nil, since the completion handler is async, and wasn't finished yet.
How can I make sure that the completion handler is finished running so I can access location.currentCity ?
Pass a closure as a function parameter in your getLocationName which
you can call inside the reverseGeocodeLocation closure.
func updateLocation(currentCity : String) -> Void
{
print(currentCity)
}
func getLocationName(callback : #escaping (String) -> Void)
{
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: currentLatitude, longitude: currentLongitude)
geoCoder.reverseGeocodeLocation(location, completionHandler: { placemarks, error in
guard let addressDict = placemarks?[0].addressDictionary else {
return
}
if let city = addressDict["City"] as? String
{
self.currentCity = city
callback(city)
print(city)
}
if let zip = addressDict["ZIP"] as? String {
print(zip)
}
if let country = addressDict["Country"] as? String {
print(country)
}
})
}
In your ViewController...
getLocationName(callback: updateLocation)
I would create a function where location.currentCity is used, and call this function from the completion handler
So if your code looks like:
func foo() {
var location
getLocationName()
print(location.currentcity) // nil
}
change it to:
func foo() {
var location
getLocationName()
}
func bar() {
print(location.currentcity) // someplace
}
and call bar() from your completion handler
I am trying to figure out how to reverse geocode a CLLocation stored in CloudKit. I have the location stored in a record and I know it stores as latitude and longitude. Here's my record. The latitude and longitude appear I just took them out for now.
However I want to be able to make the location user-readable, so AKA reverse geocode it, to get the city and state. And I've looked on here so far but nothing on reverse geocoding the location I can store in CloudKit.
Here is my model:
class Peek: CloudKitSyncable {
static let kType = "Peek"
static let kPhotoData = "photoData"
static let kTimeStamp = "timestamp"
static let kTitle = "title"
static let kText = "text"
static let kLocation = "location"
static let kCity = "city"
let title: String
let text: String
let photoData: Data?
let timestamp: Date
var location: CLLocation
var comments: [Comment]
var photo: UIImage? {
guard let photoData = self.photoData else { return nil }
return UIImage(data: photoData)
}
init(title: String, timestamp: Date = Date(), text: String, photoData: Data?, comments: [Comment] = [], location: CLLocation) {
self.title = title
self.timestamp = timestamp
self.text = text
self.photoData = photoData
self.comments = comments
self.location = location
}
var recordType: String {
return Peek.kType
}
var cloudKitRecordID: CKRecordID?
convenience required init?(record: CKRecord) {
guard let timestamp = record.creationDate,
let photoAsset = record[Peek.kPhotoData] as? CKAsset,
let title = record[Peek.kTitle] as? String,
let text = record[Peek.kText] as? String,
let location = record[Peek.kLocation] as? CLLocation else { return nil }
let photoData = try? Data(contentsOf: photoAsset.fileURL)
self.init(title: title, timestamp: timestamp, text: text, photoData: photoData, location: location)
cloudKitRecordID = record.recordID
}
fileprivate var temporaryPhotoURL: URL {
let temporaryDirectory = NSTemporaryDirectory()
let temporaryDirectoryURL = URL(fileURLWithPath: temporaryDirectory)
let fileURL = temporaryDirectoryURL.appendingPathComponent(UUID().uuidString).appendingPathExtension("jpg")
try? photoData?.write(to: fileURL, options: .atomic)
return fileURL
}
}
extension CKRecord {
convenience init(_ peek: Peek) {
let recordID = CKRecordID(recordName: UUID().uuidString)
self.init(recordType: peek.recordType, recordID: recordID)
self[Peek.kTitle] = peek.title as String? as CKRecordValue?
self[Peek.kText] = peek.text as String? as CKRecordValue?
self[Peek.kTimeStamp] = peek.timestamp as CKRecordValue?
self[Peek.kLocation] = peek.location as CKRecordValue?
self[Peek.kPhotoData] = CKAsset(fileURL: peek.temporaryPhotoURL)
}
}
I also have a LocationManager file as well:
class LocationManager: NSObject {
static let sharedInstance = LocationManager()
override init() {
super.init()
locationManager.delegate = self
}
var locationManager = CLLocationManager()
var currentLocation: CLLocation?
func requestCurrentLocation() {
locationManager.requestLocation()
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
currentLocation = locations.first
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("error: \(error.localizedDescription)")
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
locationManager.requestLocation()
}
}
}
Apple provides a method built into Core Location's CLGeocoder class. Here are the docs. If successful the completion handler will give you access to an array of CLPlacemark, so you can grab one of those and access whichever human-readable elements you need. The names of the variables are pretty generic to cover locations all over the world, so you'll have to dig in a bit to find exactly what you need. Check the docs on CLPlacemark for exact details on the variables available to you. In your particular case you'll need locality and administrativeArea for city and state, respectively.
Usage would be something like this:
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(location) { (placemarks, error) in
guard let placemarks = placemarks, let placemark = placemarks.first else { return }
if let city = placemark.locality, let state = placemark.administrativeArea {
//Set your labels or whatever
}
}
Hello I am developing an application for ios and one of it's functions has to find the user's current location. Here is my code:
import UIKit
import MapKit
import CoreLocation
import SwiftyJSON
struct City {
let name : String
let location : CLLocation
let description :String
let imageName : String
func distanceTo(location:CLLocation) -> Int
{
let distanceMeters = location.distanceFromLocation(self.location)
let distanceKilometers = distanceMeters / 1000.00
return Int(round(100 * distanceKilometers) / 100)
}
}
class FirstViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var LabelTest: UILabel!
#IBOutlet weak var Slider: UISlider!
#IBOutlet weak var LabelValueSlider: UILabel!
var MySliderCurrentValue = Double()
var manager = CLLocationManager()
var userLoc: CLLocationCoordinate2D!
{
willSet{
self.orderCitysByProximity()
self.filterCitysByProximity(Int(self.Slider.value))
self.LocateMe(manager)
}
}
var cities = [City]()
var nearbyCities = [City]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let path: String = NSBundle.mainBundle().pathForResource("cities", ofType: "json") as String!
let jsonData = NSData(contentsOfFile: path) as NSData!
//let ReadableJSON = JSON ( data:jsonData, options: NSJSONReadingOptions.MutableContainers, error: nil )
do {
let jsonObject = try NSJSONSerialization.JSONObjectWithData(jsonData!, options: NSJSONReadingOptions.MutableContainers) as! [String:AnyObject]
for city in jsonObject["cities"] as! [[String:AnyObject]] {
//let coordinates = position["Position"] as! [String:CLLocationDegrees]
let cityName = city["Name"] as! String
let latitude = city["Latitude"] as! Double
let longitude = city["Longitude"] as! Double
let description = city["Description"] as! String
let image = city["Image"] as! String
let location = CLLocation(latitude: latitude, longitude: longitude)
let city = City(name: cityName, location: location,description: description, imageName: image)
cities.append(city)
// print(cities)
}
self.orderCitysByProximity()
self.filterCitysByProximity(Int(self.Slider.value))
} catch let error as NSError {
print(error)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func SliderChange(sender: UISlider) {
let MySliderCurrentValue: String = String(Int(sender.value))
LabelValueSlider.text = MySliderCurrentValue
self.filterCitysByProximity(Int(sender.value))
}
#IBAction func goToTableView()
{
if let tableViewController = self.storyboard?.instantiateViewControllerWithIdentifier("tableViewController") as? TableViewController
{
tableViewController.cities = self.nearbyCities
self.navigationController?.pushViewController(tableViewController, animated: true)
}
}
func filterCitysByProximity(kilometers:Int)
{
self.nearbyCities.removeAll()
for city in self.cities {
if(city.distanceTo(self.userLoc) <= kilometers*2)
{
self.nearbyCities.append(city)
}
}
self.LabelTest.text = "you have \(self.nearbyCities.count) cities nearby"
let OrderedArray = self.nearbyCities.sort({ $0.distanceTo(self.userLoc) < $1.distanceTo(self.userLoc) })
self.nearbyCities = OrderedArray
}
func orderCitysByProximity()
{
let OrderedArray = self.cities.sort({ $0.distanceTo(self.userLoc) < $1.distanceTo(self.userLoc) })
self.cities = OrderedArray
}
#IBAction func LocateMe(sender: AnyObject) {
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userlocation: CLLocation = locations[0] as CLLocation
manager.stopUpdatingLocation()
let location = CLLocationCoordinate2D(latitude: userlocation.coordinate.latitude, longitude: userlocation.coordinate.longitude)
self.userLoc = location
}
}
On the bottom you will see the func locationManager, where I am trying to find the user's current location. Would you please advise me how to start this function so that the app recognise when I type userLoc. userLoc are the coordinations of the user and I have to use them in other functions like func filterCitysByProximity, and the rest, which you can see in the code.
Reason of your problem :-
When you call the delegate of the CLLocationManager in viewDidLoad() it takes some time to retrieve the usersCurrent Location , but even before usersLocation could relay back to you, your functions requiring usersCurrent Location were being triggered as you had called them in viewDidLoad() thus causing the error!
your Code should look like this:-
import UIKit
import MapKit
import CoreLocation
import SwiftyJSON
struct City {
let name : String
let location : CLLocation
let description :String
let imageName : String
func distanceTo(location:CLLocation) -> Int
{
let distanceMeters = location.distanceFromLocation(self.location)
let distanceKilometers = distanceMeters / 1000.00
return Int(round(100 * distanceKilometers) / 100)
}
}
class FirstViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var LabelTest: UILabel!
#IBOutlet weak var Slider: UISlider!
#IBOutlet weak var LabelValueSlider: UILabel!
var MySliderCurrentValue = Double()
var locationManager = CLLocationManager()
var userLoc : CLLocation!
var cities = [City]()
var nearbyCities = [City]()
override func viewDidLoad() {
super.viewDidLoad()
let path: String = NSBundle.mainBundle().pathForResource("cities", ofType: "json") as String!
let jsonData = NSData(contentsOfFile: path) as NSData!
do {
let jsonObject = try NSJSONSerialization.JSONObjectWithData(jsonData!, options: NSJSONReadingOptions.MutableContainers) as! [String:AnyObject]
for city in jsonObject["cities"] as! [[String:AnyObject]] {
//let coordinates = position["Position"] as! [String:CLLocationDegrees]
let cityName = city["Name"] as! String
let latitude = city["Latitude"] as! Double
let longitude = city["Longitude"] as! Double
let description = city["Description"] as! String
let image = city["Image"] as! String
let location = CLLocation(latitude: latitude, longitude: longitude)
let city = City(name: cityName, location: location,description: description, imageName: image)
cities.append(city)
}
} catch let error as NSError {
print(error)
}
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.requestWhenInUseAuthorization()
locationManager.startMonitoringSignificantLocationChanges()
locationManager.startUpdatingLocation()
if locationManager.respondsToSelector(#selector(locationManager.requestWhenInUseAuthorization)) {
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
}
else {
locationManager.startUpdatingLocation()
}
}
#IBAction func SliderChange(sender: UISlider) {
let MySliderCurrentValue: String = String(Int(sender.value))
LabelValueSlider.text = MySliderCurrentValue
if userLoc != nil{
self.filterCitysByProximity(Int(sender.value), location: userLoc)
}else{
let alertC : UIAlertController = UIAlertController(title: "Iwin", message: "UserLocation Not Updated wait a while", preferredStyle: .Alert)
let okAction : UIAlertAction = UIAlertAction(title: "oK", style: .Default, handler: { (okActn) in
print("User pressed ok")
})
alertC.addAction(okAction)
alertC.popoverPresentationController?.sourceView = view
alertC.popoverPresentationController?.sourceRect = view.frame
self.presentViewController(alertC, animated: true, completion: nil)
}
}
//Segue to .. :-
#IBAction func goToTableView()
{
if let tableViewController = self.storyboard?.instantiateViewControllerWithIdentifier("tableViewController") as? TableViewController
{
tableViewController.cities = self.nearbyCities
print(nearbyCities)
self.navigationController?.pushViewController(tableViewController, animated: true)
}
}
#IBAction func LocateMe(sender: AnyObject) {
}
//Class Functions call :-
func filterCitysByProximity(kilometers:Int, location: CLLocation)
{
self.nearbyCities.removeAll()
for city in self.cities {
if(city.distanceTo(location) <= kilometers*2)
{
self.nearbyCities.append(city)
}
}
self.LabelTest.text = "you have \(self.nearbyCities.count) cities nearby"
let OrderedArray = self.nearbyCities.sort({ $0.distanceTo(location) < $1.distanceTo(location) })
self.nearbyCities = OrderedArray
}
func orderCitysByProximity(location: CLLocation)
{
let OrderedArray = self.cities.sort({ $0.distanceTo(location) < $1.distanceTo(location) })
self.cities = OrderedArray
}
//Location Manager Functions :-
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
userLoc = locations[0] as CLLocation
locationManager.stopUpdatingLocation()
print(userLoc)
self.orderCitysByProximity(userLoc)
self.filterCitysByProximity(Int(self.Slider.value), location : userLoc)
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status == .AuthorizedWhenInUse {
print("authorised call came . . . . ")
locationManager.startUpdatingLocation()
}
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError){
print(error.localizedDescription)
locationManager.stopUpdatingLocation()
}
}
Add privacy - location usage description : Result to your info.plist
Also if your didUpdateLocations function doesn't get called
reset your location Services by going into settings->General->Reset
Try google, you will find multiple questions
I have a class named Location that has several methods in it that do not have any parameters.
However, when I try to create a variable with the result of the method, it wants an argument. Why is that?
Location class:
let locationManager = CLLocationManager()
public class Location {
public func coordinate() -> (latitude: Float?, longitude: Float?) {
let latitude = Float((locationManager.location?.coordinate.latitude)!)
let longitude = Float((locationManager.location?.coordinate.longitude)!)
return (latitude: latitude, longitude: longitude)
}
public func getCity() -> String {
var returnCity: String = "N/A"
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: (locationManager.location?.coordinate.latitude)!, longitude: (locationManager.location?.coordinate.longitude)!)
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
// Place details
var placeMark: CLPlacemark!
placeMark = placemarks?[0]
// City
if let city = placeMark.addressDictionary!["City"] as? String {
returnCity = city
}
})
return returnCity
}
public func getCountry() -> String {
var returnCountry: String = "N/A"
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: (locationManager.location?.coordinate.latitude)!, longitude: (locationManager.location?.coordinate.longitude)!)
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
// Place details
var placeMark: CLPlacemark!
placeMark = placemarks?[0]
// City
if let country = placeMark.addressDictionary!["Country"] as? String {
returnCountry = country
}
})
return returnCountry
}
public func getZip() -> Int {
var returnZip: Int = 0
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: (locationManager.location?.coordinate.latitude)!, longitude: (locationManager.location?.coordinate.longitude)!)
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
// Place details
var placeMark: CLPlacemark!
placeMark = placemarks?[0]
// City
if let zip = placeMark.addressDictionary!["ZIP"] as? Int {
returnZip = zip
}
})
return returnZip
}
public func getLocationName() -> String {
var returnName: String = "N/A"
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: (locationManager.location?.coordinate.latitude)!, longitude: (locationManager.location?.coordinate.longitude)!)
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
// Place details
var placeMark: CLPlacemark!
placeMark = placemarks?[0]
// City
if let locationName = placeMark.addressDictionary!["Name"] as? String {
returnName = locationName
}
})
return returnName
}
public func getStreetAddress() -> String {
var returnAddress: String = "N/A"
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: (locationManager.location?.coordinate.latitude)!, longitude: (locationManager.location?.coordinate.longitude)!)
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
// Place details
var placeMark: CLPlacemark!
placeMark = placemarks?[0]
// City
if let street = placeMark.addressDictionary!["Thoroughfare"] as? String {
returnAddress = street
}
})
return returnAddress
}
}
Trying to create a variable:
let city = Location.getCity()
Here are some screen shots of what I get:
These methods are not class methods, they are instance methods. You must call them on an instance of the Location class, not on the class itself. Evidently, Swift can call instance methods similarly to Python: the method is a function owned by the class, and its argument is an instance of the class. But you should not call instance methods this way.
The best way to solve this problem is to construct a Location object and then call the method on it:
let city: Location = Location().getCity()
Because you're trying to call it as a class function. You should be creating an instance of Location and calling the function on that. Note also that it returns String Where your code is telling the compiler you're expecting it to return a Location.