So I am trying to get CLFloor in Core Location to give me data instead of returning nil in Swift, but I have not been able to. I have tried my own code and the following from NSHipster:
import CoreLocation
class LocationManagerDelegate: NSObject, CLLocationManagerDelegate {
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: AnyObject[]!) {
let location: CLLocation? = locations[0] as? CLLocation
if let floor: CLFloor? = location?.floor {
println("Current Floor: \(floor?.level)")
}
}
}
let manager = CLLocationManager()
manager.delegate = LocationManagerDelegate()
manager.startUpdatingLocation()
Does anybody know how to make this work on a device or in the simulator, I'm sure the answer would benefit a lot of people. If anyone knows of any good resources on this or CLVisit, that would also be helpful.
As far as I understand what was said in WWDC session 708 - Taking Core Location Indoors, You need to manually register and set up the particular venue on the new Apple's Maps Connect page in order to get the floor information in iOS.
Therefore, it seems there is no way that the floor info is provided automatically (e.g. based altitude information from on GPS) for an arbitrary venue.
based on these 2 links from Apple's website
CLFloor Ref
CLLoaction Class Ref
Discussion
If floor information is not available for the current location, the value of this property is nil.
I'm not expert in swift but i think
returns nil possibly means that there is no floor information at that location.
I don't think iOS will magically tell you the floor level from what I understand https://developer.apple.com/videos/wwdc/2014/?id=708. You need to somehow get that number in another way. E.g. you may capture some iBeacon signal and from that download the floor level number, or swipe on your screen to find the correct floor map you are on?
Related
Since recently, I get in the Xcode logs a lot of strange warnings from the CoreMotion framework, related to function getLocationForBundleID:
[Client] {"msg":"#NullIsland Either the latitude or longitude was exactly 0! That's highly unlikely", "latIsZero":0, "lonIsZero":0}
[Client] {"msg":"#NullIsland Received a latitude or longitude from getLocationForBundleID that was exactly zero", "latIsZero":0, "lonIsZero":0, "location":'28 5B E0 D7 EB 7F 00 00'}
I do not see any malfunction of my app. So maybe these logs can be ignored, but they are annoying anyway.
My questions are:
How can I correct a potential error?
How can I stop these warnings?
Apparently this error message is logged if property location of a CLLocationManager is read, before the CLLocationManager delivered the very first location.
My solution is the following:
I use anyway a subclass of CLLocationManager that allows me to set the location property for tests programmatically.
This is done as follows:
private var _location: CLLocation?
#objc dynamic override var location: CLLocation? {
get {
let usedLocation = _location ?? super.location // Here the error is logged if no location has been delivered yet
return usedLocation
}
set {
_location = newValue
}
}
This subclass has now an additional property
var anyLocationDelivered = false
that is set true in
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locationManager.anyLocationDelivered = true
// …
}
and the getter is now
get {
guard anyLocationDelivered else { return nil }
let usedLocation = _location ?? super.location
return usedLocation
}
Now this error message is no longer logged.
I was getting this same error when trying to get a user's location in an app. There were two things preventing me from getting a valid lat/long. The main issue I was having was that I was using a simulator and had to set the location manually. If you're using the simulator, go to XCode, click Debug --> Simulate Location, and choose a location. I also had to make sure the correct location permission keys were added to Info.plist. You're also probably already aware, but you'll know if any keys are missing from Info.plist because a separate error message will print out to the console above or below these lat/long error messages, and it will tell you which key is missing. So basically:
Make sure you have all necessary location permissions accepted on the device/simulator being used to test the application.
Once you have location permissions, set a location if using a simulator.
Not sure if this will help at all, but I hope it does. If you've already confirmed that these are not issues, I'm not sure what may be causing the issue in your case, but stepping through the parts of your code responsible for fetching the user location would probably be beneficial to see which link in the process is failing.
The issue is likely that you ran your application on the simulator, which does NOT use your current location by default unless you manually change the simulation location in Debug, Simulate Location.
To fix the issue, either simulate your current location (as stated above) OR run your application on your physical signed device, close the application completely, and then relaunch your application.
OS_ACTIVITY_MODE = disable
To get rid of the warning do the following:
Steps
Navigate to Product -> Scheme -> Edit Scheme.
Open the Arguments tab
Environment Variables section add OS_ACTIVITY_MODE = disable
I use this function:
func getDataCity(tmpLat:Double,tmpLong:Double) {
let tmpCLGeocoder = CLGeocoder.init()
let tmpDataLoc = CLLocation.init(latitude: tmpLat, longitude: tmpLong)
tmpCLGeocoder.reverseGeocodeLocation(tmpDataLoc, completionHandler: {(placemarks,error) in
guard let tmpPlacemarks = placemarks else{
return
}
let placeMark = tmpPlacemarks[0] as CLPlacemark
// Country
guard let countryLocality = placeMark.country else{
return
}
// City
guard let cityLocality = placeMark.locality else{
return
}
print(countryLocality)
print(cityLocality)
})
}
When I use coordinates, for example, from Berlin/Germany
getDataCity(tmpLat: 52.52000659999999, tmpLong: 13.404953999999975)
the function works fine, it shows me the city and country. However I use coordinates from small cities or island, for example Djerba (island in Tunisia)
getDataCity(tmpLat: 33.8075978, tmpLong: 10.845146699999987)
the function doesn't print anything. Is there an explanation from Apple? What can I do about it?
To use the function in your own project, add CLLocationManagerDelegate to ViewController and don't forget to add the Privacy - Location When In Use Usage Description in your Info.plist
Apple's maps do not include a city for that location. Your code does recognize the country, but doesn't recognize a city there (in which case you abort).
If you open Apple Maps, you'll note that no cities are marked on that island, and searching for Houmt Arbah, Tunisia (the closest town) doesn't return a result in Apple Maps (it strangely returns Dahmani, which is a mainland town; I don't know why). It does know about Houmt Souq, but that's quite a ways from the given location.
The short and long of it is that Apple's map database doesn't know a lot about Tunisian geography. If you spend a little time looking around in Google Maps vs Apple Maps, you'll see that there are several parts of Tunisia that Apple Maps knows very little about. For example, if you look at Douz in satellite mode and then switch to map mode, you'll see that Apple's satellite imagery includes an entire village (Al-Qalah) that isn't mapped. And the street map of Douz itself (a town of 38k people), is, to put it bluntly, pathetic.
While Apple's maps have dramatically improved over the years, and in some areas they're now better than Google's maps, in most places Google tends to have far better information. If you need the best maps in arbitrary places, Google's maps are today the gold standard.
I'm making an app that tracks a user's location and currently I'm doing this (once user has authorized tracking of their location) with the following code:
override func viewDidLoad() {
super.viewDidLoad()
// set map View delegate
mapView.delegate = self
// turn on user tracking mode to follow user
if tracking == true {
mapView.userTrackingMode = MKUserTrackingMode.follow
} else {
mapView.userTrackingMode = MKUserTrackingMode.none
}
}
This seems to work fine for tracking the user's location and I was wondering when I would use this and when I would use locationManager.startUpdatingLocation() or stopUpdatingLocation().
You'd use userTrackingMode when your sole purpose is to update a map. But sometimes we want the user location for other purposes (e.g. to log it in some database, to use it to see what stores and or location-based special deals we might offer, etc.). In fact, we might use CLLocationManager when a map might not be shown at all, or not yet, at least.
So, if you only care about updating a map, then you can use userTrackingMode. But if you care about location independent of any visual map, use CLLocationManager.
I'm building an app that uses geofencing but I realised that CLRegion has many limitations, for example:
that location authorizationStatus must be .authorizedAlways in order for region monitoring to work.
only circular regions can be monitored
This limits the functionality that can be applied using region monitoring. However after doing some research i found out that there are ways to change this behaviour by creating a custom class of CLRegion.
To be honest I have no idea how to do this or where to start. Does anyone have any suggestions as to how such a custom CLRegion class could allow customised geo-fencing?
There is a tutorial on appcoda that talked about this briefly, but it wasn't in depth at all, you can find it here:
https://www.appcoda.com/geo-targeting-ios/
There they suggest starting with protocols such as:
protocol RegionProtocol {
var coordinate: CLLocation {get}
var radius: CLLocationDistance {get}
var identifier: String {get}
func updateRegion()
}
protocol RegionDelegateProtocol {
func didEnterRegion()
func didExitRegion()
}
And from these one can create custom functionality for CLRegions, such as monitoring polygons etc.
How should one get started on implementing custom regions?
Thanks!
Upon the app opening, I'd like to draw a route between user's current location to a specific location using MapKit in Swift.
I know how to request/get a hold of the users current location:
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
originalTopMargin = topMarginConstraint.constant
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
locationManager.requestLocation()
}
}
What I don't know is, where does the code to actually calculate and display the route belong? Would it be in this function?
func locationManager(manager:CLLocationManager, didUpdateLocations locations:[AnyObject])
I've read the Ray Wenderlich Mapkit tutorials and many, many other sites, I'm having a tough time finding an answer to my specific question. Everything I've found so far has useful information, but doesn't quite have what I'm looking for.
I haven't done this in a long time - but what you want to do is use the MKDirections API.
Basically you create an MKDirectionsRequest object and you can use this to eventually get a list of steps to reach your destination. You can also use this to get an MKPolyLine object to render this on your map etc.
Here is a link to a tutorial. It is in objective-C but it should be relatively easy to translate the code.http://www.devfright.com/mkdirections-tutorial/