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
This commit is contained in:
Evgeny Poberezkin 2022-01-30 18:27:20 +00:00 committed by GitHub
parent 15a91278d6
commit 3b19aaf1d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 390 additions and 132 deletions

View File

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

View File

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

View File

@ -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/<id> or group/<id>, 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
}
}

View File

@ -79,3 +79,4 @@ struct Test: Decodable {
//jsonDecoder.decode(Test.self, from: "{\"name\":\"hello\",\"id\":1}".data(using: .utf8)!)
"\(ChatType.direct)"

View File

@ -3,7 +3,7 @@
version = "3.0">
<TimelineItems>
<LoggerValueHistoryTimelineItem
documentLocation = "file:///Users/evgeny/opensource/simplex-chat/simplex-chat/apps/ios/Shared/MyPlayground.playground#CharacterRangeLen=88&amp;CharacterRangeLoc=3634&amp;EndingColumnNumber=0&amp;EndingLineNumber=80&amp;StartingColumnNumber=3&amp;StartingLineNumber=79&amp;Timestamp=665153525.657431"
documentLocation = "file:///Users/evgeny/opensource/simplex-chat/simplex-chat/apps/ios/Shared/MyPlayground.playground#CharacterRangeLen=88&amp;CharacterRangeLoc=3634&amp;EndingColumnNumber=0&amp;EndingLineNumber=80&amp;StartingColumnNumber=3&amp;StartingLineNumber=79&amp;Timestamp=665235849.610096"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>

View File

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

View File

@ -12,7 +12,7 @@ struct ChatPreviewView: View {
var chatPreview: ChatPreview
var body: some View {
Text(chatPreview.chatInfo.displayName)
Text(chatPreview.chatInfo.localDisplayName)
}
}

View File

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

View File

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

View File

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

View File

@ -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<ScanResult, ScanError>) {
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 })
}
}

View File

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

View File

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

View File

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

View File

@ -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<Bool>, 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()
}
}

View File

@ -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 = "<group>"; };
5CA05A4B27974EB60002BEB4 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
5CA05A4E279752D00002BEB4 /* MessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageView.swift; sourceTree = "<group>"; };
5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCode.swift; sourceTree = "<group>"; };
5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareSheet.swift; sourceTree = "<group>"; };
5CCD403027A5F1C600368C90 /* ChatHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatHeaderView.swift; sourceTree = "<group>"; };
5CCD403327A5F6DF00368C90 /* InviteContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteContactView.swift; sourceTree = "<group>"; };
5CCD403627A5F9A200368C90 /* ScanQRCodeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanQRCodeView.swift; sourceTree = "<group>"; };
5CCD403327A5F6DF00368C90 /* AddContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactView.swift; sourceTree = "<group>"; };
5CCD403627A5F9A200368C90 /* ConnectContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectContactView.swift; sourceTree = "<group>"; };
5CCD403927A5F9BE00368C90 /* CreateGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateGroupView.swift; sourceTree = "<group>"; };
/* 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 = "<group>";
@ -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 */;
}

View File

@ -0,0 +1,8 @@
<?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>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

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