Add iOS save action extension
This commit is contained in:
@@ -20,6 +20,8 @@
|
||||
A1B2C3021111111111111111 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C30D1111111111111111 /* ShareViewController.swift */; };
|
||||
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, ); }; };
|
||||
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 */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -37,6 +39,13 @@
|
||||
remoteGlobalIDString = A1B2C3131111111111111111;
|
||||
remoteInfo = "Share Extension";
|
||||
};
|
||||
B2C3D4042222222222222222 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = B2C3D40B2222222222222222;
|
||||
remoteInfo = "Save Action";
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
@@ -57,6 +66,7 @@
|
||||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
A1B2C3041111111111111111 /* Share Extension.appex in Embed App Extensions */,
|
||||
B2C3D4022222222222222222 /* Save Action.appex in Embed App Extensions */,
|
||||
);
|
||||
name = "Embed App Extensions";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -91,6 +101,10 @@
|
||||
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>"; };
|
||||
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; };
|
||||
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>"; };
|
||||
@@ -112,6 +126,13 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
B2C3D4092222222222222222 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
E2BAEB33085D4BFE88A55D45 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -170,6 +191,7 @@
|
||||
9740EEB11CF90186004384FC /* Flutter */,
|
||||
97C146F01CF9000F007C117D /* Runner */,
|
||||
A1B2C3121111111111111111 /* Share Extension */,
|
||||
B2C3D40A2222222222222222 /* Save Action */,
|
||||
97C146EF1CF9000F007C117D /* Products */,
|
||||
331C8082294A63A400263BE5 /* RunnerTests */,
|
||||
3D195739A0A78D037BF244F8 /* Pods */,
|
||||
@@ -182,6 +204,7 @@
|
||||
children = (
|
||||
97C146EE1CF9000F007C117D /* Runner.app */,
|
||||
A1B2C30C1111111111111111 /* Share Extension.appex */,
|
||||
B2C3D4052222222222222222 /* Save Action.appex */,
|
||||
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
@@ -215,6 +238,16 @@
|
||||
path = "Share Extension";
|
||||
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 */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@@ -255,6 +288,7 @@
|
||||
);
|
||||
dependencies = (
|
||||
A1B2C3171111111111111111 /* PBXTargetDependency */,
|
||||
B2C3D40E2222222222222222 /* PBXTargetDependency */,
|
||||
);
|
||||
name = Runner;
|
||||
productName = Runner;
|
||||
@@ -278,6 +312,23 @@
|
||||
productReference = A1B2C30C1111111111111111 /* Share Extension.appex */;
|
||||
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 */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
@@ -299,6 +350,9 @@
|
||||
A1B2C3131111111111111111 = {
|
||||
CreatedOnToolsVersion = 15.0.1;
|
||||
};
|
||||
B2C3D40B2222222222222222 = {
|
||||
CreatedOnToolsVersion = 15.0.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
||||
@@ -316,6 +370,7 @@
|
||||
targets = (
|
||||
97C146ED1CF9000F007C117D /* Runner */,
|
||||
A1B2C3131111111111111111 /* Share Extension */,
|
||||
B2C3D40B2222222222222222 /* Save Action */,
|
||||
331C8080294A63A400263BE5 /* RunnerTests */,
|
||||
);
|
||||
};
|
||||
@@ -348,6 +403,13 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
B2C3D40C2222222222222222 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
@@ -472,6 +534,14 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
B2C3D40D2222222222222222 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B2C3D4012222222222222222 /* SaveActionViewController.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
@@ -485,6 +555,11 @@
|
||||
target = A1B2C3131111111111111111 /* Share Extension */;
|
||||
targetProxy = A1B2C3051111111111111111 /* PBXContainerItemProxy */;
|
||||
};
|
||||
B2C3D40E2222222222222222 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = B2C3D40B2222222222222222 /* Save Action */;
|
||||
targetProxy = B2C3D4042222222222222222 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
@@ -520,6 +595,7 @@
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
APP_BUNDLE_IDENTIFIER = com.rijadzuzo.relationshipSaver;
|
||||
APP_ACTION_EXTENSION_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).SaveAction";
|
||||
APP_GROUP_ID = group.com.rijadzuzo.relationshipSaver.shared;
|
||||
APP_RUNNER_TESTS_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).RunnerTests";
|
||||
APP_SHARE_EXTENSION_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).ShareExtension";
|
||||
@@ -651,6 +727,7 @@
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
APP_BUNDLE_IDENTIFIER = com.rijadzuzo.relationshipSaver;
|
||||
APP_ACTION_EXTENSION_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).SaveAction";
|
||||
APP_GROUP_ID = group.com.rijadzuzo.relationshipSaver.shared;
|
||||
APP_RUNNER_TESTS_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).RunnerTests";
|
||||
APP_SHARE_EXTENSION_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).ShareExtension";
|
||||
@@ -713,6 +790,7 @@
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
APP_BUNDLE_IDENTIFIER = com.rijadzuzo.relationshipSaver;
|
||||
APP_ACTION_EXTENSION_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).SaveAction";
|
||||
APP_GROUP_ID = group.com.rijadzuzo.relationshipSaver.shared;
|
||||
APP_RUNNER_TESTS_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).RunnerTests";
|
||||
APP_SHARE_EXTENSION_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).ShareExtension";
|
||||
@@ -895,6 +973,88 @@
|
||||
};
|
||||
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 */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
@@ -938,6 +1098,16 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
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 */
|
||||
};
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user