Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions Example/Podfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.3'
use_frameworks!

target 'xDripG5_Example' do
Expand Down
6 changes: 3 additions & 3 deletions Example/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ EXTERNAL SOURCES:
:path: ../

SPEC CHECKSUMS:
xDripG5: 8779a4f495fd8eb81a3d75457afe9b95fb52f61d
xDripG5: 92588b5d1d31b337673af62bb433e503001356ae

PODFILE CHECKSUM: 6b30cba971694d5258509315fb52eb645c9bc5e3
PODFILE CHECKSUM: c62f3fcb344335d79abdcccea268615000e8801b

COCOAPODS: 1.1.0.rc.2
COCOAPODS: 1.3.1
16 changes: 13 additions & 3 deletions Example/xDripG5.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -202,13 +202,16 @@
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-xDripG5_Example-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
2FC4228A9369221D367ED688 /* [CP] Copy Pods Resources */ = {
Expand All @@ -232,9 +235,12 @@
files = (
);
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-xDripG5_Example/Pods-xDripG5_Example-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/xDripG5/xDripG5.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/xDripG5.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
Expand Down Expand Up @@ -381,9 +387,11 @@
isa = XCBuildConfiguration;
baseConfigurationReference = F3147B948B4F90A741304461 /* Pods-xDripG5_Example.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = xDripG5/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MODULE_NAME = ExampleApp;
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.xDripG5-Example";
Expand All @@ -397,9 +405,11 @@
isa = XCBuildConfiguration;
baseConfigurationReference = CFBD776BFE02F42998A8820B /* Pods-xDripG5_Example.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = xDripG5/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MODULE_NAME = ExampleApp;
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.xDripG5-Example";
Expand Down
12 changes: 6 additions & 6 deletions Example/xDripG5/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,24 +73,24 @@ class AppDelegate: UIResponder, UIApplicationDelegate, TransmitterDelegate {
}()

func transmitter(_ transmitter: Transmitter, didError error: Error) {
if let vc = window?.rootViewController as? TransmitterDelegate {
DispatchQueue.main.async {
DispatchQueue.main.async {
if let vc = self.window?.rootViewController as? TransmitterDelegate {
vc.transmitter(transmitter, didError: error)
}
}
}

func transmitter(_ transmitter: Transmitter, didRead glucose: Glucose) {
if let vc = window?.rootViewController as? TransmitterDelegate {
DispatchQueue.main.async {
DispatchQueue.main.async {
if let vc = self.window?.rootViewController as? TransmitterDelegate {
vc.transmitter(transmitter, didRead: glucose)
}
}
}

func transmitter(_ transmitter: Transmitter, didReadUnknownData data: Data) {
if let vc = window?.rootViewController as? TransmitterDelegate {
DispatchQueue.main.async {
DispatchQueue.main.async {
if let vc = self.window?.rootViewController as? TransmitterDelegate {
vc.transmitter(transmitter, didReadUnknownData: data)
}
}
Expand Down
10 changes: 5 additions & 5 deletions Example/xDripG5/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ class ViewController: UIViewController, TransmitterDelegate, UITextFieldDelegate
if let text = textField.text {
let newString = text.replacingCharacters(in: range.rangeOfString(text), with: string)

if newString.characters.count > 6 {
if newString.count > 6 {
return false
} else if newString.characters.count == 6 {
} else if newString.count == 6 {
AppDelegate.sharedDelegate.transmitter?.ID = newString
UserDefaults.standard.transmitterID = newString

Expand All @@ -86,7 +86,7 @@ class ViewController: UIViewController, TransmitterDelegate, UITextFieldDelegate
}

func textFieldDidEndEditing(_ textField: UITextField) {
if textField.text?.characters.count != 6 {
if textField.text?.count != 6 {
textField.text = UserDefaults.standard.transmitterID
}
}
Expand Down Expand Up @@ -129,8 +129,8 @@ class ViewController: UIViewController, TransmitterDelegate, UITextFieldDelegate

private extension NSRange {
func rangeOfString(_ string: String) -> Range<String.Index> {
let startIndex = string.characters.index(string.startIndex, offsetBy: location)
let endIndex = string.characters.index(startIndex, offsetBy: length)
let startIndex = string.index(string.startIndex, offsetBy: location)
let endIndex = string.index(startIndex, offsetBy: length)
return startIndex..<endIndex
}
}
Expand Down
4 changes: 2 additions & 2 deletions xDripG5.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ Please note this project is neither created nor backed by Dexcom, Inc. Use of th
s.homepage = "https://github.com/LoopKit/xDripG5"
s.license = 'MIT'
s.author = { "Nathan Racklyeft" => "loudnate@gmail.com" }
s.source = { :git => "https://github.com/LoopKit/xDripG5.git", :tag => s.version.to_s }
s.source = { :git => "https://github.com/LoopKit/xDripG5.git", :tag => "v" + s.version.to_s }

s.platform = :ios, '9.3'
s.platform = :ios, '10.3'
s.requires_arc = true

s.source_files = ['xDripG5/**/*.swift', 'xDripG5/AESCrypt.{h,m}', 'Pod/*.h']
Expand Down
2 changes: 1 addition & 1 deletion xDripG5/AESCrypt.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ @implementation AESCrypt

+ (NSData *)encryptData:(NSData *)data usingKey:(NSData *)key error:(NSError * _Nullable __autoreleasing *)error
{
NSMutableData *dataOut = [[NSMutableData alloc] initWithCapacity:16];
NSMutableData *dataOut = [NSMutableData dataWithLength: data.length + kCCBlockSizeAES128];

CCCryptorStatus status = CCCrypt(kCCEncrypt,
kCCAlgorithmAES,
Expand Down
22 changes: 19 additions & 3 deletions xDripG5/BluetoothManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ class BluetoothManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate
/// Any error surfaced during the active operation
private var operationError: Error?

func readValueForCharacteristicAndWait(_ UUID: CGMServiceCharacteristicUUID, timeout: TimeInterval = 2, expectingFirstByte firstByte: UInt8? = nil) throws -> Data {
func readValueForCharacteristicAndWait(_ UUID: CGMServiceCharacteristicUUID, timeout: TimeInterval = 10, expectingFirstByte firstByte: UInt8? = nil) throws -> Data {
guard manager.state == .poweredOn && operationConditions.isEmpty, let peripheral = peripheral else {
throw BluetoothManagerError.notReady
}
Expand Down Expand Up @@ -206,7 +206,7 @@ class BluetoothManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate
return characteristic.value ?? Data()
}

func setNotifyEnabledAndWait(_ enabled: Bool, forCharacteristicUUID UUID: CGMServiceCharacteristicUUID, timeout: TimeInterval = 2) throws {
func setNotifyEnabledAndWait(_ enabled: Bool, forCharacteristicUUID UUID: CGMServiceCharacteristicUUID, timeout: TimeInterval = 10) throws {
guard manager.state == .poweredOn && operationConditions.isEmpty, let peripheral = peripheral else {
throw BluetoothManagerError.notReady
}
Expand Down Expand Up @@ -264,7 +264,7 @@ class BluetoothManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate
return characteristic.value ?? Data()
}

func writeValueAndWait(_ value: Data, forCharacteristicUUID UUID: CGMServiceCharacteristicUUID, timeout: TimeInterval = 2, expectingFirstByte firstByte: UInt8? = nil) throws -> Data {
func writeValueAndWait(_ value: Data, forCharacteristicUUID UUID: CGMServiceCharacteristicUUID, timeout: TimeInterval = 10, expectingFirstByte firstByte: UInt8? = nil) throws -> Data {
guard manager.state == .poweredOn && operationConditions.isEmpty, let peripheral = peripheral else {
throw BluetoothManagerError.notReady
}
Expand Down Expand Up @@ -299,6 +299,22 @@ class BluetoothManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate
return characteristic.value ?? Data()
}

func waitForTime(timeout: TimeInterval = 10) throws {
guard manager.state == .poweredOn && operationConditions.isEmpty, let peripheral = peripheral else {
throw BluetoothManagerError.notReady
}

operationLock.lock()

operationLock.wait(until: Date(timeIntervalSinceNow: timeout))

defer {
operationLock.unlock()
}

return
}

// MARK: - Accessors

var isScanning: Bool {
Expand Down
2 changes: 1 addition & 1 deletion xDripG5/Messages/AuthRequestTxMessage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ struct AuthRequestTxMessage: TransmitterTxMessage {

NSUUID().getBytes(&UUIDBytes)

singleUseToken = Data(bytes: UUIDBytes)
singleUseToken = Data(bytes: UUIDBytes[0..<8])
}

var byteSequence: [Any] {
Expand Down
9 changes: 9 additions & 0 deletions xDripG5/Messages/SensorRxMessage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//
// SensorRxMessage.swift
// xDripG5
//
// Created by Renee on 04/02/2018.
// Copyright © 2018 LoopKit Authors. All rights reserved.
//

import Foundation
9 changes: 9 additions & 0 deletions xDripG5/Messages/SensorTxMessage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//
// SensorTxMessage.swift
// xDripG5
//
// Created by Renee on 04/02/2018.
// Copyright © 2018 LoopKit Authors. All rights reserved.
//

import Foundation
100 changes: 48 additions & 52 deletions xDripG5/Transmitter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -168,79 +168,75 @@ public final class Transmitter: BluetoothManagerDelegate {
// MARK: - Helpers

private func authenticate() throws {
if let data = try? bluetoothManager.readValueForCharacteristicAndWait(.Authentication),
let status = AuthStatusRxMessage(data: data), status.authenticated == 1 && status.bonded == 1
{
NSLog("Transmitter already authenticated.")
} else {
do {
try bluetoothManager.setNotifyEnabledAndWait(true, forCharacteristicUUID: .Authentication)
} catch let error {
throw TransmitterError.authenticationError("Error enabling notification: \(error)")
}
let authMessage = AuthRequestTxMessage()
let data: Data

let authMessage = AuthRequestTxMessage()
let data: Data
do {
_ = try bluetoothManager.writeValueAndWait(authMessage.data, forCharacteristicUUID: .Authentication)
data = try bluetoothManager.readValueForCharacteristicAndWait(.Authentication, expectingFirstByte: AuthChallengeRxMessage.opcode)
} catch let error {
throw TransmitterError.authenticationError("Error writing transmitter challenge: \(error)")
}

guard let response = AuthChallengeRxMessage(data: data) else {
throw TransmitterError.authenticationError("Unable to parse auth challenge: \(data)")
}

guard response.tokenHash == self.calculateHash(authMessage.singleUseToken) else {
throw TransmitterError.authenticationError("Transmitter failed auth challenge")
}

if let challengeHash = self.calculateHash(response.challenge) {
let data: Data
do {
data = try bluetoothManager.writeValueAndWait(authMessage.data, forCharacteristicUUID: .Authentication, expectingFirstByte: AuthChallengeRxMessage.opcode)
_ = try bluetoothManager.writeValueAndWait(AuthChallengeTxMessage(challengeHash: challengeHash).data, forCharacteristicUUID: .Authentication)
data = try bluetoothManager.readValueForCharacteristicAndWait(.Authentication, expectingFirstByte: AuthStatusRxMessage.opcode)
} catch let error {
throw TransmitterError.authenticationError("Error writing transmitter challenge: \(error)")
throw TransmitterError.authenticationError("Error writing challenge response: \(error)")
}

guard let response = AuthChallengeRxMessage(data: data) else {
throw TransmitterError.authenticationError("Unable to parse auth challenge: \(data)")
guard let response = AuthStatusRxMessage(data: data) else {
throw TransmitterError.authenticationError("Unable to parse auth status: \(data)")
}

guard response.tokenHash == self.calculateHash(authMessage.singleUseToken) else {
throw TransmitterError.authenticationError("Transmitter failed auth challenge")
guard response.authenticated == 1 else {
throw TransmitterError.authenticationError("Transmitter rejected auth challenge")
}

if let challengeHash = self.calculateHash(response.challenge) {
if response.bonded != 0x1 {
do {
_ = try bluetoothManager.writeValueAndWait(KeepAliveTxMessage(time: 25).data, forCharacteristicUUID: .Authentication)
} catch let error {
throw TransmitterError.authenticationError("Error writing keep-alive for bond: \(error)")
}

let data: Data
do {
data = try bluetoothManager.writeValueAndWait(AuthChallengeTxMessage(challengeHash: challengeHash).data, forCharacteristicUUID: .Authentication, expectingFirstByte: AuthStatusRxMessage.opcode)
_ = try bluetoothManager.writeValueAndWait(BondRequestTxMessage().data, forCharacteristicUUID: .Authentication)
} catch let error {
throw TransmitterError.authenticationError("Error writing challenge response: \(error)")
throw TransmitterError.authenticationError("Error writing bond request: \(error)")
}

guard let response = AuthStatusRxMessage(data: data) else {
throw TransmitterError.authenticationError("Unable to parse auth status: \(data)")
// Wait for the OS dialog to pop-up before continuing.
do {
try bluetoothManager.waitForTime(timeout: 5)
} catch let error {
throw TransmitterError.authenticationError("Error waiting for bond request response: \(error)")
}

guard response.authenticated == 1 else {
throw TransmitterError.authenticationError("Transmitter rejected auth challenge")
do {
data = try bluetoothManager.readValueForCharacteristicAndWait(.Authentication, expectingFirstByte: AuthStatusRxMessage.opcode)
} catch let error {
throw TransmitterError.authenticationError("Error reading status after bond request: \(error)")
}

if response.bonded != 0x1 {
do {
_ = try bluetoothManager.writeValueAndWait(KeepAliveTxMessage(time: 25).data, forCharacteristicUUID: .Authentication)
} catch let error {
throw TransmitterError.authenticationError("Error writing keep-alive for bond: \(error)")
}

let data: Data
do {
// Wait for the OS dialog to pop-up before continuing.
data = try bluetoothManager.writeValueAndWait(BondRequestTxMessage().data, forCharacteristicUUID: .Authentication, timeout: 15, expectingFirstByte: AuthStatusRxMessage.opcode)
} catch let error {
throw TransmitterError.authenticationError("Error writing bond request: \(error)")
}

guard let response = AuthStatusRxMessage(data: data) else {
throw TransmitterError.authenticationError("Unable to parse auth status: \(data)")
}

guard response.bonded == 0x1 else {
throw TransmitterError.authenticationError("Transmitter failed to bond")
}
guard let response = AuthStatusRxMessage(data: data) else {
throw TransmitterError.authenticationError("Unable to parse auth status: \(data)")
}
}

do {
try bluetoothManager.setNotifyEnabledAndWait(false, forCharacteristicUUID: .Authentication)
} catch let error {
throw TransmitterError.authenticationError("Error disabling notification: \(error)")
guard response.bonded == 0x1 else {
throw TransmitterError.authenticationError("Transmitter failed to bond")
}
}
}
}
Expand Down