diff --git a/.travis.yml b/.travis.yml index 5134fb4f..0b3983d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -osx_image: xcode8 +osx_image: xcode9 # cache: cocoapods # podfile: Example/Podfile @@ -12,5 +12,5 @@ script: # Build cocoapods example project # - set -o pipefail && xcodebuild -workspace Example/xDripG5.xcworkspace -scheme xDripG5-Example -sdk iphonesimulator -destination name="iPhone SE" ONLY_ACTIVE_ARCH=NO | xcpretty # Build Travis project and run tests -- xcodebuild -project xDripG5.xcodeproj -scheme xDripG5 -sdk iphonesimulator10.0 build -destination name="iPhone SE" test +- xcodebuild -project xDripG5.xcodeproj -scheme xDripG5 -sdk iphonesimulator11.0 build -destination name="iPhone SE" test # - pod lib lint diff --git a/Example/xDripG5.xcodeproj/project.pbxproj b/Example/xDripG5.xcodeproj/project.pbxproj index 8a1bd720..02b5616c 100644 --- a/Example/xDripG5.xcodeproj/project.pbxproj +++ b/Example/xDripG5.xcodeproj/project.pbxproj @@ -150,7 +150,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0800; + LastUpgradeCheck = 0900; ORGANIZATIONNAME = "Nathan Racklyeft"; TargetAttributes = { 607FACCF1AFB9204008FA782 = { @@ -286,14 +286,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -317,7 +323,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -333,14 +339,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -357,7 +369,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -369,13 +381,14 @@ isa = XCBuildConfiguration; baseConfigurationReference = F3147B948B4F90A741304461 /* Pods-xDripG5_Example.debug.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = xDripG5/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MODULE_NAME = ExampleApp; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.xDripG5-Example"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -384,13 +397,14 @@ isa = XCBuildConfiguration; baseConfigurationReference = CFBD776BFE02F42998A8820B /* Pods-xDripG5_Example.release.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = xDripG5/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MODULE_NAME = ExampleApp; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.xDripG5-Example"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; diff --git a/Example/xDripG5.xcodeproj/xcshareddata/xcschemes/xDripG5-Example.xcscheme b/Example/xDripG5.xcodeproj/xcshareddata/xcschemes/xDripG5-Example.xcscheme index 90e91734..20557d8f 100644 --- a/Example/xDripG5.xcodeproj/xcshareddata/xcschemes/xDripG5-Example.xcscheme +++ b/Example/xDripG5.xcodeproj/xcshareddata/xcschemes/xDripG5-Example.xcscheme @@ -1,6 +1,6 @@ 0 { + log.info("Discovering services") peripheral.discoverServices(servicesToDiscover) } else { self.peripheral(peripheral, didDiscoverServices: nil) @@ -401,6 +413,7 @@ class BluetoothManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate characteristicsToDiscover = characteristicsToDiscover.filter({ !knownCharacteristics.contains($0) }) if characteristicsToDiscover.count > 0 { + log.info("Discovering characteristics") peripheral.discoverCharacteristics(characteristicsToDiscover, for: service) } else { self.peripheral(peripheral, didDiscoverCharacteristicsFor: service, error: nil) @@ -409,6 +422,10 @@ class BluetoothManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate } func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { + if let error = error { + log.error("Error discovering characteristics: %{public}@", String(describing: error)) + } + self.delegate?.bluetoothManager(self, isReadyWithError: error) } diff --git a/xDripG5/Info.plist b/xDripG5/Info.plist index 5d7827c2..093aab33 100644 --- a/xDripG5/Info.plist +++ b/xDripG5/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.8.0 + 0.8.1 CFBundleSignature ???? CFBundleVersion diff --git a/xDripG5/Messages/GlucoseRxMessage.swift b/xDripG5/Messages/GlucoseRxMessage.swift index 1c6b9d87..ae984ca6 100644 --- a/xDripG5/Messages/GlucoseRxMessage.swift +++ b/xDripG5/Messages/GlucoseRxMessage.swift @@ -29,10 +29,10 @@ public struct GlucoseRxMessage: TransmitterRxMessage { } status = data[1] - sequence = data[2..<6] - timestamp = data[6..<10] + sequence = data.subdata(in: 2..<6).withUnsafeBytes { $0.pointee } + timestamp = data.subdata(in: 6..<10).withUnsafeBytes { $0.pointee } - let glucoseBytes: UInt16 = data[10..<12] + let glucoseBytes: UInt16 = data.subdata(in: 10..<12).withUnsafeBytes { $0.pointee } glucoseIsDisplayOnly = (glucoseBytes & 0xf000) > 0 glucose = glucoseBytes & 0xfff diff --git a/xDripG5/Messages/SessionStartRxMessage.swift b/xDripG5/Messages/SessionStartRxMessage.swift index 0ea5e0aa..71960dba 100644 --- a/xDripG5/Messages/SessionStartRxMessage.swift +++ b/xDripG5/Messages/SessionStartRxMessage.swift @@ -31,8 +31,8 @@ struct SessionStartRxMessage { status = data[1] received = data[2] - requestedStartTime = data[3..<7] - sessionStartTime = data[7..<11] - transmitterTime = data[11..<15] + requestedStartTime = data.subdata(in: 3..<7).withUnsafeBytes { $0.pointee } + sessionStartTime = data.subdata(in: 7..<11).withUnsafeBytes { $0.pointee } + transmitterTime = data.subdata(in: 11..<15).withUnsafeBytes { $0.pointee } } } diff --git a/xDripG5/Messages/SessionStopRxMessage.swift b/xDripG5/Messages/SessionStopRxMessage.swift index e05fadd3..36f41bc5 100644 --- a/xDripG5/Messages/SessionStopRxMessage.swift +++ b/xDripG5/Messages/SessionStopRxMessage.swift @@ -28,8 +28,8 @@ struct SessionStopRxMessage { status = data[1] received = data[2] - sessionStopTime = data[3..<7] - sessionStartTime = data[7..<11] - transmitterTime = data[11..<15] + sessionStopTime = data.subdata(in: 3..<7).withUnsafeBytes { $0.pointee } + sessionStartTime = data.subdata(in: 7..<11).withUnsafeBytes { $0.pointee } + transmitterTime = data.subdata(in: 11..<15).withUnsafeBytes { $0.pointee } } } diff --git a/xDripG5/Messages/TransmitterTimeRxMessage.swift b/xDripG5/Messages/TransmitterTimeRxMessage.swift index 8c95a312..6e9e3467 100644 --- a/xDripG5/Messages/TransmitterTimeRxMessage.swift +++ b/xDripG5/Messages/TransmitterTimeRxMessage.swift @@ -25,8 +25,8 @@ struct TransmitterTimeRxMessage: TransmitterRxMessage { } status = data[1] - currentTime = data[2..<6] - sessionStartTime = data[6..<10] + currentTime = data.subdata(in: 2..<6).withUnsafeBytes { $0.pointee } + sessionStartTime = data.subdata(in: 6..<10).withUnsafeBytes { $0.pointee } } } diff --git a/xDripG5/Messages/TransmitterVersionRxMessage.swift b/xDripG5/Messages/TransmitterVersionRxMessage.swift index fa26c1c5..0b6e5784 100644 --- a/xDripG5/Messages/TransmitterVersionRxMessage.swift +++ b/xDripG5/Messages/TransmitterVersionRxMessage.swift @@ -24,7 +24,7 @@ struct TransmitterVersionRxMessage: TransmitterRxMessage { } status = data[1] - firmwareVersion = data[2..<6] + firmwareVersion = data.subdata(in: 2..<6).map({ $0 }) } } diff --git a/xDripG5/NSData+CRC.swift b/xDripG5/NSData+CRC.swift index 5bf33cac..51cdf649 100644 --- a/xDripG5/NSData+CRC.swift +++ b/xDripG5/NSData+CRC.swift @@ -48,6 +48,6 @@ extension Data { } func crcValid() -> Bool { - return CRCCCITTXModem(subdata(in: 0.. Int8 { - let bytes: [Int8] = self[index...index] - - return bytes[0] - } - - @nonobjc subscript(index: Int) -> UInt8 { - let bytes: [UInt8] = self[index...index] - - return bytes[0] - } -*/ - @nonobjc subscript(range: Range) -> UInt16 { - var dataArray: UInt16 = 0 - let buffer = UnsafeMutableBufferPointer(start: &dataArray, count: range.count) - _ = self.copyBytes(to: buffer, from: range) - - return dataArray - } - - @nonobjc subscript(range: Range) -> UInt32 { - var dataArray: UInt32 = 0 - let buffer = UnsafeMutableBufferPointer(start: &dataArray, count: range.count) - _ = self.copyBytes(to: buffer, from: range) - - return dataArray - } -/* - subscript(range: Range) -> [Int8] { - var dataArray = [Int8](repeating: 0, count: range.count) - let buffer = UnsafeMutableBufferPointer(start: &dataArray, count: range.count) - _ = self.copyBytes(to: buffer, from: range) - - return dataArray - } -*/ - subscript(range: Range) -> [UInt8] { - var dataArray = [UInt8](repeating: 0, count: range.count) - self.copyBytes(to: &dataArray, from: range) - - return dataArray - } -/* - subscript(range: Range) -> [UInt16] { - var dataArray = [UInt16](repeating: 0, count: range.count / 2) - let buffer = UnsafeMutableBufferPointer(start: &dataArray, count: range.count) - _ = self.copyBytes(to: buffer, from: range) - - return dataArray - } - - subscript(range: Range) -> [UInt32] { - var dataArray = [UInt32](repeating: 0, count: range.count / 4) - let buffer = UnsafeMutableBufferPointer(start: &dataArray, count: range.count) - _ = self.copyBytes(to: buffer, from: range) - - return dataArray - } - */ -} diff --git a/xDripG5/OSLog.swift b/xDripG5/OSLog.swift new file mode 100644 index 00000000..b0902d98 --- /dev/null +++ b/xDripG5/OSLog.swift @@ -0,0 +1,27 @@ +// +// OSLog.swift +// Loop +// +// Copyright © 2017 LoopKit Authors. All rights reserved. +// + +import os.log + + +extension OSLog { + func debug(_ message: StaticString, _ args: CVarArg...) { + log(message, type: .debug, args) + } + + func info(_ message: StaticString, _ args: CVarArg...) { + log(message, type: .info, args) + } + + func error(_ message: StaticString, _ args: CVarArg...) { + log(message, type: .error, args) + } + + private func log(_ message: StaticString, type: OSLogType, _ args: CVarArg...) { + os_log(message, log: self, type: type, args) + } +} diff --git a/xDripG5/Transmitter.swift b/xDripG5/Transmitter.swift index 692ca235..1fe3f65a 100644 --- a/xDripG5/Transmitter.swift +++ b/xDripG5/Transmitter.swift @@ -8,6 +8,7 @@ import Foundation import CoreBluetooth +import os.log public protocol TransmitterDelegate: class { @@ -39,6 +40,8 @@ public final class Transmitter: BluetoothManagerDelegate { public weak var delegate: TransmitterDelegate? + private let log: OSLog + private let bluetoothManager = BluetoothManager() private var operationQueue = DispatchQueue(label: "com.loudnate.xDripG5.transmitterOperationQueue") @@ -47,6 +50,8 @@ public final class Transmitter: BluetoothManagerDelegate { self.ID = ID self.passiveModeEnabled = passiveModeEnabled + log = OSLog(subsystem: Bundle(for: Transmitter.self).bundleIdentifier!, category: "Transmitter") + bluetoothManager.delegate = self } @@ -116,7 +121,11 @@ public final class Transmitter: BluetoothManagerDelegate { - returns: A new string, containing the last two characters of the input string */ private func lastTwoCharactersOfString(_ string: String) -> String { - return string.substring(from: string.characters.index(string.endIndex, offsetBy: -2, limitedBy: string.startIndex)!) + #if swift(>=4) + return String(string[string.index(string.endIndex, offsetBy: -2)...]) + #else + return string.substring(from: string.characters.index(string.endIndex, offsetBy: -2, limitedBy: string.startIndex)!) + #endif } func bluetoothManager(_ manager: BluetoothManager, shouldConnectPeripheral peripheral: CBPeripheral) -> Bool { @@ -283,6 +292,7 @@ public final class Transmitter: BluetoothManagerDelegate { do { try bluetoothManager.setNotifyEnabledAndWait(true, forCharacteristicUUID: .Control) } catch let error { + log.error("Error enabling notification: %{public}@", String(describing: error)) throw TransmitterError.controlError("Error enabling notification: \(error)") } } diff --git a/xDripG5Tests/Info.plist b/xDripG5Tests/Info.plist index ab895996..2e27dfa4 100644 --- a/xDripG5Tests/Info.plist +++ b/xDripG5Tests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 0.8.0 + 0.8.1 CFBundleSignature ???? CFBundleVersion diff --git a/xDripG5Tests/NSData.swift b/xDripG5Tests/NSData.swift index 2665127e..c05da11d 100644 --- a/xDripG5Tests/NSData.swift +++ b/xDripG5Tests/NSData.swift @@ -9,47 +9,42 @@ import Foundation +// String conversion methods, adapted from https://stackoverflow.com/questions/40276322/hex-binary-string-conversion-in-swift/40278391#40278391 extension Data { - public init?(hexadecimalString: String) { - guard let chars = hexadecimalString.cString(using: String.Encoding.utf8) else { - return nil + init?(hexadecimalString: String) { + self.init(capacity: hexadecimalString.utf16.count / 2) + + // Convert 0 ... 9, a ... f, A ...F to their decimal value, + // return nil for all other input characters + func decodeNibble(u: UInt16) -> UInt8? { + switch u { + case 0x30 ... 0x39: // '0'-'9' + return UInt8(u - 0x30) + case 0x41 ... 0x46: // 'A'-'F' + return UInt8(u - 0x41 + 10) // 10 since 'A' is 10, not 0 + case 0x61 ... 0x66: // 'a'-'f' + return UInt8(u - 0x61 + 10) // 10 since 'a' is 10, not 0 + default: + return nil + } } - self.init(capacity: chars.count / 2) - - for i in 0..