mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-28 19:11:30 +00:00
Add push notification service extension
This commit is contained in:
parent
b6704c1099
commit
39a7b24370
15 changed files with 497 additions and 48 deletions
|
@ -69,15 +69,15 @@ extension IdentityService {
|
||||||
static let development = try! IdentitiesService.development.identityService(id: devIdentityID)
|
static let development = try! IdentitiesService.development.identityService(id: devIdentityID)
|
||||||
}
|
}
|
||||||
|
|
||||||
extension NotificationService {
|
extension UserNotificationService {
|
||||||
static let development = NotificationService(userNotificationCenter: .current())
|
static let development = UserNotificationService(userNotificationCenter: .current())
|
||||||
}
|
}
|
||||||
|
|
||||||
extension RootViewModel {
|
extension RootViewModel {
|
||||||
static let development = RootViewModel(
|
static let development = RootViewModel(
|
||||||
appDelegate: AppDelegate(),
|
appDelegate: AppDelegate(),
|
||||||
identitiesService: .development,
|
identitiesService: .development,
|
||||||
notificationService: .development)
|
userNotificationService: .development)
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AddIdentityViewModel {
|
extension AddIdentityViewModel {
|
||||||
|
|
|
@ -23,11 +23,11 @@ extension MockKeychainService: KeychainService {
|
||||||
items[account]
|
items[account]
|
||||||
}
|
}
|
||||||
|
|
||||||
static func generateKeyAndReturnPublicKey(applicationTag: String) throws -> Data {
|
static func generateKeyAndReturnPublicKey(applicationTag: String, attributes: [String: Any]) throws -> Data {
|
||||||
fatalError("not implemented")
|
fatalError("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
static func getPrivateKey(applicationTag: String) throws -> Data? {
|
static func getPrivateKey(applicationTag: String, attributes: [String: Any]) throws -> Data? {
|
||||||
fatalError("not implemented")
|
fatalError("not implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,9 @@
|
||||||
<dict>
|
<dict>
|
||||||
<key>aps-environment</key>
|
<key>aps-environment</key>
|
||||||
<string>development</string>
|
<string>development</string>
|
||||||
|
<key>keychain-access-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(AppIdentifierPrefix)com.metabolist.metatext</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -132,6 +132,17 @@
|
||||||
D0DC176124D0171800A75C65 /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = D0DC176024D0171800A75C65 /* Alamofire */; };
|
D0DC176124D0171800A75C65 /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = D0DC176024D0171800A75C65 /* Alamofire */; };
|
||||||
D0DC177724D0CF2600A75C65 /* MockKeychainService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC177624D0CF2600A75C65 /* MockKeychainService.swift */; };
|
D0DC177724D0CF2600A75C65 /* MockKeychainService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC177624D0CF2600A75C65 /* MockKeychainService.swift */; };
|
||||||
D0DC177824D0CF2600A75C65 /* MockKeychainService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC177624D0CF2600A75C65 /* MockKeychainService.swift */; };
|
D0DC177824D0CF2600A75C65 /* MockKeychainService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC177624D0CF2600A75C65 /* MockKeychainService.swift */; };
|
||||||
|
D0E5361C24E3EB4D00FB1CE1 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E5361B24E3EB4D00FB1CE1 /* NotificationService.swift */; };
|
||||||
|
D0E5362024E3EB4D00FB1CE1 /* Notification Service Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = D0E5361924E3EB4D00FB1CE1 /* Notification Service Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
|
D0E5362524E3FE2300FB1CE1 /* SecretsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DC724DF8B3C00A08489 /* SecretsService.swift */; };
|
||||||
|
D0E5362624E3FE2C00FB1CE1 /* KeychainService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DC424DF842700A08489 /* KeychainService.swift */; };
|
||||||
|
D0E5362724E4047C00FB1CE1 /* NSError+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B23F0C24D210E90066F411 /* NSError+Extensions.swift */; };
|
||||||
|
D0E5362C24E534BD00FB1CE1 /* Unknowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CD847B24DBEA9F00CF380C /* Unknowable.swift */; };
|
||||||
|
D0E5362D24E5430F00FB1CE1 /* MastodonDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = D019E6D624DF728400697C7D /* MastodonDecoder.swift */; };
|
||||||
|
D0E5362E24E5432000FB1CE1 /* MastodonAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC175A24D0154F00A75C65 /* MastodonAPI.swift */; };
|
||||||
|
D0E5363024E5436C00FB1CE1 /* PushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E5362F24E5436C00FB1CE1 /* PushNotification.swift */; };
|
||||||
|
D0E5363124E5453E00FB1CE1 /* PushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E5362F24E5436C00FB1CE1 /* PushNotification.swift */; };
|
||||||
|
D0E5363224E5453F00FB1CE1 /* PushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E5362F24E5436C00FB1CE1 /* PushNotification.swift */; };
|
||||||
D0EC8DC224DF7D9C00A08489 /* IdentityService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DC124DF7D9C00A08489 /* IdentityService.swift */; };
|
D0EC8DC224DF7D9C00A08489 /* IdentityService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DC124DF7D9C00A08489 /* IdentityService.swift */; };
|
||||||
D0EC8DC324DF7D9C00A08489 /* IdentityService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DC124DF7D9C00A08489 /* IdentityService.swift */; };
|
D0EC8DC324DF7D9C00A08489 /* IdentityService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DC124DF7D9C00A08489 /* IdentityService.swift */; };
|
||||||
D0EC8DC524DF842700A08489 /* KeychainService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DC424DF842700A08489 /* KeychainService.swift */; };
|
D0EC8DC524DF842700A08489 /* KeychainService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DC424DF842700A08489 /* KeychainService.swift */; };
|
||||||
|
@ -145,8 +156,8 @@
|
||||||
D0EC8DD424DFE38900A08489 /* AuthenticationServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DD324DFE38900A08489 /* AuthenticationServiceTests.swift */; };
|
D0EC8DD424DFE38900A08489 /* AuthenticationServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DD324DFE38900A08489 /* AuthenticationServiceTests.swift */; };
|
||||||
D0EC8DDF24E09D7000A08489 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DDE24E09D7000A08489 /* AppDelegate.swift */; };
|
D0EC8DDF24E09D7000A08489 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DDE24E09D7000A08489 /* AppDelegate.swift */; };
|
||||||
D0EC8DE024E09D7000A08489 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DDE24E09D7000A08489 /* AppDelegate.swift */; };
|
D0EC8DE024E09D7000A08489 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DDE24E09D7000A08489 /* AppDelegate.swift */; };
|
||||||
D0EC8DE424E0B44400A08489 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DD724E096C900A08489 /* NotificationService.swift */; };
|
D0EC8DE424E0B44400A08489 /* UserNotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DD724E096C900A08489 /* UserNotificationService.swift */; };
|
||||||
D0EC8DE524E0B44500A08489 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DD724E096C900A08489 /* NotificationService.swift */; };
|
D0EC8DE524E0B44500A08489 /* UserNotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DD724E096C900A08489 /* UserNotificationService.swift */; };
|
||||||
D0EC8DE824E21FEC00A08489 /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DE724E21FEC00A08489 /* Data+Extensions.swift */; };
|
D0EC8DE824E21FEC00A08489 /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DE724E21FEC00A08489 /* Data+Extensions.swift */; };
|
||||||
D0EC8DE924E21FEC00A08489 /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DE724E21FEC00A08489 /* Data+Extensions.swift */; };
|
D0EC8DE924E21FEC00A08489 /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DE724E21FEC00A08489 /* Data+Extensions.swift */; };
|
||||||
D0EC8DEB24E26F1100A08489 /* PushSubscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DEA24E26F1100A08489 /* PushSubscription.swift */; };
|
D0EC8DEB24E26F1100A08489 /* PushSubscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DEA24E26F1100A08489 /* PushSubscription.swift */; };
|
||||||
|
@ -180,8 +191,29 @@
|
||||||
remoteGlobalIDString = D047FA8B24C3E21200AF17C5;
|
remoteGlobalIDString = D047FA8B24C3E21200AF17C5;
|
||||||
remoteInfo = "Metatext (iOS)";
|
remoteInfo = "Metatext (iOS)";
|
||||||
};
|
};
|
||||||
|
D0E5361E24E3EB4D00FB1CE1 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = D047FA8024C3E21000AF17C5 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = D0E5361824E3EB4D00FB1CE1;
|
||||||
|
remoteInfo = "Notification Service Extension";
|
||||||
|
};
|
||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
D0E5362424E3EB4D00FB1CE1 /* Embed App Extensions */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 13;
|
||||||
|
files = (
|
||||||
|
D0E5362024E3EB4D00FB1CE1 /* Notification Service Extension.appex in Embed App Extensions */,
|
||||||
|
);
|
||||||
|
name = "Embed App Extensions";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
D0091B6724DC10B30040E8D2 /* PostingReadingPreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostingReadingPreferencesView.swift; sourceTree = "<group>"; };
|
D0091B6724DC10B30040E8D2 /* PostingReadingPreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostingReadingPreferencesView.swift; sourceTree = "<group>"; };
|
||||||
D0091B6A24DC10CE0040E8D2 /* PostingReadingPreferencesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostingReadingPreferencesViewModel.swift; sourceTree = "<group>"; };
|
D0091B6A24DC10CE0040E8D2 /* PostingReadingPreferencesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostingReadingPreferencesViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
@ -254,13 +286,18 @@
|
||||||
D0DC175724D0130800A75C65 /* HTTPStubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPStubs.swift; sourceTree = "<group>"; };
|
D0DC175724D0130800A75C65 /* HTTPStubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPStubs.swift; sourceTree = "<group>"; };
|
||||||
D0DC175A24D0154F00A75C65 /* MastodonAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonAPI.swift; sourceTree = "<group>"; };
|
D0DC175A24D0154F00A75C65 /* MastodonAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonAPI.swift; sourceTree = "<group>"; };
|
||||||
D0DC177624D0CF2600A75C65 /* MockKeychainService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockKeychainService.swift; sourceTree = "<group>"; };
|
D0DC177624D0CF2600A75C65 /* MockKeychainService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockKeychainService.swift; sourceTree = "<group>"; };
|
||||||
|
D0E5361924E3EB4D00FB1CE1 /* Notification Service Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Notification Service Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
D0E5361B24E3EB4D00FB1CE1 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
|
||||||
|
D0E5361D24E3EB4D00FB1CE1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
D0E5362824E4A06B00FB1CE1 /* Notification Service Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Notification Service Extension.entitlements"; sourceTree = "<group>"; };
|
||||||
|
D0E5362F24E5436C00FB1CE1 /* PushNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotification.swift; sourceTree = "<group>"; };
|
||||||
D0EC8DC124DF7D9C00A08489 /* IdentityService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IdentityService.swift; sourceTree = "<group>"; };
|
D0EC8DC124DF7D9C00A08489 /* IdentityService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IdentityService.swift; sourceTree = "<group>"; };
|
||||||
D0EC8DC424DF842700A08489 /* KeychainService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainService.swift; sourceTree = "<group>"; };
|
D0EC8DC424DF842700A08489 /* KeychainService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainService.swift; sourceTree = "<group>"; };
|
||||||
D0EC8DC724DF8B3C00A08489 /* SecretsService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsService.swift; sourceTree = "<group>"; };
|
D0EC8DC724DF8B3C00A08489 /* SecretsService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsService.swift; sourceTree = "<group>"; };
|
||||||
D0EC8DCA24DFA06700A08489 /* IdentitiesService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentitiesService.swift; sourceTree = "<group>"; };
|
D0EC8DCA24DFA06700A08489 /* IdentitiesService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentitiesService.swift; sourceTree = "<group>"; };
|
||||||
D0EC8DCD24DFB64200A08489 /* AuthenticationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationService.swift; sourceTree = "<group>"; };
|
D0EC8DCD24DFB64200A08489 /* AuthenticationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationService.swift; sourceTree = "<group>"; };
|
||||||
D0EC8DD324DFE38900A08489 /* AuthenticationServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationServiceTests.swift; sourceTree = "<group>"; };
|
D0EC8DD324DFE38900A08489 /* AuthenticationServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationServiceTests.swift; sourceTree = "<group>"; };
|
||||||
D0EC8DD724E096C900A08489 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
|
D0EC8DD724E096C900A08489 /* UserNotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationService.swift; sourceTree = "<group>"; };
|
||||||
D0EC8DDE24E09D7000A08489 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
D0EC8DDE24E09D7000A08489 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
D0EC8DE624E0BA6500A08489 /* Metatext.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Metatext.entitlements; sourceTree = "<group>"; };
|
D0EC8DE624E0BA6500A08489 /* Metatext.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Metatext.entitlements; sourceTree = "<group>"; };
|
||||||
D0EC8DE724E21FEC00A08489 /* Data+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Extensions.swift"; sourceTree = "<group>"; };
|
D0EC8DE724E21FEC00A08489 /* Data+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -306,6 +343,13 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
D0E5361624E3EB4D00FB1CE1 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
@ -375,7 +419,7 @@
|
||||||
D0EC8DCA24DFA06700A08489 /* IdentitiesService.swift */,
|
D0EC8DCA24DFA06700A08489 /* IdentitiesService.swift */,
|
||||||
D0EC8DC124DF7D9C00A08489 /* IdentityService.swift */,
|
D0EC8DC124DF7D9C00A08489 /* IdentityService.swift */,
|
||||||
D0EC8DC424DF842700A08489 /* KeychainService.swift */,
|
D0EC8DC424DF842700A08489 /* KeychainService.swift */,
|
||||||
D0EC8DD724E096C900A08489 /* NotificationService.swift */,
|
D0EC8DD724E096C900A08489 /* UserNotificationService.swift */,
|
||||||
D0EC8DC724DF8B3C00A08489 /* SecretsService.swift */,
|
D0EC8DC724DF8B3C00A08489 /* SecretsService.swift */,
|
||||||
);
|
);
|
||||||
path = Services;
|
path = Services;
|
||||||
|
@ -384,11 +428,12 @@
|
||||||
D047FA7F24C3E21000AF17C5 = {
|
D047FA7F24C3E21000AF17C5 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
D0EC8DE624E0BA6500A08489 /* Metatext.entitlements */,
|
|
||||||
D0ED1BB224CE3A1600B4899C /* Development Assets */,
|
D0ED1BB224CE3A1600B4899C /* Development Assets */,
|
||||||
D0666A7924C7745A00F3F04B /* Frameworks */,
|
D0666A7924C7745A00F3F04B /* Frameworks */,
|
||||||
D047FA8E24C3E21200AF17C5 /* iOS */,
|
D047FA8E24C3E21200AF17C5 /* iOS */,
|
||||||
D047FA9524C3E21200AF17C5 /* macOS */,
|
D047FA9524C3E21200AF17C5 /* macOS */,
|
||||||
|
D0EC8DE624E0BA6500A08489 /* Metatext.entitlements */,
|
||||||
|
D0E5361A24E3EB4D00FB1CE1 /* Notification Service Extension */,
|
||||||
D047FA8D24C3E21200AF17C5 /* Products */,
|
D047FA8D24C3E21200AF17C5 /* Products */,
|
||||||
D047FA8424C3E21000AF17C5 /* Shared */,
|
D047FA8424C3E21000AF17C5 /* Shared */,
|
||||||
D0666A2224C677B400F3F04B /* Tests */,
|
D0666A2224C677B400F3F04B /* Tests */,
|
||||||
|
@ -419,6 +464,7 @@
|
||||||
D047FA8C24C3E21200AF17C5 /* Metatext.app */,
|
D047FA8C24C3E21200AF17C5 /* Metatext.app */,
|
||||||
D047FA9424C3E21200AF17C5 /* Metatext.app */,
|
D047FA9424C3E21200AF17C5 /* Metatext.app */,
|
||||||
D0666A2124C677B400F3F04B /* Tests.xctest */,
|
D0666A2124C677B400F3F04B /* Tests.xctest */,
|
||||||
|
D0E5361924E3EB4D00FB1CE1 /* Notification Service Extension.appex */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -468,6 +514,7 @@
|
||||||
D0666A4D24C6C39600F3F04B /* Instance.swift */,
|
D0666A4D24C6C39600F3F04B /* Instance.swift */,
|
||||||
D0ED1BE224CFA84400B4899C /* MastodonError.swift */,
|
D0ED1BE224CFA84400B4899C /* MastodonError.swift */,
|
||||||
D0CD847224DBDEC700CF380C /* MastodonPreferences.swift */,
|
D0CD847224DBDEC700CF380C /* MastodonPreferences.swift */,
|
||||||
|
D0E5362F24E5436C00FB1CE1 /* PushNotification.swift */,
|
||||||
D0EC8DEA24E26F1100A08489 /* PushSubscription.swift */,
|
D0EC8DEA24E26F1100A08489 /* PushSubscription.swift */,
|
||||||
D0CD847524DBDF3C00CF380C /* Status.swift */,
|
D0CD847524DBDF3C00CF380C /* Status.swift */,
|
||||||
D0CD847B24DBEA9F00CF380C /* Unknowable.swift */,
|
D0CD847B24DBEA9F00CF380C /* Unknowable.swift */,
|
||||||
|
@ -552,6 +599,16 @@
|
||||||
path = "Mastodon API Stubs";
|
path = "Mastodon API Stubs";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
D0E5361A24E3EB4D00FB1CE1 /* Notification Service Extension */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D0E5362824E4A06B00FB1CE1 /* Notification Service Extension.entitlements */,
|
||||||
|
D0E5361B24E3EB4D00FB1CE1 /* NotificationService.swift */,
|
||||||
|
D0E5361D24E3EB4D00FB1CE1 /* Info.plist */,
|
||||||
|
);
|
||||||
|
path = "Notification Service Extension";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
D0EC8DD024DFE34F00A08489 /* Services */ = {
|
D0EC8DD024DFE34F00A08489 /* Services */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -610,10 +667,12 @@
|
||||||
D047FA8924C3E21200AF17C5 /* Frameworks */,
|
D047FA8924C3E21200AF17C5 /* Frameworks */,
|
||||||
D047FA8A24C3E21200AF17C5 /* Resources */,
|
D047FA8A24C3E21200AF17C5 /* Resources */,
|
||||||
D0666A2E24C67E6700F3F04B /* ShellScript */,
|
D0666A2E24C67E6700F3F04B /* ShellScript */,
|
||||||
|
D0E5362424E3EB4D00FB1CE1 /* Embed App Extensions */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
|
D0E5361F24E3EB4D00FB1CE1 /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
name = "Metatext (iOS)";
|
name = "Metatext (iOS)";
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
|
@ -669,6 +728,25 @@
|
||||||
productReference = D0666A2124C677B400F3F04B /* Tests.xctest */;
|
productReference = D0666A2124C677B400F3F04B /* Tests.xctest */;
|
||||||
productType = "com.apple.product-type.bundle.unit-test";
|
productType = "com.apple.product-type.bundle.unit-test";
|
||||||
};
|
};
|
||||||
|
D0E5361824E3EB4D00FB1CE1 /* Notification Service Extension */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = D0E5362124E3EB4D00FB1CE1 /* Build configuration list for PBXNativeTarget "Notification Service Extension" */;
|
||||||
|
buildPhases = (
|
||||||
|
D0E5361524E3EB4D00FB1CE1 /* Sources */,
|
||||||
|
D0E5361624E3EB4D00FB1CE1 /* Frameworks */,
|
||||||
|
D0E5361724E3EB4D00FB1CE1 /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = "Notification Service Extension";
|
||||||
|
packageProductDependencies = (
|
||||||
|
);
|
||||||
|
productName = "Notification Service Extension";
|
||||||
|
productReference = D0E5361924E3EB4D00FB1CE1 /* Notification Service Extension.appex */;
|
||||||
|
productType = "com.apple.product-type.app-extension";
|
||||||
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
|
@ -690,6 +768,9 @@
|
||||||
LastSwiftMigration = 1200;
|
LastSwiftMigration = 1200;
|
||||||
TestTargetID = D047FA8B24C3E21200AF17C5;
|
TestTargetID = D047FA8B24C3E21200AF17C5;
|
||||||
};
|
};
|
||||||
|
D0E5361824E3EB4D00FB1CE1 = {
|
||||||
|
CreatedOnToolsVersion = 12.0;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = D047FA8324C3E21000AF17C5 /* Build configuration list for PBXProject "Metatext" */;
|
buildConfigurationList = D047FA8324C3E21000AF17C5 /* Build configuration list for PBXProject "Metatext" */;
|
||||||
|
@ -714,6 +795,7 @@
|
||||||
D047FA8B24C3E21200AF17C5 /* Metatext (iOS) */,
|
D047FA8B24C3E21200AF17C5 /* Metatext (iOS) */,
|
||||||
D047FA9324C3E21200AF17C5 /* Metatext (macOS) */,
|
D047FA9324C3E21200AF17C5 /* Metatext (macOS) */,
|
||||||
D0666A2024C677B400F3F04B /* Tests */,
|
D0666A2024C677B400F3F04B /* Tests */,
|
||||||
|
D0E5361824E3EB4D00FB1CE1 /* Notification Service Extension */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
|
@ -744,6 +826,13 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
D0E5361724E3EB4D00FB1CE1 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
@ -830,11 +919,12 @@
|
||||||
D0DC175824D0130800A75C65 /* HTTPStubs.swift in Sources */,
|
D0DC175824D0130800A75C65 /* HTTPStubs.swift in Sources */,
|
||||||
D0DC177724D0CF2600A75C65 /* MockKeychainService.swift in Sources */,
|
D0DC177724D0CF2600A75C65 /* MockKeychainService.swift in Sources */,
|
||||||
D0EC8DC224DF7D9C00A08489 /* IdentityService.swift in Sources */,
|
D0EC8DC224DF7D9C00A08489 /* IdentityService.swift in Sources */,
|
||||||
|
D0E5363024E5436C00FB1CE1 /* PushNotification.swift in Sources */,
|
||||||
D0C963FB24CC359D003BD330 /* AlertItem.swift in Sources */,
|
D0C963FB24CC359D003BD330 /* AlertItem.swift in Sources */,
|
||||||
D0DC174624CFEC2000A75C65 /* StubbingURLProtocol.swift in Sources */,
|
D0DC174624CFEC2000A75C65 /* StubbingURLProtocol.swift in Sources */,
|
||||||
D019E6F024DF7C2F00697C7D /* DatabaseError.swift in Sources */,
|
D019E6F024DF7C2F00697C7D /* DatabaseError.swift in Sources */,
|
||||||
D019E6D724DF728400697C7D /* MastodonEncoder.swift in Sources */,
|
D019E6D724DF728400697C7D /* MastodonEncoder.swift in Sources */,
|
||||||
D0EC8DE524E0B44500A08489 /* NotificationService.swift in Sources */,
|
D0EC8DE524E0B44500A08489 /* UserNotificationService.swift in Sources */,
|
||||||
D0EC8DCB24DFA06700A08489 /* IdentitiesService.swift in Sources */,
|
D0EC8DCB24DFA06700A08489 /* IdentitiesService.swift in Sources */,
|
||||||
D0091B7124DD68220040E8D2 /* PreferencesViewModel.swift in Sources */,
|
D0091B7124DD68220040E8D2 /* PreferencesViewModel.swift in Sources */,
|
||||||
D0DC174D24CFF1F100A75C65 /* Stubbing.swift in Sources */,
|
D0DC174D24CFF1F100A75C65 /* Stubbing.swift in Sources */,
|
||||||
|
@ -872,6 +962,7 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
D0E5363124E5453E00FB1CE1 /* PushNotification.swift in Sources */,
|
||||||
D04FD73A24D4A7B4007D572D /* AccountEndpoint+Stubbing.swift in Sources */,
|
D04FD73A24D4A7B4007D572D /* AccountEndpoint+Stubbing.swift in Sources */,
|
||||||
D0DB6F0A24C65AC000D965FE /* AddIdentityViewModel.swift in Sources */,
|
D0DB6F0A24C65AC000D965FE /* AddIdentityViewModel.swift in Sources */,
|
||||||
D0CD847424DBDEC700CF380C /* MastodonPreferences.swift in Sources */,
|
D0CD847424DBDEC700CF380C /* MastodonPreferences.swift in Sources */,
|
||||||
|
@ -891,7 +982,7 @@
|
||||||
D0BEC93924C9632800E864C4 /* RootViewModel.swift in Sources */,
|
D0BEC93924C9632800E864C4 /* RootViewModel.swift in Sources */,
|
||||||
D0ED1BC224CED48800B4899C /* HTTPClient.swift in Sources */,
|
D0ED1BC224CED48800B4899C /* HTTPClient.swift in Sources */,
|
||||||
D0666A4C24C6C37700F3F04B /* Identity.swift in Sources */,
|
D0666A4C24C6C37700F3F04B /* Identity.swift in Sources */,
|
||||||
D0EC8DE424E0B44400A08489 /* NotificationService.swift in Sources */,
|
D0EC8DE424E0B44400A08489 /* UserNotificationService.swift in Sources */,
|
||||||
D0EC8DCC24DFA06700A08489 /* IdentitiesService.swift in Sources */,
|
D0EC8DCC24DFA06700A08489 /* IdentitiesService.swift in Sources */,
|
||||||
D0666A5524C6C3E500F3F04B /* Emoji.swift in Sources */,
|
D0666A5524C6C3E500F3F04B /* Emoji.swift in Sources */,
|
||||||
D019E6EE24DF7BF300697C7D /* IdentityDatabase.swift in Sources */,
|
D019E6EE24DF7BF300697C7D /* IdentityDatabase.swift in Sources */,
|
||||||
|
@ -957,6 +1048,21 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
D0E5361524E3EB4D00FB1CE1 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
D0E5363224E5453F00FB1CE1 /* PushNotification.swift in Sources */,
|
||||||
|
D0E5362C24E534BD00FB1CE1 /* Unknowable.swift in Sources */,
|
||||||
|
D0E5362D24E5430F00FB1CE1 /* MastodonDecoder.swift in Sources */,
|
||||||
|
D0E5362E24E5432000FB1CE1 /* MastodonAPI.swift in Sources */,
|
||||||
|
D0E5361C24E3EB4D00FB1CE1 /* NotificationService.swift in Sources */,
|
||||||
|
D0E5362724E4047C00FB1CE1 /* NSError+Extensions.swift in Sources */,
|
||||||
|
D0E5362524E3FE2300FB1CE1 /* SecretsService.swift in Sources */,
|
||||||
|
D0E5362624E3FE2C00FB1CE1 /* KeychainService.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXTargetDependency section */
|
/* Begin PBXTargetDependency section */
|
||||||
|
@ -965,6 +1071,11 @@
|
||||||
target = D047FA8B24C3E21200AF17C5 /* Metatext (iOS) */;
|
target = D047FA8B24C3E21200AF17C5 /* Metatext (iOS) */;
|
||||||
targetProxy = D0666A2624C677B400F3F04B /* PBXContainerItemProxy */;
|
targetProxy = D0666A2624C677B400F3F04B /* PBXContainerItemProxy */;
|
||||||
};
|
};
|
||||||
|
D0E5361F24E3EB4D00FB1CE1 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = D0E5361824E3EB4D00FB1CE1 /* Notification Service Extension */;
|
||||||
|
targetProxy = D0E5361E24E3EB4D00FB1CE1 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
/* End PBXTargetDependency section */
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
|
@ -1082,6 +1193,7 @@
|
||||||
D047FAB724C3E21200AF17C5 /* Debug */ = {
|
D047FAB724C3E21200AF17C5 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_ENTITLEMENTS = Metatext.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Metatext.entitlements;
|
||||||
|
@ -1106,6 +1218,7 @@
|
||||||
D047FAB824C3E21200AF17C5 /* Release */ = {
|
D047FAB824C3E21200AF17C5 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_ENTITLEMENTS = Metatext.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Metatext.entitlements;
|
||||||
|
@ -1226,6 +1339,51 @@
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
D0E5362224E3EB4D00FB1CE1 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_ENTITLEMENTS = "Notification Service Extension/Notification Service Extension.entitlements";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
DEVELOPMENT_TEAM = 82HL67AXQ2;
|
||||||
|
INFOPLIST_FILE = "Notification Service Extension/Info.plist";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "com.metabolist.metatext.notification-service-extension";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
D0E5362324E3EB4D00FB1CE1 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_ENTITLEMENTS = "Notification Service Extension/Notification Service Extension.entitlements";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
DEVELOPMENT_TEAM = 82HL67AXQ2;
|
||||||
|
INFOPLIST_FILE = "Notification Service Extension/Info.plist";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "com.metabolist.metatext.notification-service-extension";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
|
@ -1265,6 +1423,15 @@
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
|
D0E5362124E3EB4D00FB1CE1 /* Build configuration list for PBXNativeTarget "Notification Service Extension" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
D0E5362224E3EB4D00FB1CE1 /* Debug */,
|
||||||
|
D0E5362324E3EB4D00FB1CE1 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
|
|
31
Notification Service Extension/Info.plist
Normal file
31
Notification Service Extension/Info.plist
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>Notification Service Extension</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
<key>NSExtension</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.usernotifications.service</string>
|
||||||
|
<key>NSExtensionPrincipalClass</key>
|
||||||
|
<string>$(PRODUCT_MODULE_NAME).NotificationService</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -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>keychain-access-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(AppIdentifierPrefix)com.metabolist.metatext</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
166
Notification Service Extension/NotificationService.swift
Normal file
166
Notification Service Extension/NotificationService.swift
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import UserNotifications
|
||||||
|
import CryptoKit
|
||||||
|
|
||||||
|
class NotificationService: UNNotificationServiceExtension {
|
||||||
|
|
||||||
|
var contentHandler: ((UNNotificationContent) -> Void)?
|
||||||
|
var bestAttemptContent: UNMutableNotificationContent?
|
||||||
|
|
||||||
|
override func didReceive(
|
||||||
|
_ request: UNNotificationRequest,
|
||||||
|
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
|
||||||
|
self.contentHandler = contentHandler
|
||||||
|
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
|
||||||
|
|
||||||
|
guard let bestAttemptContent = bestAttemptContent else { return }
|
||||||
|
|
||||||
|
let pushNotification: PushNotification
|
||||||
|
|
||||||
|
do {
|
||||||
|
let decryptedJSON = try Self.extractAndDecrypt(userInfo: request.content.userInfo)
|
||||||
|
|
||||||
|
pushNotification = try MastodonDecoder().decode(PushNotification.self, from: decryptedJSON)
|
||||||
|
} catch {
|
||||||
|
contentHandler(bestAttemptContent)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bestAttemptContent.title = pushNotification.title
|
||||||
|
bestAttemptContent.body = pushNotification.body
|
||||||
|
|
||||||
|
let fileName = pushNotification.icon.lastPathComponent
|
||||||
|
let fileURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent(fileName)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let iconData = try Data(contentsOf: pushNotification.icon)
|
||||||
|
|
||||||
|
try iconData.write(to: fileURL)
|
||||||
|
bestAttemptContent.attachments = [try UNNotificationAttachment(identifier: fileName, url: fileURL)]
|
||||||
|
} catch {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
contentHandler(bestAttemptContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func serviceExtensionTimeWillExpire() {
|
||||||
|
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
|
||||||
|
contentHandler(bestAttemptContent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum NotificationServiceError: Error {
|
||||||
|
case userInfoDataAbsent
|
||||||
|
case keychainDataAbsent
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension NotificationService {
|
||||||
|
static let identityIDUserInfoKey = "i"
|
||||||
|
static let encryptedMessageUserInfoKey = "m"
|
||||||
|
static let saltUserInfoKey = "s"
|
||||||
|
static let serverPublicKeyUserInfoKey = "k"
|
||||||
|
static let keyLength = 16
|
||||||
|
static let nonceLength = 12
|
||||||
|
static let pseudoRandomKeyLength = 32
|
||||||
|
static let paddedByteCount = 2
|
||||||
|
static let curve = "P-256"
|
||||||
|
|
||||||
|
enum HKDFInfo: String {
|
||||||
|
case auth, aesgcm, nonce
|
||||||
|
|
||||||
|
var bytes: [UInt8] {
|
||||||
|
Array("Content-Encoding: \(self)\0".utf8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func extractAndDecrypt(userInfo: [AnyHashable: Any]) throws -> Data {
|
||||||
|
guard
|
||||||
|
let identityIDString = userInfo[identityIDUserInfoKey] as? String,
|
||||||
|
let identityID = UUID(uuidString: identityIDString),
|
||||||
|
let encryptedMessageBase64 = (userInfo[encryptedMessageUserInfoKey] as? String)?.URLSafeBase64ToBase64(),
|
||||||
|
let encryptedMessage = Data(base64Encoded: encryptedMessageBase64),
|
||||||
|
let saltBase64 = (userInfo[saltUserInfoKey] as? String)?.URLSafeBase64ToBase64(),
|
||||||
|
let salt = Data(base64Encoded: saltBase64),
|
||||||
|
let serverPublicKeyBase64 = (userInfo[serverPublicKeyUserInfoKey] as? String)?.URLSafeBase64ToBase64(),
|
||||||
|
let serverPublicKeyData = Data(base64Encoded: serverPublicKeyBase64)
|
||||||
|
else { throw NotificationServiceError.userInfoDataAbsent }
|
||||||
|
|
||||||
|
let secretsService = SecretsService(identityID: identityID, keychainServiceType: LiveKeychainService.self)
|
||||||
|
|
||||||
|
guard
|
||||||
|
let auth = try secretsService.getPushAuth(),
|
||||||
|
let pushKey = try secretsService.getPushKey()
|
||||||
|
else { throw NotificationServiceError.keychainDataAbsent }
|
||||||
|
|
||||||
|
return try decrypt(encryptedMessage: encryptedMessage,
|
||||||
|
privateKeyData: pushKey,
|
||||||
|
serverPublicKeyData: serverPublicKeyData,
|
||||||
|
auth: auth,
|
||||||
|
salt: salt)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func decrypt(encryptedMessage: Data,
|
||||||
|
privateKeyData: Data,
|
||||||
|
serverPublicKeyData: Data,
|
||||||
|
auth: Data,
|
||||||
|
salt: Data) throws -> Data {
|
||||||
|
let privateKey = try P256.KeyAgreement.PrivateKey(x963Representation: privateKeyData)
|
||||||
|
let serverPublicKey = try P256.KeyAgreement.PublicKey(x963Representation: serverPublicKeyData)
|
||||||
|
let sharedSecret = try privateKey.sharedSecretFromKeyAgreement(with: serverPublicKey)
|
||||||
|
|
||||||
|
var keyInfo = HKDFInfo.aesgcm.bytes
|
||||||
|
var nonceInfo = HKDFInfo.nonce.bytes
|
||||||
|
var context = Array(curve.utf8)
|
||||||
|
let publicKeyData = privateKey.publicKey.x963Representation
|
||||||
|
|
||||||
|
context.append(0)
|
||||||
|
context.append(0)
|
||||||
|
context.append(UInt8(publicKeyData.count))
|
||||||
|
context += Array(publicKeyData)
|
||||||
|
context.append(0)
|
||||||
|
context.append(UInt8(serverPublicKeyData.count))
|
||||||
|
context += Array(serverPublicKeyData)
|
||||||
|
|
||||||
|
keyInfo += context
|
||||||
|
nonceInfo += context
|
||||||
|
|
||||||
|
let pseudoRandomKey = sharedSecret.hkdfDerivedSymmetricKey(
|
||||||
|
using: SHA256.self,
|
||||||
|
salt: auth,
|
||||||
|
sharedInfo: HKDFInfo.auth.bytes,
|
||||||
|
outputByteCount: pseudoRandomKeyLength)
|
||||||
|
let key = HKDF<SHA256>.deriveKey(
|
||||||
|
inputKeyMaterial: pseudoRandomKey,
|
||||||
|
salt: salt,
|
||||||
|
info: keyInfo,
|
||||||
|
outputByteCount: keyLength)
|
||||||
|
let nonce = HKDF<SHA256>.deriveKey(
|
||||||
|
inputKeyMaterial: pseudoRandomKey,
|
||||||
|
salt: salt,
|
||||||
|
info: nonceInfo,
|
||||||
|
outputByteCount: nonceLength)
|
||||||
|
|
||||||
|
let sealedBox = try AES.GCM.SealedBox(combined: nonce.withUnsafeBytes(Array.init) + encryptedMessage)
|
||||||
|
let decrypted = try AES.GCM.open(sealedBox, using: key)
|
||||||
|
let unpadded = decrypted.suffix(from: paddedByteCount)
|
||||||
|
|
||||||
|
return Data(unpadded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
func URLSafeBase64ToBase64() -> String {
|
||||||
|
var base64 = replacingOccurrences(of: "-", with: "+").replacingOccurrences(of: "_", with: "/")
|
||||||
|
let countMod4 = count % 4
|
||||||
|
|
||||||
|
if countMod4 != 0 {
|
||||||
|
base64.append(String(repeating: "=", count: 4 - countMod4))
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
// Copyright © 2020 Metabolist. All rights reserved.
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
"apns-default-message" = "New notification";
|
||||||
"add-identity.instance-url" = "Instance URL";
|
"add-identity.instance-url" = "Instance URL";
|
||||||
"add-identity.log-in" = "Log in";
|
"add-identity.log-in" = "Log in";
|
||||||
"add-identity.browse-anonymously" = "Browse anonymously";
|
"add-identity.browse-anonymously" = "Browse anonymously";
|
||||||
|
|
|
@ -29,7 +29,7 @@ struct MetatextApp: App {
|
||||||
RootView(
|
RootView(
|
||||||
viewModel: RootViewModel(appDelegate: appDelegate,
|
viewModel: RootViewModel(appDelegate: appDelegate,
|
||||||
identitiesService: identitiesService,
|
identitiesService: identitiesService,
|
||||||
notificationService: NotificationService()))
|
userNotificationService: UserNotificationService()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
23
Shared/Model/PushNotification.swift
Normal file
23
Shared/Model/PushNotification.swift
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct PushNotification: Codable {
|
||||||
|
enum NotificationType: String, Codable, Unknowable {
|
||||||
|
case mention
|
||||||
|
case reblog
|
||||||
|
case favourite
|
||||||
|
case follow
|
||||||
|
case unknown
|
||||||
|
|
||||||
|
static var unknownCase: Self { .unknown }
|
||||||
|
}
|
||||||
|
|
||||||
|
let accessToken: String
|
||||||
|
let body: String
|
||||||
|
let title: String
|
||||||
|
let icon: URL
|
||||||
|
let notificationId: Int
|
||||||
|
let notificationType: NotificationType
|
||||||
|
let preferredLocale: String
|
||||||
|
}
|
|
@ -6,8 +6,8 @@ protocol KeychainService {
|
||||||
static func setGenericPassword(data: Data, forAccount key: String, service: String) throws
|
static func setGenericPassword(data: Data, forAccount key: String, service: String) throws
|
||||||
static func deleteGenericPassword(account: String, service: String) throws
|
static func deleteGenericPassword(account: String, service: String) throws
|
||||||
static func getGenericPassword(account: String, service: String) throws -> Data?
|
static func getGenericPassword(account: String, service: String) throws -> Data?
|
||||||
static func generateKeyAndReturnPublicKey(applicationTag: String) throws -> Data
|
static func generateKeyAndReturnPublicKey(applicationTag: String, attributes: [String: Any]) throws -> Data
|
||||||
static func getPrivateKey(applicationTag: String) throws -> Data?
|
static func getPrivateKey(applicationTag: String, attributes: [String: Any]) throws -> Data?
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LiveKeychainService {}
|
struct LiveKeychainService {}
|
||||||
|
@ -52,13 +52,21 @@ extension LiveKeychainService: KeychainService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func generateKeyAndReturnPublicKey(applicationTag: String) throws -> Data {
|
static func generateKeyAndReturnPublicKey(applicationTag: String, attributes: [String: Any]) throws -> Data {
|
||||||
var attributes = keyAttributes
|
var attributes = attributes
|
||||||
var error: Unmanaged<CFError>?
|
var error: Unmanaged<CFError>?
|
||||||
|
|
||||||
|
guard let accessControl = SecAccessControlCreateWithFlags(
|
||||||
|
kCFAllocatorDefault,
|
||||||
|
kSecAttrAccessibleAfterFirstUnlock,
|
||||||
|
[],
|
||||||
|
&error)
|
||||||
|
else { throw error?.takeRetainedValue() ?? NSError() }
|
||||||
|
|
||||||
attributes[kSecPrivateKeyAttrs as String] = [
|
attributes[kSecPrivateKeyAttrs as String] = [
|
||||||
kSecAttrIsPermanent as String: true,
|
kSecAttrIsPermanent as String: true,
|
||||||
kSecAttrApplicationTag as String: Data(applicationTag.utf8)]
|
kSecAttrApplicationTag as String: Data(applicationTag.utf8),
|
||||||
|
kSecAttrAccessControl as String: accessControl]
|
||||||
|
|
||||||
guard
|
guard
|
||||||
let key = SecKeyCreateRandomKey(attributes as CFDictionary, &error),
|
let key = SecKeyCreateRandomKey(attributes as CFDictionary, &error),
|
||||||
|
@ -69,13 +77,27 @@ extension LiveKeychainService: KeychainService {
|
||||||
return publicKeyData
|
return publicKeyData
|
||||||
}
|
}
|
||||||
|
|
||||||
static func getPrivateKey(applicationTag: String) throws -> Data? {
|
static func getPrivateKey(applicationTag: String, attributes: [String: Any]) throws -> Data? {
|
||||||
var result: AnyObject?
|
var result: AnyObject?
|
||||||
let status = SecItemCopyMatching(keyQueryDictionary(applicationTag: applicationTag) as CFDictionary, &result)
|
var error: Unmanaged<CFError>?
|
||||||
|
var query = keyQueryDictionary(applicationTag: applicationTag)
|
||||||
|
|
||||||
|
query.merge(attributes, uniquingKeysWith: { $1 })
|
||||||
|
query[kSecMatchLimit as String] = kSecMatchLimitOne
|
||||||
|
query[kSecReturnRef as String] = kCFBooleanTrue
|
||||||
|
|
||||||
|
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
||||||
|
|
||||||
switch status {
|
switch status {
|
||||||
case errSecSuccess:
|
case errSecSuccess:
|
||||||
return result as? Data
|
// swiftlint:disable force_cast
|
||||||
|
let secKey = result as! SecKey
|
||||||
|
// swiftlint:enable force_cast
|
||||||
|
guard let data = SecKeyCopyExternalRepresentation(secKey, &error) else {
|
||||||
|
throw error?.takeRetainedValue() ?? NSError()
|
||||||
|
}
|
||||||
|
|
||||||
|
return data as Data
|
||||||
case errSecItemNotFound:
|
case errSecItemNotFound:
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
|
@ -85,8 +107,6 @@ extension LiveKeychainService: KeychainService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension LiveKeychainService {
|
private extension LiveKeychainService {
|
||||||
static let keySizeInBits = 256
|
|
||||||
|
|
||||||
static func genericPasswordQueryDictionary(account: String, service: String) -> [String: Any] {
|
static func genericPasswordQueryDictionary(account: String, service: String) -> [String: Any] {
|
||||||
[kSecAttrService as String: service,
|
[kSecAttrService as String: service,
|
||||||
kSecAttrAccount as String: account,
|
kSecAttrAccount as String: account,
|
||||||
|
@ -96,13 +116,7 @@ private extension LiveKeychainService {
|
||||||
static func keyQueryDictionary(applicationTag: String) -> [String: Any] {
|
static func keyQueryDictionary(applicationTag: String) -> [String: Any] {
|
||||||
[kSecClass as String: kSecClassKey,
|
[kSecClass as String: kSecClassKey,
|
||||||
kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
|
kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
|
||||||
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
|
|
||||||
kSecAttrKeySizeInBits as String: keySizeInBits,
|
|
||||||
kSecAttrApplicationTag as String: applicationTag,
|
kSecAttrApplicationTag as String: applicationTag,
|
||||||
kSecReturnRef as String: true]
|
kSecReturnRef as String: true]
|
||||||
}
|
}
|
||||||
|
|
||||||
static let keyAttributes: [String: Any] = [
|
|
||||||
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
|
|
||||||
kSecAttrKeySizeInBits as String: keySizeInBits]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,17 +58,21 @@ extension SecretsService {
|
||||||
}
|
}
|
||||||
|
|
||||||
func generatePushKeyAndReturnPublicKey() throws -> Data {
|
func generatePushKeyAndReturnPublicKey() throws -> Data {
|
||||||
try keychainServiceType.generateKeyAndReturnPublicKey(applicationTag: key(item: .pushKey))
|
try keychainServiceType.generateKeyAndReturnPublicKey(
|
||||||
|
applicationTag: key(item: .pushKey),
|
||||||
|
attributes: PushKey.attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPushKey() throws -> Data? {
|
func getPushKey() throws -> Data? {
|
||||||
try keychainServiceType.getPrivateKey(applicationTag: key(item: .pushKey))
|
try keychainServiceType.getPrivateKey(
|
||||||
|
applicationTag: key(item: .pushKey),
|
||||||
|
attributes: PushKey.attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func generatePushAuth() throws -> Data {
|
func generatePushAuth() throws -> Data {
|
||||||
var bytes = [UInt8](repeating: 0, count: Self.authLength)
|
var bytes = [UInt8](repeating: 0, count: PushKey.authLength)
|
||||||
|
|
||||||
_ = SecRandomCopyBytes(kSecRandomDefault, Self.authLength, &bytes)
|
_ = SecRandomCopyBytes(kSecRandomDefault, PushKey.authLength, &bytes)
|
||||||
|
|
||||||
let pushAuth = Data(bytes)
|
let pushAuth = Data(bytes)
|
||||||
|
|
||||||
|
@ -84,7 +88,6 @@ extension SecretsService {
|
||||||
|
|
||||||
private extension SecretsService {
|
private extension SecretsService {
|
||||||
static let keychainServiceName = "com.metabolist.metatext"
|
static let keychainServiceName = "com.metabolist.metatext"
|
||||||
private static let authLength = 16
|
|
||||||
|
|
||||||
func key(item: Item) -> String {
|
func key(item: Item) -> String {
|
||||||
identityID.uuidString + "." + item.rawValue
|
identityID.uuidString + "." + item.rawValue
|
||||||
|
@ -110,3 +113,11 @@ extension String: SecretsStorable {
|
||||||
return string
|
return string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PushKey {
|
||||||
|
static let authLength = 16
|
||||||
|
static let sizeInBits = 256
|
||||||
|
static let attributes: [String: Any] = [
|
||||||
|
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
|
||||||
|
kSecAttrKeySizeInBits as String: sizeInBits]
|
||||||
|
}
|
||||||
|
|
|
@ -4,21 +4,27 @@ import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
import UserNotifications
|
import UserNotifications
|
||||||
|
|
||||||
struct NotificationService {
|
class UserNotificationService: NSObject {
|
||||||
private let userNotificationCenter: UNUserNotificationCenter
|
private let userNotificationCenter: UNUserNotificationCenter
|
||||||
|
|
||||||
init(userNotificationCenter: UNUserNotificationCenter = .current()) {
|
init(userNotificationCenter: UNUserNotificationCenter = .current()) {
|
||||||
self.userNotificationCenter = userNotificationCenter
|
self.userNotificationCenter = userNotificationCenter
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
userNotificationCenter.delegate = self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension NotificationService {
|
extension UserNotificationService {
|
||||||
func isAuthorized() -> AnyPublisher<Bool, Error> {
|
func isAuthorized() -> AnyPublisher<Bool, Error> {
|
||||||
getNotificationSettings()
|
getNotificationSettings()
|
||||||
.map(\.authorizationStatus)
|
.map(\.authorizationStatus)
|
||||||
.flatMap { status -> AnyPublisher<Bool, Error> in
|
.flatMap { [weak self] status -> AnyPublisher<Bool, Error> in
|
||||||
if status == .notDetermined {
|
if status == .notDetermined {
|
||||||
return requestProvisionalAuthorization().eraseToAnyPublisher()
|
return self?.requestProvisionalAuthorization()
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
?? Empty().eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
return Just(status == .authorized || status == .provisional)
|
return Just(status == .authorized || status == .provisional)
|
||||||
|
@ -29,17 +35,17 @@ extension NotificationService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension NotificationService {
|
private extension UserNotificationService {
|
||||||
func getNotificationSettings() -> AnyPublisher<UNNotificationSettings, Never> {
|
func getNotificationSettings() -> AnyPublisher<UNNotificationSettings, Never> {
|
||||||
Future<UNNotificationSettings, Never> { promise in
|
Future<UNNotificationSettings, Never> { [weak self] promise in
|
||||||
userNotificationCenter.getNotificationSettings { promise(.success($0)) }
|
self?.userNotificationCenter.getNotificationSettings { promise(.success($0)) }
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestProvisionalAuthorization() -> AnyPublisher<Bool, Error> {
|
func requestProvisionalAuthorization() -> AnyPublisher<Bool, Error> {
|
||||||
Future<Bool, Error> { promise in
|
Future<Bool, Error> { [weak self] promise in
|
||||||
userNotificationCenter.requestAuthorization(
|
self?.userNotificationCenter.requestAuthorization(
|
||||||
options: [.alert, .sound, .badge, .provisional]) { granted, error in
|
options: [.alert, .sound, .badge, .provisional]) { granted, error in
|
||||||
if let error = error {
|
if let error = error {
|
||||||
return promise(.failure(error))
|
return promise(.failure(error))
|
||||||
|
@ -51,3 +57,13 @@ private extension NotificationService {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension UserNotificationService: UNUserNotificationCenterDelegate {
|
||||||
|
func userNotificationCenter(
|
||||||
|
_ center: UNUserNotificationCenter,
|
||||||
|
willPresent notification: UNNotification,
|
||||||
|
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
||||||
|
print(notification.request.content.body)
|
||||||
|
completionHandler(.banner)
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,17 +10,19 @@ class RootViewModel: ObservableObject {
|
||||||
private let appDelegate: AppDelegate
|
private let appDelegate: AppDelegate
|
||||||
// swiftlint:enable weak_delegate
|
// swiftlint:enable weak_delegate
|
||||||
private let identitiesService: IdentitiesService
|
private let identitiesService: IdentitiesService
|
||||||
private let notificationService: NotificationService
|
private let userNotificationService: UserNotificationService
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
init(appDelegate: AppDelegate, identitiesService: IdentitiesService, notificationService: NotificationService) {
|
init(appDelegate: AppDelegate,
|
||||||
|
identitiesService: IdentitiesService,
|
||||||
|
userNotificationService: UserNotificationService) {
|
||||||
self.appDelegate = appDelegate
|
self.appDelegate = appDelegate
|
||||||
self.identitiesService = identitiesService
|
self.identitiesService = identitiesService
|
||||||
self.notificationService = notificationService
|
self.userNotificationService = userNotificationService
|
||||||
|
|
||||||
newIdentitySelected(id: identitiesService.mostRecentlyUsedIdentityID)
|
newIdentitySelected(id: identitiesService.mostRecentlyUsedIdentityID)
|
||||||
|
|
||||||
notificationService.isAuthorized()
|
userNotificationService.isAuthorized()
|
||||||
.filter { $0 }
|
.filter { $0 }
|
||||||
.zip(appDelegate.registerForRemoteNotifications())
|
.zip(appDelegate.registerForRemoteNotifications())
|
||||||
.map { $1 }
|
.map { $1 }
|
||||||
|
@ -62,7 +64,7 @@ extension RootViewModel {
|
||||||
func newIdentityCreated(id: UUID, instanceURL: URL) {
|
func newIdentityCreated(id: UUID, instanceURL: URL) {
|
||||||
newIdentitySelected(id: id)
|
newIdentitySelected(id: id)
|
||||||
|
|
||||||
notificationService.isAuthorized()
|
userNotificationService.isAuthorized()
|
||||||
.filter { $0 }
|
.filter { $0 }
|
||||||
.zip(appDelegate.registerForRemoteNotifications())
|
.zip(appDelegate.registerForRemoteNotifications())
|
||||||
.map { (id, instanceURL, $1, nil) }
|
.map { (id, instanceURL, $1, nil) }
|
||||||
|
|
|
@ -10,5 +10,9 @@
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.network.client</key>
|
<key>com.apple.security.network.client</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>keychain-access-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(AppIdentifierPrefix)com.metabolist.metatext</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
Loading…
Reference in a new issue