feat(intents): onscreen entity annotations + App Intents tests
All checks were successful
CI / build-and-deploy (push) Successful in 28s
All checks were successful
CI / build-and-deploy (push) Successful in 28s
- bookmarkOnscreen(_:) publishes the viewed bookmark as the on-screen entity (NSUserActivity.appEntityIdentifier) so Siri can resolve "this" / "summarize this one"; applied to the BrowserView sheets in BookmarksView and SearchView - Add MarksTests (XCTest) covering entity mapping, EntityIdentifier, IntentRouter, and the real perform() of SearchMarksIntent and OpenBookmarkIntent; wired into the Marks scheme test phase Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@
|
|||||||
706642006800745A0A0188D0 /* RandomBookmarkWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96F73C2A0B8AB36C6F80D5FE /* RandomBookmarkWidget.swift */; };
|
706642006800745A0A0188D0 /* RandomBookmarkWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96F73C2A0B8AB36C6F80D5FE /* RandomBookmarkWidget.swift */; };
|
||||||
706CCE40744D7F382AFFE429 /* BrowserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 629C41E0BC28EB6359D50CFD /* BrowserView.swift */; };
|
706CCE40744D7F382AFFE429 /* BrowserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 629C41E0BC28EB6359D50CFD /* BrowserView.swift */; };
|
||||||
76223AA04DF97B24C1490B20 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADEAC824576633CC77370262 /* ShareViewController.swift */; };
|
76223AA04DF97B24C1490B20 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADEAC824576633CC77370262 /* ShareViewController.swift */; };
|
||||||
|
778B82E075D4DAF5E8446D6F /* BookmarkOnscreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1EEDFAE9FCA24192640CA7A /* BookmarkOnscreen.swift */; };
|
||||||
8140EEEFBB7A3510A73D1A93 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 093402014727A338745A06DA /* SwiftUI.framework */; };
|
8140EEEFBB7A3510A73D1A93 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 093402014727A338745A06DA /* SwiftUI.framework */; };
|
||||||
849C9CB4E1E8A6ABA1BC7251 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C29CB878BC334639E6194E2 /* SettingsView.swift */; };
|
849C9CB4E1E8A6ABA1BC7251 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C29CB878BC334639E6194E2 /* SettingsView.swift */; };
|
||||||
852A3E18661C190622FF5A85 /* AppIntents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9803BA80BEAE9A3AE4AF5E7 /* AppIntents.framework */; };
|
852A3E18661C190622FF5A85 /* AppIntents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9803BA80BEAE9A3AE4AF5E7 /* AppIntents.framework */; };
|
||||||
@@ -47,6 +48,7 @@
|
|||||||
EE540B8367519EDE679C1B70 /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCC3BB2525F0F63445D419B9 /* SearchView.swift */; };
|
EE540B8367519EDE679C1B70 /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCC3BB2525F0F63445D419B9 /* SearchView.swift */; };
|
||||||
EFF8E4CD63CAE1342CE3A4F0 /* IntentSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6ACABF0CA940312B4195456 /* IntentSupport.swift */; };
|
EFF8E4CD63CAE1342CE3A4F0 /* IntentSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6ACABF0CA940312B4195456 /* IntentSupport.swift */; };
|
||||||
FBAE1329DD9C3152FBB53AD4 /* BookmarkEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D650D0784B847BFEDCB3141 /* BookmarkEntity.swift */; };
|
FBAE1329DD9C3152FBB53AD4 /* BookmarkEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D650D0784B847BFEDCB3141 /* BookmarkEntity.swift */; };
|
||||||
|
FD656A44CEE8AE28B136AD85 /* AppIntentsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7623601C25E481DF58371F2A /* AppIntentsTests.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -64,6 +66,13 @@
|
|||||||
remoteGlobalIDString = CF31B2655F8F30CF6DF3C26F;
|
remoteGlobalIDString = CF31B2655F8F30CF6DF3C26F;
|
||||||
remoteInfo = ShareExtension;
|
remoteInfo = ShareExtension;
|
||||||
};
|
};
|
||||||
|
F63E42D8413C0F6ED8BB78CF /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 0CC2596AE7F05EAF051B2FCD /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 76C272776D8EBCCD3FB6715A;
|
||||||
|
remoteInfo = Marks;
|
||||||
|
};
|
||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
@@ -101,6 +110,7 @@
|
|||||||
64E9DEC5CD89FF346E23A14F /* MarksAppIntents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarksAppIntents.swift; sourceTree = "<group>"; };
|
64E9DEC5CD89FF346E23A14F /* MarksAppIntents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarksAppIntents.swift; sourceTree = "<group>"; };
|
||||||
69D868AF1DAF3F1BBEACBFF6 /* ClaudeService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClaudeService.swift; sourceTree = "<group>"; };
|
69D868AF1DAF3F1BBEACBFF6 /* ClaudeService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClaudeService.swift; sourceTree = "<group>"; };
|
||||||
759BA3FCF8BEE1D4EA1CDC17 /* String+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Helpers.swift"; sourceTree = "<group>"; };
|
759BA3FCF8BEE1D4EA1CDC17 /* String+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Helpers.swift"; sourceTree = "<group>"; };
|
||||||
|
7623601C25E481DF58371F2A /* AppIntentsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIntentsTests.swift; sourceTree = "<group>"; };
|
||||||
86B2DE40098D2B39FFE1AC36 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
86B2DE40098D2B39FFE1AC36 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||||
8CA428181B35885F7D9F4D55 /* Bookmark.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bookmark.swift; sourceTree = "<group>"; };
|
8CA428181B35885F7D9F4D55 /* Bookmark.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bookmark.swift; sourceTree = "<group>"; };
|
||||||
8D650D0784B847BFEDCB3141 /* BookmarkEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkEntity.swift; sourceTree = "<group>"; };
|
8D650D0784B847BFEDCB3141 /* BookmarkEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkEntity.swift; sourceTree = "<group>"; };
|
||||||
@@ -115,9 +125,11 @@
|
|||||||
B0C6ABE160A2C90EB965D811 /* BookmarkRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkRow.swift; sourceTree = "<group>"; };
|
B0C6ABE160A2C90EB965D811 /* BookmarkRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkRow.swift; sourceTree = "<group>"; };
|
||||||
B13B9F2D890C7953531AC0D2 /* TagsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagsView.swift; sourceTree = "<group>"; };
|
B13B9F2D890C7953531AC0D2 /* TagsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagsView.swift; sourceTree = "<group>"; };
|
||||||
BCC3BB2525F0F63445D419B9 /* SearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchView.swift; sourceTree = "<group>"; };
|
BCC3BB2525F0F63445D419B9 /* SearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchView.swift; sourceTree = "<group>"; };
|
||||||
|
C8B10AC011AF8F242E525440 /* MarksTests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = MarksTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
CBE3C5E420F078D499B2D926 /* BookmarksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksView.swift; sourceTree = "<group>"; };
|
CBE3C5E420F078D499B2D926 /* BookmarksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksView.swift; sourceTree = "<group>"; };
|
||||||
CBFB5EFC9764B22A2622EA4A /* BookmarksViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksViewModel.swift; sourceTree = "<group>"; };
|
CBFB5EFC9764B22A2622EA4A /* BookmarksViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksViewModel.swift; sourceTree = "<group>"; };
|
||||||
CC6B10FBB227F426A2B597C8 /* BookmarkListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkListRow.swift; sourceTree = "<group>"; };
|
CC6B10FBB227F426A2B597C8 /* BookmarkListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkListRow.swift; sourceTree = "<group>"; };
|
||||||
|
D1EEDFAE9FCA24192640CA7A /* BookmarkOnscreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkOnscreen.swift; sourceTree = "<group>"; };
|
||||||
D3A4B1E764CC88A774AF8EA5 /* EditBookmarkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditBookmarkView.swift; sourceTree = "<group>"; };
|
D3A4B1E764CC88A774AF8EA5 /* EditBookmarkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditBookmarkView.swift; sourceTree = "<group>"; };
|
||||||
D6ACABF0CA940312B4195456 /* IntentSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentSupport.swift; sourceTree = "<group>"; };
|
D6ACABF0CA940312B4195456 /* IntentSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentSupport.swift; sourceTree = "<group>"; };
|
||||||
D92575C7C710347F226EC74A /* MarksApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarksApp.swift; sourceTree = "<group>"; };
|
D92575C7C710347F226EC74A /* MarksApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarksApp.swift; sourceTree = "<group>"; };
|
||||||
@@ -176,10 +188,19 @@
|
|||||||
path = Models;
|
path = Models;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
3F5B1508DEFC894F33CBD16C /* MarksTests */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
7623601C25E481DF58371F2A /* AppIntentsTests.swift */,
|
||||||
|
);
|
||||||
|
path = MarksTests;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
58E8E316BE3F10C5149AADC3 /* Intents */ = {
|
58E8E316BE3F10C5149AADC3 /* Intents */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
8D650D0784B847BFEDCB3141 /* BookmarkEntity.swift */,
|
8D650D0784B847BFEDCB3141 /* BookmarkEntity.swift */,
|
||||||
|
D1EEDFAE9FCA24192640CA7A /* BookmarkOnscreen.swift */,
|
||||||
9D8E2E470C9336209B7E8543 /* IntentSnippetViews.swift */,
|
9D8E2E470C9336209B7E8543 /* IntentSnippetViews.swift */,
|
||||||
D6ACABF0CA940312B4195456 /* IntentSupport.swift */,
|
D6ACABF0CA940312B4195456 /* IntentSupport.swift */,
|
||||||
64E9DEC5CD89FF346E23A14F /* MarksAppIntents.swift */,
|
64E9DEC5CD89FF346E23A14F /* MarksAppIntents.swift */,
|
||||||
@@ -247,6 +268,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
3952A081972B444674BEB51B /* Marks */,
|
3952A081972B444674BEB51B /* Marks */,
|
||||||
|
3F5B1508DEFC894F33CBD16C /* MarksTests */,
|
||||||
85E717A515682EBF67DD199A /* MarksWidget */,
|
85E717A515682EBF67DD199A /* MarksWidget */,
|
||||||
7A85D09B4E109E1D9CD8F5BB /* ShareExtension */,
|
7A85D09B4E109E1D9CD8F5BB /* ShareExtension */,
|
||||||
F5B3C85424137FA0CE2467B1 /* Frameworks */,
|
F5B3C85424137FA0CE2467B1 /* Frameworks */,
|
||||||
@@ -258,6 +280,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
AB6C53AB14A38FCD4CC7628D /* Marks.app */,
|
AB6C53AB14A38FCD4CC7628D /* Marks.app */,
|
||||||
|
C8B10AC011AF8F242E525440 /* MarksTests.xctest */,
|
||||||
23F172EC9977CD5C51B228B9 /* MarksWidget.appex */,
|
23F172EC9977CD5C51B228B9 /* MarksWidget.appex */,
|
||||||
938672D3D6ADC73B354B5EA5 /* ShareExtension.appex */,
|
938672D3D6ADC73B354B5EA5 /* ShareExtension.appex */,
|
||||||
);
|
);
|
||||||
@@ -334,6 +357,24 @@
|
|||||||
productReference = 938672D3D6ADC73B354B5EA5 /* ShareExtension.appex */;
|
productReference = 938672D3D6ADC73B354B5EA5 /* ShareExtension.appex */;
|
||||||
productType = "com.apple.product-type.app-extension";
|
productType = "com.apple.product-type.app-extension";
|
||||||
};
|
};
|
||||||
|
F96181AD21F4A6755214E19B /* MarksTests */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = CE9D24DDB32E8B7EFDDD9D59 /* Build configuration list for PBXNativeTarget "MarksTests" */;
|
||||||
|
buildPhases = (
|
||||||
|
1035354FBD99C8ED4ACA93DC /* Sources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
A665E75E77C12B6A3519ABF9 /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
name = MarksTests;
|
||||||
|
packageProductDependencies = (
|
||||||
|
);
|
||||||
|
productName = MarksTests;
|
||||||
|
productReference = C8B10AC011AF8F242E525440 /* MarksTests.xctest */;
|
||||||
|
productType = "com.apple.product-type.bundle.unit-test";
|
||||||
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
@@ -355,6 +396,10 @@
|
|||||||
DevelopmentTeam = AE5DZKJHGN;
|
DevelopmentTeam = AE5DZKJHGN;
|
||||||
ProvisioningStyle = Automatic;
|
ProvisioningStyle = Automatic;
|
||||||
};
|
};
|
||||||
|
F96181AD21F4A6755214E19B = {
|
||||||
|
DevelopmentTeam = AE5DZKJHGN;
|
||||||
|
ProvisioningStyle = Automatic;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = 4BACCD33EEE63890F06D7057 /* Build configuration list for PBXProject "Marks" */;
|
buildConfigurationList = 4BACCD33EEE63890F06D7057 /* Build configuration list for PBXProject "Marks" */;
|
||||||
@@ -372,6 +417,7 @@
|
|||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
targets = (
|
targets = (
|
||||||
76C272776D8EBCCD3FB6715A /* Marks */,
|
76C272776D8EBCCD3FB6715A /* Marks */,
|
||||||
|
F96181AD21F4A6755214E19B /* MarksTests */,
|
||||||
61046BF39C48393DD96687A0 /* MarksWidget */,
|
61046BF39C48393DD96687A0 /* MarksWidget */,
|
||||||
CF31B2655F8F30CF6DF3C26F /* ShareExtension */,
|
CF31B2655F8F30CF6DF3C26F /* ShareExtension */,
|
||||||
);
|
);
|
||||||
@@ -390,6 +436,14 @@
|
|||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
1035354FBD99C8ED4ACA93DC /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
FD656A44CEE8AE28B136AD85 /* AppIntentsTests.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
2210C849B430CC6609106C62 /* Sources */ = {
|
2210C849B430CC6609106C62 /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@@ -419,6 +473,7 @@
|
|||||||
457FCE503CCA82C5F27C6C90 /* Bookmark.swift in Sources */,
|
457FCE503CCA82C5F27C6C90 /* Bookmark.swift in Sources */,
|
||||||
FBAE1329DD9C3152FBB53AD4 /* BookmarkEntity.swift in Sources */,
|
FBAE1329DD9C3152FBB53AD4 /* BookmarkEntity.swift in Sources */,
|
||||||
CD3013ED0FD018091D18F9FE /* BookmarkListRow.swift in Sources */,
|
CD3013ED0FD018091D18F9FE /* BookmarkListRow.swift in Sources */,
|
||||||
|
778B82E075D4DAF5E8446D6F /* BookmarkOnscreen.swift in Sources */,
|
||||||
96698499C0501D0A897D7E08 /* BookmarkRow.swift in Sources */,
|
96698499C0501D0A897D7E08 /* BookmarkRow.swift in Sources */,
|
||||||
A8B8D58C5B68F54DC20126C1 /* BookmarksView.swift in Sources */,
|
A8B8D58C5B68F54DC20126C1 /* BookmarksView.swift in Sources */,
|
||||||
5D86F3F0F603B248776916C7 /* BookmarksViewModel.swift in Sources */,
|
5D86F3F0F603B248776916C7 /* BookmarksViewModel.swift in Sources */,
|
||||||
@@ -452,6 +507,11 @@
|
|||||||
target = 61046BF39C48393DD96687A0 /* MarksWidget */;
|
target = 61046BF39C48393DD96687A0 /* MarksWidget */;
|
||||||
targetProxy = 22E9426D1CE4E3C473C5CD5D /* PBXContainerItemProxy */;
|
targetProxy = 22E9426D1CE4E3C473C5CD5D /* PBXContainerItemProxy */;
|
||||||
};
|
};
|
||||||
|
A665E75E77C12B6A3519ABF9 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 76C272776D8EBCCD3FB6715A /* Marks */;
|
||||||
|
targetProxy = F63E42D8413C0F6ED8BB78CF /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
A87C510E52F149E7E3CA0283 /* PBXTargetDependency */ = {
|
A87C510E52F149E7E3CA0283 /* PBXTargetDependency */ = {
|
||||||
isa = PBXTargetDependency;
|
isa = PBXTargetDependency;
|
||||||
target = CF31B2655F8F30CF6DF3C26F /* ShareExtension */;
|
target = CF31B2655F8F30CF6DF3C26F /* ShareExtension */;
|
||||||
@@ -571,6 +631,23 @@
|
|||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
68D4EEAD1A7AA570E88EF498 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@loader_path/Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.magicive.marks.MarksTests;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Marks.app/Marks";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
6CDF13C33FDD1141CFE677AD /* Debug */ = {
|
6CDF13C33FDD1141CFE677AD /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
@@ -689,6 +766,23 @@
|
|||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
E92B937142E9625F95B51699 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@loader_path/Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.magicive.marks.MarksTests;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Marks.app/Marks";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
@@ -728,6 +822,15 @@
|
|||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Debug;
|
defaultConfigurationName = Debug;
|
||||||
};
|
};
|
||||||
|
CE9D24DDB32E8B7EFDDD9D59 /* Build configuration list for PBXNativeTarget "MarksTests" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
68D4EEAD1A7AA570E88EF498 /* Debug */,
|
||||||
|
E92B937142E9625F95B51699 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Debug;
|
||||||
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
};
|
};
|
||||||
rootObject = 0CC2596AE7F05EAF051B2FCD /* Project object */;
|
rootObject = 0CC2596AE7F05EAF051B2FCD /* Project object */;
|
||||||
|
|||||||
@@ -67,6 +67,17 @@
|
|||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
<Testables>
|
<Testables>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO"
|
||||||
|
parallelizable = "NO">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "F96181AD21F4A6755214E19B"
|
||||||
|
BuildableName = "MarksTests.xctest"
|
||||||
|
BlueprintName = "MarksTests"
|
||||||
|
ReferencedContainer = "container:Marks.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
</Testables>
|
</Testables>
|
||||||
<CommandLineArguments>
|
<CommandLineArguments>
|
||||||
</CommandLineArguments>
|
</CommandLineArguments>
|
||||||
|
|||||||
19
Marks/Intents/BookmarkOnscreen.swift
Normal file
19
Marks/Intents/BookmarkOnscreen.swift
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import AppIntents
|
||||||
|
|
||||||
|
extension View {
|
||||||
|
/// Marks `bookmark` as the on-screen entity via an `NSUserActivity`, so
|
||||||
|
/// Siri / Apple Intelligence can resolve references like "summarize this"
|
||||||
|
/// or "open this one" while the bookmark is being viewed.
|
||||||
|
///
|
||||||
|
/// iOS 26.0 ships no SwiftUI `.appEntityIdentifier` modifier, so this uses
|
||||||
|
/// the supported path: the standard `.userActivity` modifier plus
|
||||||
|
/// `NSUserActivity.appEntityIdentifier` (from `AppEntityAnnotatable`).
|
||||||
|
func bookmarkOnscreen(_ bookmark: Bookmark) -> some View {
|
||||||
|
userActivity("com.magicive.marks.viewBookmark", element: bookmark.id) { id, activity in
|
||||||
|
activity.title = bookmark.displayTitle
|
||||||
|
activity.appEntityIdentifier = EntityIdentifier(for: BookmarkEntity.self, identifier: id)
|
||||||
|
activity.isEligibleForSearch = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -178,6 +178,7 @@ struct BookmarksView: View {
|
|||||||
BrowserView(url: url, title: bookmark.displayTitle, claude: viewModel.claude, podcastPlayer: viewModel.podcastPlayer) {
|
BrowserView(url: url, title: bookmark.displayTitle, claude: viewModel.claude, podcastPlayer: viewModel.podcastPlayer) {
|
||||||
await viewModel.archive(bookmark)
|
await viewModel.archive(bookmark)
|
||||||
}
|
}
|
||||||
|
.bookmarkOnscreen(bookmark)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onChange(of: browsingBookmark) { _, new in
|
.onChange(of: browsingBookmark) { _, new in
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ struct SearchView: View {
|
|||||||
) {
|
) {
|
||||||
await viewModel.archive(bookmark)
|
await viewModel.archive(bookmark)
|
||||||
}
|
}
|
||||||
|
.bookmarkOnscreen(bookmark)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
107
MarksTests/AppIntentsTests.swift
Normal file
107
MarksTests/AppIntentsTests.swift
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import XCTest
|
||||||
|
import AppIntents
|
||||||
|
@testable import Marks
|
||||||
|
|
||||||
|
/// Tests for the App Intents layer.
|
||||||
|
///
|
||||||
|
/// Note: WWDC26's dedicated `AppIntentsTesting` framework (out-of-process
|
||||||
|
/// `IntentDefinitions(bundleIdentifier:)` / `.run()`) is not present in this
|
||||||
|
/// Xcode, so these are standard XCTest cases. They work because Marks has no
|
||||||
|
/// AppIntents extension — intents run in the app process, so `perform()` can be
|
||||||
|
/// called directly and asserted against `IntentRouter`.
|
||||||
|
final class AppIntentsTests: XCTestCase {
|
||||||
|
|
||||||
|
private func sampleBookmark(
|
||||||
|
id: Int = 42,
|
||||||
|
url: String = "https://example.com/article",
|
||||||
|
title: String = "Concurrency in Swift",
|
||||||
|
tags: [String] = ["swift", "concurrency"],
|
||||||
|
unread: Bool = true,
|
||||||
|
aiSummary: String? = "A short summary."
|
||||||
|
) -> Bookmark {
|
||||||
|
Bookmark(
|
||||||
|
id: id, url: url, title: title, description: "desc",
|
||||||
|
tagNames: tags, dateAdded: Date(), dateModified: Date(),
|
||||||
|
isArchived: false, unread: unread, shared: false,
|
||||||
|
websiteTitle: nil, websiteDescription: nil,
|
||||||
|
faviconUrl: nil, previewImageUrl: nil,
|
||||||
|
aiSummary: aiSummary, aiTags: nil
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Entity mapping
|
||||||
|
|
||||||
|
func testEntityMapsFromBookmark() {
|
||||||
|
let entity = BookmarkEntity(from: sampleBookmark())
|
||||||
|
XCTAssertEqual(entity.id, 42)
|
||||||
|
XCTAssertEqual(entity.title, "Concurrency in Swift")
|
||||||
|
XCTAssertEqual(entity.url, URL(string: "https://example.com/article"))
|
||||||
|
XCTAssertEqual(entity.host, "example.com")
|
||||||
|
XCTAssertEqual(entity.tags, ["swift", "concurrency"])
|
||||||
|
XCTAssertTrue(entity.unread)
|
||||||
|
XCTAssertEqual(entity.summary, "A short summary.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testEntityUsesDisplayTitleFallback() {
|
||||||
|
// empty title → falls back to url (Bookmark.displayTitle)
|
||||||
|
let entity = BookmarkEntity(from: sampleBookmark(title: ""))
|
||||||
|
XCTAssertFalse(entity.title.isEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMakeBookmarkRoundTripsCoreFields() {
|
||||||
|
let entity = BookmarkEntity(from: sampleBookmark())
|
||||||
|
let back = entity.makeBookmark()
|
||||||
|
XCTAssertEqual(back.id, 42)
|
||||||
|
XCTAssertEqual(back.url, "https://example.com/article")
|
||||||
|
XCTAssertEqual(back.tagNames, ["swift", "concurrency"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testEntityIdentifierEncodesServerId() {
|
||||||
|
let entity = BookmarkEntity(from: sampleBookmark(id: 7))
|
||||||
|
let eid = EntityIdentifier(for: entity)
|
||||||
|
XCTAssertEqual(eid.identifier, "7")
|
||||||
|
XCTAssertTrue(eid.entityType == BookmarkEntity.self)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Router
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
func testRouterSearch() {
|
||||||
|
IntentRouter.shared.searchRequest = nil
|
||||||
|
IntentRouter.shared.search("hello")
|
||||||
|
XCTAssertEqual(IntentRouter.shared.searchRequest, "hello")
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
func testRouterOpenBookmark() {
|
||||||
|
IntentRouter.shared.openBookmarkURL = nil
|
||||||
|
IntentRouter.shared.openBookmark(url: "https://example.com")
|
||||||
|
XCTAssertEqual(IntentRouter.shared.openBookmarkURL, "https://example.com")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Intent perform() wiring (no network)
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
func testSearchIntentRoutesCriteriaTerm() async throws {
|
||||||
|
IntentRouter.shared.searchRequest = nil
|
||||||
|
let intent = SearchMarksIntent()
|
||||||
|
intent.criteria = StringSearchCriteria(term: "graphql")
|
||||||
|
_ = try await intent.perform()
|
||||||
|
XCTAssertEqual(IntentRouter.shared.searchRequest, "graphql")
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
func testOpenIntentRoutesURL() async throws {
|
||||||
|
IntentRouter.shared.openBookmarkURL = nil
|
||||||
|
let intent = OpenBookmarkIntent()
|
||||||
|
intent.target = BookmarkEntity(from: sampleBookmark())
|
||||||
|
_ = try await intent.perform()
|
||||||
|
XCTAssertEqual(IntentRouter.shared.openBookmarkURL, "https://example.com/article")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - App Shortcuts
|
||||||
|
|
||||||
|
func testAppShortcutsAreRegistered() {
|
||||||
|
XCTAssertGreaterThanOrEqual(MarksShortcuts.appShortcuts.count, 4)
|
||||||
|
}
|
||||||
|
}
|
||||||
14
project.yml
14
project.yml
@@ -26,6 +26,8 @@ schemes:
|
|||||||
config: Debug
|
config: Debug
|
||||||
test:
|
test:
|
||||||
config: Debug
|
config: Debug
|
||||||
|
targets:
|
||||||
|
- MarksTests
|
||||||
profile:
|
profile:
|
||||||
config: Release
|
config: Release
|
||||||
analyze:
|
analyze:
|
||||||
@@ -89,6 +91,18 @@ targets:
|
|||||||
SKIP_INSTALL: YES
|
SKIP_INSTALL: YES
|
||||||
APPLICATION_EXTENSION_API_ONLY: YES
|
APPLICATION_EXTENSION_API_ONLY: YES
|
||||||
|
|
||||||
|
MarksTests:
|
||||||
|
type: bundle.unit-test
|
||||||
|
platform: iOS
|
||||||
|
sources:
|
||||||
|
- path: MarksTests
|
||||||
|
settings:
|
||||||
|
base:
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER: com.magicive.marks.MarksTests
|
||||||
|
GENERATE_INFOPLIST_FILE: YES
|
||||||
|
dependencies:
|
||||||
|
- target: Marks
|
||||||
|
|
||||||
MarksWidget:
|
MarksWidget:
|
||||||
type: app-extension
|
type: app-extension
|
||||||
platform: iOS
|
platform: iOS
|
||||||
|
|||||||
Reference in New Issue
Block a user