Эх сурвалжийг харах

Implementation of the main functions of XMPPFramework

Sergey 2 жил өмнө
parent
commit
883b75bfee

+ 24 - 0
Chat.xcodeproj/project.pbxproj

@@ -23,6 +23,14 @@
 		2F6045A6288E19ED008F005E /* KeyboardReadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F6045A4288E19ED008F005E /* KeyboardReadable.swift */; };
 		2F6045A9288E9B2C008F005E /* XMPPFramework in Frameworks */ = {isa = PBXBuildFile; productRef = 2F6045A8288E9B2C008F005E /* XMPPFramework */; };
 		2F6045AB288E9B2C008F005E /* XMPPFrameworkSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 2F6045AA288E9B2C008F005E /* XMPPFrameworkSwift */; };
+		2F6045AD288E9B73008F005E /* XMPPController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F6045AC288E9B73008F005E /* XMPPController.swift */; };
+		2F6045AE288E9B73008F005E /* XMPPController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F6045AC288E9B73008F005E /* XMPPController.swift */; };
+		2F6045B0288E9D3E008F005E /* ChatListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F6045AF288E9D3E008F005E /* ChatListViewModel.swift */; };
+		2F6045B1288E9D3E008F005E /* ChatListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F6045AF288E9D3E008F005E /* ChatListViewModel.swift */; };
+		2F6045B3288EA0E6008F005E /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F6045B2288EA0E6008F005E /* LoginView.swift */; };
+		2F6045B4288EA0E6008F005E /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F6045B2288EA0E6008F005E /* LoginView.swift */; };
+		2F6045B6288EA10B008F005E /* LoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F6045B5288EA10B008F005E /* LoginViewModel.swift */; };
+		2F6045B7288EA10B008F005E /* LoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F6045B5288EA10B008F005E /* LoginViewModel.swift */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
@@ -36,6 +44,10 @@
 		2F60459B288D8000008F005E /* MessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageView.swift; sourceTree = "<group>"; };
 		2F60459E288D869B008F005E /* ChatListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListView.swift; sourceTree = "<group>"; };
 		2F6045A4288E19ED008F005E /* KeyboardReadable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardReadable.swift; sourceTree = "<group>"; };
+		2F6045AC288E9B73008F005E /* XMPPController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XMPPController.swift; sourceTree = "<group>"; };
+		2F6045AF288E9D3E008F005E /* ChatListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListViewModel.swift; sourceTree = "<group>"; };
+		2F6045B2288EA0E6008F005E /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; };
+		2F6045B5288EA10B008F005E /* LoginViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewModel.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -71,8 +83,12 @@
 			isa = PBXGroup;
 			children = (
 				2F60457A288D787E008F005E /* ChatApp.swift */,
+				2F6045AC288E9B73008F005E /* XMPPController.swift */,
 				2F60457B288D787E008F005E /* ContentView.swift */,
+				2F6045B2288EA0E6008F005E /* LoginView.swift */,
+				2F6045B5288EA10B008F005E /* LoginViewModel.swift */,
 				2F60459E288D869B008F005E /* ChatListView.swift */,
+				2F6045AF288E9D3E008F005E /* ChatListViewModel.swift */,
 				2F604598288D7E00008F005E /* ChatView.swift */,
 				2F60459B288D8000008F005E /* MessageView.swift */,
 				2F6045A4288E19ED008F005E /* KeyboardReadable.swift */,
@@ -204,10 +220,14 @@
 			buildActionMask = 2147483647;
 			files = (
 				2F60459C288D8000008F005E /* MessageView.swift in Sources */,
+				2F6045AD288E9B73008F005E /* XMPPController.swift in Sources */,
+				2F6045B0288E9D3E008F005E /* ChatListViewModel.swift in Sources */,
+				2F6045B3288EA0E6008F005E /* LoginView.swift in Sources */,
 				2F60459F288D869B008F005E /* ChatListView.swift in Sources */,
 				2F6045A5288E19ED008F005E /* KeyboardReadable.swift in Sources */,
 				2F60458C288D787F008F005E /* ContentView.swift in Sources */,
 				2F604599288D7E00008F005E /* ChatView.swift in Sources */,
+				2F6045B6288EA10B008F005E /* LoginViewModel.swift in Sources */,
 				2F60458A288D787F008F005E /* ChatApp.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -217,10 +237,14 @@
 			buildActionMask = 2147483647;
 			files = (
 				2F60459D288D8000008F005E /* MessageView.swift in Sources */,
+				2F6045AE288E9B73008F005E /* XMPPController.swift in Sources */,
+				2F6045B1288E9D3E008F005E /* ChatListViewModel.swift in Sources */,
+				2F6045B4288EA0E6008F005E /* LoginView.swift in Sources */,
 				2F6045A0288D869C008F005E /* ChatListView.swift in Sources */,
 				2F6045A6288E19ED008F005E /* KeyboardReadable.swift in Sources */,
 				2F60458D288D787F008F005E /* ContentView.swift in Sources */,
 				2F60459A288D7E00008F005E /* ChatView.swift in Sources */,
+				2F6045B7288EA10B008F005E /* LoginViewModel.swift in Sources */,
 				2F60458B288D787F008F005E /* ChatApp.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;

+ 11 - 0
Shared/ChatApp.swift

@@ -6,6 +6,7 @@
 //
 
 import SwiftUI
+import XMPPFramework
 
 @main
 struct ChatApp: App {
@@ -17,6 +18,16 @@ struct ChatApp: App {
     }
 }
 
+class AppDelegate: NSObject, UIApplicationDelegate {
+    func application(
+        _ application: UIApplication,
+        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
+    ) -> Bool {
+        DDLog.add(DDTTYLogger.sharedInstance!, with: DDLogLevel.all)
+        return true
+    }
+}
+
 extension UIApplication {
     func addTapGestureRecognizer() {
         guard let window = windows.first else { return }

+ 51 - 42
Shared/ChatListView.swift

@@ -9,68 +9,77 @@ import SwiftUI
 
 struct ChatListView: View {
     @Environment(\.colorScheme) var colorScheme
+    @ObservedObject var viewModel: ChatListViewModel
     var body: some View {
-        List {
-            ForEach(1..<14) { index in
-                ZStack {
-                    HStack {
-                        Image(systemName: "person.crop.circle")
-                            .resizable()
-                            .scaledToFit()
-                            .foregroundColor(.secondary.opacity(0.5))
-                            .padding(.vertical, 8)
-                        VStack(alignment: .leading, spacing: 0) {
-                            HStack {
-                                Text("Андрей")
-                                Spacer()
-                                Text("23:04")
-                                    .font(.callout)
-                                    .foregroundColor(.secondary)
-                            }
-                            .padding(.vertical, 4)
+        Group {
+            if viewModel.jids.isEmpty {
+                ProgressView()
+            } else {
+                List {
+                    ForEach(viewModel.jids.indices, id: \.self) { index in
+                        ZStack {
                             HStack {
-                                VStack {
-                                    Text("Сообщение \(index)")
-                                        .foregroundColor(.secondary)
-                                    Spacer()
+                                Image(systemName: "person.crop.circle")
+                                    .resizable()
+                                    .scaledToFit()
+                                    .foregroundColor(.secondary.opacity(0.5))
+                                    .padding(.vertical, 8)
+                                VStack(alignment: .leading, spacing: 0) {
+                                    HStack {
+                                        Text(viewModel.jids[index].user ?? "")
+                                        Spacer()
+                                        Text("23:04")
+                                            .font(.callout)
+                                            .foregroundColor(.secondary)
+                                    }
+                                    .padding(.vertical, 4)
+                                    HStack {
+                                        VStack {
+                                            Text("Сообщение \(index)")
+                                                .foregroundColor(.secondary)
+                                            Spacer()
+                                        }
+                                        Spacer()
+                                        Text("\(index)")
+                                            .font(.callout)
+                                            .foregroundColor(colorScheme == .light ? .white : .black)
+                                            .padding(.horizontal, 6)
+                                            .background(
+                                                Capsule().foregroundColor(.secondary)
+                                            )
+                                    }
+                                    .padding(.vertical, 6)
                                 }
-                                Spacer()
-                                Text("\(index)")
-                                    .font(.callout)
-                                    .foregroundColor(colorScheme == .light ? .white : .black)
-                                    .padding(.horizontal, 6)
-                                    .background(
-                                        Capsule().foregroundColor(.secondary)
-                                    )
                             }
-                            .padding(.vertical, 6)
+                            .foregroundColor(.primary)
+                            .frame(height: 60)
+                            NavigationLink {
+                                ChatView()
+                                    .navigationTitle(viewModel.jids[index].user ?? "")
+                            } label: {
+                                EmptyView()
+                            }
+                                .opacity(0.0)
                         }
                     }
-                    .foregroundColor(.primary)
-                    .frame(height: 60)
-                    NavigationLink {
-                        ChatView()
-                    } label: {
-                        EmptyView()
-                    }
-                        .opacity(0.0)
                 }
+                .listStyle(.plain)
             }
         }
-        .listStyle(.plain)
         .navigationTitle("Чаты")
         .navigationBarTitleDisplayMode(.inline)
     }
 }
 
 struct ChatListView_Previews: PreviewProvider {
+    static var viewModel: ChatListViewModel = ChatListViewModel()
     static var previews: some View {
         Group {
             NavigationView {
-                ChatListView()
+                ChatListView(viewModel: viewModel)
             }
             NavigationView {
-                ChatListView()
+                ChatListView(viewModel: viewModel)
             }
             .preferredColorScheme(.dark)
         }

+ 37 - 0
Shared/ChatListViewModel.swift

@@ -0,0 +1,37 @@
+//
+//  ChatListViewModel.swift
+//  Chat
+//
+//  Created by Sergey Tarasov on 25.07.2022.
+//
+
+import Foundation
+import XMPPFramework
+import XMPPFrameworkSwift
+
+class ChatListViewModel: ObservableObject {
+    let xmppController = XMPPController.shared
+
+    @Published var jids: [XMPPJID] = []
+    var timer: Timer?
+
+    init() {
+        self.timer = Timer.scheduledTimer(
+            timeInterval: 2,
+            target: self,
+            selector: #selector(fetchChatList),
+            userInfo: nil,
+            repeats: true
+        )
+    }
+
+    @objc
+    public func fetchChatList() {
+        if jids.isEmpty {
+            let jids = xmppController.xmppRosterStorage.jids(for: self.xmppController.xmppStream)
+            self.jids = jids
+        } else {
+            self.timer?.invalidate()
+        }
+    }
+}

+ 1 - 2
Shared/ChatView.swift

@@ -12,7 +12,7 @@ extension CGFloat {
 }
 
 struct ChatView: View, KeyboardReadable {
-    
+
     @State private var isKeyboardVisible = false
     @State var message: String = ""
     @State var messages: [String] = ["Display a short text message, called an alert, that draws attention to something new in your app.", "Play a notification sound.", "Set a badge number on the app’s icon to let the user know there are new items.", "Provide actions the user can take without opening the app.", "Show a media attachment.", "Be silent, allowing the app to perform a task in the background.", "No", "Group notifications into threads.", "Yes", "Edit or remove delivered notifications.", "Run code to change your notification before displaying it.", "Display a custom, interactive UI for your notification.", "And probably more.", "Yes", "Edit or remove delivered notifications.", "Run code to change your notification before displaying it.", "Display a custom, interactive UI for your notification.", "And probably more."]
@@ -76,7 +76,6 @@ struct ChatView: View, KeyboardReadable {
             .padding(.vertical, 6)
             .background()
         }
-        .navigationTitle("Андрей")
         .navigationBarTitleDisplayMode(.inline)
     }
 }

+ 12 - 2
Shared/ContentView.swift

@@ -8,9 +8,19 @@
 import SwiftUI
 
 struct ContentView: View {
+    @ObservedObject var viewModel: ChatListViewModel = ChatListViewModel()
+    @ObservedObject var loginViewModel: LoginViewModel = LoginViewModel()
+
+    @State var authStatus: Bool = false
     var body: some View {
-        NavigationView {
-            ChatListView()
+        Group {
+            if authStatus {
+                NavigationView {
+                    ChatListView(viewModel: viewModel)
+                }
+            } else {
+                LoginView(viewModel: loginViewModel, authStatus: $authStatus)
+            }
         }
     }
 }

+ 39 - 0
Shared/LoginView.swift

@@ -0,0 +1,39 @@
+//
+//  LoginView.swift
+//  Chat
+//
+//  Created by Sergey Tarasov on 25.07.2022.
+//
+
+import SwiftUI
+
+struct LoginView: View {
+    @ObservedObject var viewModel: LoginViewModel
+
+    @Binding var authStatus: Bool
+    var body: some View {
+        VStack(alignment: .center, spacing: 16) {
+            TextField("Login", text: $viewModel.login)
+                .textFieldStyle(.roundedBorder)
+            SecureField("Password", text: $viewModel.password)
+                .textFieldStyle(.roundedBorder)
+            Button {
+                viewModel.didTouchLogIn { success in
+                    if success {
+                        authStatus = true
+                    }
+                }
+            } label: {
+                Text("Login")
+            }
+        }
+        .padding()
+    }
+}
+
+struct LoginView_Previews: PreviewProvider {
+    static var viewModel: LoginViewModel = LoginViewModel()
+    static var previews: some View {
+        LoginView(viewModel: viewModel, authStatus: .constant(false))
+    }
+}

+ 24 - 0
Shared/LoginViewModel.swift

@@ -0,0 +1,24 @@
+//
+//  LoginViewModel.swift
+//  Chat
+//
+//  Created by Sergey Tarasov on 25.07.2022.
+//
+
+import Foundation
+
+class LoginViewModel: ObservableObject {
+    let xmppController = XMPPController.shared
+
+    @Published var login: String = "test11@msg.sharix-app.org"
+    @Published var password: String = "test11_-"
+
+    var isShowError: String? = nil
+
+    func didTouchLogIn(_ completion: @escaping (Bool) -> Void) {
+        self.xmppController.setupStream(with: login, password: password)
+        self.xmppController.connect()
+        completion(true)
+    }
+}
+

+ 127 - 0
Shared/XMPPController.swift

@@ -0,0 +1,127 @@
+//
+//  XMPPController.swift
+//  Chat
+//
+//  Created by Sergey Tarasov on 25.07.2022.
+//
+
+import Foundation
+import XMPPFramework
+import XMPPFrameworkSwift
+
+final class XMPPController: NSObject {
+    static let shared = XMPPController()
+
+    var xmppStream: XMPPStream
+    var xmppReconnect: XMPPReconnect
+    var xmppRoster: XMPPRoster
+    var xmppRosterStorage: XMPPRosterMemoryStorage
+    var xmppCapabilities: XMPPCapabilities
+    var xmppCapabilitiesStorage: XMPPCapabilitiesStorage
+    var xmppPing: XMPPPing
+    var xmppTime: XMPPTime
+
+    var turnSockets: NSMutableArray
+
+    var password: String?
+
+    override init() {
+        self.xmppStream = XMPPStream()
+        self.xmppReconnect = XMPPReconnect()
+        self.xmppRosterStorage = XMPPRosterMemoryStorage()
+        self.xmppRoster = XMPPRoster(rosterStorage: self.xmppRosterStorage, dispatchQueue: DispatchQueue.main)
+        self.xmppCapabilitiesStorage = XMPPCapabilitiesCoreDataStorage.sharedInstance()
+        self.xmppCapabilities = XMPPCapabilities(capabilitiesStorage: self.xmppCapabilitiesStorage)
+        self.xmppCapabilities.autoFetchHashedCapabilities = true
+        self.xmppCapabilities.autoFetchNonHashedCapabilities = false
+        self.xmppPing = XMPPPing()
+        self.xmppTime = XMPPTime()
+
+        self.xmppReconnect.activate(self.xmppStream)
+        self.xmppRoster.activate(self.xmppStream)
+        self.xmppCapabilities.activate(self.xmppStream)
+        self.xmppPing.activate(self.xmppStream)
+        self.xmppTime.activate(self.xmppStream)
+
+        self.turnSockets = []
+
+        super.init()
+
+        self.xmppStream.addDelegate(self, delegateQueue: DispatchQueue.main)
+        self.xmppReconnect.addDelegate(self, delegateQueue: DispatchQueue.main)
+        self.xmppCapabilities.addDelegate(self, delegateQueue: DispatchQueue.main)
+    }
+
+    func setupStream(with login: String?, password: String?) {
+        self.password = password ?? ""
+        self.xmppStream.hostName = "msg.sharix-app.org"
+        self.xmppStream.hostPort = 5222
+        self.xmppStream.myJID = XMPPJID(string: login ?? "test11@msg.sharix-app.org")
+        self.xmppStream.startTLSPolicy = XMPPStreamStartTLSPolicy.allowed
+    }
+
+    func connect() {
+        if !self.xmppStream.isDisconnected { return }
+        try! self.xmppStream.connect(withTimeout: XMPPStreamTimeoutNone)
+    }
+}
+
+extension XMPPController: TURNSocketDelegate {
+    func connectViaXEP65(with jid: XMPPJID) {
+        let turnSocket = TURNSocket(stream: self.xmppStream, to: jid)
+        self.turnSockets.add(turnSocket)
+        turnSocket.start(with: self, delegateQueue: DispatchQueue.main)
+    }
+
+    func xmppStream(_ sender: XMPPStream, didReceive iq: XMPPIQ) -> Bool {
+        print("---------- xmppStream:didReceiveIQ: ----------")
+        if TURNSocket.isNewStartTURNRequest(iq) {
+            let turnSocket = TURNSocket(stream: self.xmppStream, incomingTURNRequest: iq)
+            self.turnSockets.add(turnSocket)
+            turnSocket.start(with: self, delegateQueue: DispatchQueue.main)
+            return true
+        }
+        return false
+    }
+
+    func turnSocket(_ sender: TURNSocket, didSucceed socket: GCDAsyncSocket) {
+        self.turnSockets.remove(sender)
+    }
+
+    func turnSocketDidFail(_ sender: TURNSocket) {
+        self.turnSockets.remove(sender)
+    }
+}
+
+extension XMPPController: XMPPReconnectDelegate {
+    func xmppReconnect(
+        _ sender: XMPPReconnect,
+        shouldAttemptAutoReconnect connectionFlags: SCNetworkConnectionFlags
+    ) -> Bool {
+        return true
+    }
+}
+
+extension XMPPController: XMPPCapabilitiesDelegate {
+    func xmppCapabilities(
+        _ sender: XMPPCapabilities,
+        didDiscoverCapabilities caps: DDXMLElement,
+        for jid: XMPPJID
+    ) {
+        print("---------- xmppCapabilities:didDiscoverCapabilities:forJID: ----------")
+        print("jid: " + jid.debugDescription)
+        print("capabilities: " + caps.debugDescription)
+    }
+}
+
+extension XMPPController: XMPPStreamDelegate {
+    func xmppStreamDidConnect(_ stream: XMPPStream) {
+        print("Stream: Connected")
+        try! stream.authenticate(withPassword: self.password ?? "test11_-")
+    }
+
+    func xmppStreamDidAuthenticate(_ sender: XMPPStream) {
+        self.xmppStream.send(XMPPPresence())
+        print("Stream: Authenticated")
+    }
+}