Skip to content

Commit

Permalink
Geofencing (#341)
Browse files Browse the repository at this point in the history
* Using regions instead of significant location change

* Setup a trigger of 1 second for the notification

* Removing the history guard to show notifications

* Correct version of the library

* Adding suggestions
  • Loading branch information
arcturus authored Nov 18, 2016
1 parent 1eb7d6e commit 3a2e9e6
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 68 deletions.
2 changes: 1 addition & 1 deletion ios/Cartfile
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
github "stephencelis/SQLite.swift" "swift-2.3"
github "mozilla-magnet/magnet-scanner-ios" ~> 0.3.4
github "mozilla-magnet/magnet-scanner-ios" ~> 0.3.6
2 changes: 1 addition & 1 deletion ios/Cartfile.resolved
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
github "Alamofire/Alamofire" "3.5.1"
github "stephencelis/SQLite.swift" "8b95a0c4a884f35e5102cd306f634680255473bb"
github "SwiftyJSON/SwiftyJSON" "54017d514a87b2b855b407131292c510cdeb65f8"
github "mozilla-magnet/magnet-scanner-ios" "0.3.4"
github "mozilla-magnet/magnet-scanner-ios" "0.3.6"
12 changes: 12 additions & 0 deletions ios/History.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ class History {
db.insert(url)
}

#if DEBUG
func getRecent(url: String) -> HistoryRecord! {
let earlyDate = NSCalendar.currentCalendar().dateByAddingUnit(
.Minute,
value: -1,
toDate: NSDate(),
options: [])
return db.getSince(url, sinceDate: earlyDate!)
}
#else
func getRecent(url: String) -> HistoryRecord! {
let earlyDate = NSCalendar.currentCalendar().dateByAddingUnit(
.Day,
Expand All @@ -45,6 +55,8 @@ class History {
options: [])
return db.getSince(url, sinceDate: earlyDate!)
}
#endif


func clear() {
db.clear()
Expand Down
47 changes: 0 additions & 47 deletions ios/LocationChangeReceiver.swift

This file was deleted.

16 changes: 12 additions & 4 deletions ios/Magnet.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
55055CDD1DCCBBBB00EC736F /* NotificationsHelperIOS10.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55055CDC1DCCBBBB00EC736F /* NotificationsHelperIOS10.swift */; };
550CECBF1D3536F80076D807 /* NotificationsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550CECBE1D3536F80076D807 /* NotificationsHelper.swift */; };
552134411DCA30E600A562AC /* ApiSubscriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552134401DCA30E600A562AC /* ApiSubscriptions.swift */; };
553647D81DBE206000ADFBB3 /* LocationChangeReceiver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553647D71DBE206000ADFBB3 /* LocationChangeReceiver.swift */; };
552E19871DDB400C00AA07F9 /* RegionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552E19861DDB400C00AA07F9 /* RegionManager.swift */; };
555A52AB1DDA3295005D4B98 /* BugfenderSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 555A52AA1DDA3295005D4B98 /* BugfenderSDK.framework */; };
555A52B91DDA32C5005D4B98 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 555A52B81DDA32C5005D4B98 /* Log.swift */; };
5583EF0E1D3D55AD00852649 /* HistoryRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5583EF0D1D3D55AD00852649 /* HistoryRecord.swift */; };
Expand Down Expand Up @@ -207,7 +207,7 @@
55055CDC1DCCBBBB00EC736F /* NotificationsHelperIOS10.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsHelperIOS10.swift; sourceTree = "<group>"; };
550CECBE1D3536F80076D807 /* NotificationsHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsHelper.swift; sourceTree = "<group>"; };
552134401DCA30E600A562AC /* ApiSubscriptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiSubscriptions.swift; sourceTree = "<group>"; };
553647D71DBE206000ADFBB3 /* LocationChangeReceiver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationChangeReceiver.swift; sourceTree = "<group>"; };
552E19861DDB400C00AA07F9 /* RegionManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RegionManager.swift; sourceTree = "<group>"; };
555800951CBE5F7800A21868 /* Magnet-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Magnet-Bridging-Header.h"; sourceTree = "<group>"; };
555A52AA1DDA3295005D4B98 /* BugfenderSDK.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = BugfenderSDK.framework; path = "3rdparty/BugfenderSDK-iOS/BugfenderSDK.framework"; sourceTree = "<group>"; };
555A52B81DDA32C5005D4B98 /* Log.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -367,6 +367,7 @@
13B07FAE1A68108700A75B9A /* Magnet */ = {
isa = PBXGroup;
children = (
552E19851DDB3FFA00AA07F9 /* Location */,
55055CB51DCC9F3E00EC736F /* Notifications */,
55C013F51DC8EC5900154F76 /* Utils */,
55C013DD1DC8CBEE00154F76 /* Api */,
Expand All @@ -382,7 +383,6 @@
13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
13B07FB71A68108700A75B9A /* main.m */,
555800951CBE5F7800A21868 /* Magnet-Bridging-Header.h */,
553647D71DBE206000ADFBB3 /* LocationChangeReceiver.swift */,
);
name = Magnet;
sourceTree = "<group>";
Expand All @@ -404,6 +404,14 @@
name = Notifications;
sourceTree = "<group>";
};
552E19851DDB3FFA00AA07F9 /* Location */ = {
isa = PBXGroup;
children = (
552E19861DDB400C00AA07F9 /* RegionManager.swift */,
);
name = Location;
sourceTree = "<group>";
};
5583EF0A1D3D519D00852649 /* History */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -880,6 +888,7 @@
552134411DCA30E600A562AC /* ApiSubscriptions.swift in Sources */,
55C013FD1DC8FFC600154F76 /* ApiMagnetReact.swift in Sources */,
8D57224A1CDCB16400480505 /* RNMagnetWebViewManagerExport.m in Sources */,
552E19871DDB400C00AA07F9 /* RegionManager.swift in Sources */,
55C013EB1DC8CC2600154F76 /* RequestStore.swift in Sources */,
55C013EE1DC8CE9100154F76 /* RequestStoreSQLite.swift in Sources */,
5583EF0E1D3D55AD00852649 /* HistoryRecord.swift in Sources */,
Expand All @@ -902,7 +911,6 @@
13B07FC11A68108700A75B9A /* main.m in Sources */,
55C013F71DC8EC6800154F76 /* System.swift in Sources */,
8D7567311D2BF7F300315AEE /* MagnetScannerReact.swift in Sources */,
553647D81DBE206000ADFBB3 /* LocationChangeReceiver.swift in Sources */,
555A52B91DDA32C5005D4B98 /* Log.swift in Sources */,
55C013F41DC8E99100154F76 /* ApiChannels.swift in Sources */,
8D5722491CDCB16400480505 /* RNMagnetWebViewManager.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion ios/Magnet/AppDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@

@property (nonatomic, strong) UIWindow *window;
@property (nonatomic, strong) RCTBridge *bridge;
@property (nonatomic, strong) LocationChangeReceiver *locationReceiver;
@property (nonatomic, strong) RegionManager *regionManager;

@end
15 changes: 6 additions & 9 deletions ios/Magnet/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
NSURL *jsCodeLocation;

// Load from the server
//jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
// jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
// Load from local bundle
jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];

Expand All @@ -42,15 +42,12 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
self.bridge = [[RCTBridge alloc] initWithBundleURL:jsCodeLocation
moduleProvider: nil
launchOptions:launchOptions];

// Listen to location changes, will be stopped when we go to foreground and reactivated
// when we go to background
self.locationReceiver = [[LocationChangeReceiver alloc] init];
[self.locationReceiver startSignificantLocationChanges];

self.regionManager = [[RegionManager alloc] init];

// Remote logs
#if DEBUG
[Bugfender enableAllWithToken:@"<REPLACE_WITH_APP_KEY>"];
[Bugfender enableAllWithToken:@""];
#endif
[Log log:@"Magnet Client starting"];

Expand All @@ -60,13 +57,13 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
- (void)applicationDidBecomeActive:(UIApplication *)application {
[NotificationsHelper clearNotifications];
[NotificationsHelper disable];
[self.locationReceiver stopSignificantLocationChanges];
[self.regionManager stopListeningToRegionChange];
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
[NotificationsHelper clearNotifications];
[NotificationsHelper enable];
[self.locationReceiver startSignificantLocationChanges];
[self.regionManager startListeningToRegionChange];
}

-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
Expand Down
7 changes: 5 additions & 2 deletions ios/NotificationsHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,13 @@ import UserNotifications
// Throttles the notification process, waiting for 10 seconds until
// setting up the badge with the number of elements nearby.
class func notifyUser(url: String, channel: String?) {
guard History.getInstance().getRecent(url) == nil else { return }
guard toNotify[url] == nil else { return }
guard toNotify[url] == nil else {
Log.l("Abort, notification already scheduled for \(url)")
return
}

if channel != nil && !subscriptions.exists(channel!) {
Log.l("Abort notification for \(url), user not subscribed to channel \(channel)")
return
}

Expand Down
16 changes: 13 additions & 3 deletions ios/NotificationsHelperIOS10.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class NotificationsHelperIOS10: NSObject, UNUserNotificationCenterDelegate {
}

private func processNotification(url: String, channel: String) {
Log.l("Processing notification for \(url)")
fetchData(url, callback: { (json) in
do {
guard json[0] != nil && json[0]["description"] != nil && json[0]["title"] != nil else {
Expand All @@ -38,8 +39,9 @@ class NotificationsHelperIOS10: NSObject, UNUserNotificationCenterDelegate {
subtitle: "by \(channel)",
body: json[0]["description"].string!,
url: url)
Log.l("Dispatching rich notification for \(json.rawString())")
} catch {
debugPrint("Could not launh notification for \(url) : \(channel)")
Log.w("Could not launch notification for \(url) : \(channel)")
}
})
}
Expand Down Expand Up @@ -68,8 +70,16 @@ class NotificationsHelperIOS10: NSObject, UNUserNotificationCenterDelegate {
let category = UNNotificationCategory(identifier: NotificationsHelperIOS10.CATEGORY, actions: [action], intentIdentifiers: [], options: [])
UNUserNotificationCenter.currentNotificationCenter().setNotificationCategories([category])

let request = UNNotificationRequest(identifier: url, content: content, trigger: nil)
UNUserNotificationCenter.currentNotificationCenter().addNotificationRequest(request, withCompletionHandler: nil)
// Trigger this notification in 1 second from now.
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)

let request = UNNotificationRequest(identifier: url, content: content, trigger: trigger)
UNUserNotificationCenter.currentNotificationCenter().addNotificationRequest(request, withCompletionHandler: {error in
guard let error = error else {
return
}
Log.w("Error while sending notification \(error)")
})
}

func userNotificationCenter(center: UNUserNotificationCenter, didReceiveNotificationResponse response: UNNotificationResponse, withCompletionHandler completionHandler: () -> Void) {
Expand Down
87 changes: 87 additions & 0 deletions ios/RegionManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//
// RegionManager.swift
// Magnet
//
// Created by Francisco Jordano on 09/11/2016.
// Copyright © 2016 Mozilla. All rights reserved.
//
// This class takes care of waking up in background and
// checking the current location to know if there are interesting
// things around.
// In order to do this we use the region functionality in iOS.
// We setup circular regions centered in the current location of the user,
// when the OS detect that we abandon that region, we get a callback and have
// some time in the background to get a meassure of the new location, check
// if there are interesting things around and setup the new region.
//
// This process will happen, creation of the region, detection of the region
// being abandoned, checking things around, and again we start.
import Foundation
import CoreLocation
import MagnetScannerIOS

@objc(RegionManager) class RegionManager: NSObject, CLLocationManagerDelegate {
let locationManager:CLLocationManager = CLLocationManager()
private static let REGION_NAME = "region.magnet.mozilla.org"
private static let REGION_RADIUS: Double = 50
var scanner: MagnetScanner? = nil
var isEnabled = true
var currentRegion: CLRegion?
let locationResolver: LocationHelper = LocationHelper()

@objc override init() {
super.init()

isEnabled = CLLocationManager.isMonitoringAvailableForClass(CLRegion.self)

guard isEnabled else {
Log.w("Region monitoring not enabled for this device")
return;
}

scanner = MagnetScanner(callback: self.onItemFound)
}

private func onItemFound(item: Dictionary<String, AnyObject>) {
Log.l("Found item on leaving region \(item)")
let url = item["url"] as! String
var channel: String? = nil
if item["channel_id"] != nil {
channel = item["channel_id"] as! String
}
NotificationsHelper.notifyUser(url, channel: channel)
}

@objc func startListeningToRegionChange() {
guard isEnabled else {
return
}
Log.l("Start listening to region changes")
self.locationManager.delegate = self
setupRegion()
}

@objc func stopListeningToRegionChange() {
guard isEnabled && self.currentRegion != nil else {
return
}
Log.l("Stoping listening to region changes")

self.locationManager.stopMonitoringForRegion(self.currentRegion!)
}

private func setupRegion() {
locationResolver.start { location in
let region = CLCircularRegion(center: location.coordinate, radius: RegionManager.REGION_RADIUS, identifier: RegionManager.REGION_NAME)
self.currentRegion = region
Log.l("Setting up region \(region)")
self.locationManager.startMonitoringForRegion(region)
}
}

func locationManager(manager: CLLocationManager, didExitRegion region: CLRegion) {
setupRegion()
Log.l("Waking up case region abandoned")
self.scanner!.start()
}
}

0 comments on commit 3a2e9e6

Please sign in to comment.