feat: persist notification keys into Keychian

This commit is contained in:
CMK 2021-04-27 16:26:59 +08:00
parent 59760696b5
commit aca358db26
29 changed files with 673 additions and 218 deletions

12
AppShared/AppName.swift Normal file
View File

@ -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"
}

103
AppShared/AppSecret.swift Normal file
View File

@ -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
}
}

18
AppShared/AppShared.h Normal file
View File

@ -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>

22
AppShared/Info.plist Normal file
View File

@ -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>

View File

@ -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)!
}

View File

@ -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])
}

View File

@ -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" */;

View File

@ -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>

View File

@ -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",

View File

@ -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
}
}

View File

@ -6,10 +6,7 @@
//
import Foundation
extension UserDefaults {
static let shared = UserDefaults(suiteName: AppSharedName.groupID)!
}
import AppShared
extension UserDefaults {

View File

@ -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 }
}
}

View File

@ -14,7 +14,7 @@ extension UserDefaults {
register(defaults: [#function: 0])
return integer(forKey: #function)
}
set { UserDefaults.shared[#function] = newValue }
set { self[#function] = newValue }
}
}

View File

@ -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(

View File

@ -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)
}
}

View File

@ -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)

View File

@ -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(

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -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
}
}

View File

@ -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"
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}
}

View File

@ -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>

View File

@ -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
View File

@ -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"
]
}

View File

@ -30,6 +30,6 @@ SPEC CHECKSUMS:
SwiftGen: 67860cc7c3cfc2ed25b9b74cfd55495fc89f9108
"UITextField+Shake": 298ac5a0f239d731bdab999b19b628c956ca0ac3
PODFILE CHECKSUM: b99204f58cb11d471cfad7269bbf0abb853dc953
PODFILE CHECKSUM: a8dbae22e6e0bfb84f7db59aef1aa1716793d287
COCOAPODS: 1.10.1

View File

@ -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)