Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

218 changes: 218 additions & 0 deletions Sources/gattserver/DarwinLocation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
//
// DarwinLocationManager.swift
// gattserver
//
// Created by Carlos Duclos on 7/4/18.
//

//#if os(macOS)

import Foundation
import CoreLocation

public final class DarwinLocationManager: LocationManager {

// MARK: - Properties

public var didUpdate: ((Location) -> ())?

private var _location: Location? {

didSet { if let location = self.location { didUpdate?(location) } }
}

public fileprivate(set) var location: Location? {

get { return accessQueue.sync { [unowned self] in return self._location } }

set { accessQueue.sync { [unowned self] in self._location = newValue } }
}

internal let internalManager: CLLocationManager

internal let delegate: InternalDelegate

internal var internalState = InternalState()

internal lazy var accessQueue: DispatchQueue = DispatchQueue(label: "\(type(of: self)) Access Queue", attributes: [])

public static var isEnabled: Bool {

return CLLocationManager.locationServicesEnabled()
}

public static var authorizationStatus: CLAuthorizationStatus {

return CLLocationManager.authorizationStatus()
}

// MARK: - Initialization

public init(didUpdate: ((Location) -> ())? = nil) throws {

// initialize properties
self.internalManager = CLLocationManager()
self.delegate = InternalDelegate()
self.didUpdate = didUpdate

// set delegate
self.delegate.locationManager = self
internalManager.delegate = self.delegate

// start updating location
try start()
}

deinit {

stop()
}

// MARK: - Methods

// only call once
private func start() throws {

let semaphore = Semaphore(timeout: 30, operation: .startUpdatingLocation)
accessQueue.sync { [unowned self] in self.internalState.start.semaphore = semaphore }
defer { accessQueue.sync { [unowned self] in self.internalState.start.semaphore = nil } }

internalManager.startUpdatingLocation()

try semaphore.wait()
}

private func stop() {

internalManager.stopUpdatingLocation()
}
}

// MARK: - CLLocationManagerDelegate

extension DarwinLocationManager {

@objc(DarwinLocationManagerInternalDelegate)
final class InternalDelegate: NSObject {

weak var locationManager: DarwinLocationManager?
}
}

extension DarwinLocationManager.InternalDelegate: CLLocationManagerDelegate {

@objc
public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

/**
An array of CLLocation objects containing the location data. This array always contains at least one object representing the current location. If updates were deferred or if multiple locations arrived before they could be delivered, the array may contain additional entries. The objects in the array are organized in the order in which they occurred. Therefore, the most recent location update is at the end of the array.
*/

guard let location = locations.last
else { assertionFailure("Array always contains at least one object representing the current location."); return }

self.locationManager?.accessQueue.async { [weak self] in
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't you need to use here self.locationManager?.accessQueue.async(flags: .barrier)?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, improve as you see fit.

self?.locationManager?.location = Location(location)
}
}

@objc
public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {

// check for pending operations
self.locationManager?.accessQueue.sync { [weak self] in

// return error when starting updating locations
if let semaphore = self?.locationManager?.internalState.start.semaphore {

semaphore.stopWaiting(error) // throw error
self?.locationManager?.internalState.start.semaphore = nil // stop waiting
}
}
}
}

internal extension Location {

init(_ location: CLLocation) {

self.init(coordinate: Coordinate(location.coordinate))
}
}

internal extension LocationCoordinate {

init(_ location: CLLocationCoordinate2D) {

self.init(latitude: location.latitude, longitude: location.longitude)
}
}

// MARK: - Supporting Types

public enum DarwinLocationError: Error {

case timeout
}

internal extension DarwinLocationManager {

struct InternalState {

fileprivate init() { }

struct Start {

var semaphore: Semaphore?
}

var start = Start()
}

enum Operation {

case startUpdatingLocation
}

final class Semaphore {

let operation: Operation
let semaphore: DispatchSemaphore
let timeout: TimeInterval
var error: Swift.Error?

init(timeout: TimeInterval,
operation: Operation) {

self.operation = operation
self.timeout = timeout
self.semaphore = DispatchSemaphore(value: 0)
self.error = nil
}

func wait() throws {

let dispatchTime: DispatchTime = .now() + timeout

let success = semaphore.wait(timeout: dispatchTime) == .success

if let error = self.error {

throw error
}

guard success else { throw DarwinLocationError.timeout }
}

func stopWaiting(_ error: Swift.Error? = nil) {

// store signal
self.error = error

// stop blocking
semaphore.signal()
}
}
}

//#endif
3 changes: 2 additions & 1 deletion Sources/gattserver/GATTServiceController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ public protocol GATTServiceController: class {

internal let serviceControllers: [GATTServiceController.Type] = [
GATTBatteryServiceController.self,
GATTDeviceInformationServiceController.self
GATTDeviceInformationServiceController.self,
GATTIndoorPositioningnServiceController.self
]
79 changes: 79 additions & 0 deletions Sources/gattserver/IndoorPositioningService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//
// IndoorPositioningService.swift
// gattserver
//
// Created by Carlos Duclos on 7/4/18.
//

import Foundation
import Bluetooth
import GATT

public final class GATTIndoorPositioningnServiceController: GATTServiceController {

public static let service: BluetoothUUID = .indoorPositioning

// MARK: - Properties

public let peripheral: PeripheralManager

public private(set) var latitude: GATTLatitude = 0 {

didSet { peripheral[characteristic: latitudeHandle] = latitude.data }
}

internal let serviceHandle: UInt16

internal let latitudeHandle: UInt16

internal let locationManager: LocationManager

// MARK: - Initialization

public init(peripheral: PeripheralManager) throws {

self.peripheral = peripheral

let serviceUUID = type(of: self).service

#if os(Linux)
let descriptors = [GATTClientCharacteristicConfiguration().descriptor]
#else
let descriptors: [GATT.Descriptor] = []
#endif

let characteristics = [
GATT.Characteristic(uuid: type(of: latitude).uuid,
value: latitude.data,
permissions: [.read],
properties: [.read, .notify],
descriptors: descriptors)
]

let service = GATT.Service(uuid: serviceUUID,
primary: true,
characteristics: characteristics)

self.serviceHandle = try peripheral.add(service: service)
self.latitudeHandle = peripheral.characteristics(for: type(of: latitude).uuid)[0]

// start updating location
#if os(macOS)
self.locationManager = try DarwinLocationManager()
self.locationManager.didUpdate = { [weak self] in self?.updateValues($0) }
#elseif os(Linux)
#endif
}

deinit {

self.peripheral.remove(service: serviceHandle)
}

// MARK: - Methods

private func updateValues(_ location: Location) {

self.latitude = GATTLatitude(rawValue: Int32(location.coordinate.latitude))
}
}
29 changes: 29 additions & 0 deletions Sources/gattserver/LinuxLocation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// LinuxLocation.swift
// gattserver
//
// Created by Carlos Duclos on 7/4/18.
//

import Foundation

#if os(Linux)

public final class LinuxLocation: LocationManagerProtocol {

public var locationServicesEnabled: Bool {

return false
}

public func startUpdatingLocation() {

}

public func stopUpdatingLocation() {

}

}

#endif
Loading