mirror of
https://github.com/metabolist/metatext.git
synced 2025-04-24 04:24:11 +00:00
Implement Open in Metatext action extension
This commit is contained in:
parent
9bfc6c1449
commit
09da2bca1f
9 changed files with 618 additions and 4 deletions
Action Extension
Metatext.xcodeproj
project.pbxproj
xcshareddata/xcschemes
Supporting Files
Views/SwiftUI
18
Action Extension/Action Extension.entitlements
Normal file
18
Action Extension/Action Extension.entitlements
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?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.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.metabolist.metatext</string>
|
||||
</array>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)com.metabolist.metatext</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
66
Action Extension/ActionExtensionViewController.swift
Normal file
66
Action Extension/ActionExtensionViewController.swift
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Copyright © 2022 Metabolist. All rights reserved.
|
||||
|
||||
import MobileCoreServices
|
||||
import UIKit
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
/// Let the user open a URL that might be a Mastodon profile or post in Metatext.
|
||||
/// Should activate for one or more web URLs, but will only do anything with the first..
|
||||
class ActionExtensionViewController: UIViewController {
|
||||
/// Extensions aren't allowed to call `UIApplication.shared
|
||||
/// and thus don't have direct access to its `openURL` method.
|
||||
/// `self.extensionContext?.open(<#T##URL: URL##URL#>)` only works for Today extensions.
|
||||
/// As a workaround, we find a parent responder that has an `openURL(_:)` method.
|
||||
/// This will be `UIApplication`. It's cursed, but it uses public APIs and works.
|
||||
/// See <https://liman.io/blog/open-url-share-extension-swiftui>.
|
||||
private func open(url: URL) {
|
||||
var responder: UIResponder? = self as UIResponder
|
||||
let selector = #selector(openURL(_:))
|
||||
while responder != nil {
|
||||
if responder!.responds(to: selector) && responder != self {
|
||||
responder!.perform(selector, with: url)
|
||||
return
|
||||
}
|
||||
responder = responder?.next
|
||||
}
|
||||
}
|
||||
|
||||
/// Only exists so we can create a selector from it.
|
||||
@objc private func openURL(_ url: URL) {
|
||||
return
|
||||
}
|
||||
|
||||
/// This extension has no actual UI, so we act on extension input items as soon as we load.
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Find the first URL or thing coerceable to a URL.
|
||||
for item in self.extensionContext!.inputItems as? [NSExtensionItem] ?? [] {
|
||||
for provider in item.attachments! where provider.hasItemConformingToTypeIdentifier(UTType.url.identifier) {
|
||||
_ = provider.loadObject(ofClass: URL.self) { (url, error) in
|
||||
OperationQueue.main.addOperation {
|
||||
if let error = error {
|
||||
self.extensionContext!.cancelRequest(withError: error)
|
||||
} else if let url = url {
|
||||
// Create a `metatext:search?url=https…` URL from our web URL.
|
||||
var urlBuilder = URLComponents()
|
||||
urlBuilder.scheme = "metatext"
|
||||
urlBuilder.path = "search"
|
||||
urlBuilder.queryItems = [.init(name: "url", value: url.absoluteString)]
|
||||
let metatextURL = urlBuilder.url!
|
||||
self.open(url: metatextURL)
|
||||
self.extensionContext!.completeRequest(returningItems: [])
|
||||
} else {
|
||||
// Should never happen. Return a generic not-found error.
|
||||
self.extensionContext!.cancelRequest(withError: CocoaError(.fileNoSuchFile))
|
||||
}
|
||||
}
|
||||
}
|
||||
// We do not attempt to handle multiple URLs.
|
||||
return
|
||||
}
|
||||
}
|
||||
// No URLs. Return a generic not-found error.
|
||||
self.extensionContext!.cancelRequest(withError: CocoaError(.fileNoSuchFile))
|
||||
}
|
||||
}
|
39
Action Extension/Info.plist
Normal file
39
Action Extension/Info.plist
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?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>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Action Extension</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionAttributes</key>
|
||||
<dict>
|
||||
<key>NSExtensionActivationRule</key>
|
||||
<dict>
|
||||
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.ui-services</string>
|
||||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).ActionExtensionViewController</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
|
@ -7,6 +7,9 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
94B2C8C02929617A00087955 /* UniformTypeIdentifiers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94B2C8BF2929617A00087955 /* UniformTypeIdentifiers.framework */; };
|
||||
94B2C8C52929617A00087955 /* ActionExtensionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94B2C8C42929617A00087955 /* ActionExtensionViewController.swift */; };
|
||||
94B2C8CC2929617A00087955 /* Action Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 94B2C8BE2929617A00087955 /* Action Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
D0030982250C6C8500EACB32 /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0030981250C6C8500EACB32 /* URL+Extensions.swift */; };
|
||||
D005A1D825EF189A008B2E63 /* ReportViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D005A1D725EF189A008B2E63 /* ReportViewController.swift */; };
|
||||
D005A1E625EF3D11008B2E63 /* ReportHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D005A1E525EF3D11008B2E63 /* ReportHeaderView.swift */; };
|
||||
|
@ -248,6 +251,13 @@
|
|||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
94B2C8CA2929617A00087955 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = D047FA8024C3E21000AF17C5 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 94B2C8BD2929617A00087955;
|
||||
remoteInfo = "com.metabolist.metatext.action-extension";
|
||||
};
|
||||
D0666A2624C677B400F3F04B /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = D047FA8024C3E21000AF17C5 /* Project object */;
|
||||
|
@ -279,6 +289,7 @@
|
|||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
D0E5362024E3EB4D00FB1CE1 /* Notification Service Extension.appex in Embed Foundation Extensions */,
|
||||
94B2C8CC2929617A00087955 /* Action Extension.appex in Embed Foundation Extensions */,
|
||||
D08E5276257C36CA00FA2C5F /* Share Extension.appex in Embed Foundation Extensions */,
|
||||
);
|
||||
name = "Embed Foundation Extensions";
|
||||
|
@ -299,6 +310,11 @@
|
|||
68865A2F292349BF001BF177 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fr; path = fr.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
68865A3029234A68001BF177 /* gd */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gd; path = gd.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
68865A3129234A68001BF177 /* gd */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = gd; path = gd.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
94B2C8BE2929617A00087955 /* Action Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Action Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
94B2C8BF2929617A00087955 /* UniformTypeIdentifiers.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UniformTypeIdentifiers.framework; path = System/Library/Frameworks/UniformTypeIdentifiers.framework; sourceTree = SDKROOT; };
|
||||
94B2C8C42929617A00087955 /* ActionExtensionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionExtensionViewController.swift; sourceTree = "<group>"; };
|
||||
94B2C8C92929617A00087955 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
94B2C8CD2929617A00087955 /* Action Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Action Extension.entitlements"; sourceTree = "<group>"; };
|
||||
D0030981250C6C8500EACB32 /* URL+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extensions.swift"; sourceTree = "<group>"; };
|
||||
D005A1D725EF189A008B2E63 /* ReportViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportViewController.swift; sourceTree = "<group>"; };
|
||||
D005A1E525EF3D11008B2E63 /* ReportHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportHeaderView.swift; sourceTree = "<group>"; };
|
||||
|
@ -510,6 +526,14 @@
|
|||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
94B2C8BB2929617A00087955 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
94B2C8C02929617A00087955 /* UniformTypeIdentifiers.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
D047FA8924C3E21200AF17C5 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -550,6 +574,16 @@
|
|||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
94B2C8C12929617A00087955 /* Action Extension */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
94B2C8CD2929617A00087955 /* Action Extension.entitlements */,
|
||||
94B2C8C42929617A00087955 /* ActionExtensionViewController.swift */,
|
||||
94B2C8C92929617A00087955 /* Info.plist */,
|
||||
);
|
||||
path = "Action Extension";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D021A66325C3E167008A0C0D /* UIKit */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -723,6 +757,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
D0477F2A25C6EB90005C5368 /* Activities */,
|
||||
94B2C8C12929617A00087955 /* Action Extension */,
|
||||
D0D4301925F08CE600BE5504 /* App Icons */,
|
||||
D0C7D45224F76169001EBDBB /* Assets.xcassets */,
|
||||
D0FE1C9625368A15003EF1EB /* Caches */,
|
||||
|
@ -758,6 +793,7 @@
|
|||
D0666A2124C677B400F3F04B /* Tests.xctest */,
|
||||
D0E5361924E3EB4D00FB1CE1 /* Notification Service Extension.appex */,
|
||||
D08E526C257C36CA00FA2C5F /* Share Extension.appex */,
|
||||
94B2C8BE2929617A00087955 /* Action Extension.appex */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
|
@ -773,6 +809,7 @@
|
|||
D0666A7924C7745A00F3F04B /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
94B2C8BF2929617A00087955 /* UniformTypeIdentifiers.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
|
@ -947,6 +984,23 @@
|
|||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
94B2C8BD2929617A00087955 /* Action Extension */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 94B2C8D02929617A00087955 /* Build configuration list for PBXNativeTarget "Action Extension" */;
|
||||
buildPhases = (
|
||||
94B2C8BA2929617A00087955 /* Sources */,
|
||||
94B2C8BB2929617A00087955 /* Frameworks */,
|
||||
94B2C8BC2929617A00087955 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "Action Extension";
|
||||
productName = "com.metabolist.metatext.action-extension";
|
||||
productReference = 94B2C8BE2929617A00087955 /* Action Extension.appex */;
|
||||
productType = "com.apple.product-type.app-extension";
|
||||
};
|
||||
D047FA8B24C3E21200AF17C5 /* Metatext */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = D047FAB624C3E21200AF17C5 /* Build configuration list for PBXNativeTarget "Metatext" */;
|
||||
|
@ -962,6 +1016,7 @@
|
|||
dependencies = (
|
||||
D0E5361F24E3EB4D00FB1CE1 /* PBXTargetDependency */,
|
||||
D08E5275257C36CA00FA2C5F /* PBXTargetDependency */,
|
||||
94B2C8CB2929617A00087955 /* PBXTargetDependency */,
|
||||
);
|
||||
name = Metatext;
|
||||
packageProductDependencies = (
|
||||
|
@ -1043,10 +1098,13 @@
|
|||
D047FA8024C3E21000AF17C5 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1220;
|
||||
LastSwiftUpdateCheck = 1410;
|
||||
LastUpgradeCheck = 1410;
|
||||
ORGANIZATIONNAME = Metabolist;
|
||||
TargetAttributes = {
|
||||
94B2C8BD2929617A00087955 = {
|
||||
CreatedOnToolsVersion = 14.1;
|
||||
};
|
||||
D047FA8B24C3E21200AF17C5 = {
|
||||
CreatedOnToolsVersion = 12.0;
|
||||
};
|
||||
|
@ -1096,11 +1154,19 @@
|
|||
D0666A2024C677B400F3F04B /* Tests */,
|
||||
D0E5361824E3EB4D00FB1CE1 /* Notification Service Extension */,
|
||||
D08E526B257C36CA00FA2C5F /* Share Extension */,
|
||||
94B2C8BD2929617A00087955 /* Action Extension */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
94B2C8BC2929617A00087955 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
D047FA8A24C3E21200AF17C5 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -1170,6 +1236,14 @@
|
|||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
94B2C8BA2929617A00087955 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
94B2C8C52929617A00087955 /* ActionExtensionViewController.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
D047FA8824C3E21200AF17C5 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -1411,6 +1485,11 @@
|
|||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
94B2C8CB2929617A00087955 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 94B2C8BD2929617A00087955 /* Action Extension */;
|
||||
targetProxy = 94B2C8CA2929617A00087955 /* PBXContainerItemProxy */;
|
||||
};
|
||||
D0666A2724C677B400F3F04B /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = D047FA8B24C3E21200AF17C5 /* Metatext */;
|
||||
|
@ -1472,6 +1551,69 @@
|
|||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
94B2C8CE2929617A00087955 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CODE_SIGN_ENTITLEMENTS = "Action Extension/Action Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = 82HL67AXQ2;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "Action Extension/Info.plist";
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Open in Metatext";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Metabolist. All rights reserved.";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.2;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.6.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.metabolist.metatext.action-extension";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
94B2C8CF2929617A00087955 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CODE_SIGN_ENTITLEMENTS = "Action Extension/Action Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = 82HL67AXQ2;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "Action Extension/Info.plist";
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Open in Metatext";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Metabolist. All rights reserved.";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.2;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.6.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.metabolist.metatext.action-extension";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
D047FAB424C3E21200AF17C5 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
|
@ -1801,6 +1943,15 @@
|
|||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
94B2C8D02929617A00087955 /* Build configuration list for PBXNativeTarget "Action Extension" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
94B2C8CE2929617A00087955 /* Debug */,
|
||||
94B2C8CF2929617A00087955 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
D047FA8324C3E21000AF17C5 /* Build configuration list for PBXProject "Metatext" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1410"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "94B2C8BD2929617A00087955"
|
||||
BuildableName = "Action Extension.appex"
|
||||
BlueprintName = "Action Extension"
|
||||
ReferencedContainer = "container:Metatext.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D047FA8B24C3E21200AF17C5"
|
||||
BuildableName = "Metatext.app"
|
||||
BlueprintName = "Metatext"
|
||||
ReferencedContainer = "container:Metatext.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<RemoteRunnable
|
||||
runnableDebuggingMode = "1"
|
||||
BundleIdentifier = "com.apple.mobilesafari"
|
||||
RemotePath = "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/Applications/MobileSafari.app">
|
||||
</RemoteRunnable>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D047FA8B24C3E21200AF17C5"
|
||||
BuildableName = "Metatext.app"
|
||||
BlueprintName = "Metatext"
|
||||
ReferencedContainer = "container:Metatext.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D047FA8B24C3E21200AF17C5"
|
||||
BuildableName = "Metatext.app"
|
||||
BlueprintName = "Metatext"
|
||||
ReferencedContainer = "container:Metatext.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
|
@ -0,0 +1,96 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1410"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D0E5361824E3EB4D00FB1CE1"
|
||||
BuildableName = "Notification Service Extension.appex"
|
||||
BlueprintName = "Notification Service Extension"
|
||||
ReferencedContainer = "container:Metatext.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D047FA8B24C3E21200AF17C5"
|
||||
BuildableName = "Metatext.app"
|
||||
BlueprintName = "Metatext"
|
||||
ReferencedContainer = "container:Metatext.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D047FA8B24C3E21200AF17C5"
|
||||
BuildableName = "Metatext.app"
|
||||
BlueprintName = "Metatext"
|
||||
ReferencedContainer = "container:Metatext.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D047FA8B24C3E21200AF17C5"
|
||||
BuildableName = "Metatext.app"
|
||||
BlueprintName = "Metatext"
|
||||
ReferencedContainer = "container:Metatext.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
|
@ -0,0 +1,96 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1410"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D08E526B257C36CA00FA2C5F"
|
||||
BuildableName = "Share Extension.appex"
|
||||
BlueprintName = "Share Extension"
|
||||
ReferencedContainer = "container:Metatext.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D047FA8B24C3E21200AF17C5"
|
||||
BuildableName = "Metatext.app"
|
||||
BlueprintName = "Metatext"
|
||||
ReferencedContainer = "container:Metatext.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D047FA8B24C3E21200AF17C5"
|
||||
BuildableName = "Metatext.app"
|
||||
BlueprintName = "Metatext"
|
||||
ReferencedContainer = "container:Metatext.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D047FA8B24C3E21200AF17C5"
|
||||
BuildableName = "Metatext.app"
|
||||
BlueprintName = "Metatext"
|
||||
ReferencedContainer = "container:Metatext.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
|
@ -118,6 +118,21 @@
|
|||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>CFBundleURLIconFile</key>
|
||||
<string>AppIconClassic@3x</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>com.metabolist.metatext</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>metatext</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
|
@ -147,16 +162,16 @@
|
|||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -13,6 +13,8 @@ struct RootView: View {
|
|||
.id(navigationViewModel.identityContext.identity.id)
|
||||
.environmentObject(viewModel)
|
||||
.transition(.opacity)
|
||||
.handlesExternalEvents(preferring: ["*"], allowing: ["*"])
|
||||
.onOpenURL(perform: { openUrl(navigationViewModel, $0) })
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
.onReceive(navigationViewModel.identityContext.$appPreferences.map(\.colorScheme),
|
||||
perform: setColorScheme)
|
||||
|
@ -27,6 +29,37 @@ struct RootView: View {
|
|||
.environmentObject(viewModel)
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
.transition(.opacity)
|
||||
.handlesExternalEvents(preferring: ["*"], allowing: ["*"])
|
||||
.onOpenURL(perform: { openUrl(nil, $0) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Open `metatext:` URLs from the action extension.
|
||||
private func openUrl(_ navigationViewModel: NavigationViewModel?, _ metatextUrl: URL) {
|
||||
guard let navigationViewModel = navigationViewModel else {
|
||||
// TODO: (Vyr) We haven't logged into an instance yet. Opening a URL should show an error message.
|
||||
return
|
||||
}
|
||||
guard
|
||||
let metatextComponents = URLComponents(url: metatextUrl, resolvingAgainstBaseURL: true),
|
||||
metatextComponents.scheme == "metatext"
|
||||
else {
|
||||
return
|
||||
}
|
||||
switch metatextComponents.path {
|
||||
case "search":
|
||||
// Expecting `metatext:search?url=https://…`, which we open by searching for the wrapped URL.
|
||||
guard
|
||||
let searchUrlString = metatextComponents.queryItems?.first(where: { $0.name == "url" })?.value,
|
||||
let searchComponents = URLComponents(string: searchUrlString),
|
||||
searchComponents.scheme == "https",
|
||||
let searchUrl = searchComponents.url
|
||||
else {
|
||||
return
|
||||
}
|
||||
navigationViewModel.navigateToURL(searchUrl)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue