Skip to content
Open
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
36 changes: 18 additions & 18 deletions ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -791,9 +791,9 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 52;
CURRENT_PROJECT_VERSION = 54;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 0.9.21;
MARKETING_VERSION = 0.9.23;
PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).RunnerTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
Expand All @@ -809,9 +809,9 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 52;
CURRENT_PROJECT_VERSION = 54;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 0.9.21;
MARKETING_VERSION = 0.9.23;
PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).RunnerTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
Expand All @@ -825,9 +825,9 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 52;
CURRENT_PROJECT_VERSION = 54;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 0.9.21;
MARKETING_VERSION = 0.9.23;
PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).RunnerTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
Expand All @@ -849,7 +849,7 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = OpenStrapWidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 52;
CURRENT_PROJECT_VERSION = 54;
DEVELOPMENT_TEAM = "$(APPLE_DEVELOPMENT_TEAM)";
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
Expand All @@ -865,7 +865,7 @@
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 0.9.21;
MARKETING_VERSION = 0.9.23;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "$(APP_WIDGET_BUNDLE_IDENTIFIER)";
Expand Down Expand Up @@ -896,7 +896,7 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = OpenStrapWidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 52;
CURRENT_PROJECT_VERSION = 54;
DEVELOPMENT_TEAM = "$(APPLE_DEVELOPMENT_TEAM)";
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
Expand All @@ -912,7 +912,7 @@
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 0.9.21;
MARKETING_VERSION = 0.9.23;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "$(APP_WIDGET_BUNDLE_IDENTIFIER)";
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down Expand Up @@ -940,7 +940,7 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = OpenStrapWidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 52;
CURRENT_PROJECT_VERSION = 54;
DEVELOPMENT_TEAM = "$(APPLE_DEVELOPMENT_TEAM)";
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
Expand All @@ -956,7 +956,7 @@
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 0.9.21;
MARKETING_VERSION = 0.9.23;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "$(APP_WIDGET_BUNDLE_IDENTIFIER)";
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down Expand Up @@ -987,7 +987,7 @@
CODE_SIGN_ENTITLEMENTS = "OpenStrapWatch Watch App/OpenStrapWatch Watch App.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 52;
CURRENT_PROJECT_VERSION = 54;
DEVELOPMENT_TEAM = "$(APPLE_DEVELOPMENT_TEAM)";
ENABLE_PREVIEWS = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
Expand All @@ -1003,7 +1003,7 @@
"@executable_path/Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 0.9.21;
MARKETING_VERSION = 0.9.23;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).watchkitapp";
Expand Down Expand Up @@ -1042,7 +1042,7 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = "OpenStrapWatch Watch App/OpenStrapWatch Watch App.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 52;
CURRENT_PROJECT_VERSION = 54;
DEVELOPMENT_TEAM = "$(APPLE_DEVELOPMENT_TEAM)";
ENABLE_PREVIEWS = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
Expand All @@ -1058,7 +1058,7 @@
"@executable_path/Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 0.9.21;
MARKETING_VERSION = 0.9.23;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).watchkitapp";
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down Expand Up @@ -1093,7 +1093,7 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = "OpenStrapWatch Watch App/OpenStrapWatch Watch App.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 52;
CURRENT_PROJECT_VERSION = 54;
DEVELOPMENT_TEAM = "$(APPLE_DEVELOPMENT_TEAM)";
ENABLE_PREVIEWS = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
Expand All @@ -1109,7 +1109,7 @@
"@executable_path/Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 0.9.21;
MARKETING_VERSION = 0.9.23;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).watchkitapp";
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down
51 changes: 30 additions & 21 deletions ios/Runner/AccessorySetup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,13 @@ import AccessorySetupKit
/// - `removeAll` -> nil (deprovision all — used on unpair)
enum AccessorySetup {
private static let channelName = "openstrap/accessory_setup"
// The WHOOP "Harvard" Gen4 GATT service (matches GattUuids.service in Dart).
// `fileprivate` so the iOS-18 Impl below can read it.
fileprivate static let whoopServiceUUID = "61080001-8d6d-82b8-614a-1c8cb0f8dcc6"
// WHOOP GATT service UUIDs, one per generation (match GattProfile in Dart).
// `fileprivate` so the iOS-18 Impl below can read them. BOTH must also be
// listed in Info.plist under NSAccessorySetupBluetoothServices.
// • gen4 ("Harvard", WHOOP 4) — 6108…
// • gen5 ("fd4b", WHOOP 5) — fd4b… (EXPERIMENTAL)
fileprivate static let whoopServiceUUIDGen4 = "61080001-8d6d-82b8-614a-1c8cb0f8dcc6"
fileprivate static let whoopServiceUUIDGen5 = "fd4b0001-cce1-4033-93ce-002d5875f58a"

static func register(messenger: FlutterBinaryMessenger) {
let channel = FlutterMethodChannel(name: channelName, binaryMessenger: messenger)
Expand Down Expand Up @@ -136,30 +140,35 @@ private final class Impl {
return
}

let descriptor = ASDiscoveryDescriptor()
// Match on the WHOOP custom service UUID alone. The foreground scan finds the
// band via startScan(withServices:[thisUUID]) and succeeds, which proves the
// band advertises this service — so it's a reliable, sufficient filter. Every
// descriptor criterion must be declared in Info.plist; the UUID is listed under
// NSAccessorySetupBluetoothServices. (No bluetoothNameSubstring: a single
// descriptor AND-combines its criteria, and a name filter would also require an
// NSAccessorySetupBluetoothNames entry and risk excluding the band on a name
// mismatch.)
descriptor.bluetoothServiceUUID = CBUUID(string: AccessorySetup.whoopServiceUUID)

// Show the actual strap render in the ASK pairing sheet (asset catalog →
// StrapProduct.imageset). Fall back to an SF Symbol if the asset is missing.
// band via startScan(withServices:[…]) and succeeds, which proves the band
// advertises this service — so it's a reliable, sufficient filter. Every
// descriptor criterion must be declared in Info.plist; the UUIDs are listed
// under NSAccessorySetupBluetoothServices. (No bluetoothNameSubstring: a
// single descriptor AND-combines its criteria, and a name filter would also
// require an NSAccessorySetupBluetoothNames entry and risk excluding the band
// on a name mismatch.)
//
// ASK matches ANY item in the picker list, so we offer one item per WHOOP
// generation: gen4 (WHOOP 4) and gen5 (WHOOP 5, experimental). A band that
// advertises either service can be provisioned; the provisioned identifier is
// the same CoreBluetooth UUID regardless of generation.
let productImage = UIImage(named: "StrapProduct")
?? UIImage(systemName: "sensor.tag.radiowave.forward")
?? UIImage()
let item = ASPickerDisplayItem(
name: "WHOOP band",
productImage: productImage,
descriptor: descriptor
)
func item(_ serviceUUID: String, _ name: String) -> ASPickerDisplayItem {
let descriptor = ASDiscoveryDescriptor()
descriptor.bluetoothServiceUUID = CBUUID(string: serviceUUID)
return ASPickerDisplayItem(
name: name, productImage: productImage, descriptor: descriptor)
}
let items = [
item(AccessorySetup.whoopServiceUUIDGen4, "WHOOP band"),
item(AccessorySetup.whoopServiceUUIDGen5, "WHOOP 5 band"),
]

pickerResult = completion
session.showPicker(for: [item]) { [weak self] error in
session.showPicker(for: items) { [weak self] error in
guard let self = self else { return }
if let error = error {
if let cb = self.pickerResult {
Expand Down
1 change: 1 addition & 0 deletions ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
<key>NSAccessorySetupBluetoothServices</key>
<array>
<string>61080001-8D6D-82B8-614A-1C8CB0F8DCC6</string>
<string>FD4B0001-CCE1-4033-93CE-002D5875F58A</string>
</array>
<key>NSAccessorySetupKitSupports</key>
<array>
Expand Down
Loading
Loading