From 54126eba6ba6ae5cf6739497df42890a62d03940 Mon Sep 17 00:00:00 2001
From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
Date: Mon, 27 Jun 2022 23:03:27 +0100
Subject: [PATCH] APNS push environments (#756)
---
apps/ios/Shared/AppDelegate.swift | 7 ++--
apps/ios/Shared/Model/ChatModel.swift | 2 +-
apps/ios/Shared/Model/PushEnvironment.swift | 41 ++++++++++++++++++
apps/ios/Shared/Model/SimpleXAPI.swift | 10 ++---
.../Views/UserSettings/SettingsView.swift | 4 +-
apps/ios/SimpleX.xcodeproj/project.pbxproj | 4 ++
apps/ios/SimpleXChat/APITypes.swift | 42 +++++++++++++++----
cabal.project | 2 +-
scripts/nix/sha256map.nix | 2 +-
src/Simplex/Chat.hs | 2 +-
src/Simplex/Chat/Controller.hs | 2 +-
stack.yaml | 2 +-
12 files changed, 96 insertions(+), 24 deletions(-)
create mode 100644 apps/ios/Shared/Model/PushEnvironment.swift
diff --git a/apps/ios/Shared/AppDelegate.swift b/apps/ios/Shared/AppDelegate.swift
index ec8a19cde6..c58912dd71 100644
--- a/apps/ios/Shared/AppDelegate.swift
+++ b/apps/ios/Shared/AppDelegate.swift
@@ -21,12 +21,13 @@ class AppDelegate: NSObject, UIApplicationDelegate {
let token = deviceToken.map { String(format: "%02hhx", $0) }.joined()
logger.debug("AppDelegate: didRegisterForRemoteNotificationsWithDeviceToken \(token)")
let m = ChatModel.shared
- m.deviceToken = token
+ let deviceToken = DeviceToken(env: pushEnvironment, token: token)
+ m.deviceToken = deviceToken
let useNotifications = UserDefaults.standard.bool(forKey: "useNotifications")
if useNotifications {
Task {
do {
- m.tokenStatus = try await apiRegisterToken(token: token, notificationMode: .instant)
+ m.tokenStatus = try await apiRegisterToken(token: deviceToken, notificationMode: .instant)
} catch {
logger.error("apiRegisterToken error: \(responseError(error))")
}
@@ -53,7 +54,7 @@ class AppDelegate: NSObject, UIApplicationDelegate {
let m = ChatModel.shared
do {
if case .active = m.tokenStatus {} else { m.tokenStatus = .confirmed }
- try await apiVerifyToken(token: token, code: verification, nonce: nonce)
+ try await apiVerifyToken(token: token, nonce: nonce, code: verification)
m.tokenStatus = .active
try await apiIntervalNofication(token: token, interval: 20)
} catch {
diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift
index 78fc66bf1c..be3e1fe446 100644
--- a/apps/ios/Shared/Model/ChatModel.swift
+++ b/apps/ios/Shared/Model/ChatModel.swift
@@ -28,7 +28,7 @@ final class ChatModel: ObservableObject {
@Published var userAddress: String?
@Published var userSMPServers: [String]?
@Published var appOpenUrl: URL?
- @Published var deviceToken: String?
+ @Published var deviceToken: DeviceToken?
@Published var tokenStatus = NtfTknStatus.new
@Published var notificationMode = NotificationMode.off
@Published var notificationPreview: NotificationPreviewMode? = .message
diff --git a/apps/ios/Shared/Model/PushEnvironment.swift b/apps/ios/Shared/Model/PushEnvironment.swift
new file mode 100644
index 0000000000..7fa7ef0b99
--- /dev/null
+++ b/apps/ios/Shared/Model/PushEnvironment.swift
@@ -0,0 +1,41 @@
+//
+// PushEnvironment.swift
+// SimpleX (iOS)
+//
+// Created by Evgeny on 27/06/2022.
+// Copyright © 2022 SimpleX Chat. All rights reserved.
+//
+
+import Foundation
+import SimpleXChat
+
+let pushEnvironment: PushEnvironment = {
+ guard let provisioningProfile = try? provisioningProfile(),
+ let entitlements = provisioningProfile["Entitlements"] as? [String: Any],
+ let environment = entitlements["aps-environment"] as? String,
+ let env = PushEnvironment(rawValue: environment)
+ else {
+ logger.warning("pushEnvironment: unknown, assuming production")
+ return .production
+ }
+ logger.debug("pushEnvironment: \(env.rawValue)")
+ return env
+}()
+
+private func provisioningProfile() throws -> [String: Any]? {
+ guard let url = Bundle.main.url(forResource: "embedded", withExtension: "mobileprovision") else {
+ return nil
+ }
+
+ let binaryString = try String(contentsOf: url, encoding: .isoLatin1)
+
+ let scanner = Scanner(string: binaryString)
+ guard scanner.scanUpToString(""),
+ let data = (plistString + "").data(using: .isoLatin1)
+ else {
+ return nil
+ }
+
+ return try PropertyListSerialization.propertyList(from: data, options: [], format: nil) as? [String: Any]
+}
diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift
index e353ebf1ed..a40e5c0e48 100644
--- a/apps/ios/Shared/Model/SimpleXAPI.swift
+++ b/apps/ios/Shared/Model/SimpleXAPI.swift
@@ -226,21 +226,21 @@ func apiDeleteChatItem(type: ChatType, id: Int64, itemId: Int64, mode: CIDeleteM
throw r
}
-func apiRegisterToken(token: String, notificationMode: NotificationMode) async throws -> NtfTknStatus {
+func apiRegisterToken(token: DeviceToken, notificationMode: NotificationMode) async throws -> NtfTknStatus {
let r = await chatSendCmd(.apiRegisterToken(token: token, notificationMode: notificationMode))
if case let .ntfTokenStatus(status) = r { return status }
throw r
}
-func apiVerifyToken(token: String, code: String, nonce: String) async throws {
- try await sendCommandOkResp(.apiVerifyToken(token: token, code: code, nonce: nonce))
+func apiVerifyToken(token: DeviceToken, nonce: String, code: String) async throws {
+ try await sendCommandOkResp(.apiVerifyToken(token: token, nonce: nonce, code: code))
}
-func apiIntervalNofication(token: String, interval: Int) async throws {
+func apiIntervalNofication(token: DeviceToken, interval: Int) async throws {
try await sendCommandOkResp(.apiIntervalNofication(token: token, interval: interval))
}
-func apiDeleteToken(token: String) async throws {
+func apiDeleteToken(token: DeviceToken) async throws {
try await sendCommandOkResp(.apiDeleteToken(token: token))
}
diff --git a/apps/ios/Shared/Views/UserSettings/SettingsView.swift b/apps/ios/Shared/Views/UserSettings/SettingsView.swift
index 47dff51fe1..cc48759c4e 100644
--- a/apps/ios/Shared/Views/UserSettings/SettingsView.swift
+++ b/apps/ios/Shared/Views/UserSettings/SettingsView.swift
@@ -234,7 +234,7 @@ struct SettingsView: View {
.foregroundColor(color)
}
- private func notificationsToggle(_ token: String) -> some View {
+ private func notificationsToggle(_ token: DeviceToken) -> some View {
Toggle("Check messages", isOn: $useNotifications)
.onChange(of: useNotifications) { enable in
if enable {
@@ -269,7 +269,7 @@ struct SettingsView: View {
}
}
- private func enableNotificationsAlert(_ token: String) -> Alert {
+ private func enableNotificationsAlert(_ token: DeviceToken) -> Alert {
Alert(
title: Text("Enable notifications? (BETA)"),
message: Text("The app can receive background notifications every 20 minutes to check the new messages.\n*Please note*: if you confirm, your device token will be sent to SimpleX Chat notifications server."),
diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj
index c408623643..4edc81a91b 100644
--- a/apps/ios/SimpleX.xcodeproj/project.pbxproj
+++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj
@@ -65,6 +65,7 @@
5CB0BA9A2827FD8800B3292C /* HowItWorks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB0BA992827FD8800B3292C /* HowItWorks.swift */; };
5CB346E52868AA7F001FD2EF /* SuspendChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB346E42868AA7F001FD2EF /* SuspendChat.swift */; };
5CB346E72868D76D001FD2EF /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB346E62868D76D001FD2EF /* NotificationsView.swift */; };
+ 5CB346E92869E8BA001FD2EF /* PushEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB346E82869E8BA001FD2EF /* PushEnvironment.swift */; };
5CB924D427A853F100ACCCDD /* SettingsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924D327A853F100ACCCDD /* SettingsButton.swift */; };
5CB924D727A8563F00ACCCDD /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924D627A8563F00ACCCDD /* SettingsView.swift */; };
5CB924E127A867BA00ACCCDD /* UserProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924E027A867BA00ACCCDD /* UserProfile.swift */; };
@@ -240,6 +241,7 @@
5CB0BA992827FD8800B3292C /* HowItWorks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HowItWorks.swift; sourceTree = ""; };
5CB346E42868AA7F001FD2EF /* SuspendChat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuspendChat.swift; sourceTree = ""; };
5CB346E62868D76D001FD2EF /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = ""; };
+ 5CB346E82869E8BA001FD2EF /* PushEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushEnvironment.swift; sourceTree = ""; };
5CB924D327A853F100ACCCDD /* SettingsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsButton.swift; sourceTree = ""; };
5CB924D627A8563F00ACCCDD /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; };
5CB924E027A867BA00ACCCDD /* UserProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfile.swift; sourceTree = ""; };
@@ -410,6 +412,7 @@
5C35CFC727B2782E00FB6C6D /* BGManager.swift */,
5C35CFCA27B2E91D00FB6C6D /* NtfManager.swift */,
5CB346E42868AA7F001FD2EF /* SuspendChat.swift */,
+ 5CB346E82869E8BA001FD2EF /* PushEnvironment.swift */,
);
path = Model;
sourceTree = "";
@@ -850,6 +853,7 @@
5C5F2B6D27EBC3FE006A9D5F /* ImagePicker.swift in Sources */,
5C577F7D27C83AA10006112D /* MarkdownHelp.swift in Sources */,
5CA059EB279559F40002BEB4 /* SimpleXApp.swift in Sources */,
+ 5CB346E92869E8BA001FD2EF /* PushEnvironment.swift in Sources */,
5C55A91F283AD0E400C4E99E /* CallManager.swift in Sources */,
5CCD403727A5F9A200368C90 /* ScanToConnectView.swift in Sources */,
5CFA59D12864782E00863A68 /* ChatArchiveView.swift in Sources */,
diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift
index f5474623f3..7b07940416 100644
--- a/apps/ios/SimpleXChat/APITypes.swift
+++ b/apps/ios/SimpleXChat/APITypes.swift
@@ -28,10 +28,10 @@ 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: String, notificationMode: NotificationMode)
- case apiVerifyToken(token: String, code: String, nonce: String)
- case apiIntervalNofication(token: String, interval: Int)
- case apiDeleteToken(token: String)
+ case apiRegisterToken(token: DeviceToken, notificationMode: NotificationMode)
+ case apiVerifyToken(token: DeviceToken, nonce: String, code: String)
+ case apiIntervalNofication(token: DeviceToken, interval: Int)
+ case apiDeleteToken(token: DeviceToken)
case apiGetNtfMessage(nonce: String, encNtfInfo: String)
case getUserSMPServers
case setUserSMPServers(smpServers: [String])
@@ -77,10 +77,10 @@ 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 let .apiRegisterToken(token, notificationMode): return "/_ntf register apns \(token) \(notificationMode.rawValue)"
- case let .apiVerifyToken(token, code, nonce): return "/_ntf verify apns \(token) \(code) \(nonce)"
- case let .apiIntervalNofication(token, interval): return "/_ntf interval apns \(token) \(interval)"
- case let .apiDeleteToken(token): return "/_ntf delete apns \(token)"
+ 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 .apiIntervalNofication(token, interval): return "/_ntf interval \(token.cmdString) \(interval)"
+ case let .apiDeleteToken(token): return "/_ntf delete \(token.cmdString)"
case let .apiGetNtfMessage(nonce, encNtfInfo): return "/_ntf message \(nonce) \(encNtfInfo)"
case .getUserSMPServers: return "/smp_servers"
case let .setUserSMPServers(smpServers): return "/smp_servers \(smpServersStr(smpServers: smpServers))"
@@ -386,6 +386,32 @@ public protocol SelectableItem: Hashable, Identifiable {
static var values: [Self] { get }
}
+public struct DeviceToken {
+ var env: PushEnvironment
+ var token: String
+
+ public init(env: PushEnvironment, token: String) {
+ self.env = env
+ self.token = token
+ }
+
+ public var cmdString: String {
+ "\(env.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 NotificationMode: String, SelectableItem {
case off = "OFF"
case periodic = "PERIODIC"
diff --git a/cabal.project b/cabal.project
index 3b8c5538a1..7af4430249 100644
--- a/cabal.project
+++ b/cabal.project
@@ -5,7 +5,7 @@ constraints: zip +disable-bzip2 +disable-zstd
source-repository-package
type: git
location: https://github.com/simplex-chat/simplexmq.git
- tag: 51d0b48ce15433d8473770522b1eb814688d2aea
+ tag: ba40d75886c14d70fb6af61c22f917069d7b478f
source-repository-package
type: git
diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix
index fcc28b64f2..bec8b11529 100644
--- a/scripts/nix/sha256map.nix
+++ b/scripts/nix/sha256map.nix
@@ -1,5 +1,5 @@
{
- "https://github.com/simplex-chat/simplexmq.git"."51d0b48ce15433d8473770522b1eb814688d2aea" = "1v1n96lq9k6cvayqhlc7ywr1zwj2qnnsirv4dilqwl507gb6gqyj";
+ "https://github.com/simplex-chat/simplexmq.git"."ba40d75886c14d70fb6af61c22f917069d7b478f" = "1cashma9g0lw1bvaw4fcvfzzjqbpwjxhz42fvl83pi43j8i2plpn";
"https://github.com/simplex-chat/aeson.git"."3eb66f9a68f103b5f1489382aad89f5712a64db7" = "0kilkx59fl6c3qy3kjczqvm8c3f4n3p0bdk9biyflf51ljnzp4yp";
"https://github.com/simplex-chat/haskell-terminal.git"."f708b00009b54890172068f168bf98508ffcd495" = "0zmq7lmfsk8m340g47g5963yba7i88n4afa6z93sg9px5jv1mijj";
"https://github.com/zw3rk/android-support.git"."3c3a5ab0b8b137a072c98d3d0937cbdc96918ddb" = "1r6jyxbim3dsvrmakqfyxbd6ms6miaghpbwyl0sr6dzwpgaprz97";
diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs
index 8a99e2deb5..b1d6ff4fd5 100644
--- a/src/Simplex/Chat.hs
+++ b/src/Simplex/Chat.hs
@@ -542,7 +542,7 @@ processChatCommand = \case
APIParseMarkdown text -> pure . CRApiParsedMarkdown $ parseMaybeMarkdownList text
APIGetNtfToken -> withUser $ \_ -> crNtfToken <$> withAgent getNtfToken
APIRegisterToken token mode -> CRNtfTokenStatus <$> withUser (\_ -> withAgent $ \a -> registerNtfToken a token mode)
- APIVerifyToken token code nonce -> withUser $ \_ -> withAgent (\a -> verifyNtfToken a token code nonce) $> CRCmdOk
+ APIVerifyToken token nonce code -> withUser $ \_ -> withAgent (\a -> verifyNtfToken a token nonce code) $> CRCmdOk
APIDeleteToken token -> withUser $ \_ -> withAgent (`deleteNtfToken` token) $> CRCmdOk
APIGetNtfMessage nonce encNtfInfo -> withUser $ \user -> do
(NotificationInfo {ntfConnId, ntfMsgMeta}, msgs) <- withAgent $ \a -> getNotificationMessage a nonce encNtfInfo
diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs
index 0b12fb7bae..1a3980cc93 100644
--- a/src/Simplex/Chat/Controller.hs
+++ b/src/Simplex/Chat/Controller.hs
@@ -131,7 +131,7 @@ data ChatCommand
| APIParseMarkdown Text
| APIGetNtfToken
| APIRegisterToken DeviceToken NotificationsMode
- | APIVerifyToken DeviceToken ByteString C.CbNonce
+ | APIVerifyToken DeviceToken C.CbNonce ByteString
| APIDeleteToken DeviceToken
| APIGetNtfMessage {nonce :: C.CbNonce, encNtfInfo :: ByteString}
| GetUserSMPServers
diff --git a/stack.yaml b/stack.yaml
index 63b756b335..89564af995 100644
--- a/stack.yaml
+++ b/stack.yaml
@@ -49,7 +49,7 @@ extra-deps:
# - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561
# - ../simplexmq
- github: simplex-chat/simplexmq
- commit: 51d0b48ce15433d8473770522b1eb814688d2aea
+ commit: ba40d75886c14d70fb6af61c22f917069d7b478f
# - terminal-0.2.0.0@sha256:de6770ecaae3197c66ac1f0db5a80cf5a5b1d3b64a66a05b50f442de5ad39570,2977
- github: simplex-chat/aeson
commit: 3eb66f9a68f103b5f1489382aad89f5712a64db7