Skip to content

Instantly share code, notes, and snippets.

@DaisukeHirata
Last active May 19, 2020 13:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DaisukeHirata/d40e7e6d22a281e1525fa6e7fe6fcc0b to your computer and use it in GitHub Desktop.
Save DaisukeHirata/d40e7e6d22a281e1525fa6e7fe6fcc0b to your computer and use it in GitHub Desktop.
Beacon Round Robin and Core Bluetooth to stay awake forever next to each other
//
// BeaconRoundRobinHandler.swift
// use beacon ranging the os handles bringing your app back up,
// if you make the devices round robin the devices pretty much stay awake forever next to each other.
// The round robin causes the device to broadcast as a new location beacon causing other phones to wake up.
// This implementation comes from Fuzz.
//
import UIKit
import CoreLocation
import CoreBluetooth
enum Beacon: String, CaseIterable {
case beacon1 = "D73D3920-1711-4F41-88D6-39BF383C4D7F"
case beacon2 = "0D712E02-7D43-4014-9404-2E6E13663B23"
case beacon3 = "8830D5FA-54C2-4D19-BD67-CA63C24BF751"
case beacon4 = "D66B66E2-8C9C-4BE5-90BC-6853F4C9869B"
case beacon5 = "D3E5EC73-3E45-4A6B-8159-CD32BD2F98EA"
case beacon6 = "BB109B2B-AF2E-49D3-B7FF-776F238C77EA"
var uuid: UUID? {
return UUID(uuidString: rawValue)
}
var major: CLBeaconMajorValue? {
return CLBeaconMajorValue("0")
}
var minor: CLBeaconMinorValue? {
return CLBeaconMinorValue("0")
}
var region: CLBeaconRegion? {
if let cachedRegion = Beacon.regionCache[self] { return cachedRegion }
guard let uuid = uuid else { return nil }
guard let major = major else { return nil }
guard let minor = minor else { return nil }
let region = CLBeaconRegion(proximityUUID: uuid, major: major, minor: minor, identifier: rawValue)
region.notifyOnEntry = true
region.notifyOnExit = true
region.notifyEntryStateOnDisplay = true
Beacon.regionCache[self] = region
return region
}
var next: Beacon {
if let index = Beacon.allCases.index(of: self){
if index + 1 < Beacon.allCases.count {
return Beacon.allCases[index + 1]
}
}
return Beacon.allCases[0]
}
static private var regionCache = [Beacon: CLBeaconRegion]()
static var regions: [CLBeaconRegion] { Beacon.allCases.compactMap { $0.region } }
}
extension CLRegion {
// This is helpfull because all location managers
// share region monitoring. So we need to filter for
// the correct regions when using delegate methods
var isOutbreakBeaconRegion: Bool {
Beacon.regions.contains(where: { $0.identifier == self.identifier })
}
}
class BeaconReceiver: NSObject, CLLocationManagerDelegate {
var locationManager = CLLocationManager()
func start() {
if #available(iOS 11.0, *) {
locationManager.showsBackgroundLocationIndicator = false
}
locationManager.allowsBackgroundLocationUpdates = true
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
locationManager.monitoredRegions.filter({ $0.isOutbreakBeaconRegion }).forEach {
self.locationManager.stopMonitoring(for: $0)
}
Beacon.regions.forEach {
locationManager.startMonitoring(for: $0)
}
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
guard region.isOutbreakBeaconRegion else { return }
print("enter region: \(region)")
UpdateTimer.getInstance().wakeupFromBackground()
}
}
class BeaconBroadcaster: NSObject, CBPeripheralManagerDelegate {
var peripheral: CBPeripheralManager?
var timer: Timer?
var beacon: Beacon?
let refreshRate = 10.0
func start() {
peripheral?.stopAdvertising()
peripheral = CBPeripheralManager(delegate: self, queue: nil)
}
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
print(peripheral.state.rawValue)
if peripheral.state == .poweredOn {
broadcast(beacon: self.beacon?.next ?? Beacon.beacon1)
}
}
private func broadcast(beacon: Beacon) {
guard let region = beacon.region else { return }
let peripheralData: NSMutableDictionary = region.peripheralData(withMeasuredPower: nil)
peripheral?.startAdvertising(((peripheralData as NSDictionary) as! [String : Any]))
timer = Timer.scheduledTimer(withTimeInterval: refreshRate, repeats: false) { _ in
DispatchQueue.main.async { [unowned self] in
self.peripheral?.stopAdvertising()
self.broadcast(beacon: beacon.next)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment