iOS Tutorial Geofence - Monitoring a region

Status
Not open for further replies.
This example extends iLocation library to allow monitoring regions in the background.

The code is pretty simple. You first need to create a circular region which you want to track and then call MonitorRegion to start tracking it.

The RegionEnter / RegionExit events will be raised, even if the app is in the background.
The StateChanged event is called once after each call to MonitorRegion.

A few notes:
- The minimum radius is 100 meters. The OS uses less accurate means to determine the location so it is not very accurate. You can start another app that uses the GPS while testing it.
- The example tracks a single region. You can track up to 20 regions.
- Don't kill the app with a swipe as it will prevent it from being started.
- Run your app in release mode if you want to test the background behavior. It should show a notification when the app is in the background and it detects a state change.
- No special background mode is required.


You need to add these lines to the example:
B4X:
#PlistExtra:<key>NSLocationAlwaysAndWhenInUseUsageDescription</key><string>Monitor some region.</string>
#PlistExtra:<key>NSLocationWhenInUseUsageDescription</key><string>Monitor some region.</string>
 

Attachments

  • Geofence.zip
    169.6 KB · Views: 920
Last edited:

Christian García S.

Active Member
Licensed User
Thanks Erel, it is a great contribution.

I have a question, can this function coexist with ebeacon monitoring region ?? https://www.b4x.com/android/forum/threads/ibeacons.79257/

I saw that you have replaced in the function of ebeacon, this:

NSLog (@ "Enter");
NSLog (@ "Exit");

with this new functions:

_regionexit
_regionenter

As I can differentiate what type of region is entering, circulate region or ebeacon region so that it does not release the same message ???

If I put some code in LocManager_StateChanged for ebeacon region and circular region I can't differentiate which region is, or I must put code in LocManager_RegionEnter and differentiate with other variable ??

Thanks,

Regards,
Christian
 

Christian García S.

Active Member
Licensed User
Thanks Erel, attached the project.

I am trying to combine but I receive the same notification when enter to Circular or Beacon region.

Maybe can we distinguish with value from identifier:

Start monitoring: CLBeaconRegion (identifier:'monitor', uuid:AEA51B96-3199-11E7-93AE-92361F002671, major: (null), minor: (null))
Start monitoring: CLCircularRegion (identifier:'region2', center:<-0.18291040,-78.47987960>, radius:100.00m)

For example I would need put a code like that in LocManager_RegionEnter:

B4X:
If identifier = "BeaconRegion" Then
        ShowNotification("Exclusive content for you")
Else If identifier = "CircularRegion" Then
        ShowNotification("Welcome to store open your APP")
End If

In this documentation you can see the following: https://developer.apple.com/documen...ng_the_user_s_proximity_to_geographic_regions

upload_2017-9-13_13-22-2.png


Thanks for your help.

Regards,
Christian
 

Attachments

  • MonitoringRegionIOS.zip
    4.6 KB · Views: 714

Christian García S.

Active Member
Licensed User
Thanks Erel for your reply, after I maked some tests I have these conclusions:
  • When I test separately (different apps) with same code, region enter and exit for Beacon and Circular region works.
  • When I test together (Region for Beacons and Circular same app), region enter and exit doesn't raise.
  • When I test together, event StateChanged raise some times but I don't know how I recognize which region is, and I want take control about this notifications.
This is the code:

B4X:
#if objc
- (NSObject*)createCircularRegdion:(double)lat :(double)lon :(double)radius :(NSString*)identifier {
    return [[CLCircularRegion alloc] initWithCenter:CLLocationCoordinate2DMake(lat, lon) radius:(CLLocationDistance)radius identifier:identifier];
}
@end
@interface B4ILocationManager (beacon)
@end

@implementation B4ILocationManager (beacon)

- (void)locationManager:(CLLocationManager *)manager
        didRangeBeacons:(NSArray*)beacons
               inRegion:(CLBeaconRegion *)region {
              if (beacons.count > 0) {
                [B4IObjectWrapper raiseEvent:self :@"_didrangebeacons:"
                    :@[[B4IObjectWrapper createWrapper:[B4IList new] object:beacons]]];
                }
    }

- (void)locationManager:(CLLocationManager *)manager
          didExitRegion:(CLRegion *)region {
          [B4IObjectWrapper raiseEvent:self :@"_regionexit:" :@[region]];
    }
- (void)locationManager:(CLLocationManager *)manager
         didEnterRegion:(CLRegion *)region {
         [B4IObjectWrapper raiseEvent:self :@"_regionenter:" :@[region]];
}
- (void)locationManager:(CLLocationManager *)manager
monitoringDidFailForRegion:(CLRegion *)region
              withError:(NSError *)error {
    NSLog(@"Error: %@", error);
    [B4IObjectWrapper raiseEvent:self :@"_statechanged:" :@[@(3)]];
}
- (void)locationManager:(CLLocationManager *)manager
didStartMonitoringForRegion:(CLRegion *)region {
    NSLog(@"Start monitoring: %@", region);
}
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)State forRegion:(CLRegion *)region {
   NSLog(@"didDetermineState: %@", @(State));
   [B4IObjectWrapper raiseEvent:self :@"_statechanged:" :@[@(State)]];
}
#End If

Can you add other parameter to StateChanged like this from objc:

B4X:
Sub LocManager_StateChanged(State As Int, Region As NativeObject)

I was trying change in code below, but I don't know how, I has many erros:

B4X:
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)State forRegion:(CLRegion *)region {
   NSLog(@"didDetermineState: %@", @(State));
   [B4IObjectWrapper raiseEvent:self :@"_statechanged:" :@[@(State)]];
}

Please you can make some test or changes to these functions that can works together.

Thanks in advance,

Regards,

Christian
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
The event sub signature should be:
B4X:
Sub LocManager_StateChanged(State As Int, Region As Object)

OBJC Code:
B4X:
#if objc
- (NSObject*)createCircularRegdion:(double)lat :(double)lon :(double)radius :(NSString*)identifier {
   return [[CLCircularRegion alloc] initWithCenter:CLLocationCoordinate2DMake(lat, lon) radius:(CLLocationDistance)radius identifier:identifier];
}
@end
@interface B4ILocationManager (beacon)
@end

@implementation B4ILocationManager (beacon)

- (void)locationManager:(CLLocationManager *)manager
  didExitRegion:(CLRegion *)region {
      [B4IObjectWrapper raiseEvent:self :@"_regionexit:" :@[region]];
   }
- (void)locationManager:(CLLocationManager *)manager
  didEnterRegion:(CLRegion *)region {
      [B4IObjectWrapper raiseEvent:self :@"_regionenter:" :@[region]];
}
- (void)locationManager:(CLLocationManager *)manager
monitoringDidFailForRegion:(CLRegion *)region
  withError:(NSError *)error {
   NSLog(@"Error: %@", error);
   [B4IObjectWrapper raiseEvent:self :@"_statechanged:" :@[@(3), region]];
}
- (void)locationManager:(CLLocationManager *)manager
didStartMonitoringForRegion:(CLRegion *)region {
   NSLog(@"Start monitoring: %@", region);
}
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)State forRegion:(CLRegion *)region {
  NSLog(@"didDetermineState: %@", @(State));
  [B4IObjectWrapper raiseEvent:self :@"_statechanged::" :@[@(State), region]];
}
#End If
 

Christian García S.

Active Member
Licensed User
I changed code to for monitoring beacons and circular regions:
Sub LocManager_StateChanged(State As Int, Region As Object)

I explained from post 2 to 7.

Log convert Region.GetField("identifier") to string, but When I want use with a variable string I have this error.

The complete code is:

B4X:
#if objc
- (NSObject*)createCircularRegdion:(double)lat :(double)lon :(double)radius :(NSString*)identifier {
   return [[CLCircularRegion alloc] initWithCenter:CLLocationCoordinate2DMake(lat, lon) radius:(CLLocationDistance)radius identifier:identifier];
}
@end
@interface B4ILocationManager (beacon)
@end

@implementation B4ILocationManager (beacon)
- (void)locationManager:(CLLocationManager *)manager
        didRangeBeacons:(NSArray*)beacons
               inRegion:(CLBeaconRegion *)region {
              if (beacons.count > 0) {
                [B4IObjectWrapper raiseEvent:self :@"_didrangebeacons:"
                    :@[[B4IObjectWrapper createWrapper:[B4IList new] object:beacons]]];
                }
    }
- (void)locationManager:(CLLocationManager *)manager
  didExitRegion:(CLRegion *)region {
      [B4IObjectWrapper raiseEvent:self :@"_regionexit:" :@[region]];
   }
- (void)locationManager:(CLLocationManager *)manager
  didEnterRegion:(CLRegion *)region {
      [B4IObjectWrapper raiseEvent:self :@"_regionenter:" :@[region]];
}
- (void)locationManager:(CLLocationManager *)manager
monitoringDidFailForRegion:(CLRegion *)region
  withError:(NSError *)error {
   NSLog(@"Error: %@", error);
   [B4IObjectWrapper raiseEvent:self :@"_statechanged:" :@[@(3), region]];
}
- (void)locationManager:(CLLocationManager *)manager
didStartMonitoringForRegion:(CLRegion *)region {
   NSLog(@"Start monitoring: %@", region);
}
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)State forRegion:(CLRegion *)region {
  NSLog(@"didDetermineState: %@", @(State));
  [B4IObjectWrapper raiseEvent:self :@"_statechanged::" :@[@(State), region]];
}
#End If
 
Status
Not open for further replies.
Top