Created
February 2, 2017 05:29
-
-
Save augusteo/687a6516c6f239be878c0432d9c1b592 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// BeaconManager.swift | |
// Keno | |
// | |
// Created by Victor Augusteo on 17/8/16. | |
// Copyright © 2016 Tabcorp. All rights reserved. | |
// | |
import Foundation | |
import ReactiveCocoa | |
import ReactiveSwift | |
import CoreBluetooth | |
import Result | |
class BeaconManager: NSObject { | |
static let instance = BeaconManager() | |
let isBluetoothOn = MutableProperty<Bool>(true) | |
fileprivate var bluetoothManager: CBPeripheralManager! | |
fileprivate var initState = false | |
fileprivate let tpState = MutableProperty<Bool>(true) | |
fileprivate var shouldRegisterForAppNotification = true | |
fileprivate var isFirstTime = true | |
let beaconStatus = MutableProperty<BeaconStatus>(.outside) | |
let isInVenue = MutableProperty<Bool>(false) | |
var spreoKey = SpreoKeys.mvp | |
private override init() { | |
super.init() | |
log.debug(IDKit.version()) | |
IDKit.setDebugMode(false) | |
if UserDefaults.standard.bool(forKey: SpreoDefaultKeyIsTest) { | |
IDKit.setCustomerAPIKey(Constants.BeaconKeys.CustomerKeyTest) | |
spreoKey = .others | |
} else { | |
IDKit.setCustomerAPIKey(Constants.BeaconKeys.CustomerKeyProduction) | |
spreoKey = .mvp | |
} | |
bluetoothManager = CBPeripheralManager(delegate: self, queue: nil, options: nil) | |
isInVenue <~ beaconStatus.producer | |
.map { zone in | |
if let testInVenue = ProcessInfo.processInfo.environment["IN_VENUE_TEST"], Bool(testInVenue) == true { | |
return true | |
} | |
return zone == .inside | |
} | |
SignalProducer.combineLatest(tpState.producer, isBluetoothOn.producer) | |
.filter { !$0 || !$1 } | |
.startWithValues { _, _ in | |
self.beaconStatus.value = .outside | |
} | |
NotificationCenter.default.reactive.notifications(forName: NSNotification.Name.UIApplicationDidEnterBackground) | |
.observeValues { [weak self] _ in | |
self?.beaconStatus.value = .outside | |
} | |
let becomeActiveTrigger = | |
NotificationCenter.default.reactive.notifications(forName: NSNotification.Name.UIApplicationDidBecomeActive) | |
.map { _ in () } | |
let becomeActiveProducer: SignalProducer<(), NoError> = SignalProducer(becomeActiveTrigger) | |
.beginWith(()) | |
let locationProducer = LocationManager.sharedInstance | |
.authorizationStatus | |
.producer | |
.map { $0 == .authorizedWhenInUse } | |
let jurisdictionProducer = AccountManager.sharedInstance.account | |
.producer | |
.filterMap { $0.jurisdiction } | |
.map { $0 == .NSW } | |
let bluetoothProducer = isBluetoothOn.producer | |
.skipRepeats() | |
SignalProducer.combineLatest(jurisdictionProducer, locationProducer, becomeActiveProducer, bluetoothProducer) | |
.logEvents() | |
.map { (isNSW, locationOn, _, bluetoothOn) in | |
return isNSW && locationOn && bluetoothOn | |
} | |
.skipRepeats() | |
.startWithValues { [weak self] shouldStart in | |
self?.handleZoneDetection(shouldStart: shouldStart) | |
} | |
} | |
func doInitialDetection() { | |
if (!initState && isBluetoothOn.value) { | |
initState = true; | |
IDKit.startInitialZoneDetection(); | |
} | |
} | |
/// call just before placing a bet, returns all data needed to place a bet | |
func getVenueData() -> VenueData? { | |
if let testInVenue = ProcessInfo.processInfo.environment["IN_VENUE_TEST"] , Bool(testInVenue) == true { | |
return handleTestVenue() | |
} | |
if beaconStatus.value == .outside { return nil } | |
guard let projectID = IDKit.getCurrentProjectId() else { return nil } | |
let date = Date() | |
let signature = generateSignature(projectID: projectID, date: date) | |
return VenueData(venueID: projectID, date: date, signature: signature) | |
} | |
private func handleTestVenue() -> VenueData? { | |
let projectID = "00000_melbourne" | |
let date = Date() | |
let signature = generateSignature(projectID: projectID, date: date) | |
return VenueData(venueID: projectID, date: date, signature: signature) | |
} | |
} | |
extension BeaconManager { | |
func handleZoneDetection(shouldStart: Bool) { | |
if shouldStart { | |
IDKit.registerToZoneDetectionListener(withDelegate: self) | |
IDKit.startSearching(forCampus: nil) | |
log.debug("Beacon manager started searching") | |
} else { | |
IDKit.stopZoneDetection() | |
log.debug("Beacon manager stopped searching") | |
} | |
} | |
} | |
extension BeaconManager: IDZoneDetectionListener { | |
func onUpdateGate(with aZoneStatus: IDZoneStatus, detectionType aZoneDetectionType: IDZoneDetectionType) { | |
log.debug("zone status: \(aZoneStatus.stringValue), detectionType: \(aZoneDetectionType.stringValue)") | |
let isLocationOn = LocationManager.sharedInstance.authorizationStatus.value == .authorizedWhenInUse | |
if aZoneDetectionType == .loadingStateDetection && isBluetoothOn.value && isLocationOn { | |
beaconStatus.value = .loading | |
return | |
} | |
if (aZoneDetectionType == .initialDetection) { | |
initState = false; | |
if (aZoneStatus == .in) { | |
beaconStatus.value = .inside | |
return | |
} | |
} | |
if (aZoneDetectionType == .deviceConectivetyDetectionOn) { | |
if (isFirstTime) { | |
isFirstTime = false; | |
} else { | |
initState = false; | |
doInitialDetection() | |
return | |
} | |
} | |
if (aZoneDetectionType == .deviceConectivetyDetection) { | |
beaconStatus.value = aZoneStatus == .in ? .inside : .outside | |
return | |
} | |
//Gate Detection | |
if (aZoneStatus == .in && tpState.value) { | |
beaconStatus.value = .inside | |
} | |
if (aZoneStatus == .out) { | |
beaconStatus.value = .outside | |
} | |
} | |
func onUpdateTheftProtectionState(withMode aTPStateMode: Bool) { | |
log.debug("tpState: \(aTPStateMode)") | |
tpState.value = aTPStateMode | |
} | |
} | |
extension BeaconManager { | |
fileprivate func generateSignature(projectID: String, date: Date) -> String { | |
let str = "\(projectID)\(date.iso)" | |
let signature = DigitalSignature.signature(from: str) | |
return signature! | |
} | |
} | |
extension BeaconManager: CBPeripheralManagerDelegate { | |
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) { | |
if let testInVenue = ProcessInfo.processInfo.environment["IN_VENUE_TEST"], Bool(testInVenue) == true { | |
// Pretend we have bluetooth turned on. | |
isBluetoothOn.value = true | |
} else { | |
isBluetoothOn.value = peripheral.state == .poweredOn | |
} | |
} | |
} | |
/// we need to pass venueID, device location, and one-time security token for placing a bet | |
struct VenueData { | |
let venueID : String | |
let date: Date | |
let signature: String | |
} | |
extension IDZoneStatus { | |
var stringValue: String { | |
switch self { | |
case .in : return "In" | |
case .out: return "Out" | |
} | |
} | |
} | |
extension IDZoneDetectionType { | |
var stringValue: String { | |
switch self { | |
case .initialDetection: | |
return "Initial" | |
case .unknownDetection: | |
return "Unkown" | |
case .gateDetection: | |
return "Gate" | |
case .deviceConectivetyDetection: | |
return "DeviceConnectivity" | |
case .deviceConectivetyDetectionOn: | |
return "DeviceConnectivityOn" | |
case .userDefenitionDetection: | |
return "User Definition" | |
case .loadingStateDetection: | |
return "Loading detection..." | |
case .notInRangeDetection: | |
return "no beacon in range" | |
case .bleNotAvailableDetection: | |
return "bluetooth is not available" | |
} | |
} | |
} | |
enum BeaconStatus { | |
case inside, outside, loading | |
} | |
enum SpreoKeys { | |
case mvp | |
case others | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment