Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
1262377bcb
commit
54d4561158
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
EA837E672F107D6800077F87 /* Bedrock in Frameworks */ = {isa = PBXBuildFile; productRef = EA837E662F107D6800077F87 /* Bedrock */; };
|
EA837E672F107D6800077F87 /* Bedrock in Frameworks */ = {isa = PBXBuildFile; productRef = EA837E662F107D6800077F87 /* Bedrock */; };
|
||||||
|
EAAE892A2F12DE110075BC8A /* BusinessCardWatch Watch App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = EA837F982F11B16400077F87 /* BusinessCardWatch Watch App.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@ -25,8 +26,29 @@
|
|||||||
remoteGlobalIDString = EA8379222F105F2600077F87;
|
remoteGlobalIDString = EA8379222F105F2600077F87;
|
||||||
remoteInfo = BusinessCard;
|
remoteInfo = BusinessCard;
|
||||||
};
|
};
|
||||||
|
EAAE892B2F12DE110075BC8A /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = EA83791B2F105F2600077F87 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = EA837F972F11B16400077F87;
|
||||||
|
remoteInfo = "BusinessCardWatch Watch App";
|
||||||
|
};
|
||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
EAAE892D2F12DE110075BC8A /* Embed Watch Content */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "$(CONTENTS_FOLDER_PATH)/Watch";
|
||||||
|
dstSubfolderSpec = 16;
|
||||||
|
files = (
|
||||||
|
EAAE892A2F12DE110075BC8A /* BusinessCardWatch Watch App.app in Embed Watch Content */,
|
||||||
|
);
|
||||||
|
name = "Embed Watch Content";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
EA8379232F105F2600077F87 /* BusinessCard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BusinessCard.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
EA8379232F105F2600077F87 /* BusinessCard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BusinessCard.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
EA8379302F105F2800077F87 /* BusinessCardTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BusinessCardTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
EA8379302F105F2800077F87 /* BusinessCardTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BusinessCardTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@ -110,6 +132,7 @@
|
|||||||
EA8379332F105F2800077F87 /* BusinessCardTests */,
|
EA8379332F105F2800077F87 /* BusinessCardTests */,
|
||||||
EA83793D2F105F2800077F87 /* BusinessCardUITests */,
|
EA83793D2F105F2800077F87 /* BusinessCardUITests */,
|
||||||
EA837F992F11B16400077F87 /* BusinessCardWatch Watch App */,
|
EA837F992F11B16400077F87 /* BusinessCardWatch Watch App */,
|
||||||
|
EAAE89292F12DE110075BC8A /* Frameworks */,
|
||||||
EA8379242F105F2600077F87 /* Products */,
|
EA8379242F105F2600077F87 /* Products */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -125,6 +148,13 @@
|
|||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
EAAE89292F12DE110075BC8A /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
@ -135,10 +165,12 @@
|
|||||||
EA83791F2F105F2600077F87 /* Sources */,
|
EA83791F2F105F2600077F87 /* Sources */,
|
||||||
EA8379202F105F2600077F87 /* Frameworks */,
|
EA8379202F105F2600077F87 /* Frameworks */,
|
||||||
EA8379212F105F2600077F87 /* Resources */,
|
EA8379212F105F2600077F87 /* Resources */,
|
||||||
|
EAAE892D2F12DE110075BC8A /* Embed Watch Content */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
|
EAAE892C2F12DE110075BC8A /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
fileSystemSynchronizedGroups = (
|
fileSystemSynchronizedGroups = (
|
||||||
EA8379252F105F2600077F87 /* BusinessCard */,
|
EA8379252F105F2600077F87 /* BusinessCard */,
|
||||||
@ -345,6 +377,11 @@
|
|||||||
target = EA8379222F105F2600077F87 /* BusinessCard */;
|
target = EA8379222F105F2600077F87 /* BusinessCard */;
|
||||||
targetProxy = EA83793B2F105F2800077F87 /* PBXContainerItemProxy */;
|
targetProxy = EA83793B2F105F2800077F87 /* PBXContainerItemProxy */;
|
||||||
};
|
};
|
||||||
|
EAAE892C2F12DE110075BC8A /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = EA837F972F11B16400077F87 /* BusinessCardWatch Watch App */;
|
||||||
|
targetProxy = EAAE892B2F12DE110075BC8A /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
/* End PBXTargetDependency section */
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
|
|||||||
@ -59,7 +59,9 @@ final class WatchConnectivityService: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func sendCardsToWatch(_ cards: [BusinessCard]) {
|
private func sendCardsToWatch(_ cards: [BusinessCard]) {
|
||||||
Design.debugLog("WatchConnectivity: Syncing \(cards.count) cards to Watch")
|
guard let session = session else { return }
|
||||||
|
|
||||||
|
Design.debugLog("WatchConnectivity: Syncing \(cards.count) cards to Watch (reachable: \(session.isReachable))")
|
||||||
|
|
||||||
let syncableCards = cards.map { card in
|
let syncableCards = cards.map { card in
|
||||||
createSyncableCard(from: card)
|
createSyncableCard(from: card)
|
||||||
@ -67,12 +69,26 @@ final class WatchConnectivityService: NSObject {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
let encoded = try JSONEncoder().encode(syncableCards)
|
let encoded = try JSONEncoder().encode(syncableCards)
|
||||||
let userInfo: [String: Any] = ["cards": encoded]
|
let message: [String: Any] = ["cards": encoded]
|
||||||
|
|
||||||
// Use transferUserInfo instead of updateApplicationContext
|
// ALWAYS update application context - this persists and will be available
|
||||||
// This queues the transfer and works even in debug mode
|
// when watch app launches, even if not currently reachable
|
||||||
session?.transferUserInfo(userInfo)
|
do {
|
||||||
Design.debugLog("WatchConnectivity: Queued \(encoded.count) bytes via transferUserInfo")
|
try session.updateApplicationContext(message)
|
||||||
|
Design.debugLog("WatchConnectivity: Updated application context with \(encoded.count) bytes")
|
||||||
|
} catch {
|
||||||
|
Design.debugLog("WatchConnectivity: updateApplicationContext failed: \(error)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also try immediate delivery if reachable
|
||||||
|
if session.isReachable {
|
||||||
|
session.sendMessage(message, replyHandler: nil) { error in
|
||||||
|
Task { @MainActor in
|
||||||
|
Design.debugLog("WatchConnectivity: sendMessage failed: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Design.debugLog("WatchConnectivity: Also sent via sendMessage (reachable)")
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
Design.debugLog("WatchConnectivity: ERROR - Failed to encode: \(error)")
|
Design.debugLog("WatchConnectivity: ERROR - Failed to encode: \(error)")
|
||||||
}
|
}
|
||||||
@ -81,6 +97,8 @@ final class WatchConnectivityService: NSObject {
|
|||||||
fileprivate func handleActivation() {
|
fileprivate func handleActivation() {
|
||||||
isActivated = true
|
isActivated = true
|
||||||
|
|
||||||
|
Design.debugLog("WatchConnectivity: Activation complete - paired: \(session?.isPaired ?? false), installed: \(session?.isWatchAppInstalled ?? false), reachable: \(session?.isReachable ?? false)")
|
||||||
|
|
||||||
// Send any pending cards that were queued before activation
|
// Send any pending cards that were queued before activation
|
||||||
if let cards = pendingCards {
|
if let cards = pendingCards {
|
||||||
Design.debugLog("WatchConnectivity: Sending \(cards.count) queued cards after activation")
|
Design.debugLog("WatchConnectivity: Sending \(cards.count) queued cards after activation")
|
||||||
@ -89,6 +107,17 @@ final class WatchConnectivityService: NSObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Force a retry - call this after watch app is confirmed running
|
||||||
|
func retrySyncIfNeeded() {
|
||||||
|
guard let session = session else { return }
|
||||||
|
Design.debugLog("WatchConnectivity: Manual retry - paired: \(session.isPaired), installed: \(session.isWatchAppInstalled), reachable: \(session.isReachable)")
|
||||||
|
|
||||||
|
if let cards = pendingCards {
|
||||||
|
pendingCards = nil
|
||||||
|
syncCards(cards)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func createSyncableCard(from card: BusinessCard) -> SyncableCard {
|
private func createSyncableCard(from card: BusinessCard) -> SyncableCard {
|
||||||
// Get first email, phone, website, location from contact fields
|
// Get first email, phone, website, location from contact fields
|
||||||
let email = card.firstContactField(ofType: "email")?.value ?? ""
|
let email = card.firstContactField(ofType: "email")?.value ?? ""
|
||||||
|
|||||||
@ -96,6 +96,13 @@ extension WatchConnectivityService: WCSessionDelegate {
|
|||||||
processReceivedContext(userInfo)
|
processReceivedContext(userInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nonisolated func session(_ session: WCSession, didReceiveMessage message: [String: Any]) {
|
||||||
|
Task { @MainActor in
|
||||||
|
WatchDesign.debugLog("WatchConnectivity: Received message")
|
||||||
|
processReceivedContext(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Syncable card structure matching iOS side (for decoding)
|
/// Syncable card structure matching iOS side (for decoding)
|
||||||
|
|||||||
@ -16,8 +16,41 @@ final class WatchCardStore {
|
|||||||
init() {
|
init() {
|
||||||
loadDefaultID()
|
loadDefaultID()
|
||||||
WatchDesign.debugLog("WatchCardStore: Initialized, waiting for cards from iPhone")
|
WatchDesign.debugLog("WatchCardStore: Initialized, waiting for cards from iPhone")
|
||||||
|
|
||||||
|
// Set up callback for when cards are received from iPhone
|
||||||
|
WatchConnectivityService.shared.onCardsReceived = { [weak self] cards in
|
||||||
|
self?.updateCards(cards)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if targetEnvironment(simulator)
|
||||||
|
// Load sample data on simulator since WatchConnectivity often doesn't work
|
||||||
|
if cards.isEmpty {
|
||||||
|
loadSimulatorSampleData()
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if targetEnvironment(simulator)
|
||||||
|
private func loadSimulatorSampleData() {
|
||||||
|
WatchDesign.debugLog("WatchCardStore: Loading simulator sample data")
|
||||||
|
cards = [
|
||||||
|
WatchCard(
|
||||||
|
id: UUID(),
|
||||||
|
displayName: "Test User",
|
||||||
|
role: "iOS Developer",
|
||||||
|
company: "Sample Corp",
|
||||||
|
email: "test@example.com",
|
||||||
|
phone: "+1 555-0123",
|
||||||
|
website: "example.com",
|
||||||
|
location: "San Francisco, CA",
|
||||||
|
isDefault: true,
|
||||||
|
qrCodeImageData: nil
|
||||||
|
)
|
||||||
|
]
|
||||||
|
defaultCardID = cards.first?.id
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
var defaultCard: WatchCard? {
|
var defaultCard: WatchCard? {
|
||||||
guard let defaultCardID else { return cards.first(where: { $0.isDefault }) ?? cards.first }
|
guard let defaultCardID else { return cards.first(where: { $0.isDefault }) ?? cards.first }
|
||||||
return cards.first(where: { $0.id == defaultCardID }) ?? cards.first(where: { $0.isDefault }) ?? cards.first
|
return cards.first(where: { $0.id == defaultCardID }) ?? cards.first(where: { $0.isDefault }) ?? cards.first
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user