Merge tag '1.0.0' into develop

no message
This commit is contained in:
CMK 2021-07-30 18:57:21 +08:00
commit 28838b65f1
30 changed files with 171 additions and 79 deletions

View File

@ -534,7 +534,7 @@
} }
}, },
"footer": { "footer": {
"mastodon_description": "Mastodon is open source software. You can contribute or report issues on GitHub at %s (%s)" "mastodon_description": "Mastodon is open source software. You can report issues on GitHub at %s (%s)"
}, },
"keyboard": { "keyboard": {
"close_settings_window": "Close Settings Window" "close_settings_window": "Close Settings Window"

View File

@ -4482,7 +4482,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 47; CURRENT_PROJECT_VERSION = 50;
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
DEVELOPMENT_TEAM = 5Z4GVSS33P; DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = Mastodon/Info.plist; INFOPLIST_FILE = Mastodon/Info.plist;
@ -4490,7 +4490,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 0.9.4; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -4509,7 +4509,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 47; CURRENT_PROJECT_VERSION = 50;
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
DEVELOPMENT_TEAM = 5Z4GVSS33P; DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = Mastodon/Info.plist; INFOPLIST_FILE = Mastodon/Info.plist;
@ -4517,7 +4517,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 0.9.4; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -4772,7 +4772,7 @@
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 47; CURRENT_PROJECT_VERSION = 50;
DEVELOPMENT_TEAM = 5Z4GVSS33P; DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = MastodonIntent/Info.plist; INFOPLIST_FILE = MastodonIntent/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -4780,7 +4780,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 0.9.4; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.MastodonIntent; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.MastodonIntent;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -4796,7 +4796,7 @@
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 47; CURRENT_PROJECT_VERSION = 50;
DEVELOPMENT_TEAM = 5Z4GVSS33P; DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = MastodonIntent/Info.plist; INFOPLIST_FILE = MastodonIntent/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -4804,7 +4804,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 0.9.4; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.MastodonIntent; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.MastodonIntent;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -4820,7 +4820,7 @@
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 47; CURRENT_PROJECT_VERSION = 50;
DEVELOPMENT_TEAM = 5Z4GVSS33P; DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = MastodonIntent/Info.plist; INFOPLIST_FILE = MastodonIntent/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -4828,7 +4828,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 0.9.4; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.MastodonIntent; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.MastodonIntent;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -4844,7 +4844,7 @@
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 47; CURRENT_PROJECT_VERSION = 50;
DEVELOPMENT_TEAM = 5Z4GVSS33P; DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = MastodonIntent/Info.plist; INFOPLIST_FILE = MastodonIntent/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -4852,7 +4852,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 0.9.4; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.MastodonIntent; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.MastodonIntent;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -4868,7 +4868,7 @@
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 47; CURRENT_PROJECT_VERSION = 50;
DEVELOPMENT_TEAM = 5Z4GVSS33P; DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = ShareActionExtension/Info.plist; INFOPLIST_FILE = ShareActionExtension/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -4876,7 +4876,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 0.9.4; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -4892,7 +4892,7 @@
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 47; CURRENT_PROJECT_VERSION = 50;
DEVELOPMENT_TEAM = 5Z4GVSS33P; DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = ShareActionExtension/Info.plist; INFOPLIST_FILE = ShareActionExtension/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -4900,7 +4900,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 0.9.4; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -4916,7 +4916,7 @@
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 47; CURRENT_PROJECT_VERSION = 50;
DEVELOPMENT_TEAM = 5Z4GVSS33P; DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = ShareActionExtension/Info.plist; INFOPLIST_FILE = ShareActionExtension/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -4924,7 +4924,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 0.9.4; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -4940,7 +4940,7 @@
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 47; CURRENT_PROJECT_VERSION = 50;
DEVELOPMENT_TEAM = 5Z4GVSS33P; DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = ShareActionExtension/Info.plist; INFOPLIST_FILE = ShareActionExtension/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -4948,7 +4948,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 0.9.4; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -5017,7 +5017,6 @@
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = ASDK; SWIFT_ACTIVE_COMPILATION_CONDITIONS = ASDK;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
}; };
name = "ASDK - Release"; name = "ASDK - Release";
@ -5031,7 +5030,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 47; CURRENT_PROJECT_VERSION = 50;
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
DEVELOPMENT_TEAM = 5Z4GVSS33P; DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = Mastodon/Info.plist; INFOPLIST_FILE = Mastodon/Info.plist;
@ -5039,7 +5038,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 0.9.4; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -5145,7 +5144,7 @@
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 47; CURRENT_PROJECT_VERSION = 50;
DEVELOPMENT_TEAM = 5Z4GVSS33P; DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = NotificationService/Info.plist; INFOPLIST_FILE = NotificationService/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -5153,7 +5152,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 0.9.4; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -5265,7 +5264,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 47; CURRENT_PROJECT_VERSION = 50;
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
DEVELOPMENT_TEAM = 5Z4GVSS33P; DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = Mastodon/Info.plist; INFOPLIST_FILE = Mastodon/Info.plist;
@ -5273,7 +5272,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 0.9.4; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -5379,7 +5378,7 @@
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 47; CURRENT_PROJECT_VERSION = 50;
DEVELOPMENT_TEAM = 5Z4GVSS33P; DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = NotificationService/Info.plist; INFOPLIST_FILE = NotificationService/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -5387,7 +5386,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 0.9.4; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -5433,7 +5432,7 @@
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 47; CURRENT_PROJECT_VERSION = 50;
DEVELOPMENT_TEAM = 5Z4GVSS33P; DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = NotificationService/Info.plist; INFOPLIST_FILE = NotificationService/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -5441,7 +5440,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 0.9.4; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -5456,7 +5455,7 @@
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 47; CURRENT_PROJECT_VERSION = 50;
DEVELOPMENT_TEAM = 5Z4GVSS33P; DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = NotificationService/Info.plist; INFOPLIST_FILE = NotificationService/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -5464,7 +5463,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 0.9.4; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;

View File

@ -74,6 +74,7 @@
savedToolIdentifier = "" savedToolIdentifier = ""
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES" debugDocumentVersioning = "YES"
askForAppToLaunch = "Yes"
launchAutomaticallySubstyle = "2"> launchAutomaticallySubstyle = "2">
<BuildableProductRunnable <BuildableProductRunnable
runnableDebuggingMode = "0"> runnableDebuggingMode = "0">

View File

@ -7,12 +7,12 @@
<key>AppShared.xcscheme_^#shared#^_</key> <key>AppShared.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>31</integer> <integer>26</integer>
</dict> </dict>
<key>CoreDataStack.xcscheme_^#shared#^_</key> <key>CoreDataStack.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>30</integer> <integer>24</integer>
</dict> </dict>
<key>Mastodon - ASDK.xcscheme_^#shared#^_</key> <key>Mastodon - ASDK.xcscheme_^#shared#^_</key>
<dict> <dict>
@ -37,7 +37,7 @@
<key>MastodonIntent.xcscheme_^#shared#^_</key> <key>MastodonIntent.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>29</integer> <integer>25</integer>
</dict> </dict>
<key>MastodonIntents.xcscheme_^#shared#^_</key> <key>MastodonIntents.xcscheme_^#shared#^_</key>
<dict> <dict>
@ -52,12 +52,12 @@
<key>NotificationService.xcscheme_^#shared#^_</key> <key>NotificationService.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>3</integer> <integer>4</integer>
</dict> </dict>
<key>ShareActionExtension.xcscheme_^#shared#^_</key> <key>ShareActionExtension.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>28</integer> <integer>23</integer>
</dict> </dict>
</dict> </dict>
<key>SuppressBuildableAutocreation</key> <key>SuppressBuildableAutocreation</key>

View File

@ -111,9 +111,11 @@ internal enum Asset {
} }
} }
internal enum Settings { internal enum Settings {
internal static let appearanceAutomatic = ImageAsset(name: "Settings/appearance.automatic") internal static let blackAuto = ImageAsset(name: "Settings/black.auto")
internal static let appearanceDark = ImageAsset(name: "Settings/appearance.dark") internal static let black = ImageAsset(name: "Settings/black")
internal static let appearanceLight = ImageAsset(name: "Settings/appearance.light") internal static let darkAuto = ImageAsset(name: "Settings/dark.auto")
internal static let dark = ImageAsset(name: "Settings/dark")
internal static let light = ImageAsset(name: "Settings/light")
} }
internal enum Theme { internal enum Theme {
internal enum Mastodon { internal enum Mastodon {

View File

@ -923,7 +923,7 @@ internal enum L10n {
/// Settings /// Settings
internal static let title = L10n.tr("Localizable", "Scene.Settings.Title") internal static let title = L10n.tr("Localizable", "Scene.Settings.Title")
internal enum Footer { internal enum Footer {
/// Mastodon is open source software. You can contribute or report issues on GitHub at %@ (%@) /// Mastodon is open source software. You can report issues on GitHub at %@ (%@)
internal static func mastodonDescription(_ p1: Any, _ p2: Any) -> String { internal static func mastodonDescription(_ p1: Any, _ p2: Any) -> String {
return L10n.tr("Localizable", "Scene.Settings.Footer.MastodonDescription", String(describing: p1), String(describing: p2)) return L10n.tr("Localizable", "Scene.Settings.Footer.MastodonDescription", String(describing: p1), String(describing: p2))
} }

View File

@ -1,7 +1,7 @@
{ {
"images" : [ "images" : [
{ {
"filename" : "iPhone 11 Pro _ X - 1.pdf", "filename" : "Mixed_Black_Light.png",
"idiom" : "universal" "idiom" : "universal"
} }
], ],

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

View File

@ -1,7 +1,7 @@
{ {
"images" : [ "images" : [
{ {
"filename" : "iPhone 11 Pro _ X - 1 (1).pdf", "filename" : "Home Black.png",
"idiom" : "universal" "idiom" : "universal"
} }
], ],

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

View File

@ -1,7 +1,7 @@
{ {
"images" : [ "images" : [
{ {
"filename" : "iPhone 11 Pro _ X - 1 (2).pdf", "filename" : "Mixed_Dark_Light.png",
"idiom" : "universal" "idiom" : "universal"
} }
], ],

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Home Dark.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Home Light.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 KiB

View File

@ -315,7 +315,7 @@ any server.";
"Scene.ServerRules.Subtitle" = "These rules are set by the admins of %@."; "Scene.ServerRules.Subtitle" = "These rules are set by the admins of %@.";
"Scene.ServerRules.TermsOfService" = "terms of service"; "Scene.ServerRules.TermsOfService" = "terms of service";
"Scene.ServerRules.Title" = "Some ground rules."; "Scene.ServerRules.Title" = "Some ground rules.";
"Scene.Settings.Footer.MastodonDescription" = "Mastodon is open source software. You can contribute or report issues on GitHub at %@ (%@)"; "Scene.Settings.Footer.MastodonDescription" = "Mastodon is open source software. You can report issues on GitHub at %@ (%@)";
"Scene.Settings.Keyboard.CloseSettingsWindow" = "Close Settings Window"; "Scene.Settings.Keyboard.CloseSettingsWindow" = "Close Settings Window";
"Scene.Settings.Section.Appearance.Automatic" = "Automatic"; "Scene.Settings.Section.Appearance.Automatic" = "Automatic";
"Scene.Settings.Section.Appearance.Dark" = "Always Dark"; "Scene.Settings.Section.Appearance.Dark" = "Always Dark";

View File

@ -315,7 +315,7 @@ any server.";
"Scene.ServerRules.Subtitle" = "These rules are set by the admins of %@."; "Scene.ServerRules.Subtitle" = "These rules are set by the admins of %@.";
"Scene.ServerRules.TermsOfService" = "terms of service"; "Scene.ServerRules.TermsOfService" = "terms of service";
"Scene.ServerRules.Title" = "Some ground rules."; "Scene.ServerRules.Title" = "Some ground rules.";
"Scene.Settings.Footer.MastodonDescription" = "Mastodon is open source software. You can contribute or report issues on GitHub at %@ (%@)"; "Scene.Settings.Footer.MastodonDescription" = "Mastodon is open source software. You can report issues on GitHub at %@ (%@)";
"Scene.Settings.Keyboard.CloseSettingsWindow" = "Close Settings Window"; "Scene.Settings.Keyboard.CloseSettingsWindow" = "Close Settings Window";
"Scene.Settings.Section.Appearance.Automatic" = "Automatic"; "Scene.Settings.Section.Appearance.Automatic" = "Automatic";
"Scene.Settings.Section.Appearance.Dark" = "Always Dark"; "Scene.Settings.Section.Appearance.Dark" = "Always Dark";

View File

@ -105,18 +105,18 @@ class SettingsViewController: UIViewController, NeedsDependency {
}() }()
let tableFooterLabel = MetaLabel(style: .settingTableFooter) let tableFooterLabel = MetaLabel(style: .settingTableFooter)
lazy var tableFooterView: UIView = { // lazy var tableFooterView: UIView = {
// init with a frame to fix a conflict ('UIView-Encapsulated-Layout-Height' UIStackView:0x7ffe41e47da0.height == 0) // // init with a frame to fix a conflict ('UIView-Encapsulated-Layout-Height' UIStackView:0x7ffe41e47da0.height == 0)
let view = UIStackView(frame: CGRect(x: 0, y: 0, width: 320, height: 320)) // let view = UIStackView(frame: CGRect(x: 0, y: 0, width: 320, height: 320))
view.isLayoutMarginsRelativeArrangement = true // view.isLayoutMarginsRelativeArrangement = true
view.layoutMargins = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20) // view.layoutMargins = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
view.axis = .vertical // view.axis = .vertical
view.alignment = .center // view.alignment = .center
//
tableFooterLabel.linkDelegate = self // tableFooterLabel.linkDelegate = self
view.addArrangedSubview(tableFooterLabel) // view.addArrangedSubview(tableFooterLabel)
return view // return view
}() // }()
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
@ -259,7 +259,7 @@ class SettingsViewController: UIViewController, NeedsDependency {
settingsAppearanceTableViewCellDelegate: self, settingsAppearanceTableViewCellDelegate: self,
settingsToggleCellDelegate: self settingsToggleCellDelegate: self
) )
tableView.tableFooterView = tableFooterView // tableView.tableFooterView = tableFooterView
} }
func alertToSignout() { func alertToSignout() {

View File

@ -10,6 +10,9 @@ import UIKit
class AppearanceView: UIView { class AppearanceView: UIView {
lazy var imageView: UIImageView = { lazy var imageView: UIImageView = {
let view = UIImageView() let view = UIImageView()
view.layer.masksToBounds = true
view.layer.cornerRadius = 14
view.layer.cornerCurve = .continuous
// accessibility // accessibility
view.accessibilityIgnoresInvertColors = true view.accessibilityIgnoresInvertColors = true
return view return view

View File

@ -16,25 +16,25 @@ class SettingsAppearanceTableViewCell: UITableViewCell {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
static let spacing: CGFloat = 18
weak var delegate: SettingsAppearanceTableViewCellDelegate? weak var delegate: SettingsAppearanceTableViewCellDelegate?
var appearance: SettingsItem.AppearanceMode = .automatic var appearance: SettingsItem.AppearanceMode = .automatic
lazy var stackView: UIStackView = { lazy var stackView: UIStackView = {
let view = UIStackView() let view = UIStackView()
view.isLayoutMarginsRelativeArrangement = true
view.layoutMargins = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
view.axis = .horizontal view.axis = .horizontal
view.distribution = .fillEqually view.distribution = .fillEqually
view.spacing = 18 view.spacing = SettingsAppearanceTableViewCell.spacing
view.translatesAutoresizingMaskIntoConstraints = false view.translatesAutoresizingMaskIntoConstraints = false
return view return view
}() }()
let automatic = AppearanceView(image: Asset.Settings.appearanceAutomatic.image, let automatic = AppearanceView(image: Asset.Settings.darkAuto.image,
title: L10n.Scene.Settings.Section.Appearance.automatic) title: L10n.Scene.Settings.Section.Appearance.automatic)
let light = AppearanceView(image: Asset.Settings.appearanceLight.image, let light = AppearanceView(image: Asset.Settings.light.image,
title: L10n.Scene.Settings.Section.Appearance.light) title: L10n.Scene.Settings.Section.Appearance.light)
let dark = AppearanceView(image: Asset.Settings.appearanceDark.image, let dark = AppearanceView(image: Asset.Settings.dark.image,
title: L10n.Scene.Settings.Section.Appearance.dark) title: L10n.Scene.Settings.Section.Appearance.dark)
lazy var automaticTap: UITapGestureRecognizer = { lazy var automaticTap: UITapGestureRecognizer = {
@ -80,6 +80,8 @@ class SettingsAppearanceTableViewCell: UITableViewCell {
subview.removeFromSuperview() subview.removeFromSuperview()
} }
} }
setupAsset(theme: ThemeService.shared.currentTheme.value)
} }
func update(with data: SettingsItem.AppearanceMode) { func update(with data: SettingsItem.AppearanceMode) {
@ -115,10 +117,36 @@ class SettingsAppearanceTableViewCell: UITableViewCell {
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: contentView.topAnchor), stackView.topAnchor.constraint(equalTo: contentView.topAnchor),
stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), stackView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor),
stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), stackView.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor),
]) ])
setupAsset(theme: ThemeService.shared.currentTheme.value)
ThemeService.shared.currentTheme
.receive(on: DispatchQueue.main)
.sink { [weak self] theme in
guard let self = self else { return }
self.setupAsset(theme: theme)
}
.store(in: &disposeBag)
}
private func setupAsset(theme: Theme) {
let aspectRatio = Asset.Settings.light.image.size
let width = floor(frame.width - 2 * SettingsAppearanceTableViewCell.spacing) / 3
let height = width / aspectRatio.width * aspectRatio.height
let size = CGSize(width: width, height: height)
light.imageView.image = Asset.Settings.light.image.af.imageAspectScaled(toFill: size, scale: UIScreen.main.scale)
switch theme.themeName {
case .mastodon:
automatic.imageView.image = Asset.Settings.darkAuto.image.af.imageAspectScaled(toFill: size, scale: UIScreen.main.scale)
dark.imageView.image = Asset.Settings.dark.image.af.imageAspectScaled(toFill: size, scale: UIScreen.main.scale)
case .system:
automatic.imageView.image = Asset.Settings.blackAuto.image.af.imageAspectScaled(toFill: size, scale: UIScreen.main.scale)
dark.imageView.image = Asset.Settings.black.image.af.imageAspectScaled(toFill: size, scale: UIScreen.main.scale)
}
} }
// MARK: - Actions // MARK: - Actions

View File

@ -19,7 +19,7 @@ extension APIService {
private static let clientName = "Mastodon for iOS" private static let clientName = "Mastodon for iOS"
#endif #endif
private static let appWebsite = "https://joinmastodon.org/apps" private static let appWebsite = "https://app.joinmastodon.org/ios"
func createApplication(domain: String) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Application>, Error> { func createApplication(domain: String) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Application>, Error> {
let query = Mastodon.API.App.CreateQuery( let query = Mastodon.API.App.CreateQuery(

View File

@ -8,6 +8,9 @@
import UIKit import UIKit
struct MastodonTheme: Theme { struct MastodonTheme: Theme {
let themeName: ThemeName = .mastodon
let systemBackgroundColor = Asset.Theme.Mastodon.systemBackground.color let systemBackgroundColor = Asset.Theme.Mastodon.systemBackground.color
let secondarySystemBackgroundColor = Asset.Theme.Mastodon.secondarySystemBackground.color let secondarySystemBackgroundColor = Asset.Theme.Mastodon.secondarySystemBackground.color
let tertiarySystemBackgroundColor = Asset.Theme.Mastodon.tertiarySystemBackground.color let tertiarySystemBackgroundColor = Asset.Theme.Mastodon.tertiarySystemBackground.color

View File

@ -8,6 +8,9 @@
import UIKit import UIKit
struct SystemTheme: Theme { struct SystemTheme: Theme {
let themeName: ThemeName = .system
let systemBackgroundColor = Asset.Theme.System.systemBackground.color let systemBackgroundColor = Asset.Theme.System.systemBackground.color
let secondarySystemBackgroundColor = Asset.Theme.System.secondarySystemBackground.color let secondarySystemBackgroundColor = Asset.Theme.System.secondarySystemBackground.color
let tertiarySystemBackgroundColor = Asset.Theme.System.tertiarySystemBackground.color let tertiarySystemBackgroundColor = Asset.Theme.System.tertiarySystemBackground.color

View File

@ -9,6 +9,8 @@ import UIKit
public protocol Theme { public protocol Theme {
var themeName: ThemeName { get }
var systemBackgroundColor: UIColor { get } var systemBackgroundColor: UIColor { get }
var secondarySystemBackgroundColor: UIColor { get } var secondarySystemBackgroundColor: UIColor { get }
var tertiarySystemBackgroundColor: UIColor { get } var tertiarySystemBackgroundColor: UIColor { get }

View File

@ -131,11 +131,15 @@ extension SceneDelegate {
case "org.joinmastodon.app.new-post": case "org.joinmastodon.app.new-post":
if coordinator?.tabBarController.topMost is ComposeViewController { if coordinator?.tabBarController.topMost is ComposeViewController {
logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): composing…") logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): composing…")
} else {
if AppContext.shared.authenticationService.activeMastodonAuthenticationBox.value == nil {
logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): not authenticated")
} else { } else {
let composeViewModel = ComposeViewModel(context: AppContext.shared, composeKind: .post) let composeViewModel = ComposeViewModel(context: AppContext.shared, composeKind: .post)
coordinator?.present(scene: .compose(viewModel: composeViewModel), from: nil, transition: .modal(animated: true, completion: nil)) coordinator?.present(scene: .compose(viewModel: composeViewModel), from: nil, transition: .modal(animated: true, completion: nil))
logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): present compose scene") logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): present compose scene")
} }
}
case "org.joinmastodon.app.search": case "org.joinmastodon.app.search":
coordinator?.switchToTabBar(tab: .search) coordinator?.switchToTabBar(tab: .search)
logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): select search tab") logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): select search tab")

View File

@ -3,12 +3,12 @@
## Requirements ## Requirements
- Xcode 12.4+ - Xcode 12.5+
- Swift 5.3+ - Swift 5.3+
- iOS 14.0+ - iOS 14.0+
## Setup ## Setup
We needs the latest version Xcode from App Store. And install Cocoapods for dependency management. We needs the latest version Xcode from App Store. And use Cocoapods for dependency management.
### CocoaPods ### CocoaPods
@ -16,7 +16,8 @@ We needs the latest version Xcode from App Store. And install Cocoapods for depe
```zsh ```zsh
# install cocoapods from Homebrew # install cocoapods from Homebrew
brew install cocoapods sudo gem install cocoapods
sudo gem install cocoapods-keys
pod install pod install
``` ```
@ -24,7 +25,8 @@ pod install
```zsh ```zsh
# install cocoapods from Homebrew # install cocoapods from Homebrew
brew install cocoapods sudo gem install cocoapods
sudo gem install cocoapods-keys
# pod install may not works on M1 Mac. Fix by install ffi # pod install may not works on M1 Mac. Fix by install ffi
# ref: https://github.com/CocoaPods/CocoaPods/issues/10220 # ref: https://github.com/CocoaPods/CocoaPods/issues/10220
@ -40,22 +42,43 @@ arch -x86_64 pod install
2. Check the signing settings make sure choose a team. [More info…](https://help.apple.com/xcode/mac/current/#/dev23aab79b4) 2. Check the signing settings make sure choose a team. [More info…](https://help.apple.com/xcode/mac/current/#/dev23aab79b4)
3. Select `Mastodon` scheme and run it. 3. Select `Mastodon` scheme and run it.
#### Contributors
The app require the `App Group` capability. To make sure it works for your developer membership. Please check [AppName.swift](AppShared/AppName.swift) file and set another unique `groupID` and update `App Group` settings.
The app is compatible with [toot-relay](https://github.com/DagAgren/toot-relay) APNs. You can set your push notification endpoint via cocoapod-keys.
## Acknowledgements ## Acknowledgements
- [ActiveLabel](https://github.com/TwidereProject/ActiveLabel.swift)
- [AlamofireImage](https://github.com/Alamofire/AlamofireImage) - [AlamofireImage](https://github.com/Alamofire/AlamofireImage)
- [AlamofireNetworkActivityIndicator](https://github.com/Alamofire/AlamofireNetworkActivityIndicator) - [AlamofireNetworkActivityIndicator](https://github.com/Alamofire/AlamofireNetworkActivityIndicator)
- [Alamofire](https://github.com/Alamofire/Alamofire) - [Alamofire](https://github.com/Alamofire/Alamofire)
- [CommonOSLog](https://github.com/mainasuk/CommonOSLog) - [CommonOSLog](https://github.com/mainasuk/CommonOSLog)
- [CryptoSwift](https://github.com/krzyzanowskim/CryptoSwift) - [CryptoSwift](https://github.com/krzyzanowskim/CryptoSwift)
- [DateToolSwift](https://github.com/MatthewYork/DateTools) - [DateToolSwift](https://github.com/MatthewYork/DateTools)
- [DiffableDataSources](https://github.com/ra1028/DiffableDataSources)
- [DifferenceKit](https://github.com/ra1028/DifferenceKit)
- [FLAnimatedImage](https://github.com/Flipboard/FLAnimatedImage)
- [FLEX](https://github.com/FLEXTool/FLEX)
- [FPSIndicator](https://github.com/MainasuK/FPSIndicator)
- [Fuzi](https://github.com/cezheng/Fuzi)
- [Kanna](https://github.com/tid-kijyun/Kanna) - [Kanna](https://github.com/tid-kijyun/Kanna)
- [KeychainAccess](https://github.com/kishikawakatsumi/KeychainAccess.git) - [KeychainAccess](https://github.com/kishikawakatsumi/KeychainAccess.git)
- [Kingfisher](https://github.com/onevcat/Kingfisher) - [Kingfisher](https://github.com/onevcat/Kingfisher)
- [MetaTextKit](https://github.com/TwidereProject/MetaTextKit)
- [Nuke-FLAnimatedImage-Plugin](https://github.com/kean/Nuke-FLAnimatedImage-Plugin)
- [Nuke](https://github.com/kean/Nuke)
- [Pageboy](https://github.com/uias/Pageboy#the-basics)
- [SDWebImage](https://github.com/SDWebImage/SDWebImage)
- [swift-nio](https://github.com/apple/swift-nio)
- [SwiftGen](https://github.com/SwiftGen/SwiftGen) - [SwiftGen](https://github.com/SwiftGen/SwiftGen)
- [SwiftUI-Introspect](https://github.com/siteline/SwiftUI-Introspect)
- [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON) - [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON)
- [TwitterTextEditor](https://github.com/twitter/TwitterTextEditor) - [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON)
- [Tabman](https://github.com/uias/Tabman)
- [Texture](https://github.com/TextureGroup/Texture)
- [ThirdPartyMailer](https://github.com/vtourraine/ThirdPartyMailer)
- [TOCropViewController](https://github.com/TimOliver/TOCropViewController)
- [TwitterProfile](https://github.com/OfTheWolf/TwitterProfile) - [TwitterProfile](https://github.com/OfTheWolf/TwitterProfile)
- [UITextView-Placeholder](https://github.com/devxoul/UITextView-Placeholder) - [UITextView-Placeholder](https://github.com/devxoul/UITextView-Placeholder)