Create local location based notifications in swift - ios

I'm a relatively new swift developer and I've heard in iOS 8 you can send local notifications based on a users Location. I have had a look at some code, particularly this one to create simple time based local notifications.
var leftNotification1:UILocalNotification = UILocalNotification()
leftNotification1.alertBody = "NotidicationA"
leftNotification1.repeatInterval = NSCalendarUnit.CalendarUnitHour
leftNotification1.fireDate = NSDate(timeIntervalSinceNow: 900)
UIApplication.sharedApplication().scheduleLocalNotification(leftNotification1)
I have also seen that you can replace fireDate with a location trigger like something like this:
var localNotification:UILocalNotification = UILocalNotification()
localNotification.regionTriggersOnce = true
localNotification.region = CLCircularRegion(circularRegionWithCenter: CLLocationCoordinate2D(latitude: 37.33233141, longitude: -122.03121860), radius: 50.0, identifier: "Location1")
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
I know with locations you need permission from the user and poll for locations and that. I am not aware of how to do that sort of thing and link it with that code. When I just enter this code into my ViewDidLoad like I do for the top one it doesn't work for obvious reasons, the CoreLocation isn't registered and its not polling for locations. If someone could inform me about how to get this code working or even better give me an example code to take a look at that'll be great. Thanks.

I found a few guides that had different ways of approaching this, you need to import CoreLocation then you'll want to register the CLLocationManagerDelegate in the ViewController function in ViewController.swift.
You'll need to get permission for using the location with
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
UIApplication.sharedApplication().cancelAllLocalNotifications()
If you use the requestWhenInUseAuthorization instead it won't display the notifications as they only are displayed when you are not in the app. In iOS 8 it requires you to declare NSLocationAlwaysUsageDescription in the info.plist file you'll just enter some text to appear when location access is requested.
Once you've done all of that you can start creating your notifications, you can use the following code to do that
let locattionnotification = UILocalNotification()
locattionnotification.alertBody = "You have entered a high risk zone (Crown Range Road) , proceed with caution"
locattionnotification.regionTriggersOnce = false
locattionnotification.region = CLCircularRegion(circularRegionWithCenter: CLLocationCoordinate2D(latitude:
37.33182, longitude: -122.03118), radius: 100.0, identifier: "Location1")
UIApplication.sharedApplication().scheduleLocalNotification(locattionnotification)
The regionTriggersOnce option allows you to turn on and off if you get multiple messages when you enter and exit the zone or only one when you first enter the area.
You can change the latitude, longitude and radius (in meters) values to what you want and when you get within the radius of the point you will be alerted with the contents of the message.
For more details about the different options have a look at https://developer.apple.com/library/ios/documentation/iPhone/Reference/UILocalNotification_Class/

The SetSDK allows you to set this up really really simply now. Basically, just a few lines of code.
SetSDK.instance.onDeparture(from: .any) { departed in
/* do your Swift things here */
let departureCoordinates = departed.location
// keep going
}
SetSDK.instance.onArrival(to: .any) { arrived in
/* do your Swift things here */
let arrivalCoordinates = arrived.location
// keep going
}
The SDK handles doing the persistent location collection without killing your battery and the locations that it sends notifications for are continuously being learned, so no manual entry of geofences or anything like that. Just start it and go.

Related

App Clip - Support Multiple Businesses Locations

When I designed my App Clip Launch experience, I had in mind that the App can only be triggered via QR code, NFC or App Clip Code. That why I linked the App Launch to a specific location with specific Id.
When my App went live last week, and when I try to scan a NFC tag the App is launching as expected every time.
Now, if I tap the App Clip icon on the home screen, the App is launching with the last URL scanned I dig some googling and I found that the App Clip is caching the last URL scanned and simulating a universal link launch when icon tapped!
This is not working for me! So I am looking for a way to check if the App was launched via scan or tap? I tried to log the App launch but it's always running in the order either via Scan (NFC) or icon tap:
AppDelegate.didFinishLaunchingWithOptions()
SceneDelegate.willConnectTo() // It's here where I am handling the Universal Link
How can I check if the user launched the App via Tap or Scan? Knowing that the App is always simulating Universal launch Link when icon tapped!
Or how I can look for the saved URL? I tried to fetch all UserDefaults and Some Keychain data, but I found nothing!
I faced the same issue! And unfortunately there’s no way to:
Check how the App was launched, icon tap or NFC/QR scan
To retrieve cached data from either UserDefaults or Keychain
Apple says clearly on their Human Interface Guidelines that if you want support multiple businesses you should add the location services factor!
Consider multiple businesses. An App Clip may power many different
businesses or a business that has multiple locations. In both
scenarios, people may end up using the App Clip for more than one
business or location at a time. The App Clip must handle this use case
and update its user interface accordingly. For example, consider a way
to switch between recent businesses or locations within your App Clip,
and verify the user’s location when they launch it.
So, now your tags for specific location should be mapped to a coordinates [Longitude, Latitude]. Apple has introduced a new location verification API just for App Clips that allows you to do a one-time check to see if the App Clip code, NFC tag or QR code that the user scanned is where it says it is.
Enable Your App Clip to Verify the User’s Location
To enable your App Clip to verify the user’s location, modify your App Clip’s Info.plist file:
Open your App Clip’s Info.plist, add the NSAppClip key, and set its
type to Dictionary.
Add an entry to the dictionary with NSAppClipRequestLocationConfirmation as the key, select Boolean as
its type, and set its value to true.
But using App Clip Location services is different:
Parse the information on the URL that launches the App CLip
Send a request to your Database to fetch the location information for this business
Use activity.appClipActivationPayload to confirm if the location (in Step 2) is in region where the user is right now.
The Code bellow (Copied from Apple) explains how to do it.
import UIKit
import AppClip
import CoreLocation
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
// Call the verifyUserLocation(_:) function in all applicable life-cycle callbacks.
func verifyUserLocation(_ activity: NSUserActivity?) {
// Guard against faulty data.
guard activity != nil else { return }
guard activity!.activityType == NSUserActivityTypeBrowsingWeb else { return }
guard let payload = activity!.appClipActivationPayload else { return }
guard let incomingURL = activity?.webpageURL else { return }
// Create a CLRegion object.
guard let region = location(from: incomingURL) else {
// Respond to parsing errors here.
return
}
// Verify that the invocation happened at the expected location.
payload.confirmAcquired(in: region) { (inRegion, error) in
guard let confirmationError = error as? APActivationPayloadError else {
if inRegion {
// The location of the NFC tag matches the user's location.
} else {
// The location of the NFC tag doesn't match the records;
// for example, if someone moved the NFC tag.
}
return
}
if confirmationError.code == .doesNotMatch {
// The scanned URL wasn't registered for the App Clip.
} else {
// The user denied location access, or the source of the
// App Clip’s invocation wasn’t an NFC tag or visual code.
}
}
}
func location(from url:URL) -> CLRegion? {
// You should retrieve the coordinates from your Database
let coordinates = CLLocationCoordinate2D(latitude: 37.334722,
longitude: 122.008889)
return CLCircularRegion(center: coordinates,
radius: 100,
identifier: "Apple Park")
}
}
And that’s it, this his how your support multiple businesses with App Clip

When to use MKMapView's MKUserTrackingMode vs Location Manager's start updating location?

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.

Estimote indoor location list can't work

I use Estimote indoor location example to make the location list. But the location list is null. I think the problem that is the App can't find my estimote cloud. Please tell me how to solve the problem.
Code:
weak var weakSelf:ListViewController? = self
let onSuccess:(AnyObject!) -> (Void) =
{ (locations:AnyObject!) in
weakSelf!.ownLocationsArray = locations as! NSMutableArray
weakSelf!.tableView.reloadData()
weakSelf!.hadError = false;
self.finishedSectionRefresh()
}
self.manager.fetchUserLocationsWithSuccess(onSuccess, failure: onFailure)
In order for the app to "find" your Estimote Cloud account, you need to use ESTConfig's setupAppID:andAppToken: method. You can generate the token on:
https://cloud.estimote.com/#/apps/add/your-own-app
Since you're writing in Swift, you might have to add #import <EstimoteSDK/EstimoteSDK.h> to your Objective-C Bridging Header, so that your app gains access to the ESTConfig class.

swift user's location issue when trying to get current location

I am doing an app wich uses the current user location and of it's friend's agenda.
I wrote a little bit of code which is suppossed to show your location
var loc :CLLocationCoordinate2D = self.mapView.userLocation.coordinate
var region = MKCoordinateRegionMakeWithDistance(loc, 5, 5)
self.mapView.setRegion(region, animated: true)
Furthermore I am getting the error saying :
Trying to start MapKit location updates without prompting for location authorization. Must call -[CLLocationManager requestWhenInUseAuthorization] or -[CLLocationManager requestAlwaysAuthorization] first.
After some research I don't understand what I'm supposed to do to improve my location[i changed from debug->simulateLocation but nothing changed]
Can you please explain what I have to keep in mind and what I need to do to improve my app?
[edit1] Is it possible that the simulator behaves this way and the phone should not behave the same way?
As of iOS 8 you have to explicitly ask the user for permission:
var systemVersion = NSOperatingSystemVersion(majorVersion: 8, minorVersion: 0, patchVersion: 0)
if NSProcessInfo.processInfo().isOperatingSystemAtLeastVersion(systemVersion) {
locationManager.requestAlwaysAuthorization()
}
// get current location
if CLLocationManager.authorizationStatus() == CLAuthorizationStatus.Authorized {
locationManager.startUpdatingLocation()
}
Furthermore you have to explicitly declare the permission question in your Info.plist
NSLocationAlwaysUsageDescription
I figured this issue out by google-ing it a little bit:
http://rshankar.com/get-your-current-address-in-swift/
One of the steps is going to settings->General->...Use Location->Always,This is suppossed to solve it and you will not get that error.
Furthermore the person that made the tutorial mentioned that you should enter a location just ot be sure it works and VOILA!

CLFloor returning nil in iOS 8

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?

Resources