From 3b19aaf1d178aed7b47c85eed35caacc22b2b989 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sun, 30 Jan 2022 18:27:20 +0000 Subject: [PATCH] iOS: send/receive messages in chats, connect via QR code (#238) * send messages from chats * update API to use chat IDs * send messages to groups * generate invitation QR code * connect via QR code --- apps/ios/README.md | 35 ---------- apps/ios/Shared/Model/ChatModel.swift | 28 ++++---- apps/ios/Shared/Model/SimpleXAPI.swift | 66 ++++++++++++++----- .../MyPlayground.playground/Contents.swift | 1 + .../timeline.xctimeline | 2 +- apps/ios/Shared/Views/ChatListView.swift | 5 +- apps/ios/Shared/Views/ChatPreviewView.swift | 2 +- apps/ios/Shared/Views/ChatView.swift | 9 ++- .../Shared/Views/Helpers/AddContactView.swift | 43 ++++++++++++ .../Shared/Views/Helpers/ChatHeaderView.swift | 56 ++++++++++++++-- .../Views/Helpers/ConnectContactView.swift | 54 +++++++++++++++ .../Views/Helpers/InviteContactView.swift | 21 ------ apps/ios/Shared/Views/Helpers/QRCode.swift | 50 ++++++++++++++ .../Shared/Views/Helpers/ScanQRCodeView.swift | 21 ------ .../ios/Shared/Views/Helpers/ShareSheet.swift | 40 +++++++++++ apps/ios/SimpleX.xcodeproj/project.pbxproj | 65 ++++++++++++++---- .../xcshareddata/WorkspaceSettings.xcsettings | 8 +++ .../xcshareddata/swiftpm/Package.resolved | 16 +++++ 18 files changed, 390 insertions(+), 132 deletions(-) delete mode 100644 apps/ios/README.md create mode 100644 apps/ios/Shared/Views/Helpers/AddContactView.swift create mode 100644 apps/ios/Shared/Views/Helpers/ConnectContactView.swift delete mode 100644 apps/ios/Shared/Views/Helpers/InviteContactView.swift create mode 100644 apps/ios/Shared/Views/Helpers/QRCode.swift delete mode 100644 apps/ios/Shared/Views/Helpers/ScanQRCodeView.swift create mode 100644 apps/ios/Shared/Views/Helpers/ShareSheet.swift create mode 100644 apps/ios/SimpleX.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 apps/ios/SimpleX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/apps/ios/README.md b/apps/ios/README.md deleted file mode 100644 index 3a637d7973..0000000000 --- a/apps/ios/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Setup for iOS - -## Prerequisites - -- Prepare folders: - - ```sh - mkdir -p ./apps/ios/Libraries/mac ./apps/ios/Libraries/ios ./apps/ios/Libraries/sim - ``` - -## Update binaries - -1. Extract binaries to `./apps/ios/Libraries/mac`. - -2. Prepare binaries: - - ```sh - chmod +w ./apps/ios/Libraries/mac/* - cp ./apps/ios/Libraries/mac/* ./apps/ios/Libraries/ios - cp ./apps/ios/Libraries/mac/* ./apps/ios/Libraries/sim - for f in ./apps/ios/Libraries/ios/*; do mac2ios $f; done | wc -l - for f in ./apps/ios/Libraries/sim/*; do mac2ios -s $f; done | wc -l - ``` - -3. Put binaries into `./apps/ios/Libraries`. - - ```sh - cp ./apps/ios/Libraries/sim/* ./apps/ios/Libraries - ``` - - or: - - ```sh - cp ./apps/ios/Libraries/ios/* ./apps/ios/Libraries - ``` diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index cd87d3e035..1e48953651 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -57,15 +57,15 @@ struct ChatPreview: Identifiable, Decodable { } enum ChatType: String { - case direct - case group + case direct = "@" + case group = "#" } enum ChatInfo: Identifiable, Codable { case direct(contact: Contact) case group(groupInfo: GroupInfo) - var displayName: String { + var localDisplayName: String { get { switch self { case let .direct(contact): return "@\(contact.localDisplayName)" @@ -216,6 +216,15 @@ enum MsgContent { } } + var cmdString: String { + get { + switch self { + case let .text(text): return "text \(text)" + default: return "" + } + } + } + enum CodingKeys: String, CodingKey { case type case text @@ -240,16 +249,3 @@ extension MsgContent: Decodable { } } } - -//func parseMsgContent(_ mc: SomeMsgContent) -> MsgContent { -// if let type = mc["type"] as? String { -// let text_ = mc["text"] as? String -// switch type { -// case "text": -// if let text = text_ { return .text(text) } -// case let t: -// return .unknown(type: t, text: text_ ?? "unknown item", json: prettyJSON(mc) ?? "error") -// } -// } -// return .invalid(json: prettyJSON(mc) ?? "error") -//} diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index fe022d8d55..944c206c33 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -16,20 +16,27 @@ private let jsonEncoder = getJSONEncoder() enum ChatCommand { case apiGetChats - case apiGetChatItems(type: ChatType, id: Int64) + case apiGetChat(type: ChatType, id: Int64) + case apiSendMessage(type: ChatType, id: Int64, msg: MsgContent) + case addContact + case connect(connReq: String) case string(String) - case help var cmdString: String { get { switch self { case .apiGetChats: - return "/api/v1/chats" - case let .apiGetChatItems(type, id): - return "/api/v1/chat/\(type)/\(id)" + return "/get chats" + case let .apiGetChat(type, id): + return "/get chat \(type.rawValue)\(id)" + case let .apiSendMessage(type, id, mc): + return "/send msg \(type.rawValue)\(id) \(mc.cmdString)" + case .addContact: + return "/c" + case let .connect(connReq): + return "/c \(connReq)" case let .string(str): return str - case .help: return "/help" } } } @@ -42,7 +49,10 @@ struct APIResponse: Decodable { enum ChatResponse: Decodable, Error { case response(type: String, json: String) case apiChats(chats: [ChatPreview]) - case apiDirectChat(chat: Chat) // direct/ or group/, same as ChatPreview.id + case apiChat(chat: Chat) + case invitation(connReqInvitation: String) + case sentConfirmation + case sentInvitation // case newSentInvitation case contactConnected(contact: Contact) case newChatItem(chatItem: AChatItem) @@ -52,7 +62,10 @@ enum ChatResponse: Decodable, Error { switch self { case let .response(type, _): return "* \(type)" case .apiChats: return "apiChats" - case .apiDirectChat: return "apiDirectChat" + case .apiChat: return "apiChat" + case .invitation: return "invitation" + case .sentConfirmation: return "sentConfirmation" + case .sentInvitation: return "sentInvitation" case .contactConnected: return "contactConnected" case .newChatItem: return "newChatItem" } @@ -64,7 +77,10 @@ enum ChatResponse: Decodable, Error { switch self { case let .response(_, json): return json case let .apiChats(chats): return String(describing: chats) - case let .apiDirectChat(chat): return String(describing: chat) + case let .apiChat(chat): return String(describing: chat) + case let .invitation(connReqInvitation): return connReqInvitation + case .sentConfirmation: return "sentConfirmation: no details" + case .sentInvitation: return "sentInvitation: no details" case let .contactConnected(contact): return String(describing: contact) case let .newChatItem(chatItem): return String(describing: chatItem) } @@ -127,6 +143,7 @@ func chatCreateUser(_ p: Profile) -> User? { func chatSendCmd(_ cmd: ChatCommand) throws -> ChatResponse { var c = cmd.cmdString.cString(using: .utf8)! + print("command", cmd.cmdString) // TODO some mechanism to update model without passing it - maybe Publisher / Subscriber? // DispatchQueue.main.async { // termId += 1 @@ -141,16 +158,33 @@ func chatRecvMsg() throws -> ChatResponse { func apiGetChats() throws -> [ChatPreview] { let r = try chatSendCmd(.apiGetChats) - switch r { - case let .apiChats(chats): return chats - default: throw r - } + if case let .apiChats(chats) = r { return chats } + throw r } -func apiGetChatItems(type: ChatType, id: Int64) throws -> Chat { - let r = try chatSendCmd(.apiGetChatItems(type: type, id: id)) +func apiGetChat(type: ChatType, id: Int64) throws -> Chat { + let r = try chatSendCmd(.apiGetChat(type: type, id: id)) + if case let .apiChat(chat) = r { return chat } + throw r +} + +func apiSendMessage(type: ChatType, id: Int64, msg: MsgContent) throws -> ChatItem { + let r = try chatSendCmd(.apiSendMessage(type: type, id: id, msg: msg)) + if case let .newChatItem(aChatItem) = r { return aChatItem.chatItem } + throw r +} + +func apiAddContact() throws -> String { + let r = try chatSendCmd(.addContact) + if case let .invitation(connReqInvitation) = r { return connReqInvitation } + throw r +} + +func apiConnect(connReq: String) throws { + let r = try chatSendCmd(.connect(connReq: connReq)) switch r { - case let .apiDirectChat(chat): return chat + case .sentConfirmation: return + case .sentInvitation: return default: throw r } } diff --git a/apps/ios/Shared/MyPlayground.playground/Contents.swift b/apps/ios/Shared/MyPlayground.playground/Contents.swift index b46b3c1374..aa4740fee5 100644 --- a/apps/ios/Shared/MyPlayground.playground/Contents.swift +++ b/apps/ios/Shared/MyPlayground.playground/Contents.swift @@ -79,3 +79,4 @@ struct Test: Decodable { //jsonDecoder.decode(Test.self, from: "{\"name\":\"hello\",\"id\":1}".data(using: .utf8)!) +"\(ChatType.direct)" diff --git a/apps/ios/Shared/MyPlayground.playground/timeline.xctimeline b/apps/ios/Shared/MyPlayground.playground/timeline.xctimeline index 7b48f2c7ec..19563d37f5 100644 --- a/apps/ios/Shared/MyPlayground.playground/timeline.xctimeline +++ b/apps/ios/Shared/MyPlayground.playground/timeline.xctimeline @@ -3,7 +3,7 @@ version = "3.0"> diff --git a/apps/ios/Shared/Views/ChatListView.swift b/apps/ios/Shared/Views/ChatListView.swift index 5a27e2aa06..a29283ee69 100644 --- a/apps/ios/Shared/Views/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatListView.swift @@ -46,8 +46,9 @@ struct ChatListView: View { ChatView(chatInfo: chatPreview.chatInfo) .onAppear { do { - let chat = try apiGetChatItems(type: .direct, id: chatPreview.chatInfo.apiId) - chatModel.chats[chat.chatInfo.id] = chat + let ci = chatPreview.chatInfo + let chat = try apiGetChat(type: ci.chatType, id: ci.apiId) + chatModel.chats[ci.id] = chat } catch { print("apiGetChatItems", error) } diff --git a/apps/ios/Shared/Views/ChatPreviewView.swift b/apps/ios/Shared/Views/ChatPreviewView.swift index 6dda250ba5..16a0cb8711 100644 --- a/apps/ios/Shared/Views/ChatPreviewView.swift +++ b/apps/ios/Shared/Views/ChatPreviewView.swift @@ -12,7 +12,7 @@ struct ChatPreviewView: View { var chatPreview: ChatPreview var body: some View { - Text(chatPreview.chatInfo.displayName) + Text(chatPreview.chatInfo.localDisplayName) } } diff --git a/apps/ios/Shared/Views/ChatView.swift b/apps/ios/Shared/Views/ChatView.swift index 9e4a040057..419fef2f79 100644 --- a/apps/ios/Shared/Views/ChatView.swift +++ b/apps/ios/Shared/Views/ChatView.swift @@ -36,7 +36,14 @@ struct ChatView: View { } func sendMessage(_ msg: String) { - + do { + let chatItem = try apiSendMessage(type: chatInfo.chatType, id: chatInfo.apiId, msg: .text(msg)) + let chat = chatModel.chats[chatInfo.id] ?? Chat(chatInfo: chatInfo, chatItems: []) + chatModel.chats[chatInfo.id] = chat + chat.chatItems.append(chatItem) + } catch { + print(error) + } } } diff --git a/apps/ios/Shared/Views/Helpers/AddContactView.swift b/apps/ios/Shared/Views/Helpers/AddContactView.swift new file mode 100644 index 0000000000..fc23891112 --- /dev/null +++ b/apps/ios/Shared/Views/Helpers/AddContactView.swift @@ -0,0 +1,43 @@ +// +// AddContactView.swift +// SimpleX +// +// Created by Evgeny Poberezkin on 29/01/2022. +// Copyright © 2022 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import CoreImage.CIFilterBuiltins + +struct AddContactView: View { + var connReqInvitation: String + @State private var shareInvitation = false + + var body: some View { + VStack { + Text("Add contact") + .font(.title) + .padding(.bottom) + Text("Show QR code to your contact\nto scan from the app") + .font(.title2) + .multilineTextAlignment(.center) + QRCode(uri: connReqInvitation) + .padding() + Text("If you can't show QR code, you can share the invitation link via any channel") + .font(.subheadline) + .multilineTextAlignment(.center) + .padding(.horizontal) + Button { shareInvitation = true } label: { + Label("Share", systemImage: "square.and.arrow.up") + } + .padding() + .shareSheet(isPresented: $shareInvitation, items: [connReqInvitation]) + } + } +} + +struct AddContactView_Previews: PreviewProvider { + static var previews: some View { + AddContactView(connReqInvitation: "https://simplex.chat/invitation#/?v=1&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FFe5ICmvrm4wkrr6X1LTMii-lhBqLeB76%23MCowBQYDK2VuAyEAdhZZsHpuaAk3Hh1q0uNb_6hGTpuwBIrsp2z9U2T0oC0%3D&e2e=v%3D1%26x3dh%3DMEIwBQYDK2VvAzkAcz6jJk71InuxA0bOX7OUhddfB8Ov7xwQIlIDeXBRZaOntUU4brU5Y3rBzroZBdQJi0FKdtt_D7I%3D%2CMEIwBQYDK2VvAzkA-hDvk1duBi1hlOr08VWSI-Ou4JNNSQjseY69QyKm7Kgg1zZjbpGfyBqSZ2eqys6xtoV4ZtoQUXQ%3D") + } +} diff --git a/apps/ios/Shared/Views/Helpers/ChatHeaderView.swift b/apps/ios/Shared/Views/Helpers/ChatHeaderView.swift index d2cf7fa70e..f618be8d25 100644 --- a/apps/ios/Shared/Views/Helpers/ChatHeaderView.swift +++ b/apps/ios/Shared/Views/Helpers/ChatHeaderView.swift @@ -10,8 +10,13 @@ import SwiftUI struct ChatHeaderView: View { @State private var showAddChat = false - @State private var inviteContact = false - @State private var scanQRCode = false + @State private var addContact = false + @State private var addContactAlert = false + @State private var addContactError: Error? + @State private var connReqInvitation: String = "" + @State private var connectContact = false + @State private var connectAlert = false + @State private var connectError: Error? @State private var createGroup = false var body: some View { @@ -24,15 +29,54 @@ struct ChatHeaderView: View { Image(systemName: "square.and.pencil") } .confirmationDialog("Start new chat", isPresented: $showAddChat, titleVisibility: .visible) { - Button("Invite contact") { inviteContact = true } - Button("Scan QR code") { scanQRCode = true } + Button("Add contact") { addContactAction() } + Button("Scan QR code") { connectContact = true } Button("Create group") { createGroup = true } } - .sheet(isPresented: $inviteContact, content: { InviteContactView() }) - .sheet(isPresented: $scanQRCode, content: { ScanQRCodeView() }) + .sheet(isPresented: $addContact, content: { + AddContactView(connReqInvitation: connReqInvitation) + }) + .alert(isPresented: $addContactAlert) { + connectionError(addContactError) + } + .sheet(isPresented: $connectContact, content: { + connectContactSheet() + }) + .alert(isPresented: $connectAlert) { + connectionError(connectError) + } .sheet(isPresented: $createGroup, content: { CreateGroupView() }) } .padding(.horizontal) + .padding(.top) + } + + func addContactAction() { + do { + connReqInvitation = try apiAddContact() + addContact = true + } catch { + addContactAlert = true + addContactError = error + print(error) + } + } + + func connectContactSheet() -> some View { + ConnectContactView(completed: { err in + connectContact = false + if err != nil { + connectAlert = true + connectError = err + } + }) + } + + func connectionError(_ error: Error?) -> Alert { + Alert( + title: Text("Connection error"), + message: Text(error?.localizedDescription ?? "") + ) } } diff --git a/apps/ios/Shared/Views/Helpers/ConnectContactView.swift b/apps/ios/Shared/Views/Helpers/ConnectContactView.swift new file mode 100644 index 0000000000..088f336f9c --- /dev/null +++ b/apps/ios/Shared/Views/Helpers/ConnectContactView.swift @@ -0,0 +1,54 @@ +// +// ConnectContactView.swift +// SimpleX +// +// Created by Evgeny Poberezkin on 29/01/2022. +// Copyright © 2022 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import CodeScanner + +struct ConnectContactView: View { + var completed: ((Error?) -> Void) + + var body: some View { + VStack { + Text("Scan QR code") + .font(.title) + .padding(.bottom) + Text("Your chat profile will be sent to your contact.") + .font(.title2) + .multilineTextAlignment(.center) + .padding() + ZStack { + CodeScannerView(codeTypes: [.qr], completion: processQRCode) + .aspectRatio(1, contentMode: .fit) + .border(.gray) + } + .padding(13.0) + } + } + + func processQRCode(_ resp: Result) { + switch resp { + case let .success(r): + do { + try apiConnect(connReq: r.string) + completed(nil) + } catch { + print(error) + completed(error) + } + case let .failure(e): + print(e) + completed(e) + } + } +} + +struct ConnectContactView_Previews: PreviewProvider { + static var previews: some View { + return ConnectContactView(completed: {_ in }) + } +} diff --git a/apps/ios/Shared/Views/Helpers/InviteContactView.swift b/apps/ios/Shared/Views/Helpers/InviteContactView.swift deleted file mode 100644 index 0c6355815a..0000000000 --- a/apps/ios/Shared/Views/Helpers/InviteContactView.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// InviteContactView.swift -// SimpleX -// -// Created by Evgeny Poberezkin on 29/01/2022. -// Copyright © 2022 SimpleX Chat. All rights reserved. -// - -import SwiftUI - -struct InviteContactView: View { - var body: some View { - Text("InviteContactView") - } -} - -struct InviteContactView_Previews: PreviewProvider { - static var previews: some View { - InviteContactView() - } -} diff --git a/apps/ios/Shared/Views/Helpers/QRCode.swift b/apps/ios/Shared/Views/Helpers/QRCode.swift new file mode 100644 index 0000000000..b92dc44fd3 --- /dev/null +++ b/apps/ios/Shared/Views/Helpers/QRCode.swift @@ -0,0 +1,50 @@ +// +// QRCode.swift +// SimpleX +// +// Created by Evgeny Poberezkin on 30/01/2022. +// Copyright © 2022 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import CoreImage.CIFilterBuiltins + +struct QRCode: View { + let uri: String + @State private var image: UIImage? + + var body: some View { + ZStack { + if let image = image { + Image(uiImage: image) + .resizable() + .interpolation(.none) + .aspectRatio(1, contentMode: .fit) + } + } + .onAppear { + generateImage() + } + } + + private func generateImage() { + guard image == nil else { return } + + let context = CIContext() + let filter = CIFilter.qrCodeGenerator() + filter.message = Data(uri.utf8) + + guard + let outputImage = filter.outputImage, + let cgImage = context.createCGImage(outputImage, from: outputImage.extent) + else { return } + + self.image = UIImage(cgImage: cgImage) + } +} + +struct QRCode_Previews: PreviewProvider { + static var previews: some View { + QRCode(uri: "https://simplex.chat/invitation#/?v=1&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FFe5ICmvrm4wkrr6X1LTMii-lhBqLeB76%23MCowBQYDK2VuAyEAdhZZsHpuaAk3Hh1q0uNb_6hGTpuwBIrsp2z9U2T0oC0%3D&e2e=v%3D1%26x3dh%3DMEIwBQYDK2VvAzkAcz6jJk71InuxA0bOX7OUhddfB8Ov7xwQIlIDeXBRZaOntUU4brU5Y3rBzroZBdQJi0FKdtt_D7I%3D%2CMEIwBQYDK2VvAzkA-hDvk1duBi1hlOr08VWSI-Ou4JNNSQjseY69QyKm7Kgg1zZjbpGfyBqSZ2eqys6xtoV4ZtoQUXQ%3D") + } +} diff --git a/apps/ios/Shared/Views/Helpers/ScanQRCodeView.swift b/apps/ios/Shared/Views/Helpers/ScanQRCodeView.swift deleted file mode 100644 index 1a21463ba3..0000000000 --- a/apps/ios/Shared/Views/Helpers/ScanQRCodeView.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// ScanQRCodeView.swift -// SimpleX -// -// Created by Evgeny Poberezkin on 29/01/2022. -// Copyright © 2022 SimpleX Chat. All rights reserved. -// - -import SwiftUI - -struct ScanQRCodeView: View { - var body: some View { - Text("ScanQRCodeView") - } -} - -struct ScanQRCodeView_Previews: PreviewProvider { - static var previews: some View { - ScanQRCodeView() - } -} diff --git a/apps/ios/Shared/Views/Helpers/ShareSheet.swift b/apps/ios/Shared/Views/Helpers/ShareSheet.swift new file mode 100644 index 0000000000..3b9dbcb5e1 --- /dev/null +++ b/apps/ios/Shared/Views/Helpers/ShareSheet.swift @@ -0,0 +1,40 @@ +// +// ShareSheet.swift +// SimpleX +// +// Created by Evgeny Poberezkin on 30/01/2022. +// Copyright © 2022 SimpleX Chat. All rights reserved. +// + +import SwiftUI + +extension UIApplication { + static let keyWindow = keyWindowScene?.windows.filter(\.isKeyWindow).first + static let keyWindowScene = shared.connectedScenes.first { $0.activationState == .foregroundActive } as? UIWindowScene +} + +extension View { + func shareSheet(isPresented: Binding, items: [Any]) -> some View { + guard isPresented.wrappedValue else { return self } + let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil) + let presentedViewController = UIApplication.keyWindow?.rootViewController?.presentedViewController ?? UIApplication.keyWindow?.rootViewController + activityViewController.completionWithItemsHandler = { _, _, _, _ in isPresented.wrappedValue = false } + presentedViewController?.present(activityViewController, animated: true) + return self + } +} + +struct ShareSheetTest: View { + @State private var isPresentingShareSheet = false + + var body: some View { + Button("Show Share Sheet") { isPresentingShareSheet = true } + .shareSheet(isPresented: $isPresentingShareSheet, items: ["Share me!"]) + } +} + +struct ShareSheetTest_Previews: PreviewProvider { + static var previews: some View { + ShareSheetTest() + } +} diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index 5a68218041..79d2bbc2f2 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -35,6 +35,7 @@ 5C764E85279C748C000C6508 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C764E7C279C71DB000C6508 /* libz.tbd */; }; 5C764E89279CBCB3000C6508 /* ChatModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C764E88279CBCB3000C6508 /* ChatModel.swift */; }; 5C764E8A279CBCB3000C6508 /* ChatModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C764E88279CBCB3000C6508 /* ChatModel.swift */; }; + 5C8F01CD27A6F0D8007D2C8D /* CodeScanner in Frameworks */ = {isa = PBXBuildFile; productRef = 5C8F01CC27A6F0D8007D2C8D /* CodeScanner */; }; 5C9FD96B27A56D4D0075386C /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9FD96A27A56D4D0075386C /* JSON.swift */; }; 5C9FD96C27A56D4D0075386C /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9FD96A27A56D4D0075386C /* JSON.swift */; }; 5C9FD96E27A5D6ED0075386C /* SendMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9FD96D27A5D6ED0075386C /* SendMessageView.swift */; }; @@ -53,12 +54,16 @@ 5CA05A4D27974EB60002BEB4 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA05A4B27974EB60002BEB4 /* WelcomeView.swift */; }; 5CA05A4F279752D00002BEB4 /* MessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA05A4E279752D00002BEB4 /* MessageView.swift */; }; 5CA05A50279752D00002BEB4 /* MessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA05A4E279752D00002BEB4 /* MessageView.swift */; }; + 5CC1C99227A6C7F5000D9FF6 /* QRCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */; }; + 5CC1C99327A6C7F5000D9FF6 /* QRCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */; }; + 5CC1C99527A6CF7F000D9FF6 /* ShareSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */; }; + 5CC1C99627A6CF7F000D9FF6 /* ShareSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */; }; 5CCD403127A5F1C600368C90 /* ChatHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403027A5F1C600368C90 /* ChatHeaderView.swift */; }; 5CCD403227A5F1C600368C90 /* ChatHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403027A5F1C600368C90 /* ChatHeaderView.swift */; }; - 5CCD403427A5F6DF00368C90 /* InviteContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403327A5F6DF00368C90 /* InviteContactView.swift */; }; - 5CCD403527A5F6DF00368C90 /* InviteContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403327A5F6DF00368C90 /* InviteContactView.swift */; }; - 5CCD403727A5F9A200368C90 /* ScanQRCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403627A5F9A200368C90 /* ScanQRCodeView.swift */; }; - 5CCD403827A5F9A200368C90 /* ScanQRCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403627A5F9A200368C90 /* ScanQRCodeView.swift */; }; + 5CCD403427A5F6DF00368C90 /* AddContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403327A5F6DF00368C90 /* AddContactView.swift */; }; + 5CCD403527A5F6DF00368C90 /* AddContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403327A5F6DF00368C90 /* AddContactView.swift */; }; + 5CCD403727A5F9A200368C90 /* ConnectContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403627A5F9A200368C90 /* ConnectContactView.swift */; }; + 5CCD403827A5F9A200368C90 /* ConnectContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403627A5F9A200368C90 /* ConnectContactView.swift */; }; 5CCD403A27A5F9BE00368C90 /* CreateGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403927A5F9BE00368C90 /* CreateGroupView.swift */; }; 5CCD403B27A5F9BE00368C90 /* CreateGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403927A5F9BE00368C90 /* CreateGroupView.swift */; }; /* End PBXBuildFile section */ @@ -113,9 +118,11 @@ 5CA059E9279559F40002BEB4 /* Tests_macOSLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_macOSLaunchTests.swift; sourceTree = ""; }; 5CA05A4B27974EB60002BEB4 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = ""; }; 5CA05A4E279752D00002BEB4 /* MessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageView.swift; sourceTree = ""; }; + 5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCode.swift; sourceTree = ""; }; + 5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareSheet.swift; sourceTree = ""; }; 5CCD403027A5F1C600368C90 /* ChatHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatHeaderView.swift; sourceTree = ""; }; - 5CCD403327A5F6DF00368C90 /* InviteContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteContactView.swift; sourceTree = ""; }; - 5CCD403627A5F9A200368C90 /* ScanQRCodeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanQRCodeView.swift; sourceTree = ""; }; + 5CCD403327A5F6DF00368C90 /* AddContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactView.swift; sourceTree = ""; }; + 5CCD403627A5F9A200368C90 /* ConnectContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectContactView.swift; sourceTree = ""; }; 5CCD403927A5F9BE00368C90 /* CreateGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateGroupView.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -124,6 +131,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 5C8F01CD27A6F0D8007D2C8D /* CodeScanner in Frameworks */, 5C764E83279C748B000C6508 /* libz.tbd in Frameworks */, 5C764E82279C748B000C6508 /* libiconv.tbd in Frameworks */, 5C1AEB86279F4A6400247F08 /* libffi.a in Frameworks */, @@ -184,9 +192,11 @@ 5C9FD96D27A5D6ED0075386C /* SendMessageView.swift */, 5CA05A4E279752D00002BEB4 /* MessageView.swift */, 5CCD403027A5F1C600368C90 /* ChatHeaderView.swift */, + 5CCD403327A5F6DF00368C90 /* AddContactView.swift */, + 5CCD403627A5F9A200368C90 /* ConnectContactView.swift */, 5CCD403927A5F9BE00368C90 /* CreateGroupView.swift */, - 5CCD403627A5F9A200368C90 /* ScanQRCodeView.swift */, - 5CCD403327A5F6DF00368C90 /* InviteContactView.swift */, + 5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */, + 5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */, ); path = Helpers; sourceTree = ""; @@ -303,6 +313,9 @@ dependencies = ( ); name = "SimpleX (iOS)"; + packageProductDependencies = ( + 5C8F01CC27A6F0D8007D2C8D /* CodeScanner */, + ); productName = "SimpleX (iOS)"; productReference = 5CA059CA279559F40002BEB4 /* SimpleX.app */; productType = "com.apple.product-type.application"; @@ -398,6 +411,9 @@ Base, ); mainGroup = 5CA059BD279559F40002BEB4; + packageReferences = ( + 5C8F01CB27A6F0D8007D2C8D /* XCRemoteSwiftPackageReference "CodeScanner" */, + ); productRefGroup = 5CA059CB279559F40002BEB4 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -453,17 +469,19 @@ 5C2E261227A30FEA00F70299 /* TerminalView.swift in Sources */, 5C9FD96B27A56D4D0075386C /* JSON.swift in Sources */, 5C9FD96E27A5D6ED0075386C /* SendMessageView.swift in Sources */, + 5CC1C99227A6C7F5000D9FF6 /* QRCode.swift in Sources */, 5CCD403127A5F1C600368C90 /* ChatHeaderView.swift in Sources */, 5CA05A4F279752D00002BEB4 /* MessageView.swift in Sources */, 5CA059ED279559F40002BEB4 /* ContentView.swift in Sources */, - 5CCD403427A5F6DF00368C90 /* InviteContactView.swift in Sources */, + 5CCD403427A5F6DF00368C90 /* AddContactView.swift in Sources */, 5CA05A4C27974EB60002BEB4 /* WelcomeView.swift in Sources */, 5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */, 5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */, 5CA059EB279559F40002BEB4 /* SimpleXApp.swift in Sources */, - 5CCD403727A5F9A200368C90 /* ScanQRCodeView.swift in Sources */, + 5CCD403727A5F9A200368C90 /* ConnectContactView.swift in Sources */, 5CCD403A27A5F9BE00368C90 /* CreateGroupView.swift in Sources */, 5C764E89279CBCB3000C6508 /* ChatModel.swift in Sources */, + 5CC1C99527A6CF7F000D9FF6 /* ShareSheet.swift in Sources */, 5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -477,17 +495,19 @@ 5C2E261327A30FEA00F70299 /* TerminalView.swift in Sources */, 5C9FD96C27A56D4D0075386C /* JSON.swift in Sources */, 5C9FD96F27A5D6ED0075386C /* SendMessageView.swift in Sources */, + 5CC1C99327A6C7F5000D9FF6 /* QRCode.swift in Sources */, 5CCD403227A5F1C600368C90 /* ChatHeaderView.swift in Sources */, 5CA05A50279752D00002BEB4 /* MessageView.swift in Sources */, 5CA059EE279559F40002BEB4 /* ContentView.swift in Sources */, - 5CCD403527A5F6DF00368C90 /* InviteContactView.swift in Sources */, + 5CCD403527A5F6DF00368C90 /* AddContactView.swift in Sources */, 5CA05A4D27974EB60002BEB4 /* WelcomeView.swift in Sources */, 5C2E261027A30FDC00F70299 /* ChatView.swift in Sources */, 5C2E260C27A30CFA00F70299 /* ChatListView.swift in Sources */, 5CA059EC279559F40002BEB4 /* SimpleXApp.swift in Sources */, - 5CCD403827A5F9A200368C90 /* ScanQRCodeView.swift in Sources */, + 5CCD403827A5F9A200368C90 /* ConnectContactView.swift in Sources */, 5CCD403B27A5F9BE00368C90 /* CreateGroupView.swift in Sources */, 5C764E8A279CBCB3000C6508 /* ChatModel.swift in Sources */, + 5CC1C99627A6CF7F000D9FF6 /* ShareSheet.swift in Sources */, 5C2E260827A2941F00F70299 /* SimpleXAPI.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -649,6 +669,7 @@ ENABLE_BITCODE = NO; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSCameraUsageDescription = "$(PRODUCT_NAME) needs camera access to scan QR codes to connect to other app users"; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -689,6 +710,7 @@ ENABLE_BITCODE = NO; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSCameraUsageDescription = "$(PRODUCT_NAME) needs camera access to scan QR codes to connect to other app users"; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -921,6 +943,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 5C8F01CB27A6F0D8007D2C8D /* XCRemoteSwiftPackageReference "CodeScanner" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/twostraws/CodeScanner"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 5C8F01CC27A6F0D8007D2C8D /* CodeScanner */ = { + isa = XCSwiftPackageProductDependency; + package = 5C8F01CB27A6F0D8007D2C8D /* XCRemoteSwiftPackageReference "CodeScanner" */; + productName = CodeScanner; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 5CA059BE279559F40002BEB4 /* Project object */; } diff --git a/apps/ios/SimpleX.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/apps/ios/SimpleX.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000000..f9b0d7c5ea --- /dev/null +++ b/apps/ios/SimpleX.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/apps/ios/SimpleX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/apps/ios/SimpleX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000000..9d4b1de373 --- /dev/null +++ b/apps/ios/SimpleX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "CodeScanner", + "repositoryURL": "https://github.com/twostraws/CodeScanner", + "state": { + "branch": null, + "revision": "c27a66149b7483fe42e2ec6aad61d5c3fffe522d", + "version": "2.1.1" + } + } + ] + }, + "version": 1 +}