ios: register notification token/mode on app start (#761)

* ios: register notification token/mode on app start

* refactor

* register token on start

* update model on main thread
This commit is contained in:
Evgeny Poberezkin
2022-07-01 09:49:30 +01:00
committed by GitHub
parent b2c455c301
commit 815981487b
8 changed files with 93 additions and 83 deletions

View File

@@ -21,17 +21,10 @@ class AppDelegate: NSObject, UIApplicationDelegate {
let token = deviceToken.map { String(format: "%02hhx", $0) }.joined()
logger.debug("AppDelegate: didRegisterForRemoteNotificationsWithDeviceToken \(token)")
let m = ChatModel.shared
let deviceToken = DeviceToken(env: pushEnvironment, token: token)
let deviceToken = DeviceToken(pushProvider: PushProvider(env: pushEnvironment), token: token)
m.deviceToken = deviceToken
let useNotifications = UserDefaults.standard.bool(forKey: "useNotifications")
if useNotifications {
Task {
do {
m.tokenStatus = try await apiRegisterToken(token: deviceToken, notificationMode: .instant)
} catch {
logger.error("apiRegisterToken error: \(responseError(error))")
}
}
if m.savedToken != nil {
registerToken(token: deviceToken)
}
}
@@ -44,14 +37,14 @@ class AppDelegate: NSObject, UIApplicationDelegate {
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
logger.debug("AppDelegate: didReceiveRemoteNotification")
print("*** userInfo", userInfo)
let m = ChatModel.shared
if let ntfData = userInfo["notificationData"] as? [AnyHashable : Any],
UserDefaults.standard.bool(forKey: "useNotifications") {
m.notificationMode != .off {
if let verification = ntfData["verification"] as? String,
let nonce = ntfData["nonce"] as? String {
if let token = ChatModel.shared.deviceToken {
logger.debug("AppDelegate: didReceiveRemoteNotification: verification, confirming \(verification)")
Task {
let m = ChatModel.shared
do {
if case .active = m.tokenStatus {} else { m.tokenStatus = .confirmed }
try await apiVerifyToken(token: token, nonce: nonce, code: verification)

View File

@@ -29,8 +29,9 @@ final class ChatModel: ObservableObject {
@Published var userSMPServers: [String]?
@Published var appOpenUrl: URL?
@Published var deviceToken: DeviceToken?
@Published var tokenStatus = NtfTknStatus.new
@Published var notificationMode = NotificationMode.off
@Published var savedToken: DeviceToken?
@Published var tokenStatus: NtfTknStatus?
@Published var notificationMode = NotificationsMode.off
@Published var notificationPreview: NotificationPreviewMode? = .message
// current WebRTC call
@Published var callInvitations: Dictionary<ChatId, CallInvitation> = [:]

View File

@@ -226,12 +226,37 @@ func apiDeleteChatItem(type: ChatType, id: Int64, itemId: Int64, mode: CIDeleteM
throw r
}
func apiRegisterToken(token: DeviceToken, notificationMode: NotificationMode) async throws -> NtfTknStatus {
func apiGetNtfToken() throws -> (DeviceToken?, NtfTknStatus?, NotificationsMode) {
let r = chatSendCmdSync(.apiGetNtfToken)
switch r {
case let .ntfToken(token, status, ntfMode): return (token, status, ntfMode)
case .chatCmdError(.errorAgent(.CMD(.PROHIBITED))): return (nil, nil, .off)
default: throw r
}
}
func apiRegisterToken(token: DeviceToken, notificationMode: NotificationsMode) async throws -> NtfTknStatus {
let r = await chatSendCmd(.apiRegisterToken(token: token, notificationMode: notificationMode))
if case let .ntfTokenStatus(status) = r { return status }
throw r
}
func registerToken(token: DeviceToken) {
let m = ChatModel.shared
let mode = m.notificationMode
if mode != .off {
logger.debug("registerToken \(mode.rawValue)")
Task {
do {
let status = try await apiRegisterToken(token: token, notificationMode: mode)
await MainActor.run { m.tokenStatus = status }
} catch let error {
logger.error("registerToken apiRegisterToken error: \(responseError(error))")
}
}
}
}
func apiVerifyToken(token: DeviceToken, nonce: String, code: String) async throws {
try await sendCommandOkResp(.apiVerifyToken(token: token, nonce: nonce, code: code))
}
@@ -501,6 +526,10 @@ func startChat() throws {
m.userAddress = try apiGetUserAddress()
m.userSMPServers = try getUserSMPServers()
m.chats = try apiGetChats()
(m.savedToken, m.tokenStatus, m.notificationMode) = try apiGetNtfToken()
if let token = m.deviceToken {
registerToken(token: token)
}
withAnimation {
m.onboardingStage = m.chats.isEmpty
? .step3_MakeConnection

View File

@@ -73,8 +73,8 @@ struct SimpleXApp: App {
private func setDbContainer() {
// Uncomment and run once to open DB in app documents folder:
// dbContainerGroupDefault.set(.documents)
// v3DBMigrationDefault.set(.offer)
// dbContainerGroupDefault.set(.documents)
// v3DBMigrationDefault.set(.offer)
// to create database in app documents folder also uncomment:
// let legacyDatabase = true
let legacyDatabase = hasLegacyDatabase()

View File

@@ -11,9 +11,8 @@ import SimpleXChat
struct NotificationsView: View {
@EnvironmentObject var m: ChatModel
@State private var notificationMode: NotificationMode?
@State private var notificationMode: NotificationsMode?
@State private var showAlert: NotificationAlert?
@AppStorage(DEFAULT_USE_NOTIFICATIONS) private var useNotifications = false
var body: some View {
List {
@@ -21,7 +20,7 @@ struct NotificationsView: View {
NavigationLink {
List {
Section {
SelectionListView(list: NotificationMode.values, selection: $notificationMode) { mode in
SelectionListView(list: NotificationsMode.values, selection: $notificationMode) { mode in
showAlert = .setMode(mode: mode)
}
} footer: {
@@ -93,7 +92,7 @@ struct NotificationsView: View {
}
}
private func ntfModeAlertTitle(_ mode: NotificationMode) -> LocalizedStringKey {
private func ntfModeAlertTitle(_ mode: NotificationsMode) -> LocalizedStringKey {
switch mode {
case .off: return "Turn off notifications?"
case .periodic: return "Enable periodic notifications?"
@@ -101,18 +100,16 @@ struct NotificationsView: View {
}
}
private func setNotificationsMode(_ mode: NotificationMode, _ token: DeviceToken) {
private func setNotificationsMode(_ mode: NotificationsMode, _ token: DeviceToken) {
Task {
switch mode {
case .off:
do {
try await apiDeleteToken(token: token)
useNotifications = false
m.tokenStatus = .new
notificationMode = .off
m.notificationMode = .off
}
catch let error {
} catch let error {
DispatchQueue.main.async {
let err = responseError(error)
logger.error("apiDeleteToken error: \(err)")
@@ -123,12 +120,10 @@ struct NotificationsView: View {
do {
do {
m.tokenStatus = try await apiRegisterToken(token: token, notificationMode: mode)
useNotifications = true
notificationMode = mode
m.notificationMode = mode
} catch let error {
DispatchQueue.main.async {
useNotifications = notificationMode != .off
let err = responseError(error)
logger.error("apiRegisterToken error: \(err)")
showAlert = .error(title: "Error enabling notifications", error: err)
@@ -140,7 +135,7 @@ struct NotificationsView: View {
}
}
func ntfModeDescription(_ mode: NotificationMode) -> LocalizedStringKey {
func ntfModeDescription(_ mode: NotificationsMode) -> LocalizedStringKey {
switch mode {
case .off: return "**Maximum privacy**: push notifications are off.\nNo meta-data is shared with SimpleX Chat notification server."
case .periodic: return "**High privacy**: new messages are checked every 20 minutes.\nYour device token is shared with SimpleX Chat notification server, but it cannot see how many connections you have or how many messages you receive."
@@ -187,7 +182,7 @@ struct SelectionListView<Item: SelectableItem>: View {
}
enum NotificationAlert: Identifiable {
case setMode(mode: NotificationMode)
case setMode(mode: NotificationsMode)
case error(title: LocalizedStringKey, error: String)
var id: String {

View File

@@ -18,7 +18,6 @@ let appBuild = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as?
let DEFAULT_SHOW_LA_NOTICE = "showLocalAuthenticationNotice"
let DEFAULT_LA_NOTICE_SHOWN = "localAuthenticationNoticeShown"
let DEFAULT_PERFORM_LA = "performLocalAuthentication"
let DEFAULT_USE_NOTIFICATIONS = "useNotifications"
let DEFAULT_PENDING_CONNECTIONS = "pendingConnections"
let DEFAULT_WEBRTC_POLICY_RELAY = "webrtcPolicyRelay"
let DEFAULT_PRIVACY_ACCEPT_IMAGES = "privacyAcceptImages"
@@ -32,7 +31,6 @@ let appDefaults: [String: Any] = [
DEFAULT_SHOW_LA_NOTICE: false,
DEFAULT_LA_NOTICE_SHOWN: false,
DEFAULT_PERFORM_LA: false,
DEFAULT_USE_NOTIFICATIONS: false,
DEFAULT_PENDING_CONNECTIONS: true,
DEFAULT_WEBRTC_POLICY_RELAY: true,
DEFAULT_PRIVACY_ACCEPT_IMAGES: true,
@@ -218,6 +216,9 @@ struct SettingsView: View {
case .expired:
icon = "bolt.slash.fill"
color = .primary
case .none:
icon = "bolt"
color = .primary
}
return Image(systemName: icon)
.padding(.trailing, 9)

View File

@@ -18,6 +18,11 @@
5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C063D2627A4564100AEC577 /* ChatPreviewView.swift */; };
5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; };
5C13730B28156D2700F43030 /* ContactConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C13730A28156D2700F43030 /* ContactConnectionView.swift */; };
5C14247E286DAC1D0004E3EE /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C142479286DAC1D0004E3EE /* libffi.a */; };
5C14247F286DAC1D0004E3EE /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C14247A286DAC1D0004E3EE /* libgmpxx.a */; };
5C142480286DAC1D0004E3EE /* libHSsimplex-chat-2.2.0-B9XlcSXdcwBJ5TIVTDfRQj-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C14247B286DAC1D0004E3EE /* libHSsimplex-chat-2.2.0-B9XlcSXdcwBJ5TIVTDfRQj-ghc8.10.7.a */; };
5C142481286DAC1D0004E3EE /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C14247C286DAC1D0004E3EE /* libgmp.a */; };
5C142482286DAC1D0004E3EE /* libHSsimplex-chat-2.2.0-B9XlcSXdcwBJ5TIVTDfRQj.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C14247D286DAC1D0004E3EE /* libHSsimplex-chat-2.2.0-B9XlcSXdcwBJ5TIVTDfRQj.a */; };
5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */; };
5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */; };
5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260A27A30CFA00F70299 /* ChatListView.swift */; };
@@ -102,11 +107,6 @@
5CEACCE327DE9246000BD591 /* ComposeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCE227DE9246000BD591 /* ComposeView.swift */; };
5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */; };
5CFA59C42860BC6200863A68 /* MigrateToAppGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */; };
5CFA59CA2864464A00863A68 /* libHSsimplex-chat-2.2.0-40J3CUJkXRXAsiR552cpHl-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CFA59C52864464A00863A68 /* libHSsimplex-chat-2.2.0-40J3CUJkXRXAsiR552cpHl-ghc8.10.7.a */; };
5CFA59CB2864464A00863A68 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CFA59C62864464A00863A68 /* libffi.a */; };
5CFA59CC2864464A00863A68 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CFA59C72864464A00863A68 /* libgmpxx.a */; };
5CFA59CD2864464A00863A68 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CFA59C82864464A00863A68 /* libgmp.a */; };
5CFA59CE2864464A00863A68 /* libHSsimplex-chat-2.2.0-40J3CUJkXRXAsiR552cpHl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CFA59C92864464A00863A68 /* libHSsimplex-chat-2.2.0-40J3CUJkXRXAsiR552cpHl.a */; };
5CFA59D12864782E00863A68 /* ChatArchiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFA59CF286477B400863A68 /* ChatArchiveView.swift */; };
5CFE0921282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; };
5CFE0922282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; };
@@ -190,6 +190,11 @@
5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactRequestView.swift; sourceTree = "<group>"; };
5C13730A28156D2700F43030 /* ContactConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactConnectionView.swift; sourceTree = "<group>"; };
5C13730C2815740A00F43030 /* DebugJSON.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = DebugJSON.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
5C142479286DAC1D0004E3EE /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
5C14247A286DAC1D0004E3EE /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5C14247B286DAC1D0004E3EE /* libHSsimplex-chat-2.2.0-B9XlcSXdcwBJ5TIVTDfRQj-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-2.2.0-B9XlcSXdcwBJ5TIVTDfRQj-ghc8.10.7.a"; sourceTree = "<group>"; };
5C14247C286DAC1D0004E3EE /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5C14247D286DAC1D0004E3EE /* libHSsimplex-chat-2.2.0-B9XlcSXdcwBJ5TIVTDfRQj.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-2.2.0-B9XlcSXdcwBJ5TIVTDfRQj.a"; sourceTree = "<group>"; };
5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemView.swift; sourceTree = "<group>"; };
5C2E260627A2941F00F70299 /* SimpleXAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleXAPI.swift; sourceTree = "<group>"; };
5C2E260A27A30CFA00F70299 /* ChatListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListView.swift; sourceTree = "<group>"; };
@@ -275,11 +280,6 @@
5CEACCE227DE9246000BD591 /* ComposeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeView.swift; sourceTree = "<group>"; };
5CEACCEC27DEA495000BD591 /* MsgContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MsgContentView.swift; sourceTree = "<group>"; };
5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToAppGroupView.swift; sourceTree = "<group>"; };
5CFA59C52864464A00863A68 /* libHSsimplex-chat-2.2.0-40J3CUJkXRXAsiR552cpHl-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-2.2.0-40J3CUJkXRXAsiR552cpHl-ghc8.10.7.a"; sourceTree = "<group>"; };
5CFA59C62864464A00863A68 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
5CFA59C72864464A00863A68 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5CFA59C82864464A00863A68 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5CFA59C92864464A00863A68 /* libHSsimplex-chat-2.2.0-40J3CUJkXRXAsiR552cpHl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-2.2.0-40J3CUJkXRXAsiR552cpHl.a"; sourceTree = "<group>"; };
5CFA59CF286477B400863A68 /* ChatArchiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatArchiveView.swift; sourceTree = "<group>"; };
5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ZoomableScrollView.swift; path = Shared/Views/ZoomableScrollView.swift; sourceTree = SOURCE_ROOT; };
640F50E227CF991C001E05C2 /* SMPServers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SMPServers.swift; sourceTree = "<group>"; };
@@ -325,13 +325,13 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5CFA59CE2864464A00863A68 /* libHSsimplex-chat-2.2.0-40J3CUJkXRXAsiR552cpHl.a in Frameworks */,
5CFA59CA2864464A00863A68 /* libHSsimplex-chat-2.2.0-40J3CUJkXRXAsiR552cpHl-ghc8.10.7.a in Frameworks */,
5CFA59CC2864464A00863A68 /* libgmpxx.a in Frameworks */,
5C14247F286DAC1D0004E3EE /* libgmpxx.a in Frameworks */,
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
5C142480286DAC1D0004E3EE /* libHSsimplex-chat-2.2.0-B9XlcSXdcwBJ5TIVTDfRQj-ghc8.10.7.a in Frameworks */,
5C14247E286DAC1D0004E3EE /* libffi.a in Frameworks */,
5C142482286DAC1D0004E3EE /* libHSsimplex-chat-2.2.0-B9XlcSXdcwBJ5TIVTDfRQj.a in Frameworks */,
5C142481286DAC1D0004E3EE /* libgmp.a in Frameworks */,
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
5CFA59CD2864464A00863A68 /* libgmp.a in Frameworks */,
5CFA59CB2864464A00863A68 /* libffi.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -385,11 +385,11 @@
5C764E5C279C70B7000C6508 /* Libraries */ = {
isa = PBXGroup;
children = (
5CFA59C62864464A00863A68 /* libffi.a */,
5CFA59C82864464A00863A68 /* libgmp.a */,
5CFA59C72864464A00863A68 /* libgmpxx.a */,
5CFA59C52864464A00863A68 /* libHSsimplex-chat-2.2.0-40J3CUJkXRXAsiR552cpHl-ghc8.10.7.a */,
5CFA59C92864464A00863A68 /* libHSsimplex-chat-2.2.0-40J3CUJkXRXAsiR552cpHl.a */,
5C142479286DAC1D0004E3EE /* libffi.a */,
5C14247C286DAC1D0004E3EE /* libgmp.a */,
5C14247A286DAC1D0004E3EE /* libgmpxx.a */,
5C14247B286DAC1D0004E3EE /* libHSsimplex-chat-2.2.0-B9XlcSXdcwBJ5TIVTDfRQj-ghc8.10.7.a */,
5C14247D286DAC1D0004E3EE /* libHSsimplex-chat-2.2.0-B9XlcSXdcwBJ5TIVTDfRQj.a */,
);
path = Libraries;
sourceTree = "<group>";
@@ -1122,8 +1122,6 @@
"$(inherited)",
"@executable_path/Frameworks",
);
"LIBRARY_SEARCH_PATHS[sdk=iphoneos*]" = "$(PROJECT_DIR)/Libraries/ios";
"LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*]" = "$(PROJECT_DIR)/Libraries/sim";
MARKETING_VERSION = 2.2.1;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX;
@@ -1164,8 +1162,6 @@
"$(inherited)",
"@executable_path/Frameworks",
);
"LIBRARY_SEARCH_PATHS[sdk=iphoneos*]" = "$(PROJECT_DIR)/Libraries/ios";
"LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*]" = "$(PROJECT_DIR)/Libraries/sim";
MARKETING_VERSION = 2.2.1;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX;
@@ -1237,14 +1233,6 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
"LIBRARY_SEARCH_PATHS[sdk=iphoneos*]" = (
"$(inherited)",
"$(PROJECT_DIR)/Libraries/ios",
);
"LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*]" = (
"$(inherited)",
"$(PROJECT_DIR)/Libraries/sim",
);
MARKETING_VERSION = 2.2.1;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1275,14 +1263,6 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
"LIBRARY_SEARCH_PATHS[sdk=iphoneos*]" = (
"$(inherited)",
"$(PROJECT_DIR)/Libraries/ios",
);
"LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*]" = (
"$(inherited)",
"$(PROJECT_DIR)/Libraries/sim",
);
MARKETING_VERSION = 2.2.1;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -28,7 +28,8 @@ public enum ChatCommand {
case apiSendMessage(type: ChatType, id: Int64, file: String?, quotedItemId: Int64?, msg: MsgContent)
case apiUpdateChatItem(type: ChatType, id: Int64, itemId: Int64, msg: MsgContent)
case apiDeleteChatItem(type: ChatType, id: Int64, itemId: Int64, mode: CIDeleteMode)
case apiRegisterToken(token: DeviceToken, notificationMode: NotificationMode)
case apiGetNtfToken
case apiRegisterToken(token: DeviceToken, notificationMode: NotificationsMode)
case apiVerifyToken(token: DeviceToken, nonce: String, code: String)
case apiDeleteToken(token: DeviceToken)
case apiGetNtfMessage(nonce: String, encNtfInfo: String)
@@ -76,6 +77,7 @@ public enum ChatCommand {
return "/_send \(ref(type, id)) json \(msg)"
case let .apiUpdateChatItem(type, id, itemId, mc): return "/_update item \(ref(type, id)) \(itemId) \(mc.cmdString)"
case let .apiDeleteChatItem(type, id, itemId, mode): return "/_delete item \(ref(type, id)) \(itemId) \(mode.rawValue)"
case .apiGetNtfToken: return "/_ntf get "
case let .apiRegisterToken(token, notificationMode): return "/_ntf register \(token.cmdString) \(notificationMode.rawValue)"
case let .apiVerifyToken(token, nonce, code): return "/_ntf verify \(token.cmdString) \(nonce) \(code)"
case let .apiDeleteToken(token): return "/_ntf delete \(token.cmdString)"
@@ -124,6 +126,7 @@ public enum ChatCommand {
case .apiSendMessage: return "apiSendMessage"
case .apiUpdateChatItem: return "apiUpdateChatItem"
case .apiDeleteChatItem: return "apiDeleteChatItem"
case .apiGetNtfToken: return "apiGetNtfToken"
case .apiRegisterToken: return "apiRegisterToken"
case .apiVerifyToken: return "apiVerifyToken"
case .apiDeleteToken: return "apiDeleteToken"
@@ -222,6 +225,7 @@ public enum ChatResponse: Decodable, Error {
case callExtraInfo(contact: Contact, extraInfo: WebRTCExtraInfo)
case callEnded(contact: Contact)
case ntfTokenStatus(status: NtfTknStatus)
case ntfToken(token: DeviceToken, status: NtfTknStatus, ntfMode: NotificationsMode)
case ntfMessages(connEntity: ConnectionEntity?, msgTs: Date?, ntfMessages: [NtfMsgInfo])
case newContactConnection(connection: PendingContactConnection)
case contactConnectionDeleted(connection: PendingContactConnection)
@@ -284,6 +288,7 @@ public enum ChatResponse: Decodable, Error {
case .callExtraInfo: return "callExtraInfo"
case .callEnded: return "callEnded"
case .ntfTokenStatus: return "ntfTokenStatus"
case .ntfToken: return "ntfToken"
case .ntfMessages: return "ntfMessages"
case .newContactConnection: return "newContactConnection"
case .contactConnectionDeleted: return "contactConnectionDeleted"
@@ -349,6 +354,7 @@ public enum ChatResponse: Decodable, Error {
case let .callExtraInfo(contact, extraInfo): return "contact: \(contact.id)\nextraInfo: \(String(describing: extraInfo))"
case let .callEnded(contact): return "contact: \(contact.id)"
case let .ntfTokenStatus(status): return String(describing: status)
case let .ntfToken(token, status, ntfMode): return "token: \(token)\nstatus: \(status.rawValue)\nntfMode: \(ntfMode.rawValue)"
case let .ntfMessages(connEntity, msgTs, ntfMessages): return "connEntity: \(String(describing: connEntity))\nmsgTs: \(String(describing: msgTs))\nntfMessages: \(String(describing: ntfMessages))"
case let .newContactConnection(connection): return String(describing: connection)
case let .contactConnectionDeleted(connection): return String(describing: connection)
@@ -383,33 +389,38 @@ public protocol SelectableItem: Hashable, Identifiable {
static var values: [Self] { get }
}
public struct DeviceToken {
var env: PushEnvironment
public struct DeviceToken: Decodable {
var pushProvider: PushProvider
var token: String
public init(env: PushEnvironment, token: String) {
self.env = env
public init(pushProvider: PushProvider, token: String) {
self.pushProvider = pushProvider
self.token = token
}
public var cmdString: String {
"\(env.pushProvider) \(token)"
"\(pushProvider) \(token)"
}
}
public enum PushEnvironment: String {
case development
case production
}
public var pushProvider: String {
switch self {
case .development: return "apns_dev"
case .production: return "apns_prod"
public enum PushProvider: String, Decodable {
case apns_dev
case apns_prod
public init(env: PushEnvironment) {
switch env {
case .development: self = .apns_dev
case .production: self = .apns_prod
}
}
}
public enum NotificationMode: String, SelectableItem {
public enum NotificationsMode: String, Decodable, SelectableItem {
case off = "OFF"
case periodic = "PERIODIC"
case instant = "INSTANT"
@@ -424,7 +435,7 @@ public enum NotificationMode: String, SelectableItem {
public var id: String { self.rawValue }
public static var values: [NotificationMode] = [.instant, .periodic, .off]
public static var values: [NotificationsMode] = [.instant, .periodic, .off]
}
public enum NotificationPreviewMode: String, SelectableItem {