Skip to content

Commit

Permalink
Update to Xcode 9 recommended and migration to Swift 4 (#96)
Browse files Browse the repository at this point in the history
* Migrate to Swift 4

* Update project settings to Xcode 9 recommended

* Version Bump to 0.3.0

* Use Swift 4 Codable instead of NSCoding for ListenCache

- Adopt Codable and remove extraneous code
- Add new errors to handle encoding/decoding failures

* Error domain should be static

* Update documentation

* Update Swift version to 4.0
  • Loading branch information
larryonoff authored and Jeremy Chiang committed Oct 11, 2017
1 parent 860a7e3 commit 4a5898f
Show file tree
Hide file tree
Showing 90 changed files with 3,843 additions and 1,065 deletions.
2 changes: 1 addition & 1 deletion .swift-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.2
4.0
4 changes: 2 additions & 2 deletions Bluejay.podspec
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = 'Bluejay'
spec.version = '0.2.0'
spec.version = '0.3.0'
spec.license = { type: 'MIT', file: 'LICENSE' }
spec.homepage = 'https://github.com/steamclock/bluejay'
spec.authors = { 'Jeremy Chiang' => 'jeremy@steamclock.com' }
spec.summary = 'Bluejay is a simple Swift framework for building reliable Bluetooth apps.'
spec.homepage = 'https://github.com/steamclock/bluejay'
spec.source = { git: 'https://github.com/steamclock/bluejay.git', tag: 'v0.2.0' }
spec.source = { git: 'https://github.com/steamclock/bluejay.git', tag: 'v0.3.0' }
spec.source_files = 'Bluejay/Bluejay/*.{h,swift}'
spec.framework = 'SystemConfiguration'
spec.platform = :ios, '9.3'
Expand Down
29 changes: 21 additions & 8 deletions Bluejay/Bluejay.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -498,17 +498,18 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0820;
LastUpgradeCheck = 0820;
LastUpgradeCheck = 0900;
ORGANIZATIONNAME = "Steamclock Software";
TargetAttributes = {
B8C70F741E1C22850006CF58 = {
CreatedOnToolsVersion = 8.2.1;
DevelopmentTeam = GH868RP95T;
LastSwiftMigration = 0820;
LastSwiftMigration = 0900;
ProvisioningStyle = Automatic;
};
B8C70F7D1E1C22850006CF58 = {
CreatedOnToolsVersion = 8.2.1;
LastSwiftMigration = 0900;
ProvisioningStyle = Automatic;
};
B8DEDE601E4A4B5800B2244D = {
Expand Down Expand Up @@ -806,15 +807,21 @@
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_DOCUMENTATION_COMMENTS = YES;
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;
Expand Down Expand Up @@ -859,15 +866,21 @@
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_DOCUMENTATION_COMMENTS = YES;
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;
Expand Down Expand Up @@ -916,7 +929,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 4.0;
};
name = Debug;
};
Expand All @@ -939,7 +952,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.steamclock.Bluejay;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 4.0;
};
name = Release;
};
Expand All @@ -952,7 +965,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.steamclock.BluejayTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 4.0;
};
name = Debug;
};
Expand All @@ -965,7 +978,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.steamclock.BluejayTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 4.0;
};
name = Release;
};
Expand All @@ -981,7 +994,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.steamclock.BluejayDemo;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 4.0;
};
name = Debug;
};
Expand All @@ -997,7 +1010,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.steamclock.BluejayDemo;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 4.0;
};
name = Release;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0820"
LastUpgradeVersion = "0900"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand All @@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
Expand Down Expand Up @@ -55,6 +56,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
Expand Down
54 changes: 33 additions & 21 deletions Bluejay/Bluejay/Bluejay.swift
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ public class Bluejay: NSObject {

DispatchQueue.main.async {
self?.isRunningBackgroundTask = false
completionOnMainThread(.success())
completionOnMainThread(.success(()))
}
}
catch let error as NSError {
Expand Down Expand Up @@ -710,7 +710,12 @@ extension Bluejay: CBCentralManagerDelegate {
}

if central.state == .poweredOn && connectedPeripheral != nil {
attemptListenRestoration()
do {
try requestListenRestoration()
}
catch {
log("Failed to complete listen restoration with error: \(error)")
}
}

if central.state == .poweredOff {
Expand All @@ -730,38 +735,45 @@ extension Bluejay: CBCentralManagerDelegate {
/**
Examine the listen cache in `UserDefaults` to determine whether there are any listens that might need restoration.
*/
private func attemptListenRestoration() {
debugPrint("Starting listen restoration.")
private func requestListenRestoration() throws {
log("Starting listen restoration.")

guard
let listenCaches = UserDefaults.standard.dictionary(forKey: Constant.listenCaches),
let cacheData = listenCaches[uuid.uuidString] as? [Data]
else {
debugPrint("No listens to restore.")
log("No listens to restore.")
return
}

let decoder = JSONDecoder()

for data in cacheData {
let listenCache = (NSKeyedUnarchiver.unarchiveObject(with: data) as? (ListenCache.Coding))!
.decoded as! ListenCache

debugPrint("Listen cache to restore: \(listenCache)")

let serviceIdentifier = ServiceIdentifier(uuid: listenCache.serviceUUID)
let characteristicIdentifier = CharacteristicIdentifier(uuid: listenCache.characteristicUUID, service: serviceIdentifier)

if let listenRestorer = listenRestorer?.weakReference {
if !listenRestorer.willRestoreListen(on: characteristicIdentifier) {
do {
let listenCache = try decoder.decode(ListenCache.self, from: data)

log("Listen cache to restore: \(listenCache)")

let serviceIdentifier = ServiceIdentifier(uuid: listenCache.serviceUUID)
let characteristicIdentifier = CharacteristicIdentifier(uuid: listenCache.characteristicUUID, service: serviceIdentifier)

if let listenRestorer = listenRestorer?.weakReference {
// If true, assume the listen restorable delegate will restore the listen accordingly, otherwise end the listen.
if !listenRestorer.willRestoreListen(on: characteristicIdentifier) {
endListen(to: characteristicIdentifier)
}
}
else {
// If there is no listen restorable delegate, end the listen as well.
endListen(to: characteristicIdentifier)
}
}
else {
// If there is no listen restorable delegate, end all active listening.
endListen(to: characteristicIdentifier)
catch {
throw BluejayError.listenCacheDecoding(error)
}
}

debugPrint("Listen restoration finished.")
log("Listen restoration finished.")
}

/**
Expand Down Expand Up @@ -906,10 +918,10 @@ extension Bluejay: CBCentralManagerDelegate {

}

/// Allows communication between a queue and the Bluejay instance it belongs to.
/// Allows Bluejay to receive events and delegation from its queue.
extension Bluejay: QueueObserver {

/// A way for Bluejay to update the `isConnecting` state with a more accurate timing by waiting to be informed by the queue only when it is about to start running the enqueued `Connection` task.
/// Support for the will connect state that CBCentralManagerDelegate does not have.
func willConnect(to peripheral: CBPeripheral) {
connectingPeripheral = Peripheral(bluejay: self, cbPeripheral: peripheral)
}
Expand Down
33 changes: 32 additions & 1 deletion Bluejay/Bluejay/Error.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,50 @@

import Foundation

/// Errors specific to Bluejay.
public enum BluejayError {
/// Bluetooth is either turned off or unavailable.
case bluetoothUnavailable
/// Bluejay does not support another scanning request if Bluejay is still scanning.
case multipleScanNotSupported
/// Bluejay does not support another connection request if Bluejay is already connected or still connecting.
case multipleConnectNotSupported
/// Bluejay does not support another disconnection request if Bluejay is still disconnecting.
case multipleDisconnectNotSupported
/// A connection request in Bluejay has timed out.
case connectionTimedOut
/// A Bluetooth operation such as, reading or writing, is attempted when Bluejay is not connected to a peripheral.
case notConnected
/// A Bluetooth service is not found.
case missingService(ServiceIdentifier)
/// A Bluetooth characteristic is not found.
case missingCharacteristic(CharacteristicIdentifier)
/// A Bluetooth operation is cancelled.
case cancelled
/// An attempt to listen on a characteristic has timed out.
case listenTimedOut
/// An attempt to read a characteristic has failed.
case readFailed
/// An attempt to write a characteristic has failed.
case writeFailed
/// An attempt to read a value from a characteristic has returned no data unexpectedly.
case missingData
/// An attempt to read a range of data has failed due to incorrect bounds or an unexpected length.
case dataOutOfBounds(start: Int, length: Int, count: Int)
/// An unexpected peripheral is cached and retrieved from CoreBluetooth.
case unexpectedPeripheral(PeripheralIdentifier)
/// iOS will not continue scanning in the background if allow duplicates is turned on.
case scanningWithAllowDuplicatesInBackgroundNotSupported
/// iOS will not continue scanning in the background if no service identifiers are specified.
case missingServiceIdentifiersInBackground
/// Bluejay does not support further Bluetooth operations while a Bluejay background task is still running.
case backgroundTaskRunning
/// Bluejay does not support another Bluejay background task when there is already one that is still running.
case multipleBackgroundTaskNotSupported
/// Bluejay has failed to encode a listen cache.
case listenCacheEncoding(Error)
/// Bluejay has failed to decode a listen cache.
case listenCacheDecoding(Error)
}

extension BluejayError: LocalizedError {
Expand Down Expand Up @@ -71,12 +95,17 @@ extension BluejayError: LocalizedError {
return "Regular Bluetooth operation is not available when a background task is running. For reading, writing, and listening, please use only the API found in the Synchronized Peripheral provided to you when working inside a background task block."
case .multipleBackgroundTaskNotSupported:
return "Multiple background task is not supported."
case let .listenCacheEncoding(error):
return "Listen cache encoding failed with error: \(error.localizedDescription)"
case let .listenCacheDecoding(error):
return "Listen cache decoding failed with error: \(error.localizedDescription)"
}
}
}

extension BluejayError: CustomNSError {
public var errorDomain: String {

public static var errorDomain: String {
return "Bluejay"
}

Expand All @@ -101,6 +130,8 @@ extension BluejayError: CustomNSError {
case .missingServiceIdentifiersInBackground: return 17
case .backgroundTaskRunning: return 18
case .multipleBackgroundTaskNotSupported: return 19
case .listenCacheEncoding: return 20
case .listenCacheDecoding: return 21
}
}

Expand Down
2 changes: 1 addition & 1 deletion Bluejay/Bluejay/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.2.0</string>
<string>0.3.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
Expand Down
2 changes: 1 addition & 1 deletion Bluejay/Bluejay/Integer+Transferable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import Foundation

/// Extension to Int to make it Sendable and Receivable.
extension Integer {
extension BinaryInteger {

/// This function is required to conform to `Sendable`, and figures out the size of the `Integer` used by the iOS device.
public func toBluetoothData() -> Data {
Expand Down
Loading

0 comments on commit 4a5898f

Please sign in to comment.