feat: add share action extension

This commit is contained in:
CMK 2021-07-16 16:21:47 +08:00
parent 9a19001df9
commit 8e749fd75b
15 changed files with 564 additions and 26 deletions

View File

@ -7,12 +7,14 @@
import os
import Foundation
import Combine
import CoreData
import AppShared
public final class CoreDataStack {
private(set) var storeDescriptions: [NSPersistentStoreDescription]
public let didFinishLoad = CurrentValueSubject<Bool, Never>(false)
init(persistentStoreDescriptions storeDescriptions: [NSPersistentStoreDescription]) {
self.storeDescriptions = storeDescriptions
@ -33,7 +35,10 @@ public final class CoreDataStack {
*/
let container = CoreDataStack.persistentContainer()
CoreDataStack.configure(persistentContainer: container, storeDescriptions: storeDescriptions)
CoreDataStack.load(persistentContainer: container)
CoreDataStack.load(persistentContainer: container) { [weak self] in
guard let self = self else { return }
self.didFinishLoad.value = true
}
return container
}()
@ -52,7 +57,7 @@ public final class CoreDataStack {
container.persistentStoreDescriptions = storeDescriptions
}
static func load(persistentContainer container: NSPersistentContainer) {
static func load(persistentContainer container: NSPersistentContainer, callback: @escaping () -> Void) {
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
@ -85,6 +90,8 @@ public final class CoreDataStack {
container.viewContext.automaticallyMergesChangesFromParent = true
os_log("%{public}s[%{public}ld], %{public}s: %s", ((#file as NSString).lastPathComponent), #line, #function, storeDescription.debugDescription)
callback()
})
}
@ -96,7 +103,10 @@ extension CoreDataStack {
let oldStoreURL = persistentContainer.persistentStoreCoordinator.url(for: persistentContainer.persistentStoreCoordinator.persistentStores.first!)
try! persistentContainer.persistentStoreCoordinator.destroyPersistentStore(at: oldStoreURL, ofType: NSSQLiteStoreType, options: nil)
CoreDataStack.load(persistentContainer: persistentContainer)
CoreDataStack.load(persistentContainer: persistentContainer) { [weak self] in
guard let self = self else { return }
self.didFinishLoad.value = true
}
}
}

View File

@ -423,7 +423,6 @@
DB9D7C21269824B80054B3DF /* APIService+Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB9D7C20269824B80054B3DF /* APIService+Filter.swift */; };
DB9E0D6F25EE008500CFDD76 /* UIInterpolatingMotionEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB9E0D6E25EE008500CFDD76 /* UIInterpolatingMotionEffect.swift */; };
DBA088DF26958164003EB4B2 /* UserFetchedResultsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA088DE26958164003EB4B2 /* UserFetchedResultsController.swift */; };
DBA0A10925FB3C2B0079C110 /* RoundedEdgesButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA0A10825FB3C2B0079C110 /* RoundedEdgesButton.swift */; };
DBA0A11325FB3FC10079C110 /* ComposeToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA0A11225FB3FC10079C110 /* ComposeToolbarView.swift */; };
DBA1DB80268F84F80052DB59 /* NotificationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA1DB7F268F84F80052DB59 /* NotificationType.swift */; };
DBA465932696B495002B41DB /* APIService+WebFinger.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA465922696B495002B41DB /* APIService+WebFinger.swift */; };
@ -481,6 +480,17 @@
DBBF1DC7265251D400E5B703 /* AutoCompleteViewModel+State.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBBF1DC6265251D400E5B703 /* AutoCompleteViewModel+State.swift */; };
DBBF1DC92652538500E5B703 /* AutoCompleteSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBBF1DC82652538500E5B703 /* AutoCompleteSection.swift */; };
DBBF1DCB2652539E00E5B703 /* AutoCompleteItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBBF1DCA2652539E00E5B703 /* AutoCompleteItem.swift */; };
DBC6461526A170AB00B0E31B /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC6461426A170AB00B0E31B /* ShareViewController.swift */; };
DBC6461826A170AB00B0E31B /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DBC6461626A170AB00B0E31B /* MainInterface.storyboard */; };
DBC6461C26A170AB00B0E31B /* ShareActionExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = DBC6461226A170AB00B0E31B /* ShareActionExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
DBC6462326A1712000B0E31B /* ShareViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC6462226A1712000B0E31B /* ShareViewModel.swift */; };
DBC6462526A1720B00B0E31B /* MastodonUI in Frameworks */ = {isa = PBXBuildFile; productRef = DBC6462426A1720B00B0E31B /* MastodonUI */; };
DBC6462626A1736000B0E31B /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = DB564BCE269F2F83001E39A7 /* Localizable.stringsdict */; };
DBC6462726A1736000B0E31B /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DB3D100F25BAA75E00EAA174 /* Localizable.strings */; };
DBC6462826A1736300B0E31B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DB427DDE25BAA00100D1B89D /* Assets.xcassets */; };
DBC6462926A1736700B0E31B /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98338525C945ED00AD9700 /* Strings.swift */; };
DBC6462B26A1738900B0E31B /* MastodonUI in Frameworks */ = {isa = PBXBuildFile; productRef = DBC6462A26A1738900B0E31B /* MastodonUI */; };
DBC6462C26A176B000B0E31B /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98338625C945ED00AD9700 /* Assets.swift */; };
DBC7A672260C897100E57475 /* StatusContentWarningEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC7A671260C897100E57475 /* StatusContentWarningEditorView.swift */; };
DBC7A67C260DFADE00E57475 /* StatusPublishService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC7A67B260DFADE00E57475 /* StatusPublishService.swift */; };
DBCBCBF4267CB070000F5B51 /* Decode85.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBCBCBF3267CB070000F5B51 /* Decode85.swift */; };
@ -598,6 +608,13 @@
remoteGlobalIDString = DB89B9ED25C10FD0008580ED;
remoteInfo = CoreDataStack;
};
DBC6461A26A170AB00B0E31B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = DB427DCA25BAA00100D1B89D /* Project object */;
proxyType = 1;
remoteGlobalIDString = DBC6461126A170AB00B0E31B;
remoteInfo = ShareActionExtension;
};
DBF8AE18263293E400C9C23C /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = DB427DCA25BAA00100D1B89D /* Project object */;
@ -627,6 +644,7 @@
dstPath = "";
dstSubfolderSpec = 13;
files = (
DBC6461C26A170AB00B0E31B /* ShareActionExtension.appex in Embed App Extensions */,
DBF8AE1A263293E400C9C23C /* NotificationService.appex in Embed App Extensions */,
);
name = "Embed App Extensions";
@ -1068,7 +1086,6 @@
DB9D7C20269824B80054B3DF /* APIService+Filter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Filter.swift"; sourceTree = "<group>"; };
DB9E0D6E25EE008500CFDD76 /* UIInterpolatingMotionEffect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIInterpolatingMotionEffect.swift; sourceTree = "<group>"; };
DBA088DE26958164003EB4B2 /* UserFetchedResultsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserFetchedResultsController.swift; sourceTree = "<group>"; };
DBA0A10825FB3C2B0079C110 /* RoundedEdgesButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedEdgesButton.swift; sourceTree = "<group>"; };
DBA0A11225FB3FC10079C110 /* ComposeToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeToolbarView.swift; sourceTree = "<group>"; };
DBA1DB7F268F84F80052DB59 /* NotificationType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationType.swift; sourceTree = "<group>"; };
DBA465922696B495002B41DB /* APIService+WebFinger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+WebFinger.swift"; sourceTree = "<group>"; };
@ -1121,6 +1138,11 @@
DBBF1DC6265251D400E5B703 /* AutoCompleteViewModel+State.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AutoCompleteViewModel+State.swift"; sourceTree = "<group>"; };
DBBF1DC82652538500E5B703 /* AutoCompleteSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCompleteSection.swift; sourceTree = "<group>"; };
DBBF1DCA2652539E00E5B703 /* AutoCompleteItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCompleteItem.swift; sourceTree = "<group>"; };
DBC6461226A170AB00B0E31B /* ShareActionExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ShareActionExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
DBC6461426A170AB00B0E31B /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
DBC6461726A170AB00B0E31B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
DBC6461926A170AB00B0E31B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
DBC6462226A1712000B0E31B /* ShareViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareViewModel.swift; sourceTree = "<group>"; };
DBC7A671260C897100E57475 /* StatusContentWarningEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusContentWarningEditorView.swift; sourceTree = "<group>"; };
DBC7A67B260DFADE00E57475 /* StatusPublishService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusPublishService.swift; sourceTree = "<group>"; };
DBCBCBF3267CB070000F5B51 /* Decode85.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Decode85.swift; sourceTree = "<group>"; };
@ -1190,6 +1212,7 @@
DB0140BD25C40D7500F9F3CF /* CommonOSLog in Frameworks */,
DB03F7ED268976B5007B274C /* MetaTextView in Frameworks */,
DB89BA0325C10FD0008580ED /* CoreDataStack.framework in Frameworks */,
DBC6462B26A1738900B0E31B /* MastodonUI in Frameworks */,
2D42FF6125C8177C004A627A /* ActiveLabel in Frameworks */,
DB9A487E2603456B008B817C /* UITextView+Placeholder in Frameworks */,
2D939AC825EE14620076FA61 /* CropViewController in Frameworks */,
@ -1253,6 +1276,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
DBC6460F26A170AB00B0E31B /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
DBC6462526A1720B00B0E31B /* MastodonUI in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DBF8AE10263293E400C9C23C /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@ -1458,7 +1489,6 @@
2D42FF8425C8224F004A627A /* HitTestExpandedButton.swift */,
DB118A8B25E4BFB500FAB162 /* HighlightDimmableButton.swift */,
0FAA101125E105390017CCDE /* PrimaryActionButton.swift */,
DBA0A10825FB3C2B0079C110 /* RoundedEdgesButton.swift */,
);
path = Button;
sourceTree = "<group>";
@ -1876,10 +1906,11 @@
DB427DD425BAA00100D1B89D /* Mastodon */,
DB427DEB25BAA00100D1B89D /* MastodonTests */,
DB427DF625BAA00100D1B89D /* MastodonUITests */,
DB6804802637CD4C00430867 /* AppShared */,
DB89B9EF25C10FD0008580ED /* CoreDataStack */,
DB89B9FC25C10FD0008580ED /* CoreDataStackTests */,
DBF8AE14263293E400C9C23C /* NotificationService */,
DB6804802637CD4C00430867 /* AppShared */,
DBC6461326A170AB00B0E31B /* ShareActionExtension */,
DB427DD325BAA00100D1B89D /* Products */,
1EBA4F56E920856A3FC84ACB /* Pods */,
3FE14AD363ED19AE7FF210A6 /* Frameworks */,
@ -1897,6 +1928,7 @@
DB89B9F625C10FD0008580ED /* CoreDataStackTests.xctest */,
DBF8AE13263293E400C9C23C /* NotificationService.appex */,
DB68047F2637CD4C00430867 /* AppShared.framework */,
DBC6461226A170AB00B0E31B /* ShareActionExtension.appex */,
);
name = Products;
sourceTree = "<group>";
@ -2682,6 +2714,17 @@
path = Cell;
sourceTree = "<group>";
};
DBC6461326A170AB00B0E31B /* ShareActionExtension */ = {
isa = PBXGroup;
children = (
DBC6462226A1712000B0E31B /* ShareViewModel.swift */,
DBC6461426A170AB00B0E31B /* ShareViewController.swift */,
DBC6461626A170AB00B0E31B /* MainInterface.storyboard */,
DBC6461926A170AB00B0E31B /* Info.plist */,
);
path = ShareActionExtension;
sourceTree = "<group>";
};
DBCBCBFD2680ADBA000F5B51 /* AsyncHomeTimeline */ = {
isa = PBXGroup;
children = (
@ -2831,6 +2874,7 @@
DBF8AE19263293E400C9C23C /* PBXTargetDependency */,
DB6804852637CD4C00430867 /* PBXTargetDependency */,
DB6804CA2637CE3000430867 /* PBXTargetDependency */,
DBC6461B26A170AB00B0E31B /* PBXTargetDependency */,
);
name = Mastodon;
packageProductDependencies = (
@ -2852,6 +2896,7 @@
DB0E2D2D26833FF700865C3C /* NukeFLAnimatedImagePlugin */,
DB03F7EA268976B5007B274C /* MastodonMeta */,
DB03F7EC268976B5007B274C /* MetaTextView */,
DBC6462A26A1738900B0E31B /* MastodonUI */,
);
productName = Mastodon;
productReference = DB427DD225BAA00100D1B89D /* Mastodon.app */;
@ -2956,6 +3001,26 @@
productReference = DB89B9F625C10FD0008580ED /* CoreDataStackTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
DBC6461126A170AB00B0E31B /* ShareActionExtension */ = {
isa = PBXNativeTarget;
buildConfigurationList = DBC6462126A170AB00B0E31B /* Build configuration list for PBXNativeTarget "ShareActionExtension" */;
buildPhases = (
DBC6460E26A170AB00B0E31B /* Sources */,
DBC6460F26A170AB00B0E31B /* Frameworks */,
DBC6461026A170AB00B0E31B /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = ShareActionExtension;
packageProductDependencies = (
DBC6462426A1720B00B0E31B /* MastodonUI */,
);
productName = ShareActionExtension;
productReference = DBC6461226A170AB00B0E31B /* ShareActionExtension.appex */;
productType = "com.apple.product-type.app-extension";
};
DBF8AE12263293E400C9C23C /* NotificationService */ = {
isa = PBXNativeTarget;
buildConfigurationList = DBF8AE1E263293E400C9C23C /* Build configuration list for PBXNativeTarget "NotificationService" */;
@ -2985,7 +3050,7 @@
DB427DCA25BAA00100D1B89D /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1240;
LastSwiftUpdateCheck = 1250;
LastUpgradeCheck = 1240;
TargetAttributes = {
DB427DD125BAA00100D1B89D = {
@ -3012,6 +3077,9 @@
CreatedOnToolsVersion = 12.4;
TestTargetID = DB427DD125BAA00100D1B89D;
};
DBC6461126A170AB00B0E31B = {
CreatedOnToolsVersion = 12.5.1;
};
DBF8AE12263293E400C9C23C = {
CreatedOnToolsVersion = 12.4;
};
@ -3053,10 +3121,11 @@
DB427DD125BAA00100D1B89D /* Mastodon */,
DB427DE725BAA00100D1B89D /* MastodonTests */,
DB427DF225BAA00100D1B89D /* MastodonUITests */,
DB68047E2637CD4C00430867 /* AppShared */,
DB89B9ED25C10FD0008580ED /* CoreDataStack */,
DB89B9F525C10FD0008580ED /* CoreDataStackTests */,
DBF8AE12263293E400C9C23C /* NotificationService */,
DB68047E2637CD4C00430867 /* AppShared */,
DBC6461126A170AB00B0E31B /* ShareActionExtension */,
);
};
/* End PBXProject section */
@ -3113,6 +3182,17 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
DBC6461026A170AB00B0E31B /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DBC6461826A170AB00B0E31B /* MainInterface.storyboard in Resources */,
DBC6462726A1736000B0E31B /* Localizable.strings in Resources */,
DBC6462826A1736300B0E31B /* Assets.xcassets in Resources */,
DBC6462626A1736000B0E31B /* Localizable.stringsdict in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DBF8AE11263293E400C9C23C /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@ -3364,7 +3444,6 @@
5DDDF1932617442700311060 /* Mastodon+Entity+Account.swift in Sources */,
DBAE3F882615DDF4004B8251 /* UserProviderFacade.swift in Sources */,
2D607AD826242FC500B70763 /* NotificationViewModel.swift in Sources */,
DBA0A10925FB3C2B0079C110 /* RoundedEdgesButton.swift in Sources */,
DBABE3EC25ECAC4B00879EE5 /* WelcomeIllustrationView.swift in Sources */,
DB564BD3269F3B35001E39A7 /* StatusFilterService.swift in Sources */,
DB71FD4625F8C6D200512AE1 /* StatusProvider+UITableViewDataSourcePrefetching.swift in Sources */,
@ -3798,6 +3877,17 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
DBC6460E26A170AB00B0E31B /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DBC6462326A1712000B0E31B /* ShareViewModel.swift in Sources */,
DBC6462926A1736700B0E31B /* Strings.swift in Sources */,
DBC6462C26A176B000B0E31B /* Assets.swift in Sources */,
DBC6461526A170AB00B0E31B /* ShareViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DBF8AE0F263293E400C9C23C /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@ -3860,6 +3950,11 @@
target = DB89B9ED25C10FD0008580ED /* CoreDataStack */;
targetProxy = DB89BA0125C10FD0008580ED /* PBXContainerItemProxy */;
};
DBC6461B26A170AB00B0E31B /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = DBC6461126A170AB00B0E31B /* ShareActionExtension */;
targetProxy = DBC6461A26A170AB00B0E31B /* PBXContainerItemProxy */;
};
DBF8AE19263293E400C9C23C /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = DBF8AE12263293E400C9C23C /* NotificationService */;
@ -3911,6 +4006,14 @@
name = Localizable.stringsdict;
sourceTree = "<group>";
};
DBC6461626A170AB00B0E31B /* MainInterface.storyboard */ = {
isa = PBXVariantGroup;
children = (
DBC6461726A170AB00B0E31B /* Base */,
);
name = MainInterface.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
@ -4036,6 +4139,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 2E1F6A67FDF9771D3E064FDC /* Pods-Mastodon.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
@ -4063,6 +4167,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 75E3471C898DDD9631729B6E /* Pods-Mastodon.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
@ -4325,6 +4430,86 @@
};
name = Release;
};
DBC6461D26A170AB00B0E31B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = ShareActionExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
DBC6461E26A170AB00B0E31B /* ASDK - Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = ShareActionExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = "ASDK - Debug";
};
DBC6461F26A170AB00B0E31B /* ASDK - Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = ShareActionExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = "ASDK - Release";
};
DBC6462026A170AB00B0E31B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = ShareActionExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
DBCBCC0E2680BE3E000F5B51 /* ASDK - Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -4391,6 +4576,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = BD7598A87F4497045EDEF252 /* Pods-Mastodon.asdk - release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
@ -4624,6 +4810,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 1D6D967E77A5357E2C6110D9 /* Pods-Mastodon.asdk - debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
@ -4917,6 +5104,17 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
DBC6462126A170AB00B0E31B /* Build configuration list for PBXNativeTarget "ShareActionExtension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DBC6461D26A170AB00B0E31B /* Debug */,
DBC6461E26A170AB00B0E31B /* ASDK - Debug */,
DBC6461F26A170AB00B0E31B /* ASDK - Release */,
DBC6462026A170AB00B0E31B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
DBF8AE1E263293E400C9C23C /* Build configuration list for PBXNativeTarget "NotificationService" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@ -5169,6 +5367,14 @@
package = DBB525062611EAC0002F1F29 /* XCRemoteSwiftPackageReference "Tabman" */;
productName = Tabman;
};
DBC6462426A1720B00B0E31B /* MastodonUI */ = {
isa = XCSwiftPackageProductDependency;
productName = MastodonUI;
};
DBC6462A26A1738900B0E31B /* MastodonUI */ = {
isa = XCSwiftPackageProductDependency;
productName = MastodonUI;
};
DBF7A0FB26830C33004176A2 /* FPSIndicator */ = {
isa = XCSwiftPackageProductDependency;
package = DBF7A0FA26830C33004176A2 /* XCRemoteSwiftPackageReference "FPSIndicator" */;

View File

@ -12,7 +12,7 @@
<key>CoreDataStack.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>20</integer>
<integer>21</integer>
</dict>
<key>Mastodon - ASDK.xcscheme_^#shared#^_</key>
<dict>
@ -37,7 +37,12 @@
<key>NotificationService.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>21</integer>
<integer>22</integer>
</dict>
<key>ShareActionExtension.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>30</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>

View File

@ -9,17 +9,6 @@ import CoreImage
import CoreImage.CIFilterBuiltins
import UIKit
extension UIImage {
static func placeholder(size: CGSize = CGSize(width: 1, height: 1), color: UIColor) -> UIImage {
let render = UIGraphicsImageRenderer(size: size)
return render.image { (context: UIGraphicsImageRendererContext) in
context.cgContext.setFillColor(color.cgColor)
context.fill(CGRect(origin: .zero, size: size))
}
}
}
// refs: https://www.hackingwithswift.com/example-code/media/how-to-read-the-average-color-of-a-uiimage-using-ciareaaverage
extension UIImage {
@available(iOS 14.0, *)

View File

@ -14,6 +14,7 @@ import MetaTextView
import MastodonMeta
import Meta
import Nuke
import MastodonUI
final class ComposeViewController: UIViewController, NeedsDependency {

View File

@ -7,6 +7,7 @@
import os.log
import UIKit
import MastodonUI
protocol HomeTimelineNavigationBarTitleViewDelegate: AnyObject {
func homeTimelineNavigationBarTitleView(_ titleView: HomeTimelineNavigationBarTitleView, logoButtonDidPressed sender: UIButton)

View File

@ -6,6 +6,7 @@
//
import UIKit
import MastodonUI
final class ProfileRelationshipActionButton: RoundedEdgesButton {

View File

@ -12,6 +12,12 @@ let package = Package(
.library(
name: "MastodonSDK",
targets: ["MastodonSDK"]),
.library(
name: "MastodonUI",
targets: ["MastodonUI"]),
.library(
name: "MastodonExtension",
targets: ["MastodonExtension"]),
],
dependencies: [
.package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", from: "5.0.0"),
@ -27,6 +33,14 @@ let package = Package(
.product(name: "NIOHTTP1", package: "swift-nio"),
]
),
.target(
name: "MastodonUI",
dependencies: ["MastodonExtension"]
),
.target(
name: "MastodonExtension",
dependencies: []
),
.testTarget(
name: "MastodonSDKTests",
dependencies: ["MastodonSDK"]

View File

@ -0,0 +1,19 @@
//
// UIImage.swift
//
//
// Created by MainasuK Cirno on 2021-7-16.
//
import UIKit
extension UIImage {
public static func placeholder(size: CGSize = CGSize(width: 1, height: 1), color: UIColor) -> UIImage {
let render = UIGraphicsImageRenderer(size: size)
return render.image { (context: UIGraphicsImageRendererContext) in
context.cgContext.setFillColor(color.cgColor)
context.fill(CGRect(origin: .zero, size: size))
}
}
}

View File

@ -1,15 +1,15 @@
//
// RoundedEdgesButton.swift
// Mastodon
// MastodonUI
//
// Created by MainasuK Cirno on 2021-3-12.
//
import UIKit
class RoundedEdgesButton: UIButton {
open class RoundedEdgesButton: UIButton {
override func layoutSubviews() {
open override func layoutSubviews() {
super.layoutSubviews()
layer.masksToBounds = true

View File

@ -0,0 +1 @@
@_exported import MastodonExtension

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="jKX-MD-OMs">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Share View Controller-->
<scene sceneID="ceB-am-kn3">
<objects>
<viewController id="j1y-V4-xli" customClass="ShareViewController" customModule="ShareActionExtension" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" opaque="NO" contentMode="scaleToFill" id="wbc-yd-nQP">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="1Xd-am-t49"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<navigationItem key="navigationItem" id="uKc-xP-YOE"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="CEy-Cv-SGf" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1042.0289855072465" y="116.51785714285714"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="hJg-46-PMP">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="jKX-MD-OMs" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="ERE-s0-7dc">
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="j1y-V4-xli" kind="relationship" relationship="rootViewController" id="sD9-c7-jnh"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="bwB-yX-sdw" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="131.8840579710145" y="116.51785714285714"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,45 @@
<?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>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>ShareActionExtension</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>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>4</integer>
<key>NSExtensionActivationSupportsMovieWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsText</key>
<true/>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
</dict>
</dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
</dict>
</dict>
</plist>

View File

@ -0,0 +1,102 @@
//
// ShareViewController.swift
// MastodonShareAction
//
// Created by MainasuK Cirno on 2021-7-16.
//
import os.log
import UIKit
import Combine
import MastodonUI
class ShareViewController: UIViewController {
let logger = Logger(subsystem: "ShareViewController", category: "UI")
var disposeBag = Set<AnyCancellable>()
let viewModel = ShareViewModel()
let publishButton: UIButton = {
let button = RoundedEdgesButton(type: .custom)
button.setTitle(L10n.Scene.Compose.composeAction, for: .normal)
button.titleLabel?.font = .systemFont(ofSize: 14, weight: .bold)
button.setBackgroundImage(.placeholder(color: Asset.Colors.brandBlue.color), for: .normal)
button.setBackgroundImage(.placeholder(color: Asset.Colors.brandBlue.color.withAlphaComponent(0.5)), for: .highlighted)
button.setBackgroundImage(.placeholder(color: Asset.Colors.Button.disabled.color), for: .disabled)
button.setTitleColor(.white, for: .normal)
button.contentEdgeInsets = UIEdgeInsets(top: 6, left: 16, bottom: 5, right: 16) // set 28pt height
button.adjustsImageWhenHighlighted = false
return button
}()
private(set) lazy var cancelBarButtonItem = UIBarButtonItem(title: L10n.Common.Controls.Actions.cancel, style: .plain, target: self, action: #selector(ShareViewController.cancelBarButtonItemPressed(_:)))
private(set) lazy var publishBarButtonItem: UIBarButtonItem = {
let barButtonItem = UIBarButtonItem(customView: publishButton)
barButtonItem.target = self
barButtonItem.action = #selector(ShareViewController.publishBarButtonItemPressed(_:))
return barButtonItem
}()
let activityIndicatorBarButtonItem: UIBarButtonItem = {
let indicatorView = UIActivityIndicatorView(style: .medium)
let barButtonItem = UIBarButtonItem(customView: indicatorView)
indicatorView.startAnimating()
return barButtonItem
}()
// let tableView: ComposeTableView = {
// let tableView = ComposeTableView()
// tableView.register(ComposeStatusContentTableViewCell.self, forCellReuseIdentifier: String(describing: ComposeStatusContentTableViewCell.self))
// tableView.register(ComposeStatusAttachmentTableViewCell.self, forCellReuseIdentifier: String(describing: ComposeStatusAttachmentTableViewCell.self))
// tableView.alwaysBounceVertical = true
// tableView.separatorStyle = .none
// tableView.tableFooterView = UIView()
// return tableView
// }()
}
extension ShareViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = Asset.Colors.Background.systemBackground.color
navigationItem.leftBarButtonItem = cancelBarButtonItem
viewModel.isBusy
.receive(on: DispatchQueue.main)
.sink { [weak self] isBusy in
guard let self = self else { return }
self.navigationItem.rightBarButtonItem = isBusy ? self.activityIndicatorBarButtonItem : self.publishBarButtonItem
}
.store(in: &disposeBag)
// viewModel.authentication
// .receive(on: DispatchQueue.main)
// .sink { [weak self] result in
// guard let self = self else { return }
// }
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
viewModel.viewDidAppear.value = true
// extensionContext
}
}
extension ShareViewController {
@objc private func cancelBarButtonItemPressed(_ sender: UIBarButtonItem) {
logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
extensionContext?.cancelRequest(withError: ShareViewModel.ShareError.userCancelShare)
}
@objc private func publishBarButtonItemPressed(_ sender: UIBarButtonItem) {
logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
}
}

View File

@ -0,0 +1,99 @@
//
// ShareViewModel.swift
// MastodonShareAction
//
// Created by MainasuK Cirno on 2021-7-16.
//
import os.log
import Foundation
import Combine
import CoreData
import CoreDataStack
final class ShareViewModel {
let logger = Logger(subsystem: "ShareViewModel", category: "logic")
var disposeBag = Set<AnyCancellable>()
// input
let viewDidAppear = CurrentValueSubject<Bool, Never>(false)
private var coreDataStack: CoreDataStack?
var managedObjectContext: NSManagedObjectContext?
// output
let authentication = CurrentValueSubject<Result<MastodonAuthentication, Error>?, Never>(nil)
let isFetchAuthentication = CurrentValueSubject<Bool, Never>(true)
let isBusy = CurrentValueSubject<Bool, Never>(true)
let isValid = CurrentValueSubject<Bool, Never>(false)
init() {
viewDidAppear.receive(on: DispatchQueue.main)
.removeDuplicates()
.sink { [weak self] viewDidAppear in
guard let self = self else { return }
guard viewDidAppear else { return }
self.setupCoreData()
}
.store(in: &disposeBag)
authentication
.map { result in result == nil }
.assign(to: \.value, on: isFetchAuthentication)
.store(in: &disposeBag)
isFetchAuthentication
.receive(on: DispatchQueue.main)
.assign(to: \.value, on: isBusy)
.store(in: &disposeBag)
}
}
extension ShareViewModel {
enum ShareError: Error {
case `internal`(error: Error)
case userCancelShare
case missingAuthentication
}
}
extension ShareViewModel {
private func setupCoreData() {
logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
DispatchQueue.global().async {
let _coreDataStack = CoreDataStack()
self.coreDataStack = _coreDataStack
self.managedObjectContext = _coreDataStack.persistentContainer.viewContext
_coreDataStack.didFinishLoad
.receive(on: RunLoop.main)
.sink { [weak self] didFinishLoad in
guard let self = self else { return }
guard didFinishLoad else { return }
guard let managedObjectContext = self.managedObjectContext else { return }
self.logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): fetch authentication…")
managedObjectContext.perform {
do {
let request = MastodonAuthentication.sortedFetchRequest
let authentications = try managedObjectContext.fetch(request)
let authentication = authentications.sorted(by: { $0.activedAt > $1.activedAt }).first
guard let activeAuthentication = authentication else {
self.authentication.value = .failure(ShareError.missingAuthentication)
return
}
self.authentication.value = .success(activeAuthentication)
self.logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): fetch authentication success \(activeAuthentication.userID)")
} catch {
self.authentication.value = .failure(ShareError.internal(error: error))
self.logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): fetch authentication fail \(error.localizedDescription)")
assertionFailure(error.localizedDescription)
}
}
}
.store(in: &self.disposeBag)
}
}
}