From ea0e93be1268b19b4efb41a69daac28f99a652c1 Mon Sep 17 00:00:00 2001 From: huangshijie Date: Fri, 7 Jun 2024 16:04:27 +0800 Subject: [PATCH] =?UTF-8?q?APNs=20=E6=B6=88=E6=81=AF=E6=8E=A8=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- copinism.xcodeproj/project.pbxproj | 4 + copinism.xcworkspace/contents.xcworkspacedata | 749 ------------------ copinism/AppDelegate.swift | 67 ++ copinism/Info.plist | 4 + copinism/Views/Message/MessageDetail.swift | 56 +- copinism/Views/Mine/MineView.swift | 1 - copinism/copinismApp.swift | 1 + copinism/utils/Http.swift | 1 + 8 files changed, 107 insertions(+), 776 deletions(-) create mode 100644 copinism/AppDelegate.swift diff --git a/copinism.xcodeproj/project.pbxproj b/copinism.xcodeproj/project.pbxproj index 23ab6ee..8faace1 100644 --- a/copinism.xcodeproj/project.pbxproj +++ b/copinism.xcodeproj/project.pbxproj @@ -40,6 +40,7 @@ DE9C3A322C0E390200951DEE /* copinismApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9C3A122C0E390200951DEE /* copinismApp.swift */; }; DE9C3A332C0E390200951DEE /* TestSwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9C3A132C0E390200951DEE /* TestSwiftUIView.swift */; }; DE9C3A362C10AA8000951DEE /* MessageBack.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9C3A352C10AA8000951DEE /* MessageBack.swift */; }; + DE9C3A382C12F2DE00951DEE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9C3A372C12F2DE00951DEE /* AppDelegate.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -80,6 +81,7 @@ DE9C3A132C0E390200951DEE /* TestSwiftUIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestSwiftUIView.swift; sourceTree = ""; }; DE9C3A342C0E420400951DEE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; DE9C3A352C10AA8000951DEE /* MessageBack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageBack.swift; sourceTree = ""; }; + DE9C3A372C12F2DE00951DEE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -276,6 +278,7 @@ DE9C3A112C0E390200951DEE /* ContentView.swift */, DE9C3A122C0E390200951DEE /* copinismApp.swift */, DE9C3A132C0E390200951DEE /* TestSwiftUIView.swift */, + DE9C3A372C12F2DE00951DEE /* AppDelegate.swift */, ); path = copinism; sourceTree = ""; @@ -425,6 +428,7 @@ DE9C3A2C2C0E390200951DEE /* TargetItamSwift.swift in Sources */, DE9C3A362C10AA8000951DEE /* MessageBack.swift in Sources */, DE9C3A282C0E390200951DEE /* MessageView.swift in Sources */, + DE9C3A382C12F2DE00951DEE /* AppDelegate.swift in Sources */, DE9C3A1F2C0E390200951DEE /* ArticleCardView.swift in Sources */, DE9C3A182C0E390200951DEE /* TabbarView.swift in Sources */, DE9C3A332C0E390200951DEE /* TestSwiftUIView.swift in Sources */, diff --git a/copinism.xcworkspace/contents.xcworkspacedata b/copinism.xcworkspace/contents.xcworkspacedata index 6e11b12..428024d 100644 --- a/copinism.xcworkspace/contents.xcworkspacedata +++ b/copinism.xcworkspace/contents.xcworkspacedata @@ -4,755 +4,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/copinism/AppDelegate.swift b/copinism/AppDelegate.swift new file mode 100644 index 0000000..f6d1a85 --- /dev/null +++ b/copinism/AppDelegate.swift @@ -0,0 +1,67 @@ +// +// AppDelegate.swift +// copinism +// +// Created by 黄仕杰 on 2024/6/7. +// + +import UIKit +import UserNotifications + +class AppDelegate: UIResponder, UIApplicationDelegate { + + //当应用程序启动后,可能需要进行其他逻辑处理时调用的方法 + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // 请求推送通知权限 + UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in + if granted { + DispatchQueue.main.async { + application.registerForRemoteNotifications() + } + } + } + return true + } + + /// 当设备成功注册推送通知时调用。用来获取设备令牌并发送到服务器 + func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { + // 获取 deviceToken + let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) } + let token = tokenParts.joined() + print("Device Token: \(token)") + + // 将 deviceToken 发送到服务器 + sendDeviceTokenToServer(token: token) + } + + // 当设备未能注册推送通知时调用。用来处理和记录错误信息 + func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { + print("Failed to register: \(error)") + } + + private func sendDeviceTokenToServer(token: String) { + // 服务器的 API URL + let url = URL(string: "https://yourserver.com/api/deviceToken")! + + // 设备信息 + let parameters: [String: Any] = [ + "deviceToken": token, + "deviceType": "iOS" + ] + + // 使用 URLSession 发送请求 + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + request.httpBody = try? JSONSerialization.data(withJSONObject: parameters) + + let task = URLSession.shared.dataTask(with: request) { data, response, error in + if let error = error { + print("Error sending device token: \(error)") + return + } + print("Device token sent successfully") + } + task.resume() + } +} diff --git a/copinism/Info.plist b/copinism/Info.plist index 5721eef..8ff0b35 100644 --- a/copinism/Info.plist +++ b/copinism/Info.plist @@ -18,5 +18,9 @@ NSLocalNotificationUsageDescription NSLocalNotificationUsageDescription 需要通知权限以便发送新消息通知 + UIBackgroundModes + + remote-notification + diff --git a/copinism/Views/Message/MessageDetail.swift b/copinism/Views/Message/MessageDetail.swift index a6af04c..9e77e25 100644 --- a/copinism/Views/Message/MessageDetail.swift +++ b/copinism/Views/Message/MessageDetail.swift @@ -1,7 +1,7 @@ import SwiftUI import Alamofire -struct Message: Identifiable { +struct Message: Identifiable, Codable { let id: UUID let text: String let sender: String @@ -9,7 +9,7 @@ struct Message: Identifiable { let isSelf: Bool let avatar: String let num: Int - let image: UIImage? + let image: Data? init(id: UUID = UUID(), text: String = "", sender: String, timestamp: String, isSelf: Bool, avatar: String, num: Int = 0, image: UIImage? = nil) { self.id = id @@ -19,20 +19,18 @@ struct Message: Identifiable { self.isSelf = isSelf self.avatar = avatar self.num = num - self.image = image + self.image = image?.jpegData(compressionQuality: 0.8) } } struct MessageDetailView: View { - @State private var messages: [Message] = [ - Message(text: "来了条消息", sender: "系统消息", timestamp: "4:00 PM", isSelf: false, avatar: "avatar") - ] + @State private var messages: [Message] = [] @State private var replyText = "" @State private var selectedImage: UIImage? @State private var selectedFileURL: URL? @State private var isImagePickerPresented = false @State private var isDocumentPickerPresented = false - @State private var uploadProgress: Double = 0.0 // 用于跟踪上传进度 + @State private var uploadProgress: Double = 0.0 @Environment(\.presentationMode) var presentationMode var body: some View { @@ -59,7 +57,7 @@ struct MessageDetailView: View { .font(.system(size: 18)) .foregroundColor(.black) } - .opacity(0) // 占位用 + .opacity(0) } .padding(.horizontal, 12) .padding(.vertical, 8) @@ -71,7 +69,7 @@ struct MessageDetailView: View { if !message.isSelf { Image("avatar") .CircleImage(size: 30) - if let image = message.image { + if let imageData = message.image, let image = UIImage(data: imageData) { ZStack { Image(uiImage: image) .resizable() @@ -94,7 +92,7 @@ struct MessageDetailView: View { Spacer() } else { Spacer() - if let image = message.image { + if let imageData = message.image, let image = UIImage(data: imageData) { ZStack { Image(uiImage: image) .resizable() @@ -161,7 +159,6 @@ struct MessageDetailView: View { .padding(.bottom, 8) .sheet(isPresented: $isImagePickerPresented) { ImagePicker(selectedImage: $selectedImage, onImagePicked: { image in - print(image) sendImageMessage(image: image) }) } @@ -170,6 +167,9 @@ struct MessageDetailView: View { } } .navigationBarBackButtonHidden(true) + .onAppear { + loadMessages() + } .onTapGesture { hideKeyboard() } @@ -178,30 +178,19 @@ struct MessageDetailView: View { private func sendMessage() { let newMessage = Message(text: replyText, sender: "你", timestamp: currentTimestamp(), isSelf: true, avatar: "avatar") messages.append(newMessage) -// let messageData = ["text": replyText, "sender": "你", "timestamp": currentTimestamp(), "isSelf": true, "avatar": "avatar"] -// makeAlamofireRequest(api: "YOUR_API_ENDPOINT_HERE", method: .post, param: messageData) { (result: Result, AFError>) in -// switch result { -// case .success(let response): -// print("Message sent successfully: \(response)") -// case .failure(let error): -// print("Error sending message: \(error)") -// } -// } - + saveMessages() replyText = "" } private func sendImageMessage(image: UIImage) { let newMessage = Message(sender: "你", timestamp: currentTimestamp(), isSelf: true, avatar: "avatar", image: image) messages.append(newMessage) - - // 将进度重置为 0.0 + //saveMessages() + uploadProgress = 0.0 - - // 假设你想以 base64 字符串的形式发送图片 + if let imageData = image.jpegData(compressionQuality: 0.8) { uploadFile(api: apiprefixV1+upload, file: imageData) { progress in - // 更新进度 self.uploadProgress = progress } completion: { (result: Result, AFError>) in print(result) @@ -209,6 +198,20 @@ struct MessageDetailView: View { } } + private func saveMessages() { + if let encodedMessages = try? JSONEncoder().encode(messages) { + UserDefaults.standard.set(encodedMessages, forKey: "messages") + } + + } + + private func loadMessages() { + UserDefaults.standard.removeObject(forKey: "messages") + if let savedMessagesData = UserDefaults.standard.data(forKey: "messages"), + let decodedMessages = try? JSONDecoder().decode([Message].self, from: savedMessagesData) { + messages = decodedMessages + } + } private func currentTimestamp() -> String { let formatter = DateFormatter() @@ -326,3 +329,4 @@ struct DocumentPicker: UIViewControllerRepresentable { MessageDetailView() } + diff --git a/copinism/Views/Mine/MineView.swift b/copinism/Views/Mine/MineView.swift index 92853e1..55c222b 100644 --- a/copinism/Views/Mine/MineView.swift +++ b/copinism/Views/Mine/MineView.swift @@ -299,7 +299,6 @@ struct MineView: View { } func getTarget(req: TargetMeReq){ - print(req) GetMeTargets(req: req){ (result: Result, AFError>) in switch result { case .success(let body): diff --git a/copinism/copinismApp.swift b/copinism/copinismApp.swift index ea08cd3..30bc945 100644 --- a/copinism/copinismApp.swift +++ b/copinism/copinismApp.swift @@ -9,6 +9,7 @@ import SwiftUI @main struct small_red_bookApp: App { + @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { ContentView() diff --git a/copinism/utils/Http.swift b/copinism/utils/Http.swift index 0311b7e..05f1403 100644 --- a/copinism/utils/Http.swift +++ b/copinism/utils/Http.swift @@ -43,6 +43,7 @@ func uploadFile(api: String,file: Data,progressHandler: @escaping if loginStatus.login { headers.add(name: "Authorization", value: loginStatus.token) } + print(api) AF.upload(file, to: api,headers: headers) .uploadProgress { progress in progressHandler(progress.fractionCompleted)