Can you stop regions from persisting between launches with CLLocationManager? - ios

Is there a way to prevent CLLocationManager from persisting monitored regions between launches? Every time the app is launched I need to add a new set of monitored regions and the old ones are no longer useful. Is there a way to either prevent them from persisting or clear all of the old ones at launch time?

Of course you can clear all the regions currently monitored:
+(void)clearRegionWatch
{
for(CLRegion *region in [[WGLocation shared].locationManager monitoredRegions]){
[[WGLocation shared].locationManager stopMonitoringForRegion:region];
}
}
If you had a specific identifier that you wanted to remove:
+(void)clearRegionWatchForKey:(NSString *)key
{
for(CLRegion *region in [[WGLocation shared].locationManager monitoredRegions]){
if([region.identifier isEqualToString:key]){
[[WGLocation shared].locationManager stopMonitoringForRegion:region];
}
}
}
You can copy the internals of the function into an appropriate place in your application. I've copied them from my shared manager class.

In SWIFT 4
you can stop all the regions from being monitored like
let monitoredRegions = locationManager.monitoredRegions
for region in monitoredRegions{
locationManager.stopMonitoring(for: region)
}

Related

Receiving Location even when app is not running in Swift

Still very new to Swift. I have come from an Android background where there is BroadcastReceiver that can deliver location info to a service even though the app isn't running.
So I was looking for something similar in iOS/Swift and it appears that before this wasn't possible but it may be now. I am developing for iOS 10 but would be great if it was backwards compatible.
I found
startMonitoringSignificantLocationChanges
which I can execute to start delivering location updates, although this raises a few questions. Once I call this and my app is NOT running, are the updates still being sent ? And how would the app wake up to respond ?
Also restarting the phone and when it return, does this mean I still need call startMonitoringSignificantLocationChanges again meaning that I would have to wait for the user to execute my app. Or does it remember the setting after reboot ?
Still a little confused how to get around this, here's a brief explanation of what I am trying to do.
I would like to update the location of the phone even though the app is not running, this would be sent to a rest service every so often.
This way on the backend services I could determine if somebody is within X meters of somebody also and send them a push notification.
It may or may not be a good solution but if I were you I would have used both startMonitoringSignificantLocationChanges and regionMonitoring.
Here is the sample I made which worked well with iOS 13.
Lets take regionMonitoring first. We have certainly no problems when the app is in foreground state and we can use the CLLocationManager's didUpdate delegate to get the location and send it to the server.
Keep latest current location in AppDelegate's property, lets say:
var lastLocation:CLLocation?
//And a location manager
var locationManager = CLLocationManager()
We have two UIApplicationDelegates
func applicationDidEnterBackground(_ application: UIApplication) {
//Create a region
}
func applicationWillTerminate(_ application: UIApplication) {
//Create a region
}
So whenever the user kills the app or makes the app go to background, we can certainly create a region around the latest current location fetched. Here is an example to create a region.
func createRegion(location:CLLocation?) {
if CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
let coordinate = CLLocationCoordinate2DMake((location?.coordinate.latitude)!, (location?.coordinate.longitude)!)
let regionRadius = 50.0
let region = CLCircularRegion(center: CLLocationCoordinate2D(
latitude: coordinate.latitude,
longitude: coordinate.longitude),
radius: regionRadius,
identifier: "aabb")
region.notifyOnExit = true
region.notifyOnEntry = true
//Send your fetched location to server
//Stop your location manager for updating location and start regionMonitoring
self.locationManager?.stopUpdatingLocation()
self.locationManager?.startMonitoring(for: region)
}
else {
print("System can't track regions")
}
}
Make use of RegionDelegates
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
print("Entered Region")
}
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
print("Exited Region")
locationManager?.stopMonitoring(for: region)
//Start location manager and fetch current location
locationManager?.startUpdatingLocation()
}
Grab the location from didUpdate method
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if UIApplication.shared.applicationState == .active {
} else {
//App is in BG/ Killed or suspended state
//send location to server
// create a New Region with current fetched location
let location = locations.last
lastLocation = location
//Make region and again the same cycle continues.
self.createRegion(location: lastLocation)
}
}
Here I have made a 50m region radius circle. I have tested this and it is called generally after crossing 100m from your center point.
Now the second approach can me using significantLocationChanges
On making the app go background or terminated, we can just stop location manager for further updating locations and can call the startMonitoringSignificantLocationChanges
self.locationManager?.stopUpdatingLocation()
self.locationManager?.startMonitoringSignificantLocationChanges()
When the app is killed, the location is grabbed from didFinishLaunching method's launchOptions?[UIApplicationLaunchOptionsKey.location]
if launchOptions?[UIApplicationLaunchOptionsKey.location] != nil {
//You have a location when app is in killed/ not running state
}
Make sure to keep BackgroundModes On for Location Updates
Also make sure to ask for locationManager?.requestAlwaysAuthorization() by using the key
<key>NSLocationAlwaysUsageDescription</key>
<string>Allow location</string>
in your Info.plist
There can be a third solution by taking 2 LocationManagers simultaneously.
For region
Significant Location Changes
As using significantLocationChanges
Apps can expect a notification as soon as the device moves 500 meters
or more from its previous notification. It should not expect
notifications more frequently than once every five minutes. If the
device is able to retrieve data from the network, the location manager
is much more likely to deliver notifications in a timely manner.
as per the give Apple Doc
So it totally depends on your requirements as the location fetching depends on many factors like the number of apps opened, battery power, signal strength etc when the app is not running.
Also keep in mind to always setup a region with good accuracy.
I know that this will not solve your problem completely but you will get an idea to move forward as per your requirements.

iOS swift backgound location update when killed/suspended with minimum data loss

I've several questions to ask regarding the location updates in background in swift language.
Let me explain what I'm doing in the app. I'm developing an app which regularly monitors users location (as all of you do) and updates it to the server, so the users movement is tracked and saved for future reference by the user.
Questions
What is the difference between using startMonitoringSignificantLocationChanges Vs startUpdatingLocation?
1.1 If we use startUpdatingLocation does it impact on publishing the app to the App Store?
When the app is killed/suspended (Force closed by the user) it takes some time to restart the location manager from the AppDelegate which causes loss of location data for a period of time. Any possible solution to overcome this?
2.1 The difference of time for restarting is around 30 sec to nearly 1 min, which doesn't triggers the location update and hence the route is not perfect as shown in the image
Output of the App where due to restart the locations are not recieved and hence the route goes over the road.
Code for reference
import UIKit
import GoogleMaps
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var window: UIWindow?
let DBName = "test"
var logFile: FileUtils?
var viewController:ViewController?
var count = 0
var appOpenCount = 0
let totalPath = GMSMutablePath()
var leaveCoordinates = 0
var previousLocation: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 0, longitude: 0)
var locationManager: CLLocationManager?
var significatLocationManager : CLLocationManager?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
GMSServices.provideAPIKey("*********************")
logFile = FileUtils(fileName: "\(DBName).txt")
logFile!.appendFile("\n\nlaunchOptions : \(launchOptions)")
let defaults = NSUserDefaults.standardUserDefaults()
count = defaults.integerForKey("Enabled")
appOpenCount = defaults.integerForKey("appOpenCount")
if(UIApplication.sharedApplication().backgroundRefreshStatus == UIBackgroundRefreshStatus.Available){
logFile!.appendFile("\nYessss")
} else {
logFile!.appendFile("\nNooo")
}
appOpenCount += 1
defaults.setValue(appOpenCount, forKey: "appOpenCount")
defaults.synchronize()
if count == 0 {
count += 1
defaults.setValue(count, forKey: "Enabled")
defaults.synchronize()
Util.copyFile(count)
}
if let launchOpt = launchOptions{
if (launchOpt[UIApplicationLaunchOptionsLocationKey] != nil) {
logFile!.appendFile("\nExecuted on : significatLocationManager")
self.significatLocationManager = CLLocationManager()
self.significatLocationManager?.desiredAccuracy = kCLLocationAccuracyBest
self.significatLocationManager?.delegate = self
self.significatLocationManager?.requestAlwaysAuthorization()
if #available(iOS 9.0, *) {
self.significatLocationManager!.allowsBackgroundLocationUpdates = true
}
self.significatLocationManager?.startUpdatingLocation()
}else{
logFile!.appendFile("\nExecuted on : locationManager1")
self.locationManager = CLLocationManager()
self.locationManager?.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager?.delegate = self
self.locationManager?.requestAlwaysAuthorization()
if #available(iOS 9.0, *) {
self.locationManager!.allowsBackgroundLocationUpdates = true
}
self.locationManager?.startUpdatingLocation()
}
} else {
logFile!.appendFile("\nExecuted on : locationManager2")
self.locationManager = CLLocationManager()
self.locationManager?.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager?.delegate = self
self.locationManager?.requestAlwaysAuthorization()
if #available(iOS 9.0, *) {
self.locationManager!.allowsBackgroundLocationUpdates = true
}
self.locationManager?.startUpdatingLocation()
}
return true
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
let locationArray = locations as NSArray
let newLocation = locationArray.lastObject as! CLLocation
let coordinate = newLocation.coordinate
let tempCoor = CLLocationCoordinate2D(latitude: coordinate.latitude, longitude: coordinate.longitude)
let lat = tempCoor.latitude
let lon = tempCoor.longitude
insert(lat, lon: lon)
}
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
if self.significatLocationManager != nil {
self.significatLocationManager?.startMonitoringSignificantLocationChanges()
}else{
self.locationManager?.startMonitoringSignificantLocationChanges()
}
logFile!.appendFile("\napplicationDidEnterBackground")
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
func insert(lat: Double, lon: Double){
let locationInfo: LocationDetails = LocationDetails()
locationInfo.latitude = lat
locationInfo.longitude = lon
locationInfo.time = NSDate()
let db = "\(DBName)\(count).sqlite"
ModelManager.getInstance(db).addLocationData(locationInfo)
}
}
question-1
What is the difference between using startMonitoringSignificantLocationChanges Vs startUpdatingLocation?
startUpdatingLocation updates the location when it is called first time and then when the distance filter value exceeds.
it uses the GPS when its available, if you use in continuously it Drain your power/battery
Discussion
This method returns immediately. Calling this method causes the
location manager to obtain an initial location fix (which may take
several seconds) and notify your delegate by calling its
locationManager:didUpdateLocations: method. After that, the receiver generates update events primarily when the value in the distanceFilter property is exceeded. Updates may be delivered in other situations though. For example, the receiver may send another notification if the hardware gathers a more accurate location reading.
Calling this method several times in succession does not automatically
result in new events being generated. Calling stopUpdatingLocation in
between, however, does cause a new initial event to be sent the next
time you call this method.
If you start this service and your application is suspended, the
system stops the delivery of events until your application starts
running again (either in the foreground or background). If your
application is terminated, the delivery of new location events stops
altogether. Therefore, if your application needs to receive location
events while in the background, it must include the UIBackgroundModes
key (with the location value) in its Info.plist file.
In addition to your delegate object implementing the
locationManager:didUpdateLocations: method, it should also implement
the locationManager:didFailWithError: method to respond to potential
errors.
startMonitoringSignificantLocationChanges when a significant change in position occurs.
it uses cellular or wifi, when it work the location is changed or its called in the particular Time interval.
Discussion
This method initiates the delivery of location events asynchronously,
returning shortly after you call it. Location events are delivered to
your delegate’s locationManager:didUpdateLocations: method. The first
event to be delivered is usually the most recently cached location
event (if any) but may be a newer event in some circumstances.
Obtaining a current location fix may take several additional seconds,
so be sure to check the timestamps on the location events in your
delegate method.
After returning a current location fix, the receiver generates update
events only when a significant change in the user’s location is
detected. For example, it might generate a new event when the device
becomes associated with a different cell tower. It does not rely on
the value in the distanceFilter property to generate events. Calling
this method several times in succession does not automatically result
in new events being generated. Calling stopMonitoringSignificantLocationChanges in between, however, does
cause a new initial event to be sent the next time you call this
method.
If you start this service and your application is subsequently
terminated, the system automatically relaunches the application into
the background if a new event arrives. In such a case, the options
dictionary passed to the locationManager:didUpdateLocations: method of
your application delegate contains the key
UIApplicationLaunchOptionsLocationKey to indicate that your
application was launched because of a location event. Upon relaunch,
you must still configure a location manager object and call this
method to continue receiving location events. When you restart
location services, the current event is delivered to your delegate
immediately. In addition, the location property of your location
manager object is populated with the most recent location object even
before you start location services.
Note:
Apps can expect a notification as soon as the device moves 500
meters or more from its previous notification. It should not expect
notifications more frequently than once every five minutes. If the
device is able to retrieve data from the network, the location manager
is much more likely to deliver notifications in a timely manner.
for differenciate purpose I taken from here
question-2
If we use startUpdatingLocation does it impact on publishing the app to the App Store?
One of the possible reasons for 2.16 rejection is the absence of GPS battery warning in your app description on the app meta in iTunesConnect - "The continued use of GPS may decrease battery life" or something like that.
for More information related to this
Question-3
When the app is killed/suspended (Force closed by the user) it takes some time to restart the location manager from the AppDelegate which causes loss of location data for a period of time. Any possible solution to overcome this
NO, we Can't Over Come, reason the memory newly Initiated.
startMonitoringSignificantLocationChanges
uses cellular/wifi
not accurate but very energy efficient
iOS wakes up your app at least every 15 mins or so to give location update (https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html)
startUpdatingLocation
uses GPS when available
very accurate but eats so much power
Usually, you can submit app using startUpdatingLocation without any problem as long as you state 'Continued use of GPS running in the background can dramatically decrease battery life.' on the app descriptions. And if you put background mode as 'location', use it for location only and not to do anything else.
You don't seem to make use of beginBackgroundTaskWithExpirationHandler, I suggest you use that when your app is in the background.
Obviously, if user does not want your app to get location at all, they can turn off backgroundFetch from the device settings, and touch luck in that case.

Set location simulator City/GPX during Runtime

Is there a method to programmatically change the location simulator city during runtime? For example this would allow tests to simulate London or Tokyo.
The image below shows how to switch between locations (GPX files) manually. How can I achieve this result programmatically while the app is running?
Alternate way to set location is by swizzling 'location' of 'CLLocationManager' class. In obj-c,
+(void) load {
// replace 'location' with 'custom_location' method
}
Then implement custom_location method with whatever the location you want to set by simply changing 'kMockedLatitude' and 'kMockedLongitude' variables.
//Portland, USA
CLLocationDegrees kMockedLatitude = 45.52306;
CLLocationDegrees kMockedLongitude = -122.67648;
-(CLLocation *)custom_location
{
return [[CLLocation alloc] initWithLatitude:kMockedLatitude longitude:kMockedLongitude];
}
This will work even in iOS device.

iOS CoreLocation, Determine user leaving a location

I've been working in a iOS location tracking application, and we've found a way to determine when users are leaving a place, but we're doing it by constantly listening to location updates, which ends draining our battery.
What is the most efficient way to do a thing like this? i would like to get a battery consumption similar to the reminders application.
Any idea is welcome!
Thanks!
You should set up your app to use geofences.
The method you want to look at is the CLLocationManager method startMonitoringForRegion. You need to set up your app to ask the user for permission to monitor location updates.
The system will launch your app to deliver a region update if it's not running.
It depends on your definition of "leaving a place". But you can use
func startMonitoringSignificantLocationChanges()
On CLLocationManager in order to be notified (roughly) when the user moves 500 meters or more, according to Apple documention.
This shifts the responsibility of watching battery life over to Apple's code, which as you might imagine is well optimized for the task.
Follow #rschmidt's function for starting location update on device.
Track user's current locality by keeping a variable named 'currentLocality' in the class which is delegating CLLocationManager.
Implement the CLLocationManagerDelegate in the following way
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
var currentLocation = locations.last as? CLLocation
// Getting current locality with GeoCoder
CLGeocoder().reverseGeocodeLocation(currentLocation, completionHandler: {(placemarks, error) in
if (error != nil) {
println("reverse geodcode fail: \(error.localizedDescription)")
}
else {
let placeMark = placemarks.last as? CLPlacemark
if let latestLocality = placeMark!.locality {
if latestLocality != currentLocality {
// Locality has been changed, do necessary actions
// Updating current locality
currentLocality = latestLocality
}
}
}
})
}

Best Reactive-Cocoa approach for writing a CLLocationManagerDelegate, which will infrequently fetch location

Background
I'm really excited by the ReactiveCocoa framework and the potential it has, so I've decided that I'm going to bite the bullet and write my first app using it.
In my app, I've already written the various services and delegates, but I now need to 'Reactive-Cocoa-ise' them so that I can get on with actual GUI side of things.
That said, so that I better understand this, I'm writing a simple bit of code just to try out concepts.
In this case, writing a wrapper for CLLocationManagerDelegate.
In the actual app, the use case would be this:
1) When the app is loaded up (viewDidLoad) then 2) Attempt to fetch
the location of the device by
2.1) if location services not enabled then
2.1.1) check authorisation status, and if allowed to startMonitoringSignificantLocationChanges,
2.1.2) else return an error
2.2) else (location services are enabled)
2.2.1) if the location manager last location was 'recent' (6 hours or less) return that
2.2.2) else startMonitoringSignificantLocationChanges
3) when returning the location (either straight away, or via
locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations), then we also stopMonitoringSignificantLocationChanges
4) If code that calls on the LocationManagerDelegate receives and error, then ask the delegate for a fixed 'default' value
5) Next piece of code then uses the location (either fetched, or default) to go off and do a bunch of calls on third party services (calls to WebServices etc)
You can see a test harness at https://github.com/ippoippo/reactive-core-location-test
Concerns
The harness 'works' in that it goes off and fetches the location. However, I'm really concerned I'm doing this in the right way.
Consider this code
RACSignal *fetchLocationSignal = [lmDelegate latestLocationSignal];
RACSignal *locationSignal = [fetchLocationSignal catch:^RACSignal *(NSError *error) {
NSLog(#"Unable to fetch location, going to grab default instead: %#", error);
CLLocation *defaultLocation = [lmDelegate defaultLocation];
NSLog(#"defaultLocation = [%#]", defaultLocation);
// TODO OK, so now what. I just want to handle the error and
// pass back this default location as if nothing went wrong???
return [RACSignal empty];
}];
NSLog(#"Created signal, about to bind on self.locationLabel text");
RAC(self.locationLabel, text) = [[locationSignal filter:^BOOL(CLLocation *newLocation) {
NSLog(#"Doing filter first, newLocation = [%#]", newLocation);
return newLocation != nil;
}] map:^(CLLocation *newLocation) {
NSLog(#"Going to return the coordinates in map function");
return [NSString stringWithFormat:#"%d, %d", newLocation.coordinate.longitude, newLocation.coordinate.latitude];
}];
Questions
1) How do I handle errors? I can use catch which then gives me the opportunity to then ask the delegate for a default location. But, I'm then stuck on how to then pass back that default location as a signal? Or do I just change my defaultLocation method to return a RACSignal rather than CLLocation??
2) When I return the location, should it be done as sendNext or sendCompleted? Currently it's coded as sendNext, but it seems like something that would be done as sendCompleted.
3) Actually, does the answer to that depend on how I create the Delegate. This test app creates a new Delegate each time the view is loaded. Is that something I should do, I should I make the Delegate a singleton. If it's a singleton, then sendNext seems the right thing to do. But if would then imply that I move the code that I have in latestLocationSignal in my delegate into an init method instead?
Apologies for the rambling question(s), just seem to be going around in circles in my head.
Updating the answer above to ReactiveCocoa 4 and Swift 2.1:
import Foundation
import ReactiveCocoa
class SignalCollector: NSObject, CLLocationManagerDelegate {
let (signal,sink) = Signal<CLLocation, NoError>.pipe()
let locationManager = CLLocationManager()
func start(){
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
for item in locations {
guard let location = item as CLLocation! else { return }
sink.sendNext(location)
}
}
And to listen the events:
var locationSignals : [CLLocation] = []
signal.observeNext({ newLocation in
locationSignals.append(newLocation)
})
How do I handle errors? I can use catch which then gives me the opportunity to then ask the delegate for a default location.
You're so close. Use +[RACSignal return:] to create a signal which just sends your default location:
RACSignal *locationSignal = [fetchLocationSignal catch:^RACSignal *(NSError *error) {
NSLog(#"Unable to fetch location, going to grab default instead: %#", error);
CLLocation *defaultLocation = [lmDelegate defaultLocation];
NSLog(#"defaultLocation = [%#]", defaultLocation);
return [RACSignal return:defaultLocation];
}];
When I return the location, should it be done as sendNext or sendCompleted? Currently it's coded as sendNext, but it seems like something that would be done as sendCompleted.
In order to actually send data, you'll need to use -sendNext:. If that's the only thing that the signal is sending, then it should also -sendCompleted after that. But if it's going to continue to send the location as accuracy improves or location changes, then it shouldn't complete yet.
More generally, signals can send any numbers of nexts (0*) but can only complete or error. Once completion or error is sent, the signal's done. It won't send any more values.
Actually, does the answer to that depend on how I create the Delegate. This test app creates a new Delegate each time the view is loaded. Is that something I should do, I should I make the Delegate a singleton. If it's a singleton, then sendNext seems the right thing to do. But if would then imply that I move the code that I have in latestLocationSignal in my delegate into an init method instead?
I'm not sure I have enough context to answer this, except to say that singletons are never the answer ;) Creating a new delegate each time the view is loaded seems reasonable to me.
It's hard to answer broad questions like this because I don't know your design nearly as well as you do. Hopefully these answers help a bit. If you need more clarification, specific examples are really helpful.
Seems to be super easy in ReactiveCocoa 3.0 and swift :
import ReactiveCocoa
import LlamaKit
class SignalCollector: NSObject, CLLocationManagerDelegate {
let (signal,sink) = Signal<CLLocation, NoError>.pipe()
let locationManager = CLLocationManager()
func start(){
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
for item in locations {
if let location = item as? CLLocation {
sink.put(Event.Next(Box(location))
}
}
}
}
And wherever you need to listen to the location events, use the signal with an observer :
var locationSignals : [CLLocation] = []
signal.observe(next: { value in locationSignals.append(value)})
Note that since it's pre-alpha at the moment the syntax is likely to change.

Resources