Skip to content
This repository has been archived by the owner on Dec 29, 2022. It is now read-only.

Commit

Permalink
Merge pull request #237 from themz/scanner-sample-swift-v4.0
Browse files Browse the repository at this point in the history
Update swift scanner sample to Swift v4.0
  • Loading branch information
marcwan authored Jan 8, 2018
2 parents 93bff7a + 1ed9721 commit bb8738d
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "com.google.sample.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
};
name = Debug;
};
Expand All @@ -453,6 +454,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.google.sample.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
};
name = Release;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,16 @@ class BeaconScanner: NSObject, CBCentralManagerDelegate {
var onLostTimeout: Double = 15.0

private var centralManager: CBCentralManager!
private let beaconOperationsQueue: dispatch_queue_t =
dispatch_queue_create("beacon_operations_queue", nil)
private var shouldBeScanning: Bool = false
private let beaconOperationsQueue = DispatchQueue(label: "beacon_operations_queue")
private var shouldBeScanning = false

private var seenEddystoneCache = [String : [String : AnyObject]]()
private var deviceIDCache = [NSUUID : NSData]()
private var deviceIDCache = [UUID : NSData]()

override init() {
super.init()

self.centralManager = CBCentralManager(delegate: self, queue: self.beaconOperationsQueue)
self.centralManager = CBCentralManager(delegate: self, queue: beaconOperationsQueue)
self.centralManager.delegate = self
}

Expand All @@ -61,7 +60,7 @@ class BeaconScanner: NSObject, CBCentralManagerDelegate {
/// scanning.
///
func startScanning() {
dispatch_async(self.beaconOperationsQueue) {
beaconOperationsQueue.async {
self.startScanningSynchronized()
}
}
Expand All @@ -76,8 +75,8 @@ class BeaconScanner: NSObject, CBCentralManagerDelegate {
///
/// MARK - private methods and delegate callbacks
///
func centralManagerDidUpdateState(central: CBCentralManager) {
if central.state == CBCentralManagerState.PoweredOn && self.shouldBeScanning {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn && self.shouldBeScanning {
self.startScanningSynchronized();
}
}
Expand All @@ -86,40 +85,39 @@ class BeaconScanner: NSObject, CBCentralManagerDelegate {
/// Core Bluetooth CBCentralManager callback when we discover a beacon. We're not super
/// interested in any error situations at this point in time.
///
func centralManager(central: CBCentralManager,
didDiscoverPeripheral peripheral: CBPeripheral,
advertisementData: [String : AnyObject],
RSSI: NSNumber) {
func centralManager(_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData: [String : Any],
rssi RSSI: NSNumber) {
if let serviceData = advertisementData[CBAdvertisementDataServiceDataKey]
as? [NSObject : AnyObject] {
var eft: BeaconInfo.EddystoneFrameType
eft = BeaconInfo.frameTypeForFrame(serviceData)
eft = BeaconInfo.frameTypeForFrame(advertisementFrameList: serviceData)

// If it's a telemetry frame, stash it away and we'll send it along with the next regular
// frame we see. Otherwise, process the UID frame.
if eft == BeaconInfo.EddystoneFrameType.TelemetryFrameType {
deviceIDCache[peripheral.identifier] = BeaconInfo.telemetryDataForFrame(serviceData)
deviceIDCache[peripheral.identifier] = BeaconInfo.telemetryDataForFrame(advertisementFrameList: serviceData)
} else if eft == BeaconInfo.EddystoneFrameType.UIDFrameType
|| eft == BeaconInfo.EddystoneFrameType.EIDFrameType {
let telemetry = self.deviceIDCache[peripheral.identifier]
let serviceUUID = CBUUID(string: "FEAA")
let _RSSI: Int = RSSI.integerValue
let _RSSI: Int = RSSI.intValue

if let
beaconServiceData = serviceData[serviceUUID] as? NSData,
beaconInfo =
if let beaconServiceData = serviceData[serviceUUID] as? NSData,
let beaconInfo =
(eft == BeaconInfo.EddystoneFrameType.UIDFrameType
? BeaconInfo.beaconInfoForUIDFrameData(beaconServiceData, telemetry: telemetry,
? BeaconInfo.beaconInfoForUIDFrameData(frameData: beaconServiceData, telemetry: telemetry,
RSSI: _RSSI)
: BeaconInfo.beaconInfoForEIDFrameData(beaconServiceData, telemetry: telemetry,
: BeaconInfo.beaconInfoForEIDFrameData(frameData: beaconServiceData, telemetry: telemetry,
RSSI: _RSSI)) {

// NOTE: At this point you can choose whether to keep or get rid of the telemetry
// data. You can either opt to include it with every single beacon sighting
// for this beacon, or delete it until we get a new / "fresh" TLM frame.
// We'll treat it as "report it only when you see it", so we'll delete it
// each time.
self.deviceIDCache.removeValueForKey(peripheral.identifier)
self.deviceIDCache.removeValue(forKey: peripheral.identifier)

if (self.seenEddystoneCache[beaconInfo.beaconID.description] != nil) {
// Reset the onLost timer and fire the didUpdate.
Expand All @@ -129,22 +127,22 @@ class BeaconScanner: NSObject, CBCentralManagerDelegate {
timer.reschedule()
}

self.delegate?.didUpdateBeacon(self, beaconInfo: beaconInfo)
self.delegate?.didUpdateBeacon(beaconScanner: self, beaconInfo: beaconInfo)
} else {
// We've never seen this beacon before
self.delegate?.didFindBeacon(self, beaconInfo: beaconInfo)
self.delegate?.didFindBeacon(beaconScanner: self, beaconInfo: beaconInfo)

let onLostTimer = DispatchTimer.scheduledDispatchTimer(
self.onLostTimeout,
queue: dispatch_get_main_queue()) {
delay: self.onLostTimeout,
queue: DispatchQueue.main) {
(timer: DispatchTimer) -> () in
let cacheKey = beaconInfo.beaconID.description
if let
beaconCache = self.seenEddystoneCache[cacheKey],
lostBeaconInfo = beaconCache["beaconInfo"] as? BeaconInfo {
self.delegate?.didLoseBeacon(self, beaconInfo: lostBeaconInfo)
self.seenEddystoneCache.removeValueForKey(
beaconInfo.beaconID.description)
let lostBeaconInfo = beaconCache["beaconInfo"] as? BeaconInfo {
self.delegate?.didLoseBeacon(beaconScanner: self, beaconInfo: lostBeaconInfo)
self.seenEddystoneCache.removeValue(
forKey: beaconInfo.beaconID.description)
}
}

Expand All @@ -156,12 +154,11 @@ class BeaconScanner: NSObject, CBCentralManagerDelegate {
}
} else if eft == BeaconInfo.EddystoneFrameType.URLFrameType {
let serviceUUID = CBUUID(string: "FEAA")
let _RSSI: Int = RSSI.integerValue
let _RSSI: Int = RSSI.intValue

if let
beaconServiceData = serviceData[serviceUUID] as? NSData,
URL = BeaconInfo.parseURLFromFrame(beaconServiceData) {
self.delegate?.didObserveURLBeacon(self, URL: URL, RSSI: _RSSI)
if let beaconServiceData = serviceData[serviceUUID] as? NSData,
let URL = BeaconInfo.parseURLFromFrame(frameData: beaconServiceData) {
self.delegate?.didObserveURLBeacon(beaconScanner: self, URL: URL, RSSI: _RSSI)
}
}
} else {
Expand All @@ -170,14 +167,14 @@ class BeaconScanner: NSObject, CBCentralManagerDelegate {
}

private func startScanningSynchronized() {
if self.centralManager.state != CBCentralManagerState.PoweredOn {
if self.centralManager.state != .poweredOn {
NSLog("CentralManager state is %d, cannot start scan", self.centralManager.state.rawValue)
self.shouldBeScanning = true
} else {
NSLog("Starting to scan for Eddystones")
let services = [CBUUID(string: "FEAA")]
let options = [CBCentralManagerScanOptionAllowDuplicatesKey : true]
self.centralManager.scanForPeripheralsWithServices(services, options: options)
self.centralManager.scanForPeripherals(withServices: services, options: options)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,63 +27,57 @@ class DispatchTimer: NSObject {
typealias TimerHandler = (DispatchTimer) -> Void

private let timerBlock: TimerHandler
private let queue: dispatch_queue_t
private let delay: NSTimeInterval
private let queue: DispatchQueue
private let delay: TimeInterval

private var wrappedBlock: (() -> Void)?
private let source: dispatch_source_t
private let source: DispatchSourceTimer

init(delay: NSTimeInterval, queue: dispatch_queue_t, block: TimerHandler) {
init(delay: TimeInterval, queue: DispatchQueue, block: @escaping TimerHandler) {
timerBlock = block
self.queue = queue
self.delay = delay

self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.queue)
self.source = DispatchSource.makeTimerSource(queue: queue)

super.init()

let wrapper = { () -> Void in
if dispatch_source_testcancel(self.source) == 0 {
dispatch_source_cancel(self.source)
if !self.source.isCancelled {
self.source.cancel()
self.timerBlock(self)
}
}

self.wrappedBlock = wrapper
}

class func scheduledDispatchTimer(delay: NSTimeInterval,
queue: dispatch_queue_t,
block: TimerHandler) -> DispatchTimer {
let dt = DispatchTimer(delay: delay, queue: queue, block: block)
dt.schedule()
return dt
class func scheduledDispatchTimer(delay: TimeInterval, queue: DispatchQueue, block: @escaping TimerHandler) -> DispatchTimer {
let dt = DispatchTimer(delay: delay, queue: queue, block: block)
dt.schedule()

return dt
}

func schedule() {
self.reschedule()
dispatch_source_set_event_handler(self.source, self.wrappedBlock)
dispatch_resume(self.source)
self.source.setEventHandler(handler: self.wrappedBlock)
self.source.resume()
}

func reschedule() {
let start = dispatch_time(DISPATCH_TIME_NOW, Int64(self.delay * Double(NSEC_PER_SEC)))

// Leeway is 10% of timer delay
dispatch_source_set_timer(self.source, start, DISPATCH_TIME_FOREVER,
UInt64((self.delay / 10.0) * Double(NSEC_PER_SEC)))
self.source.schedule(deadline: .now() + self.delay)
}

func suspend() {
dispatch_suspend(self.source)
self.source.suspend()
}

func resume() {
dispatch_resume(self.source)
self.source.resume()
}

func cancel() {
dispatch_source_cancel(self.source)
self.source.cancel()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ class BeaconID : NSObject {
///
let beaconID: [UInt8]

private init(beaconType: BeaconType!, beaconID: [UInt8]) {
fileprivate init(beaconType: BeaconType!, beaconID: [UInt8]) {
self.beaconID = beaconID
self.beaconType = beaconType
}

override var description: String {
if self.beaconType == BeaconType.Eddystone || self.beaconType == BeaconType.EddystoneEID {
let hexid = hexBeaconID(self.beaconID)
let hexid = hexBeaconID(beaconID: self.beaconID)
return "BeaconID beacon: \(hexid)"
} else {
return "BeaconID with invalid type (\(beaconType))"
Expand All @@ -52,7 +52,7 @@ class BeaconID : NSObject {
var retval = ""
for byte in beaconID {
var s = String(byte, radix:16, uppercase: false)
if s.characters.count == 1 {
if s.count == 1 {
s = "0" + s
}
retval += s
Expand Down Expand Up @@ -121,13 +121,12 @@ class BeaconInfo : NSObject {
self.telemetry = telemetry
}

class func frameTypeForFrame(advertisementFrameList: [NSObject : AnyObject])
-> EddystoneFrameType {
class func frameTypeForFrame(advertisementFrameList: [NSObject : AnyObject]) -> EddystoneFrameType {
let uuid = CBUUID(string: "FEAA")
if let frameData = advertisementFrameList[uuid] as? NSData {
if frameData.length > 1 {
let count = frameData.length
var frameBytes = [UInt8](count: count, repeatedValue: 0)
var frameBytes = [UInt8](repeating: 0, count: count)
frameData.getBytes(&frameBytes, length: count)

if frameBytes[0] == EddystoneUIDFrameTypeID {
Expand All @@ -154,11 +153,10 @@ class BeaconInfo : NSObject {
/// in the Swift compiler — it can't tear-down partially initialised objects, so we'll have to
/// wait until this gets fixed. For now, class method will do.
///
class func beaconInfoForUIDFrameData(frameData: NSData, telemetry: NSData?, RSSI: Int)
-> BeaconInfo? {
class func beaconInfoForUIDFrameData(frameData: NSData, telemetry: NSData?, RSSI: Int) -> BeaconInfo? {
if frameData.length > 1 {
let count = frameData.length
var frameBytes = [UInt8](count: count, repeatedValue: 0)
var frameBytes = [UInt8](repeating: 0, count: count)
frameData.getBytes(&frameBytes, length: count)

if frameBytes[0] != EddystoneUIDFrameTypeID {
Expand All @@ -177,11 +175,10 @@ class BeaconInfo : NSObject {
return nil
}

class func beaconInfoForEIDFrameData(frameData: NSData, telemetry: NSData?, RSSI: Int)
-> BeaconInfo? {
class func beaconInfoForEIDFrameData(frameData: NSData, telemetry: NSData?, RSSI: Int) -> BeaconInfo? {
if frameData.length > 1 {
let count = frameData.length
var frameBytes = [UInt8](count: count, repeatedValue: 0)
var frameBytes = [UInt8](repeating: 0, count: count)
frameData.getBytes(&frameBytes, length: count)

if frameBytes[0] != EddystoneEIDFrameTypeID {
Expand All @@ -203,14 +200,14 @@ class BeaconInfo : NSObject {
class func parseURLFromFrame(frameData: NSData) -> NSURL? {
if frameData.length > 0 {
let count = frameData.length
var frameBytes = [UInt8](count: count, repeatedValue: 0)
var frameBytes = [UInt8](repeating: 0, count: count)
frameData.getBytes(&frameBytes, length: count)

if let URLPrefix = URLPrefixFromByte(frameBytes[2]) {
if let URLPrefix = URLPrefixFromByte(schemeID: frameBytes[2]) {
var output = URLPrefix
for i in 3..<frameBytes.count {
if let encoded = encodedStringFromByte(frameBytes[i]) {
output.appendContentsOf(encoded)
if let encoded = encodedStringFromByte(charVal: frameBytes[i]) {
output.append(encoded)
}
}

Expand Down Expand Up @@ -276,8 +273,7 @@ class BeaconInfo : NSObject {
case 0x0d:
return ".gov"
default:
return String(data: NSData(bytes: [ charVal ] as [UInt8], length: 1),
encoding: NSUTF8StringEncoding)
return String(data: Data(bytes: [ charVal ] as [UInt8], count: 1), encoding: .utf8)
}
}

Expand Down

0 comments on commit bb8738d

Please sign in to comment.