feat: persist notification keys into Keychian
This commit is contained in:
parent
59760696b5
commit
aca358db26
|
@ -0,0 +1,12 @@
|
|||
//
|
||||
// AppName.swift
|
||||
// AppShared
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-4-27.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum AppName {
|
||||
public static let groupID = "group.org.joinmastodon.mastodon-temp"
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
//
|
||||
// AppSecret.swift
|
||||
// AppShared
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-4-27.
|
||||
//
|
||||
|
||||
|
||||
import Foundation
|
||||
import CryptoKit
|
||||
import KeychainAccess
|
||||
import Keys
|
||||
|
||||
public final class AppSecret {
|
||||
|
||||
public static let keychain = Keychain(service: "org.joinmastodon.Mastodon.keychain", accessGroup: AppName.groupID)
|
||||
|
||||
static let notificationPrivateKeyName = "notification-private-key-base64"
|
||||
static let notificationAuthName = "notification-auth-base64"
|
||||
|
||||
public let notificationEndpoint: String
|
||||
|
||||
public var notificationPrivateKey: P256.KeyAgreement.PrivateKey {
|
||||
AppSecret.createOrFetchNotificationPrivateKey()
|
||||
}
|
||||
public var notificationPublicKey: P256.KeyAgreement.PublicKey {
|
||||
notificationPrivateKey.publicKey
|
||||
}
|
||||
public var notificationAuth: Data {
|
||||
AppSecret.createOrFetchNotificationAuth()
|
||||
}
|
||||
|
||||
public static let `default`: AppSecret = {
|
||||
return AppSecret()
|
||||
}()
|
||||
|
||||
init() {
|
||||
let keys = MastodonKeys()
|
||||
|
||||
#if DEBUG
|
||||
self.notificationEndpoint = keys.notification_endpoint_debug
|
||||
#else
|
||||
self.notificationEndpoint = keys.notification_endpoint
|
||||
#endif
|
||||
}
|
||||
|
||||
public func register() {
|
||||
_ = AppSecret.createOrFetchNotificationPrivateKey()
|
||||
_ = AppSecret.createOrFetchNotificationAuth()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension AppSecret {
|
||||
|
||||
private static func createOrFetchNotificationPrivateKey() -> P256.KeyAgreement.PrivateKey {
|
||||
if let encoded = AppSecret.keychain[AppSecret.notificationPrivateKeyName],
|
||||
let data = Data(base64Encoded: encoded) {
|
||||
do {
|
||||
let privateKey = try P256.KeyAgreement.PrivateKey(rawRepresentation: data)
|
||||
return privateKey
|
||||
} catch {
|
||||
assertionFailure()
|
||||
return AppSecret.resetNotificationPrivateKey()
|
||||
}
|
||||
} else {
|
||||
return AppSecret.resetNotificationPrivateKey()
|
||||
}
|
||||
}
|
||||
|
||||
private static func resetNotificationPrivateKey() -> P256.KeyAgreement.PrivateKey {
|
||||
let privateKey = P256.KeyAgreement.PrivateKey()
|
||||
keychain[AppSecret.notificationPrivateKeyName] = privateKey.rawRepresentation.base64EncodedString()
|
||||
return privateKey
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension AppSecret {
|
||||
|
||||
private static func createOrFetchNotificationAuth() -> Data {
|
||||
if let encoded = keychain[AppSecret.notificationAuthName],
|
||||
let data = Data(base64Encoded: encoded) {
|
||||
return data
|
||||
} else {
|
||||
return AppSecret.resetNotificationAuth()
|
||||
}
|
||||
}
|
||||
|
||||
private static func resetNotificationAuth() -> Data {
|
||||
let auth = AppSecret.createRandomAuthBytes()
|
||||
keychain[AppSecret.notificationAuthName] = auth.base64EncodedString()
|
||||
return auth
|
||||
}
|
||||
|
||||
private static func createRandomAuthBytes() -> Data {
|
||||
let byteCount = 16
|
||||
var bytes = Data(count: byteCount)
|
||||
_ = bytes.withUnsafeMutableBytes { SecRandomCopyBytes(kSecRandomDefault, byteCount, $0.baseAddress!) }
|
||||
return bytes
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// AppShared.h
|
||||
// AppShared
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-4-27.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
//! Project version number for AppShared.
|
||||
FOUNDATION_EXPORT double AppSharedVersionNumber;
|
||||
|
||||
//! Project version string for AppShared.
|
||||
FOUNDATION_EXPORT const unsigned char AppSharedVersionString[];
|
||||
|
||||
// In this header, you should import all the public headers of your framework using statements like #import <AppShared/PublicHeader.h>
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?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>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>$(CURRENT_PROJECT_VERSION)</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,12 @@
|
|||
//
|
||||
// UserDefaults.swift
|
||||
// AppShared
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-4-27.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UserDefaults {
|
||||
public static let shared = UserDefaults(suiteName: AppName.groupID)!
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
import os
|
||||
import Foundation
|
||||
import CoreData
|
||||
import AppShared
|
||||
|
||||
public final class CoreDataStack {
|
||||
|
||||
|
@ -18,7 +19,7 @@ public final class CoreDataStack {
|
|||
}
|
||||
|
||||
public convenience init(databaseName: String = "shared") {
|
||||
let storeURL = URL.storeURL(for: AppSharedName.groupID, databaseName: databaseName)
|
||||
let storeURL = URL.storeURL(for: AppName.groupID, databaseName: databaseName)
|
||||
let storeDescription = NSPersistentStoreDescription(url: storeURL)
|
||||
self.init(persistentStoreDescriptions: [storeDescription])
|
||||
}
|
||||
|
|
|
@ -167,6 +167,7 @@
|
|||
5E44BF88AD33646E64727BCF /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */; };
|
||||
7D603B3DC600BB75956FAD7D /* Pods_Mastodon_NotificationService.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79D7EB88A6A8B34DFDFC96FC /* Pods_Mastodon_NotificationService.framework */; };
|
||||
87FFDA5D898A5C42ADCB35E7 /* Pods_Mastodon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4ABE34829701A4496C5BB64 /* Pods_Mastodon.framework */; };
|
||||
D86919F5080C3F228CCD17D1 /* Pods_Mastodon_AppShared.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 46531DECCAB422F507B2274D /* Pods_Mastodon_AppShared.framework */; };
|
||||
DB00CA972632DDB600A54956 /* CommonOSLog in Frameworks */ = {isa = PBXBuildFile; productRef = DB00CA962632DDB600A54956 /* CommonOSLog */; };
|
||||
DB0140A125C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140A025C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift */; };
|
||||
DB0140A825C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140A725C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift */; };
|
||||
|
@ -180,7 +181,6 @@
|
|||
DB118A8225E4B6E600FAB162 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DB118A8125E4B6E600FAB162 /* Preview Assets.xcassets */; };
|
||||
DB118A8C25E4BFB500FAB162 /* HighlightDimmableButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB118A8B25E4BFB500FAB162 /* HighlightDimmableButton.swift */; };
|
||||
DB1D186C25EF5BA7003F1F23 /* PollTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB1D186B25EF5BA7003F1F23 /* PollTableView.swift */; };
|
||||
DB1E05E1263180F500201847 /* AppSecret.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB1E05E0263180F500201847 /* AppSecret.swift */; };
|
||||
DB1E346825F518E20079D7DF /* CategoryPickerSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB1E346725F518E20079D7DF /* CategoryPickerSection.swift */; };
|
||||
DB1E347825F519300079D7DF /* PickServerItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB1E347725F519300079D7DF /* PickServerItem.swift */; };
|
||||
DB1FD43625F26899004CFCFC /* MastodonPickServerViewModel+LoadIndexedServerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB1FD43525F26899004CFCFC /* MastodonPickServerViewModel+LoadIndexedServerState.swift */; };
|
||||
|
@ -248,6 +248,21 @@
|
|||
DB66728C25F9F8DC00D60309 /* ComposeViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB66728B25F9F8DC00D60309 /* ComposeViewModel+Diffable.swift */; };
|
||||
DB66729625F9F91600D60309 /* ComposeStatusSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB66729525F9F91600D60309 /* ComposeStatusSection.swift */; };
|
||||
DB66729C25F9F91F00D60309 /* ComposeStatusItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB66729B25F9F91F00D60309 /* ComposeStatusItem.swift */; };
|
||||
DB68045B2636DC6A00430867 /* MastodonNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68045A2636DC6A00430867 /* MastodonNotification.swift */; };
|
||||
DB6804662636DC9000430867 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D939AB425EDD8A90076FA61 /* String.swift */; };
|
||||
DB68046C2636DC9E00430867 /* MastodonNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68045A2636DC6A00430867 /* MastodonNotification.swift */; };
|
||||
DB6804832637CD4C00430867 /* AppShared.h in Headers */ = {isa = PBXBuildFile; fileRef = DB6804812637CD4C00430867 /* AppShared.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
DB6804862637CD4C00430867 /* AppShared.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB68047F2637CD4C00430867 /* AppShared.framework */; };
|
||||
DB6804872637CD4C00430867 /* AppShared.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DB68047F2637CD4C00430867 /* AppShared.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
DB6804922637CD8700430867 /* AppName.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6804912637CD8700430867 /* AppName.swift */; };
|
||||
DB6804A52637CDCC00430867 /* AppShared.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB68047F2637CD4C00430867 /* AppShared.framework */; };
|
||||
DB6804A62637CDCC00430867 /* AppShared.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DB68047F2637CD4C00430867 /* AppShared.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
DB6804C82637CE2F00430867 /* AppShared.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB68047F2637CD4C00430867 /* AppShared.framework */; };
|
||||
DB6804D12637CE4700430867 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6804D02637CE4700430867 /* UserDefaults.swift */; };
|
||||
DB6804FD2637CFEC00430867 /* AppSecret.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6804FC2637CFEC00430867 /* AppSecret.swift */; };
|
||||
DB6805102637D0F800430867 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = DB68050F2637D0F800430867 /* KeychainAccess */; };
|
||||
DB6805262637D7DD00430867 /* AppShared.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB68047F2637CD4C00430867 /* AppShared.framework */; };
|
||||
DB6805272637D7DD00430867 /* AppShared.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DB68047F2637CD4C00430867 /* AppShared.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
DB68586425E619B700F0A850 /* NSKeyValueObservation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68586325E619B700F0A850 /* NSKeyValueObservation.swift */; };
|
||||
DB68A04A25E9027700CFDF14 /* AdaptiveStatusBarStyleNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68A04925E9027700CFDF14 /* AdaptiveStatusBarStyleNavigationController.swift */; };
|
||||
DB68A05D25E9055900CFDF14 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DB68A05C25E9055900CFDF14 /* Settings.bundle */; };
|
||||
|
@ -256,13 +271,9 @@
|
|||
DB6B351E2601FAEE00DC1E11 /* ComposeStatusAttachmentCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B351D2601FAEE00DC1E11 /* ComposeStatusAttachmentCollectionViewCell.swift */; };
|
||||
DB6C8C0F25F0A6AE00AAA452 /* Mastodon+Entity+Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6C8C0E25F0A6AE00AAA452 /* Mastodon+Entity+Error.swift */; };
|
||||
DB6D1B24263684C600ACB481 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D1B23263684C600ACB481 /* UserDefaults.swift */; };
|
||||
DB6D1B2B2636852000ACB481 /* AppSharedName.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D1B2A2636852000ACB481 /* AppSharedName.swift */; };
|
||||
DB6D1B312636853100ACB481 /* AppSharedName.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D1B2A2636852000ACB481 /* AppSharedName.swift */; };
|
||||
DB6D1B3D2636857500ACB481 /* AppearancePreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D1B3C2636857500ACB481 /* AppearancePreference.swift */; };
|
||||
DB6D1B44263691CF00ACB481 /* Mastodon+API+Subscriptions+Policy.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D1B43263691CF00ACB481 /* Mastodon+API+Subscriptions+Policy.swift */; };
|
||||
DB6D9F232635195E008423CD /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D9F222635195E008423CD /* String.swift */; };
|
||||
DB6D9F3526351B7A008423CD /* NotificationService+Decrypt.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D9F3426351B7A008423CD /* NotificationService+Decrypt.swift */; };
|
||||
DB6D9F3B26352019008423CD /* AppSecret.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB1E05E0263180F500201847 /* AppSecret.swift */; };
|
||||
DB6D9F42263527CE008423CD /* AlamofireImage in Frameworks */ = {isa = PBXBuildFile; productRef = DB6D9F41263527CE008423CD /* AlamofireImage */; };
|
||||
DB6D9F4926353FD7008423CD /* Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D9F4826353FD6008423CD /* Subscription.swift */; };
|
||||
DB6D9F502635761F008423CD /* SubscriptionAlerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D9F4F2635761F008423CD /* SubscriptionAlerts.swift */; };
|
||||
|
@ -392,7 +403,6 @@
|
|||
DBE3CE07261D6A0E00430CC6 /* FavoriteViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBE3CE06261D6A0E00430CC6 /* FavoriteViewModel+Diffable.swift */; };
|
||||
DBE3CE0D261D767100430CC6 /* FavoriteViewController+StatusProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBE3CE0C261D767100430CC6 /* FavoriteViewController+StatusProvider.swift */; };
|
||||
DBE3CE13261D7D4200430CC6 /* StatusTableViewControllerAspect.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBE3CE12261D7D4200430CC6 /* StatusTableViewControllerAspect.swift */; };
|
||||
DBE54AB92636C87B004E7C0B /* AppSharedName.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D1B2A2636852000ACB481 /* AppSharedName.swift */; };
|
||||
DBE54ABF2636C889004E7C0B /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D1B23263684C600ACB481 /* UserDefaults.swift */; };
|
||||
DBE54AC62636C89F004E7C0B /* NotificationPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBE54AC52636C89F004E7C0B /* NotificationPreference.swift */; };
|
||||
DBE54ACC2636C8FD004E7C0B /* NotificationPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBE54AC52636C89F004E7C0B /* NotificationPreference.swift */; };
|
||||
|
@ -419,6 +429,34 @@
|
|||
remoteGlobalIDString = DB427DD125BAA00100D1B89D;
|
||||
remoteInfo = Mastodon;
|
||||
};
|
||||
DB6804842637CD4C00430867 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = DB427DCA25BAA00100D1B89D /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = DB68047E2637CD4C00430867;
|
||||
remoteInfo = AppShared;
|
||||
};
|
||||
DB6804A72637CDCC00430867 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = DB427DCA25BAA00100D1B89D /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = DB68047E2637CD4C00430867;
|
||||
remoteInfo = AppShared;
|
||||
};
|
||||
DB6804C92637CE3000430867 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = DB427DCA25BAA00100D1B89D /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = DB68047E2637CD4C00430867;
|
||||
remoteInfo = AppShared;
|
||||
};
|
||||
DB6805282637D7DD00430867 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = DB427DCA25BAA00100D1B89D /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = DB68047E2637CD4C00430867;
|
||||
remoteInfo = AppShared;
|
||||
};
|
||||
DB89B9F825C10FD0008580ED /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = DB427DCA25BAA00100D1B89D /* Project object */;
|
||||
|
@ -450,12 +488,35 @@
|
|||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
DB6804A92637CDCC00430867 /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
DB6804A62637CDCC00430867 /* AppShared.framework in Embed Frameworks */,
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
DB68052A2637D7DD00430867 /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
DB6805272637D7DD00430867 /* AppShared.framework in Embed Frameworks */,
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
DB89BA0825C10FD0008580ED /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
DB6804872637CD4C00430867 /* AppShared.framework in Embed Frameworks */,
|
||||
DBE64A8C260C49D200E6359A /* TwitterTextEditor in Embed Frameworks */,
|
||||
DB89BA0425C10FD0008580ED /* CoreDataStack.framework in Embed Frameworks */,
|
||||
);
|
||||
|
@ -608,6 +669,7 @@
|
|||
3C030226D3C73DCC23D67452 /* Pods_Mastodon_MastodonUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon_MastodonUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
452147B2903DF38070FE56A2 /* Pods_MastodonTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MastodonTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
459EA4F43058CAB47719E963 /* Pods-Mastodon-MastodonUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
46531DECCAB422F507B2274D /* Pods_Mastodon_AppShared.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon_AppShared.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
5B90C456262599800002E742 /* SettingsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = "<group>"; };
|
||||
5B90C459262599800002E742 /* SettingsToggleTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsToggleTableViewCell.swift; sourceTree = "<group>"; };
|
||||
5B90C45A262599800002E742 /* SettingsAppearanceTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsAppearanceTableViewCell.swift; sourceTree = "<group>"; };
|
||||
|
@ -636,9 +698,11 @@
|
|||
8ED8C4B1F1BA2DCFF2926BB1 /* Pods-Mastodon-NotificationService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-NotificationService.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon-NotificationService/Pods-Mastodon-NotificationService.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
9780A4C98FFC65B32B50D1C0 /* Pods-MastodonTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonTests.release.xcconfig"; path = "Target Support Files/Pods-MastodonTests/Pods-MastodonTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
A4ABE34829701A4496C5BB64 /* Pods_Mastodon.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B31D44635FCF6452F7E1B865 /* Pods-Mastodon-AppShared.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-AppShared.release.xcconfig"; path = "Target Support Files/Pods-Mastodon-AppShared/Pods-Mastodon-AppShared.release.xcconfig"; sourceTree = "<group>"; };
|
||||
B44342AC2B6585F8295F1DDF /* Pods-Mastodon-NotificationService.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-NotificationService.release.xcconfig"; path = "Target Support Files/Pods-Mastodon-NotificationService/Pods-Mastodon-NotificationService.release.xcconfig"; sourceTree = "<group>"; };
|
||||
BB482D32A7B9825BF5327C4F /* Pods-Mastodon-MastodonUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.release.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MastodonTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D7D7CF93E262178800077512 /* Pods-Mastodon-AppShared.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-AppShared.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon-AppShared/Pods-Mastodon-AppShared.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
DB0140A025C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPinBasedAuthenticationViewController.swift; sourceTree = "<group>"; };
|
||||
DB0140A725C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift; sourceTree = "<group>"; };
|
||||
DB0140AD25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPinBasedAuthenticationViewModel.swift; sourceTree = "<group>"; };
|
||||
|
@ -650,7 +714,6 @@
|
|||
DB118A8125E4B6E600FAB162 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
DB118A8B25E4BFB500FAB162 /* HighlightDimmableButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HighlightDimmableButton.swift; sourceTree = "<group>"; };
|
||||
DB1D186B25EF5BA7003F1F23 /* PollTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollTableView.swift; sourceTree = "<group>"; };
|
||||
DB1E05E0263180F500201847 /* AppSecret.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSecret.swift; sourceTree = "<group>"; };
|
||||
DB1E346725F518E20079D7DF /* CategoryPickerSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CategoryPickerSection.swift; sourceTree = "<group>"; };
|
||||
DB1E347725F519300079D7DF /* PickServerItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PickServerItem.swift; sourceTree = "<group>"; };
|
||||
DB1FD43525F26899004CFCFC /* MastodonPickServerViewModel+LoadIndexedServerState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MastodonPickServerViewModel+LoadIndexedServerState.swift"; sourceTree = "<group>"; };
|
||||
|
@ -724,6 +787,14 @@
|
|||
DB66728B25F9F8DC00D60309 /* ComposeViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ComposeViewModel+Diffable.swift"; sourceTree = "<group>"; };
|
||||
DB66729525F9F91600D60309 /* ComposeStatusSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusSection.swift; sourceTree = "<group>"; };
|
||||
DB66729B25F9F91F00D60309 /* ComposeStatusItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusItem.swift; sourceTree = "<group>"; };
|
||||
DB68045A2636DC6A00430867 /* MastodonNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonNotification.swift; sourceTree = "<group>"; };
|
||||
DB68047F2637CD4C00430867 /* AppShared.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppShared.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DB6804812637CD4C00430867 /* AppShared.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppShared.h; sourceTree = "<group>"; };
|
||||
DB6804822637CD4C00430867 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
DB6804912637CD8700430867 /* AppName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppName.swift; sourceTree = "<group>"; };
|
||||
DB6804D02637CE4700430867 /* UserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaults.swift; sourceTree = "<group>"; };
|
||||
DB6804FC2637CFEC00430867 /* AppSecret.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSecret.swift; sourceTree = "<group>"; };
|
||||
DB68053E2638011000430867 /* NotificationService.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationService.entitlements; sourceTree = "<group>"; };
|
||||
DB68586325E619B700F0A850 /* NSKeyValueObservation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSKeyValueObservation.swift; sourceTree = "<group>"; };
|
||||
DB68A04925E9027700CFDF14 /* AdaptiveStatusBarStyleNavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdaptiveStatusBarStyleNavigationController.swift; sourceTree = "<group>"; };
|
||||
DB68A05C25E9055900CFDF14 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
|
||||
|
@ -732,10 +803,8 @@
|
|||
DB6B351D2601FAEE00DC1E11 /* ComposeStatusAttachmentCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusAttachmentCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
DB6C8C0E25F0A6AE00AAA452 /* Mastodon+Entity+Error.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Entity+Error.swift"; sourceTree = "<group>"; };
|
||||
DB6D1B23263684C600ACB481 /* UserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaults.swift; sourceTree = "<group>"; };
|
||||
DB6D1B2A2636852000ACB481 /* AppSharedName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSharedName.swift; sourceTree = "<group>"; };
|
||||
DB6D1B3C2636857500ACB481 /* AppearancePreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearancePreference.swift; sourceTree = "<group>"; };
|
||||
DB6D1B43263691CF00ACB481 /* Mastodon+API+Subscriptions+Policy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+API+Subscriptions+Policy.swift"; sourceTree = "<group>"; };
|
||||
DB6D9F222635195E008423CD /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
|
||||
DB6D9F3426351B7A008423CD /* NotificationService+Decrypt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationService+Decrypt.swift"; sourceTree = "<group>"; };
|
||||
DB6D9F4826353FD6008423CD /* Subscription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Subscription.swift; sourceTree = "<group>"; };
|
||||
DB6D9F4F2635761F008423CD /* SubscriptionAlerts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionAlerts.swift; sourceTree = "<group>"; };
|
||||
|
@ -888,6 +957,7 @@
|
|||
2D939AC825EE14620076FA61 /* CropViewController in Frameworks */,
|
||||
DBB525082611EAC0002F1F29 /* Tabman in Frameworks */,
|
||||
5D526FE225BE9AC400460CB9 /* MastodonSDK in Frameworks */,
|
||||
DB6804862637CD4C00430867 /* AppShared.framework in Frameworks */,
|
||||
DB5086B825CC0D6400C2C187 /* Kingfisher in Frameworks */,
|
||||
DBF96326262EC0A6001D8D25 /* AuthenticationServices.framework in Frameworks */,
|
||||
2D61336925C18A4F00CAE157 /* AlamofireNetworkActivityIndicator in Frameworks */,
|
||||
|
@ -895,6 +965,7 @@
|
|||
DBE64A8B260C49D200E6359A /* TwitterTextEditor in Frameworks */,
|
||||
2D5981BA25E4D7F8000FB903 /* ThirdPartyMailer in Frameworks */,
|
||||
87FFDA5D898A5C42ADCB35E7 /* Pods_Mastodon.framework in Frameworks */,
|
||||
DB6804C82637CE2F00430867 /* AppShared.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -915,10 +986,20 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
DB68047C2637CD4C00430867 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DB6805102637D0F800430867 /* KeychainAccess in Frameworks */,
|
||||
D86919F5080C3F228CCD17D1 /* Pods_Mastodon_AppShared.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
DB89B9EB25C10FD0008580ED /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DB6805262637D7DD00430867 /* AppShared.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -937,6 +1018,7 @@
|
|||
DB00CA972632DDB600A54956 /* CommonOSLog in Frameworks */,
|
||||
DB6D9F42263527CE008423CD /* AlamofireImage in Frameworks */,
|
||||
DBF8AE862632992800C9C23C /* Base85 in Frameworks */,
|
||||
DB6804A52637CDCC00430867 /* AppShared.framework in Frameworks */,
|
||||
7D603B3DC600BB75956FAD7D /* Pods_Mastodon_NotificationService.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -1021,6 +1103,8 @@
|
|||
9780A4C98FFC65B32B50D1C0 /* Pods-MastodonTests.release.xcconfig */,
|
||||
8ED8C4B1F1BA2DCFF2926BB1 /* Pods-Mastodon-NotificationService.debug.xcconfig */,
|
||||
B44342AC2B6585F8295F1DDF /* Pods-Mastodon-NotificationService.release.xcconfig */,
|
||||
D7D7CF93E262178800077512 /* Pods-Mastodon-AppShared.debug.xcconfig */,
|
||||
B31D44635FCF6452F7E1B865 /* Pods-Mastodon-AppShared.release.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1351,6 +1435,7 @@
|
|||
3C030226D3C73DCC23D67452 /* Pods_Mastodon_MastodonUITests.framework */,
|
||||
452147B2903DF38070FE56A2 /* Pods_MastodonTests.framework */,
|
||||
79D7EB88A6A8B34DFDFC96FC /* Pods_Mastodon_NotificationService.framework */,
|
||||
46531DECCAB422F507B2274D /* Pods_Mastodon_AppShared.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1453,8 +1538,6 @@
|
|||
children = (
|
||||
DB427DD525BAA00100D1B89D /* AppDelegate.swift */,
|
||||
DB427DD725BAA00100D1B89D /* SceneDelegate.swift */,
|
||||
DB1E05E0263180F500201847 /* AppSecret.swift */,
|
||||
DB6D1B2A2636852000ACB481 /* AppSharedName.swift */,
|
||||
DB427DDB25BAA00100D1B89D /* Main.storyboard */,
|
||||
DB427DE025BAA00100D1B89D /* LaunchScreen.storyboard */,
|
||||
DB68A05C25E9055900CFDF14 /* Settings.bundle */,
|
||||
|
@ -1485,6 +1568,7 @@
|
|||
DB89B9EF25C10FD0008580ED /* CoreDataStack */,
|
||||
DB89B9FC25C10FD0008580ED /* CoreDataStackTests */,
|
||||
DBF8AE14263293E400C9C23C /* NotificationService */,
|
||||
DB6804802637CD4C00430867 /* AppShared */,
|
||||
DB427DD325BAA00100D1B89D /* Products */,
|
||||
1EBA4F56E920856A3FC84ACB /* Pods */,
|
||||
3FE14AD363ED19AE7FF210A6 /* Frameworks */,
|
||||
|
@ -1501,6 +1585,7 @@
|
|||
DB89B9EE25C10FD0008580ED /* CoreDataStack.framework */,
|
||||
DB89B9F625C10FD0008580ED /* CoreDataStackTests.xctest */,
|
||||
DBF8AE13263293E400C9C23C /* NotificationService.appex */,
|
||||
DB68047F2637CD4C00430867 /* AppShared.framework */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1628,6 +1713,18 @@
|
|||
path = View;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB6804802637CD4C00430867 /* AppShared */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB6804812637CD4C00430867 /* AppShared.h */,
|
||||
DB6804822637CD4C00430867 /* Info.plist */,
|
||||
DB6804912637CD8700430867 /* AppName.swift */,
|
||||
DB6804FC2637CFEC00430867 /* AppSecret.swift */,
|
||||
DB6804D02637CE4700430867 /* UserDefaults.swift */,
|
||||
);
|
||||
path = AppShared;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB68A03825E900CC00CFDF14 /* Share */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1659,14 +1756,6 @@
|
|||
path = MastodonSDK;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB6D9F2926351961008423CD /* Extension */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB6D9F222635195E008423CD /* String.swift */,
|
||||
);
|
||||
path = Extension;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB72602125E36A2500235243 /* ServerRules */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -2108,9 +2197,10 @@
|
|||
DBF8AE14263293E400C9C23C /* NotificationService */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB68053E2638011000430867 /* NotificationService.entitlements */,
|
||||
DBF8AE15263293E400C9C23C /* NotificationService.swift */,
|
||||
DB6D9F3426351B7A008423CD /* NotificationService+Decrypt.swift */,
|
||||
DB6D9F2926351961008423CD /* Extension */,
|
||||
DB68045A2636DC6A00430867 /* MastodonNotification.swift */,
|
||||
DBF8AE17263293E400C9C23C /* Info.plist */,
|
||||
);
|
||||
path = NotificationService;
|
||||
|
@ -2119,6 +2209,14 @@
|
|||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
DB68047A2637CD4C00430867 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DB6804832637CD4C00430867 /* AppShared.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
DB89B9E925C10FD0008580ED /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -2148,6 +2246,8 @@
|
|||
dependencies = (
|
||||
DB89BA0225C10FD0008580ED /* PBXTargetDependency */,
|
||||
DBF8AE19263293E400C9C23C /* PBXTargetDependency */,
|
||||
DB6804852637CD4C00430867 /* PBXTargetDependency */,
|
||||
DB6804CA2637CE3000430867 /* PBXTargetDependency */,
|
||||
);
|
||||
name = Mastodon;
|
||||
packageProductDependencies = (
|
||||
|
@ -2206,6 +2306,28 @@
|
|||
productReference = DB427DF325BAA00100D1B89D /* MastodonUITests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.ui-testing";
|
||||
};
|
||||
DB68047E2637CD4C00430867 /* AppShared */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = DB6804882637CD4C00430867 /* Build configuration list for PBXNativeTarget "AppShared" */;
|
||||
buildPhases = (
|
||||
C6B7D3A8ACD77F6620D0E0AD /* [CP] Check Pods Manifest.lock */,
|
||||
DB68047A2637CD4C00430867 /* Headers */,
|
||||
DB68047B2637CD4C00430867 /* Sources */,
|
||||
DB68047C2637CD4C00430867 /* Frameworks */,
|
||||
DB68047D2637CD4C00430867 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = AppShared;
|
||||
packageProductDependencies = (
|
||||
DB68050F2637D0F800430867 /* KeychainAccess */,
|
||||
);
|
||||
productName = AppShared;
|
||||
productReference = DB68047F2637CD4C00430867 /* AppShared.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
DB89B9ED25C10FD0008580ED /* CoreDataStack */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = DB89BA0525C10FD0008580ED /* Build configuration list for PBXNativeTarget "CoreDataStack" */;
|
||||
|
@ -2214,10 +2336,12 @@
|
|||
DB89B9EA25C10FD0008580ED /* Sources */,
|
||||
DB89B9EB25C10FD0008580ED /* Frameworks */,
|
||||
DB89B9EC25C10FD0008580ED /* Resources */,
|
||||
DB68052A2637D7DD00430867 /* Embed Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
DB6805292637D7DD00430867 /* PBXTargetDependency */,
|
||||
);
|
||||
name = CoreDataStack;
|
||||
productName = CoreDataStack;
|
||||
|
@ -2251,10 +2375,12 @@
|
|||
DBF8AE0F263293E400C9C23C /* Sources */,
|
||||
DBF8AE10263293E400C9C23C /* Frameworks */,
|
||||
DBF8AE11263293E400C9C23C /* Resources */,
|
||||
DB6804A92637CDCC00430867 /* Embed Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
DB6804A82637CDCC00430867 /* PBXTargetDependency */,
|
||||
);
|
||||
name = NotificationService;
|
||||
packageProductDependencies = (
|
||||
|
@ -2287,6 +2413,10 @@
|
|||
CreatedOnToolsVersion = 12.4;
|
||||
TestTargetID = DB427DD125BAA00100D1B89D;
|
||||
};
|
||||
DB68047E2637CD4C00430867 = {
|
||||
CreatedOnToolsVersion = 12.4;
|
||||
LastSwiftMigration = 1240;
|
||||
};
|
||||
DB89B9ED25C10FD0008580ED = {
|
||||
CreatedOnToolsVersion = 12.4;
|
||||
LastSwiftMigration = 1240;
|
||||
|
@ -2321,6 +2451,7 @@
|
|||
DBE64A89260C49D200E6359A /* XCRemoteSwiftPackageReference "TwitterTextEditor" */,
|
||||
DBB525062611EAC0002F1F29 /* XCRemoteSwiftPackageReference "Tabman" */,
|
||||
DBF8AE842632992700C9C23C /* XCRemoteSwiftPackageReference "Base85" */,
|
||||
DB6804722637CC1200430867 /* XCRemoteSwiftPackageReference "KeychainAccess" */,
|
||||
);
|
||||
productRefGroup = DB427DD325BAA00100D1B89D /* Products */;
|
||||
projectDirPath = "";
|
||||
|
@ -2332,6 +2463,7 @@
|
|||
DB89B9ED25C10FD0008580ED /* CoreDataStack */,
|
||||
DB89B9F525C10FD0008580ED /* CoreDataStackTests */,
|
||||
DBF8AE12263293E400C9C23C /* NotificationService */,
|
||||
DB68047E2637CD4C00430867 /* AppShared */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
@ -2365,6 +2497,13 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
DB68047D2637CD4C00430867 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
DB89B9EC25C10FD0008580ED /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -2472,6 +2611,28 @@
|
|||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
C6B7D3A8ACD77F6620D0E0AD /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-Mastodon-AppShared-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
DB3D100425BAA71500EAA174 /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -2772,6 +2933,7 @@
|
|||
DB0140A825C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift in Sources */,
|
||||
DB1FD45025F26FA1004CFCFC /* MastodonPickServerViewModel+Diffable.swift in Sources */,
|
||||
DB2B3AE925E38850007045F9 /* UIViewPreview.swift in Sources */,
|
||||
DB68046C2636DC9E00430867 /* MastodonNotification.swift in Sources */,
|
||||
DBAE3F9E2616E308004B8251 /* APIService+Mute.swift in Sources */,
|
||||
DB427DD625BAA00100D1B89D /* AppDelegate.swift in Sources */,
|
||||
DB6D9F57263577D2008423CD /* APIService+CoreData+Setting.swift in Sources */,
|
||||
|
@ -2780,7 +2942,6 @@
|
|||
DBBE1B4525F3474B0081417A /* MastodonPickServerAppearance.swift in Sources */,
|
||||
DB98338725C945ED00AD9700 /* Strings.swift in Sources */,
|
||||
2D7867192625B77500211898 /* NotificationItem.swift in Sources */,
|
||||
DB6D1B2B2636852000ACB481 /* AppSharedName.swift in Sources */,
|
||||
DB45FAB625CA5485005A8AC7 /* UIAlertController.swift in Sources */,
|
||||
DBE0821525CD382600FD6BBD /* MastodonRegisterViewController.swift in Sources */,
|
||||
2D5A3D0325CF8742002347D6 /* ControlContainableScrollViews.swift in Sources */,
|
||||
|
@ -2853,7 +3014,6 @@
|
|||
2D6DE40026141DF600A63F6A /* SearchViewModel.swift in Sources */,
|
||||
DB51D172262832380062B7A1 /* BlurHashDecode.swift in Sources */,
|
||||
DBCC3B89261454BA0045B23D /* CGImage.swift in Sources */,
|
||||
DB1E05E1263180F500201847 /* AppSecret.swift in Sources */,
|
||||
DBCCC71E25F73297007E1AB6 /* APIService+Reblog.swift in Sources */,
|
||||
DB789A2B25F9F7AB0071ACA0 /* ComposeRepliedToStatusContentCollectionViewCell.swift in Sources */,
|
||||
DBE3CE13261D7D4200430CC6 /* StatusTableViewControllerAspect.swift in Sources */,
|
||||
|
@ -2877,6 +3037,16 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
DB68047B2637CD4C00430867 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DB6804D12637CE4700430867 /* UserDefaults.swift in Sources */,
|
||||
DB6804922637CD8700430867 /* AppName.swift in Sources */,
|
||||
DB6804FD2637CFEC00430867 /* AppSecret.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
DB89B9EA25C10FD0008580ED /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -2898,7 +3068,6 @@
|
|||
2D152A9225C2980C009AA50C /* UIFont.swift in Sources */,
|
||||
DB4481B325EE16D000BEFB67 /* PollOption.swift in Sources */,
|
||||
DB89BA4425C1165F008580ED /* Managed.swift in Sources */,
|
||||
DB6D1B312636853100ACB481 /* AppSharedName.swift in Sources */,
|
||||
2D6125472625436B00299647 /* Notification.swift in Sources */,
|
||||
DB89BA4325C1165F008580ED /* NetworkUpdatable.swift in Sources */,
|
||||
DB8AF56825C13E2A002E6C99 /* HomeTimelineIndex.swift in Sources */,
|
||||
|
@ -2926,11 +3095,10 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DBE54ACC2636C8FD004E7C0B /* NotificationPreference.swift in Sources */,
|
||||
DB6D9F232635195E008423CD /* String.swift in Sources */,
|
||||
DBE54AB92636C87B004E7C0B /* AppSharedName.swift in Sources */,
|
||||
DB68045B2636DC6A00430867 /* MastodonNotification.swift in Sources */,
|
||||
DBE54ABF2636C889004E7C0B /* UserDefaults.swift in Sources */,
|
||||
DB6D9F3B26352019008423CD /* AppSecret.swift in Sources */,
|
||||
DB6D9F3526351B7A008423CD /* NotificationService+Decrypt.swift in Sources */,
|
||||
DB6804662636DC9000430867 /* String.swift in Sources */,
|
||||
DBF8AE16263293E400C9C23C /* NotificationService.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -2948,6 +3116,26 @@
|
|||
target = DB427DD125BAA00100D1B89D /* Mastodon */;
|
||||
targetProxy = DB427DF425BAA00100D1B89D /* PBXContainerItemProxy */;
|
||||
};
|
||||
DB6804852637CD4C00430867 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = DB68047E2637CD4C00430867 /* AppShared */;
|
||||
targetProxy = DB6804842637CD4C00430867 /* PBXContainerItemProxy */;
|
||||
};
|
||||
DB6804A82637CDCC00430867 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = DB68047E2637CD4C00430867 /* AppShared */;
|
||||
targetProxy = DB6804A72637CDCC00430867 /* PBXContainerItemProxy */;
|
||||
};
|
||||
DB6804CA2637CE3000430867 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = DB68047E2637CD4C00430867 /* AppShared */;
|
||||
targetProxy = DB6804C92637CE3000430867 /* PBXContainerItemProxy */;
|
||||
};
|
||||
DB6805292637D7DD00430867 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = DB68047E2637CD4C00430867 /* AppShared */;
|
||||
targetProxy = DB6805282637D7DD00430867 /* PBXContainerItemProxy */;
|
||||
};
|
||||
DB89B9F925C10FD0008580ED /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = DB89B9ED25C10FD0008580ED /* CoreDataStack */;
|
||||
|
@ -3126,7 +3314,6 @@
|
|||
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;
|
||||
|
@ -3154,7 +3341,6 @@
|
|||
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;
|
||||
|
@ -3259,6 +3445,65 @@
|
|||
};
|
||||
name = Release;
|
||||
};
|
||||
DB6804892637CD4C00430867 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D7D7CF93E262178800077512 /* Pods-Mastodon-AppShared.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 7LFDZ96332;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = AppShared/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.Mastodon.AppShared;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
DB68048A2637CD4C00430867 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = B31D44635FCF6452F7E1B865 /* Pods-Mastodon-AppShared.release.xcconfig */;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 7LFDZ96332;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = AppShared/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.Mastodon.AppShared;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
DB89BA0625C10FD0008580ED /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
|
@ -3360,10 +3605,10 @@
|
|||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 8ED8C4B1F1BA2DCFF2926BB1 /* Pods-Mastodon-NotificationService.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 7LFDZ96332;
|
||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -3381,10 +3626,10 @@
|
|||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = B44342AC2B6585F8295F1DDF /* Pods-Mastodon-NotificationService.release.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 7LFDZ96332;
|
||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -3437,6 +3682,15 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
DB6804882637CD4C00430867 /* Build configuration list for PBXNativeTarget "AppShared" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
DB6804892637CD4C00430867 /* Debug */,
|
||||
DB68048A2637CD4C00430867 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
DB89BA0525C10FD0008580ED /* Build configuration list for PBXNativeTarget "CoreDataStack" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
@ -3523,6 +3777,14 @@
|
|||
minimumVersion = 6.1.0;
|
||||
};
|
||||
};
|
||||
DB6804722637CC1200430867 /* XCRemoteSwiftPackageReference "KeychainAccess" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/kishikawakatsumi/KeychainAccess.git";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 4.2.2;
|
||||
};
|
||||
};
|
||||
DB9A487C2603456B008B817C /* XCRemoteSwiftPackageReference "UITextView-Placeholder" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/MainasuK/UITextView-Placeholder";
|
||||
|
@ -3602,6 +3864,11 @@
|
|||
package = DB5086B625CC0D6400C2C187 /* XCRemoteSwiftPackageReference "Kingfisher" */;
|
||||
productName = Kingfisher;
|
||||
};
|
||||
DB68050F2637D0F800430867 /* KeychainAccess */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = DB6804722637CC1200430867 /* XCRemoteSwiftPackageReference "KeychainAccess" */;
|
||||
productName = KeychainAccess;
|
||||
};
|
||||
DB6D9F41263527CE008423CD /* AlamofireImage */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = DB3D0FF125BAA61700EAA174 /* XCRemoteSwiftPackageReference "AlamofireImage" */;
|
||||
|
|
|
@ -4,10 +4,15 @@
|
|||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>AppShared.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>18</integer>
|
||||
</dict>
|
||||
<key>CoreDataStack.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>13</integer>
|
||||
<integer>17</integer>
|
||||
</dict>
|
||||
<key>Mastodon - RTL.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
|
@ -27,7 +32,7 @@
|
|||
<key>NotificationService.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>14</integer>
|
||||
<integer>18</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
|
|
|
@ -55,6 +55,15 @@
|
|||
"version": "0.1.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "KeychainAccess",
|
||||
"repositoryURL": "https://github.com/kishikawakatsumi/KeychainAccess.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "84e546727d66f1adc5439debad16270d0fdd04e7",
|
||||
"version": "4.2.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Kingfisher",
|
||||
"repositoryURL": "https://github.com/onevcat/Kingfisher.git",
|
||||
|
|
|
@ -16,3 +16,25 @@ extension String {
|
|||
self = self.capitalizingFirstLetter()
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
static func normalize(base64String: String) -> String {
|
||||
let base64 = base64String
|
||||
.replacingOccurrences(of: "-", with: "+")
|
||||
.replacingOccurrences(of: "_", with: "/")
|
||||
.padding()
|
||||
return base64
|
||||
}
|
||||
|
||||
private func padding() -> String {
|
||||
let remainder = self.count % 4
|
||||
if remainder > 0 {
|
||||
return self.padding(
|
||||
toLength: self.count + 4 - remainder,
|
||||
withPad: "=",
|
||||
startingAt: 0
|
||||
)
|
||||
}
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,7 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension UserDefaults {
|
||||
static let shared = UserDefaults(suiteName: AppSharedName.groupID)!
|
||||
}
|
||||
import AppShared
|
||||
|
||||
extension UserDefaults {
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ extension UserDefaults {
|
|||
register(defaults: [#function: UIUserInterfaceStyle.unspecified.rawValue])
|
||||
return UIUserInterfaceStyle(rawValue: integer(forKey: #function)) ?? .unspecified
|
||||
}
|
||||
set { UserDefaults.shared[#function] = newValue.rawValue }
|
||||
set { self[#function] = newValue.rawValue }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ extension UserDefaults {
|
|||
register(defaults: [#function: 0])
|
||||
return integer(forKey: #function)
|
||||
}
|
||||
set { UserDefaults.shared[#function] = newValue }
|
||||
set { self[#function] = newValue }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ extension APIService {
|
|||
query: query
|
||||
)
|
||||
.flatMap { response -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Subscription>, Error> in
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: create subscription successful ", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: create subscription successful %s", ((#file as NSString).lastPathComponent), #line, #function, response.value.endpoint)
|
||||
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
return managedObjectContext.performChanges {
|
||||
|
@ -45,7 +45,8 @@ extension APIService {
|
|||
.setFailureType(to: Error.self)
|
||||
.map { _ in return response }
|
||||
.eraseToAnyPublisher()
|
||||
}.eraseToAnyPublisher()
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func cancelSubscription(
|
||||
|
|
|
@ -27,35 +27,50 @@ extension APIService.CoreData {
|
|||
}()
|
||||
|
||||
if let oldSetting = oldSetting {
|
||||
setupSettingSubscriptions(managedObjectContext: managedObjectContext, setting: oldSetting)
|
||||
return (oldSetting, false)
|
||||
} else {
|
||||
let setting = Setting.insert(
|
||||
into: managedObjectContext,
|
||||
property: property
|
||||
)
|
||||
let policies: [Mastodon.API.Subscriptions.Policy] = [
|
||||
.all,
|
||||
.followed,
|
||||
.follower,
|
||||
.none
|
||||
]
|
||||
let now = Date()
|
||||
policies.forEach { policy in
|
||||
let (subscription, _) = createOrFetchSubscription(
|
||||
into: managedObjectContext,
|
||||
setting: setting,
|
||||
policy: policy
|
||||
)
|
||||
if policy == .all {
|
||||
subscription.update(activedAt: now)
|
||||
} else {
|
||||
subscription.update(activedAt: now.addingTimeInterval(-10))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
setupSettingSubscriptions(managedObjectContext: managedObjectContext, setting: setting)
|
||||
return (setting, true)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension APIService.CoreData {
|
||||
|
||||
static func setupSettingSubscriptions(
|
||||
managedObjectContext: NSManagedObjectContext,
|
||||
setting: Setting
|
||||
) {
|
||||
guard (setting.subscriptions ?? Set()).isEmpty else { return }
|
||||
|
||||
let now = Date()
|
||||
let policies: [Mastodon.API.Subscriptions.Policy] = [
|
||||
.all,
|
||||
.followed,
|
||||
.follower,
|
||||
.none
|
||||
]
|
||||
policies.forEach { policy in
|
||||
let (subscription, _) = createOrFetchSubscription(
|
||||
into: managedObjectContext,
|
||||
setting: setting,
|
||||
policy: policy
|
||||
)
|
||||
if policy == .all {
|
||||
subscription.update(activedAt: now)
|
||||
} else {
|
||||
subscription.update(activedAt: now.addingTimeInterval(-10))
|
||||
}
|
||||
}
|
||||
|
||||
// trigger setting update
|
||||
setting.didUpdate(at: now)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ extension APIService.CoreData {
|
|||
}()
|
||||
|
||||
if let oldSubscription = oldSubscription {
|
||||
oldSubscription.setting = setting
|
||||
return (oldSubscription, false)
|
||||
} else {
|
||||
let subscriptionProperty = Subscription.Property(policyRaw: policy.rawValue)
|
||||
|
|
|
@ -11,6 +11,7 @@ import Combine
|
|||
import CoreData
|
||||
import CoreDataStack
|
||||
import MastodonSDK
|
||||
import AppShared
|
||||
|
||||
final class NotificationService {
|
||||
|
||||
|
@ -32,6 +33,16 @@ final class NotificationService {
|
|||
) {
|
||||
self.authenticationService = authenticationService
|
||||
|
||||
authenticationService.mastodonAuthentications
|
||||
.sink(receiveValue: { [weak self] mastodonAuthentications in
|
||||
guard let self = self else { return }
|
||||
|
||||
// request permission when sign-in
|
||||
guard !mastodonAuthentications.isEmpty else { return }
|
||||
self.requestNotificationPermission()
|
||||
})
|
||||
.store(in: &disposeBag)
|
||||
|
||||
deviceToken
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] deviceToken in
|
||||
|
@ -83,13 +94,7 @@ extension NotificationService {
|
|||
}
|
||||
return _notificationSubscription
|
||||
}
|
||||
|
||||
static func createRandomAuthBytes() -> Data {
|
||||
let byteCount = 16
|
||||
var bytes = Data(count: byteCount)
|
||||
_ = bytes.withUnsafeMutableBytes { SecRandomCopyBytes(kSecRandomDefault, byteCount, $0.baseAddress!) }
|
||||
return bytes
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension NotificationService {
|
||||
|
@ -120,7 +125,7 @@ extension NotificationService.NotificationViewModel {
|
|||
|
||||
let appSecret = AppSecret.default
|
||||
let endpoint = appSecret.notificationEndpoint + "/" + deviceToken
|
||||
let p256dh = appSecret.uncompressionNotificationPublicKeyData
|
||||
let p256dh = appSecret.notificationPublicKey.x963Representation
|
||||
let auth = appSecret.notificationAuth
|
||||
|
||||
let query = Mastodon.API.Subscriptions.CreateSubscriptionQuery(
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
// Created by MainasuK Cirno on 2021-4-25.
|
||||
//
|
||||
|
||||
import os.log
|
||||
import UIKit
|
||||
import Combine
|
||||
import CoreDataStack
|
||||
|
@ -108,16 +109,17 @@ final class SettingService {
|
|||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
Publishers.CombineLatest(
|
||||
Publishers.CombineLatest3(
|
||||
notificationService.deviceToken,
|
||||
currentSetting
|
||||
currentSetting.eraseToAnyPublisher(),
|
||||
authenticationService.activeMastodonAuthenticationBox
|
||||
)
|
||||
.compactMap { [weak self] deviceToken, setting -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Subscription>, Error>? in
|
||||
.compactMap { [weak self] deviceToken, setting, activeMastodonAuthenticationBox -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Subscription>, Error>? in
|
||||
guard let self = self else { return nil }
|
||||
guard let apiService = self.apiService else { return nil }
|
||||
guard let deviceToken = deviceToken else { return nil }
|
||||
guard let authenticationBox = self.authenticationService?.activeMastodonAuthenticationBox.value else { return nil }
|
||||
guard let setting = setting else { return nil }
|
||||
guard let authenticationBox = activeMastodonAuthenticationBox else { return nil }
|
||||
|
||||
guard let subscription = setting.activeSubscription else { return nil }
|
||||
|
||||
guard setting.domain == authenticationBox.domain,
|
||||
|
@ -142,21 +144,30 @@ final class SettingService {
|
|||
queryData: queryData,
|
||||
mastodonAuthenticationBox: authenticationBox
|
||||
)
|
||||
|
||||
|
||||
return apiService.createSubscription(
|
||||
subscriptionObjectID: subscription.objectID,
|
||||
query: query,
|
||||
mastodonAuthenticationBox: authenticationBox
|
||||
)
|
||||
}
|
||||
.switchToLatest()
|
||||
.sink { _ in
|
||||
// do nothing
|
||||
} receiveValue: { _ in
|
||||
// do nothing
|
||||
}
|
||||
.debounce(for: .seconds(3), scheduler: DispatchQueue.main) // limit subscribe request emit time interval
|
||||
.sink(receiveValue: { [weak self] publisher in
|
||||
guard let self = self else { return }
|
||||
publisher
|
||||
.sink { completion in
|
||||
switch completion {
|
||||
case .failure(let error):
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: [Push Notification] subscribe failure: %s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription)
|
||||
case .finished:
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: [Push Notification] subscribe success", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
}
|
||||
} receiveValue: { _ in
|
||||
// do nothing
|
||||
}
|
||||
.store(in: &self.disposeBag)
|
||||
})
|
||||
.store(in: &disposeBag)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import os.log
|
||||
import UIKit
|
||||
import AppShared
|
||||
|
||||
@main
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
@ -15,11 +16,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
|
||||
AppSecret.default.register()
|
||||
|
||||
// Update app version info. See: `Settings.bundle`
|
||||
UserDefaults.standard.setValue(UIApplication.appVersion(), forKey: "Mastodon.appVersion")
|
||||
UserDefaults.standard.setValue(UIApplication.appBuild(), forKey: "Mastodon.appBundle")
|
||||
|
||||
// UNUserNotificationCenter.current().delegate = self
|
||||
UNUserNotificationCenter.current().delegate = self
|
||||
application.registerForRemoteNotifications()
|
||||
|
||||
return true
|
||||
|
@ -57,7 +60,28 @@ extension AppDelegate {
|
|||
|
||||
// MARK: - UNUserNotificationCenterDelegate
|
||||
extension AppDelegate: UNUserNotificationCenterDelegate {
|
||||
func userNotificationCenter(
|
||||
_ center: UNUserNotificationCenter,
|
||||
willPresent notification: UNNotification,
|
||||
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
|
||||
) {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: [Push Notification]", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
if let plaintext = notification.request.content.userInfo["plaintext"] as? Data,
|
||||
let mastodonPushNotification = try? JSONDecoder().decode(MastodonPushNotification.self, from: plaintext) {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: [Push Notification] present", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
|
||||
}
|
||||
completionHandler(.banner)
|
||||
}
|
||||
|
||||
func userNotificationCenter(
|
||||
_ center: UNUserNotificationCenter,
|
||||
didReceive response: UNNotificationResponse,
|
||||
withCompletionHandler completionHandler: @escaping () -> Void
|
||||
) {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: [Push Notification]", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
extension AppContext {
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
//
|
||||
// AppSecret.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-4-22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CryptoKit
|
||||
import Keys
|
||||
|
||||
final class AppSecret {
|
||||
|
||||
let notificationEndpoint: String
|
||||
|
||||
let notificationPrivateKey: P256.KeyAgreement.PrivateKey!
|
||||
let notificationPublicKey: P256.KeyAgreement.PublicKey!
|
||||
let notificationAuth: Data
|
||||
|
||||
static let `default`: AppSecret = {
|
||||
return AppSecret()
|
||||
}()
|
||||
|
||||
init() {
|
||||
let keys = MastodonKeys()
|
||||
|
||||
#if DEBUG
|
||||
self.notificationEndpoint = keys.notification_endpoint_debug
|
||||
let nonce = keys.notification_key_nonce_debug
|
||||
let auth = keys.notification_key_auth_debug
|
||||
#else
|
||||
self.notificationEndpoint = keys.notification_endpoint
|
||||
let nonce = keys.notification_key_nonce
|
||||
let auth = keys.notification_key_auth
|
||||
#endif
|
||||
|
||||
notificationPrivateKey = try! P256.KeyAgreement.PrivateKey(rawRepresentation: Data(base64Encoded: nonce)!)
|
||||
notificationPublicKey = notificationPrivateKey!.publicKey
|
||||
notificationAuth = Data(base64Encoded: auth)!
|
||||
}
|
||||
|
||||
var uncompressionNotificationPublicKeyData: Data {
|
||||
var data = notificationPublicKey.rawRepresentation
|
||||
if data.count == 64 {
|
||||
let prefix: [UInt8] = [0x04]
|
||||
data = Data(prefix) + data
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
//
|
||||
// AppSharedName.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-4-26.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum AppSharedName {
|
||||
static let groupID = "group.org.joinmastodon.mastodon-temp"
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
//
|
||||
// String.swift
|
||||
// NotificationService
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-4-25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension String {
|
||||
static func normalize(base64String: String) -> String {
|
||||
let base64 = base64String
|
||||
.replacingOccurrences(of: "-", with: "+")
|
||||
.replacingOccurrences(of: "_", with: "/")
|
||||
.padding()
|
||||
return base64
|
||||
}
|
||||
|
||||
private func padding() -> String {
|
||||
let remainder = self.count % 4
|
||||
if remainder > 0 {
|
||||
return self.padding(
|
||||
toLength: self.count + 4 - remainder,
|
||||
withPad: "=",
|
||||
startingAt: 0
|
||||
)
|
||||
}
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// MastodonNotification.swift
|
||||
// NotificationService
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-4-26.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct MastodonPushNotification: Codable {
|
||||
|
||||
private let _accessToken: String
|
||||
var accessToken: String {
|
||||
return String.normalize(base64String: _accessToken)
|
||||
}
|
||||
|
||||
let notificationID: Int
|
||||
let notificationType: String
|
||||
|
||||
let preferredLocale: String?
|
||||
let icon: String?
|
||||
let title: String
|
||||
let body: String
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case _accessToken = "access_token"
|
||||
case notificationID = "notification_id"
|
||||
case notificationType = "notification_type"
|
||||
case preferredLocale = "preferred_locale"
|
||||
case icon
|
||||
case title
|
||||
case body
|
||||
}
|
||||
|
||||
}
|
|
@ -32,7 +32,13 @@ extension NotificationService {
|
|||
return nil
|
||||
}
|
||||
|
||||
guard let plaintext = try? AES.GCM.open(sealedBox, using: key) else {
|
||||
var _plaintext: Data?
|
||||
do {
|
||||
_plaintext = try AES.GCM.open(sealedBox, using: key)
|
||||
} catch {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: sealedBox open fail %s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription)
|
||||
}
|
||||
guard let plaintext = _plaintext else {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: failed to open sealedBox", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
return nil
|
||||
}
|
||||
|
@ -65,32 +71,3 @@ extension NotificationService {
|
|||
return info
|
||||
}
|
||||
}
|
||||
|
||||
extension NotificationService {
|
||||
struct MastodonNotification: Codable {
|
||||
|
||||
private let _accessToken: String
|
||||
var accessToken: String {
|
||||
return String.normalize(base64String: _accessToken)
|
||||
}
|
||||
|
||||
let notificationID: Int
|
||||
let notificationType: String
|
||||
|
||||
let preferredLocale: String?
|
||||
let icon: String?
|
||||
let title: String
|
||||
let body: String
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case _accessToken = "access_token"
|
||||
case notificationID = "notification_id"
|
||||
case notificationType = "notification_type"
|
||||
case preferredLocale = "preferred_locale"
|
||||
case icon
|
||||
case title
|
||||
case body
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.org.joinmastodon.mastodon-temp</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
|
@ -10,7 +10,7 @@ import CommonOSLog
|
|||
import CryptoKit
|
||||
import AlamofireImage
|
||||
import Base85
|
||||
import Keys
|
||||
import AppShared
|
||||
|
||||
class NotificationService: UNNotificationServiceExtension {
|
||||
|
||||
|
@ -25,7 +25,8 @@ class NotificationService: UNNotificationServiceExtension {
|
|||
// Modify the notification content here...
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
|
||||
let privateKey = AppSecret.default.notificationPrivateKey!
|
||||
let privateKey = AppSecret.default.notificationPrivateKey
|
||||
let auth = AppSecret.default.notificationAuth
|
||||
|
||||
guard let encodedPayload = bestAttemptContent.userInfo["p"] as? String,
|
||||
let payload = Data(base85Encoded: encodedPayload, options: [], encoding: .z85) else {
|
||||
|
@ -48,9 +49,8 @@ class NotificationService: UNNotificationServiceExtension {
|
|||
return
|
||||
}
|
||||
|
||||
let auth = AppSecret.default.notificationAuth
|
||||
guard let plaintextData = NotificationService.decrypt(payload: payload, salt: salt, auth: auth, privateKey: privateKey, publicKey: publicKey),
|
||||
let notification = try? JSONDecoder().decode(MastodonNotification.self, from: plaintextData) else {
|
||||
let notification = try? JSONDecoder().decode(MastodonPushNotification.self, from: plaintextData) else {
|
||||
contentHandler(bestAttemptContent)
|
||||
return
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ class NotificationService: UNNotificationServiceExtension {
|
|||
bestAttemptContent.title = notification.title
|
||||
bestAttemptContent.subtitle = ""
|
||||
bestAttemptContent.body = notification.body
|
||||
bestAttemptContent.userInfo["plaintext"] = plaintextData
|
||||
|
||||
UserDefaults.shared.notificationBadgeCount += 1
|
||||
bestAttemptContent.badge = NSNumber(integerLiteral: UserDefaults.shared.notificationBadgeCount)
|
||||
|
|
10
Podfile
10
Podfile
|
@ -27,16 +27,16 @@ target 'Mastodon' do
|
|||
|
||||
end
|
||||
|
||||
target 'AppShared' do
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
plugin 'cocoapods-keys', {
|
||||
:project => "Mastodon",
|
||||
:keys => [
|
||||
"notification_endpoint",
|
||||
"notification_endpoint_debug",
|
||||
"notification_key_nonce",
|
||||
"notification_key_nonce_debug",
|
||||
"notification_key_auth",
|
||||
"notification_key_auth_debug"
|
||||
"notification_endpoint_debug"
|
||||
]
|
||||
}
|
|
@ -30,6 +30,6 @@ SPEC CHECKSUMS:
|
|||
SwiftGen: 67860cc7c3cfc2ed25b9b74cfd55495fc89f9108
|
||||
"UITextField+Shake": 298ac5a0f239d731bdab999b19b628c956ca0ac3
|
||||
|
||||
PODFILE CHECKSUM: b99204f58cb11d471cfad7269bbf0abb853dc953
|
||||
PODFILE CHECKSUM: a8dbae22e6e0bfb84f7db59aef1aa1716793d287
|
||||
|
||||
COCOAPODS: 1.10.1
|
||||
|
|
|
@ -51,6 +51,7 @@ arch -x86_64 pod install
|
|||
- [CryptoSwift](https://github.com/krzyzanowskim/CryptoSwift)
|
||||
- [DateToolSwift](https://github.com/MatthewYork/DateTools)
|
||||
- [Kanna](https://github.com/tid-kijyun/Kanna)
|
||||
- [KeychainAccess](https://github.com/kishikawakatsumi/KeychainAccess.git)
|
||||
- [Kingfisher](https://github.com/onevcat/Kingfisher)
|
||||
- [SwiftGen](https://github.com/SwiftGen/SwiftGen)
|
||||
- [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON)
|
||||
|
|
Loading…
Reference in New Issue