Add iOS save action extension
This commit is contained in:
@@ -148,6 +148,9 @@ flutter run --dart-define=ENABLE_WHATSAPP_SHARE_INTAKE=false
|
|||||||
Behavior notes:
|
Behavior notes:
|
||||||
|
|
||||||
- iOS/Android: integrates with share-intent plugin (`receive_sharing_intent`)
|
- iOS/Android: integrates with share-intent plugin (`receive_sharing_intent`)
|
||||||
|
- iOS also ships a `Save to Relationship Saver` Action extension for the lower
|
||||||
|
share-sheet action row; use it when that row is easier to reach than the app
|
||||||
|
share row
|
||||||
- shared text is parsed and ingested into:
|
- shared text is parsed and ingested into:
|
||||||
- person profile (auto-create when identity is confident)
|
- person profile (auto-create when identity is confident)
|
||||||
- source->profile link map for follow-up matching
|
- source->profile link map for follow-up matching
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
A1B2C3021111111111111111 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C30D1111111111111111 /* ShareViewController.swift */; };
|
A1B2C3021111111111111111 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C30D1111111111111111 /* ShareViewController.swift */; };
|
||||||
A1B2C3031111111111111111 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A1B2C3181111111111111111 /* MainInterface.storyboard */; };
|
A1B2C3031111111111111111 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A1B2C3181111111111111111 /* MainInterface.storyboard */; };
|
||||||
A1B2C3041111111111111111 /* Share Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = A1B2C30C1111111111111111 /* Share Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
A1B2C3041111111111111111 /* Share Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = A1B2C30C1111111111111111 /* Share Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
|
B2C3D4012222222222222222 /* SaveActionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2C3D4062222222222222222 /* SaveActionViewController.swift */; };
|
||||||
|
B2C3D4022222222222222222 /* Save Action.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = B2C3D4052222222222222222 /* Save Action.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -37,6 +39,13 @@
|
|||||||
remoteGlobalIDString = A1B2C3131111111111111111;
|
remoteGlobalIDString = A1B2C3131111111111111111;
|
||||||
remoteInfo = "Share Extension";
|
remoteInfo = "Share Extension";
|
||||||
};
|
};
|
||||||
|
B2C3D4042222222222222222 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = B2C3D40B2222222222222222;
|
||||||
|
remoteInfo = "Save Action";
|
||||||
|
};
|
||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
@@ -57,6 +66,7 @@
|
|||||||
dstSubfolderSpec = 13;
|
dstSubfolderSpec = 13;
|
||||||
files = (
|
files = (
|
||||||
A1B2C3041111111111111111 /* Share Extension.appex in Embed App Extensions */,
|
A1B2C3041111111111111111 /* Share Extension.appex in Embed App Extensions */,
|
||||||
|
B2C3D4022222222222222222 /* Save Action.appex in Embed App Extensions */,
|
||||||
);
|
);
|
||||||
name = "Embed App Extensions";
|
name = "Embed App Extensions";
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -91,6 +101,10 @@
|
|||||||
A1B2C30E1111111111111111 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
|
A1B2C30E1111111111111111 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
|
||||||
A1B2C30F1111111111111111 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
A1B2C30F1111111111111111 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
A1B2C3101111111111111111 /* Share Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Share Extension.entitlements"; sourceTree = "<group>"; };
|
A1B2C3101111111111111111 /* Share Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Share Extension.entitlements"; sourceTree = "<group>"; };
|
||||||
|
B2C3D4052222222222222222 /* Save Action.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Save Action.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
B2C3D4062222222222222222 /* SaveActionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveActionViewController.swift; sourceTree = "<group>"; };
|
||||||
|
B2C3D4072222222222222222 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
B2C3D4082222222222222222 /* Save Action.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Save Action.entitlements"; sourceTree = "<group>"; };
|
||||||
CD3F9117486EC23DF50E2D6A /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
CD3F9117486EC23DF50E2D6A /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
E9B506D809ABF2A50BF6135B /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
E9B506D809ABF2A50BF6135B /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
F2C7EE505B336533B62B23EB /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
|
F2C7EE505B336533B62B23EB /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
@@ -112,6 +126,13 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
B2C3D4092222222222222222 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
E2BAEB33085D4BFE88A55D45 /* Frameworks */ = {
|
E2BAEB33085D4BFE88A55D45 /* Frameworks */ = {
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@@ -170,6 +191,7 @@
|
|||||||
9740EEB11CF90186004384FC /* Flutter */,
|
9740EEB11CF90186004384FC /* Flutter */,
|
||||||
97C146F01CF9000F007C117D /* Runner */,
|
97C146F01CF9000F007C117D /* Runner */,
|
||||||
A1B2C3121111111111111111 /* Share Extension */,
|
A1B2C3121111111111111111 /* Share Extension */,
|
||||||
|
B2C3D40A2222222222222222 /* Save Action */,
|
||||||
97C146EF1CF9000F007C117D /* Products */,
|
97C146EF1CF9000F007C117D /* Products */,
|
||||||
331C8082294A63A400263BE5 /* RunnerTests */,
|
331C8082294A63A400263BE5 /* RunnerTests */,
|
||||||
3D195739A0A78D037BF244F8 /* Pods */,
|
3D195739A0A78D037BF244F8 /* Pods */,
|
||||||
@@ -182,6 +204,7 @@
|
|||||||
children = (
|
children = (
|
||||||
97C146EE1CF9000F007C117D /* Runner.app */,
|
97C146EE1CF9000F007C117D /* Runner.app */,
|
||||||
A1B2C30C1111111111111111 /* Share Extension.appex */,
|
A1B2C30C1111111111111111 /* Share Extension.appex */,
|
||||||
|
B2C3D4052222222222222222 /* Save Action.appex */,
|
||||||
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
|
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
@@ -215,6 +238,16 @@
|
|||||||
path = "Share Extension";
|
path = "Share Extension";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
B2C3D40A2222222222222222 /* Save Action */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
B2C3D4082222222222222222 /* Save Action.entitlements */,
|
||||||
|
B2C3D4062222222222222222 /* SaveActionViewController.swift */,
|
||||||
|
B2C3D4072222222222222222 /* Info.plist */,
|
||||||
|
);
|
||||||
|
path = "Save Action";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
@@ -255,6 +288,7 @@
|
|||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
A1B2C3171111111111111111 /* PBXTargetDependency */,
|
A1B2C3171111111111111111 /* PBXTargetDependency */,
|
||||||
|
B2C3D40E2222222222222222 /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
name = Runner;
|
name = Runner;
|
||||||
productName = Runner;
|
productName = Runner;
|
||||||
@@ -278,6 +312,23 @@
|
|||||||
productReference = A1B2C30C1111111111111111 /* Share Extension.appex */;
|
productReference = A1B2C30C1111111111111111 /* Share Extension.appex */;
|
||||||
productType = "com.apple.product-type.app-extension";
|
productType = "com.apple.product-type.app-extension";
|
||||||
};
|
};
|
||||||
|
B2C3D40B2222222222222222 /* Save Action */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = B2C3D4122222222222222222 /* Build configuration list for PBXNativeTarget "Save Action" */;
|
||||||
|
buildPhases = (
|
||||||
|
B2C3D40D2222222222222222 /* Sources */,
|
||||||
|
B2C3D4092222222222222222 /* Frameworks */,
|
||||||
|
B2C3D40C2222222222222222 /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = "Save Action";
|
||||||
|
productName = "Save Action";
|
||||||
|
productReference = B2C3D4052222222222222222 /* Save Action.appex */;
|
||||||
|
productType = "com.apple.product-type.app-extension";
|
||||||
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
@@ -299,6 +350,9 @@
|
|||||||
A1B2C3131111111111111111 = {
|
A1B2C3131111111111111111 = {
|
||||||
CreatedOnToolsVersion = 15.0.1;
|
CreatedOnToolsVersion = 15.0.1;
|
||||||
};
|
};
|
||||||
|
B2C3D40B2222222222222222 = {
|
||||||
|
CreatedOnToolsVersion = 15.0.1;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
||||||
@@ -316,6 +370,7 @@
|
|||||||
targets = (
|
targets = (
|
||||||
97C146ED1CF9000F007C117D /* Runner */,
|
97C146ED1CF9000F007C117D /* Runner */,
|
||||||
A1B2C3131111111111111111 /* Share Extension */,
|
A1B2C3131111111111111111 /* Share Extension */,
|
||||||
|
B2C3D40B2222222222222222 /* Save Action */,
|
||||||
331C8080294A63A400263BE5 /* RunnerTests */,
|
331C8080294A63A400263BE5 /* RunnerTests */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -348,6 +403,13 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
B2C3D40C2222222222222222 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
@@ -472,6 +534,14 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
B2C3D40D2222222222222222 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
B2C3D4012222222222222222 /* SaveActionViewController.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXTargetDependency section */
|
/* Begin PBXTargetDependency section */
|
||||||
@@ -485,6 +555,11 @@
|
|||||||
target = A1B2C3131111111111111111 /* Share Extension */;
|
target = A1B2C3131111111111111111 /* Share Extension */;
|
||||||
targetProxy = A1B2C3051111111111111111 /* PBXContainerItemProxy */;
|
targetProxy = A1B2C3051111111111111111 /* PBXContainerItemProxy */;
|
||||||
};
|
};
|
||||||
|
B2C3D40E2222222222222222 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = B2C3D40B2222222222222222 /* Save Action */;
|
||||||
|
targetProxy = B2C3D4042222222222222222 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
/* End PBXTargetDependency section */
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin PBXVariantGroup section */
|
/* Begin PBXVariantGroup section */
|
||||||
@@ -520,6 +595,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
APP_BUNDLE_IDENTIFIER = com.rijadzuzo.relationshipSaver;
|
APP_BUNDLE_IDENTIFIER = com.rijadzuzo.relationshipSaver;
|
||||||
|
APP_ACTION_EXTENSION_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).SaveAction";
|
||||||
APP_GROUP_ID = group.com.rijadzuzo.relationshipSaver.shared;
|
APP_GROUP_ID = group.com.rijadzuzo.relationshipSaver.shared;
|
||||||
APP_RUNNER_TESTS_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).RunnerTests";
|
APP_RUNNER_TESTS_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).RunnerTests";
|
||||||
APP_SHARE_EXTENSION_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).ShareExtension";
|
APP_SHARE_EXTENSION_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).ShareExtension";
|
||||||
@@ -651,6 +727,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
APP_BUNDLE_IDENTIFIER = com.rijadzuzo.relationshipSaver;
|
APP_BUNDLE_IDENTIFIER = com.rijadzuzo.relationshipSaver;
|
||||||
|
APP_ACTION_EXTENSION_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).SaveAction";
|
||||||
APP_GROUP_ID = group.com.rijadzuzo.relationshipSaver.shared;
|
APP_GROUP_ID = group.com.rijadzuzo.relationshipSaver.shared;
|
||||||
APP_RUNNER_TESTS_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).RunnerTests";
|
APP_RUNNER_TESTS_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).RunnerTests";
|
||||||
APP_SHARE_EXTENSION_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).ShareExtension";
|
APP_SHARE_EXTENSION_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).ShareExtension";
|
||||||
@@ -713,6 +790,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
APP_BUNDLE_IDENTIFIER = com.rijadzuzo.relationshipSaver;
|
APP_BUNDLE_IDENTIFIER = com.rijadzuzo.relationshipSaver;
|
||||||
|
APP_ACTION_EXTENSION_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).SaveAction";
|
||||||
APP_GROUP_ID = group.com.rijadzuzo.relationshipSaver.shared;
|
APP_GROUP_ID = group.com.rijadzuzo.relationshipSaver.shared;
|
||||||
APP_RUNNER_TESTS_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).RunnerTests";
|
APP_RUNNER_TESTS_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).RunnerTests";
|
||||||
APP_SHARE_EXTENSION_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).ShareExtension";
|
APP_SHARE_EXTENSION_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).ShareExtension";
|
||||||
@@ -895,6 +973,88 @@
|
|||||||
};
|
};
|
||||||
name = Profile;
|
name = Profile;
|
||||||
};
|
};
|
||||||
|
B2C3D40F2222222222222222 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_ENTITLEMENTS = "Save Action/Save Action.entitlements";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
CUSTOM_GROUP_ID = "$(APP_GROUP_ID)";
|
||||||
|
DEVELOPMENT_TEAM = SR3UKQVKP6;
|
||||||
|
INFOPLIST_FILE = "Save Action/Info.plist";
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = "Save to Relationship Saver";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "$(APP_ACTION_EXTENSION_BUNDLE_IDENTIFIER)";
|
||||||
|
PRODUCT_MODULE_NAME = SaveAction;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
B2C3D4102222222222222222 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_ENTITLEMENTS = "Save Action/Save Action.entitlements";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
CUSTOM_GROUP_ID = "$(APP_GROUP_ID)";
|
||||||
|
DEVELOPMENT_TEAM = SR3UKQVKP6;
|
||||||
|
INFOPLIST_FILE = "Save Action/Info.plist";
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = "Save to Relationship Saver";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "$(APP_ACTION_EXTENSION_BUNDLE_IDENTIFIER)";
|
||||||
|
PRODUCT_MODULE_NAME = SaveAction;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
B2C3D4112222222222222222 /* Profile */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_ENTITLEMENTS = "Save Action/Save Action.entitlements";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
CUSTOM_GROUP_ID = "$(APP_GROUP_ID)";
|
||||||
|
DEVELOPMENT_TEAM = SR3UKQVKP6;
|
||||||
|
INFOPLIST_FILE = "Save Action/Info.plist";
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = "Save to Relationship Saver";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "$(APP_ACTION_EXTENSION_BUNDLE_IDENTIFIER)";
|
||||||
|
PRODUCT_MODULE_NAME = SaveAction;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Profile;
|
||||||
|
};
|
||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
@@ -938,6 +1098,16 @@
|
|||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
|
B2C3D4122222222222222222 /* Build configuration list for PBXNativeTarget "Save Action" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
B2C3D40F2222222222222222 /* Debug */,
|
||||||
|
B2C3D4102222222222222222 /* Release */,
|
||||||
|
B2C3D4112222222222222222 /* Profile */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
};
|
};
|
||||||
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>AppGroupId</key>
|
||||||
|
<string>$(CUSTOM_GROUP_ID)</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>Save to Relationship Saver</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>XPC!</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>$(MARKETING_VERSION)</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>NSExtension</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionAttributes</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionActivationRule</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionActivationSupportsText</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.ui-services</string>
|
||||||
|
<key>NSExtensionPrincipalClass</key>
|
||||||
|
<string>$(PRODUCT_MODULE_NAME).SaveActionViewController</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(CUSTOM_GROUP_ID)</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
import MobileCoreServices
|
||||||
|
import UniformTypeIdentifiers
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
private let schemePrefix = "ShareMedia"
|
||||||
|
private let userDefaultsKey = "ShareKey"
|
||||||
|
private let userDefaultsMessageKey = "ShareMessageKey"
|
||||||
|
private let appGroupIdKey = "AppGroupId"
|
||||||
|
|
||||||
|
private struct SharedMediaFile: Codable {
|
||||||
|
let path: String
|
||||||
|
let mimeType: String?
|
||||||
|
let thumbnail: String?
|
||||||
|
let duration: Double?
|
||||||
|
let message: String?
|
||||||
|
let type: SharedMediaType
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum SharedMediaType: String, Codable {
|
||||||
|
case text
|
||||||
|
case url
|
||||||
|
}
|
||||||
|
|
||||||
|
final class SaveActionViewController: UIViewController {
|
||||||
|
private var hostAppBundleIdentifier = ""
|
||||||
|
private var appGroupId = ""
|
||||||
|
private var sharedMedia: [SharedMediaFile] = []
|
||||||
|
private let group = DispatchGroup()
|
||||||
|
private var didStart = false
|
||||||
|
|
||||||
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
|
super.viewDidAppear(animated)
|
||||||
|
guard !didStart else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
didStart = true
|
||||||
|
loadIdentifiers()
|
||||||
|
loadSharedContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loadIdentifiers() {
|
||||||
|
let actionExtensionIdentifier = Bundle.main.bundleIdentifier ?? ""
|
||||||
|
if let lastDotIndex = actionExtensionIdentifier.lastIndex(of: ".") {
|
||||||
|
hostAppBundleIdentifier = String(actionExtensionIdentifier[..<lastDotIndex])
|
||||||
|
} else {
|
||||||
|
hostAppBundleIdentifier = actionExtensionIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
|
let defaultAppGroupId = "group.\(hostAppBundleIdentifier)"
|
||||||
|
appGroupId =
|
||||||
|
Bundle.main.object(forInfoDictionaryKey: appGroupIdKey) as? String
|
||||||
|
?? defaultAppGroupId
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loadSharedContent() {
|
||||||
|
guard let inputItems = extensionContext?.inputItems as? [NSExtensionItem] else {
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for item in inputItems {
|
||||||
|
appendAttributedText(item.attributedContentText)
|
||||||
|
for provider in item.attachments ?? [] {
|
||||||
|
load(provider)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
group.notify(queue: .main) { [weak self] in
|
||||||
|
self?.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func load(_ provider: NSItemProvider) {
|
||||||
|
if provider.hasItemConformingToTypeIdentifier(urlTypeIdentifier) {
|
||||||
|
group.enter()
|
||||||
|
provider.loadItem(forTypeIdentifier: urlTypeIdentifier, options: nil) { [weak self] item, _ in
|
||||||
|
defer {
|
||||||
|
self?.group.leave()
|
||||||
|
}
|
||||||
|
if let url = item as? URL {
|
||||||
|
self?.appendLiteral(url.absoluteString, type: .url, mimeType: nil)
|
||||||
|
} else if let text = item as? String {
|
||||||
|
self?.appendLiteral(text, type: .url, mimeType: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if provider.hasItemConformingToTypeIdentifier(textTypeIdentifier) {
|
||||||
|
group.enter()
|
||||||
|
provider.loadItem(forTypeIdentifier: textTypeIdentifier, options: nil) { [weak self] item, _ in
|
||||||
|
defer {
|
||||||
|
self?.group.leave()
|
||||||
|
}
|
||||||
|
if let text = item as? String {
|
||||||
|
self?.appendLiteral(text, type: .text, mimeType: "text/plain")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var textTypeIdentifier: String {
|
||||||
|
if #available(iOS 14.0, *) {
|
||||||
|
return UTType.text.identifier
|
||||||
|
}
|
||||||
|
return kUTTypeText as String
|
||||||
|
}
|
||||||
|
|
||||||
|
private var urlTypeIdentifier: String {
|
||||||
|
if #available(iOS 14.0, *) {
|
||||||
|
return UTType.url.identifier
|
||||||
|
}
|
||||||
|
return kUTTypeURL as String
|
||||||
|
}
|
||||||
|
|
||||||
|
private func appendAttributedText(_ text: NSAttributedString?) {
|
||||||
|
guard let value = text?.string else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
appendLiteral(value, type: .text, mimeType: "text/plain")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func appendLiteral(_ value: String, type: SharedMediaType, mimeType: String?) {
|
||||||
|
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
guard !trimmed.isEmpty else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard !sharedMedia.contains(where: { $0.type == type && $0.path == trimmed }) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sharedMedia.append(
|
||||||
|
SharedMediaFile(
|
||||||
|
path: trimmed,
|
||||||
|
mimeType: mimeType,
|
||||||
|
thumbnail: nil,
|
||||||
|
duration: nil,
|
||||||
|
message: nil,
|
||||||
|
type: type
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func finish() {
|
||||||
|
guard !sharedMedia.isEmpty else {
|
||||||
|
extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
saveAndRedirect()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func saveAndRedirect() {
|
||||||
|
guard let encodedData = try? JSONEncoder().encode(sharedMedia) else {
|
||||||
|
extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let userDefaults = UserDefaults(suiteName: appGroupId)
|
||||||
|
userDefaults?.set(encodedData, forKey: userDefaultsKey)
|
||||||
|
userDefaults?.set(nil, forKey: userDefaultsMessageKey)
|
||||||
|
userDefaults?.synchronize()
|
||||||
|
redirectToHostApp()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func redirectToHostApp() {
|
||||||
|
guard let url = URL(string: "\(schemePrefix)-\(hostAppBundleIdentifier):share") else {
|
||||||
|
extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var responder: UIResponder? = self
|
||||||
|
let selectorOpenUrl = sel_registerName("openURL:")
|
||||||
|
|
||||||
|
if #available(iOS 18.0, *) {
|
||||||
|
while responder != nil {
|
||||||
|
if let application = responder as? UIApplication {
|
||||||
|
application.open(url, options: [:], completionHandler: nil)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
responder = responder?.next
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while responder != nil {
|
||||||
|
if responder?.responds(to: selectorOpenUrl) == true {
|
||||||
|
_ = responder?.perform(selectorOpenUrl, with: url)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
responder = responder?.next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -118,6 +118,13 @@ class _IncomingShareListenerState extends ConsumerState<IncomingShareListener> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (file.type == SharedMediaType.url) {
|
||||||
|
final String url = file.path.trim();
|
||||||
|
if (url.isNotEmpty) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final String? mimeType = file.mimeType?.toLowerCase();
|
final String? mimeType = file.mimeType?.toLowerCase();
|
||||||
if (mimeType != null && mimeType.startsWith('text/')) {
|
if (mimeType != null && mimeType.startsWith('text/')) {
|
||||||
final String text = file.path.trim();
|
final String text = file.path.trim();
|
||||||
|
|||||||
Reference in New Issue
Block a user