Compare commits

...

4 Commits

Author SHA1 Message Date
379cff4c73 通知展示到本地通知中心 2024-06-07 18:00:03 +08:00
ea0e93be12 APNs 消息推送 2024-06-07 16:04:27 +08:00
435904ba62 删除误传的文件 2024-06-07 09:43:28 +08:00
2431df67df 发送消息 2024-06-07 09:42:34 +08:00
10 changed files with 296 additions and 793 deletions

View File

@ -39,6 +39,8 @@
DE9C3A312C0E390200951DEE /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9C3A112C0E390200951DEE /* ContentView.swift */; };
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 */
@ -78,6 +80,8 @@
DE9C3A122C0E390200951DEE /* copinismApp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = copinismApp.swift; sourceTree = "<group>"; };
DE9C3A132C0E390200951DEE /* TestSwiftUIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestSwiftUIView.swift; sourceTree = "<group>"; };
DE9C3A342C0E420400951DEE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
DE9C3A352C10AA8000951DEE /* MessageBack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageBack.swift; sourceTree = "<group>"; };
DE9C3A372C12F2DE00951DEE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -124,6 +128,7 @@
DE9C39E62C0E390200951DEE /* Api.swift */,
DE9C39E72C0E390200951DEE /* Req.swift */,
DE9C39E82C0E390200951DEE /* Target.swift */,
DE9C3A352C10AA8000951DEE /* MessageBack.swift */,
);
path = api;
sourceTree = "<group>";
@ -273,6 +278,7 @@
DE9C3A112C0E390200951DEE /* ContentView.swift */,
DE9C3A122C0E390200951DEE /* copinismApp.swift */,
DE9C3A132C0E390200951DEE /* TestSwiftUIView.swift */,
DE9C3A372C12F2DE00951DEE /* AppDelegate.swift */,
);
path = copinism;
sourceTree = "<group>";
@ -420,7 +426,9 @@
DE9C3A292C0E390200951DEE /* Login.swift in Sources */,
DE9C3A2E2C0E390200951DEE /* ShoppingView.swift in Sources */,
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 */,

View File

@ -4,755 +4,6 @@
<FileRef
location = "group:copinism.xcodeproj">
</FileRef>
<Group
location = "group:"
name = "">
<Group
location = "group:copinism"
name = "copinism">
<FileRef
location = "group:TestSwiftUIView.swift">
</FileRef>
<Group
location = "group:Assets.xcassets"
name = "Assets.xcassets">
<Group
location = "group:AppIcon.appiconset"
name = "AppIcon.appiconset">
<FileRef
location = "group:2x 1.png">
</FileRef>
<FileRef
location = "group:2x 3.png">
</FileRef>
<FileRef
location = "group:2x 2.png">
</FileRef>
<FileRef
location = "group:icons8-&#x65e5;&#x5e38;&#x8fd0;&#x52a8;-136.png">
</FileRef>
<FileRef
location = "group:icons8-&#x65e5;&#x5e38;&#x8fd0;&#x52a8;-1024.png">
</FileRef>
<FileRef
location = "group:2x 5.png">
</FileRef>
<FileRef
location = "group:2x 4.png">
</FileRef>
<FileRef
location = "group:2x.png">
</FileRef>
<FileRef
location = "group:icons8-&#x65e5;&#x5e38;&#x8fd0;&#x52a8;-76.png">
</FileRef>
<FileRef
location = "group:icons8-&#x65e5;&#x5e38;&#x8fd0;&#x52a8;-192.png">
</FileRef>
<FileRef
location = "group:3x.png">
</FileRef>
<FileRef
location = "group:3x 2.png">
</FileRef>
<FileRef
location = "group:3x 3.png">
</FileRef>
<FileRef
location = "group:3x 1.png">
</FileRef>
<FileRef
location = "group:Contents.json">
</FileRef>
<FileRef
location = "group:icons8-&#x65e5;&#x5e38;&#x8fd0;&#x52a8;-114.png">
</FileRef>
<FileRef
location = "group:icons8-&#x65e5;&#x5e38;&#x8fd0;&#x52a8;-128.png">
</FileRef>
</Group>
<Group
location = "group:AccentColor.colorset"
name = "AccentColor.colorset">
<FileRef
location = "group:Contents.json">
</FileRef>
</Group>
<FileRef
location = "group:Contents.json">
</FileRef>
</Group>
<Group
location = "group:utils"
name = "utils">
<FileRef
location = "group:Http.swift">
</FileRef>
</Group>
<FileRef
location = "group:copinismApp.swift">
</FileRef>
<Group
location = "group:Preview Content"
name = "Preview Content">
<Group
location = "group:Preview Assets.xcassets"
name = "Preview Assets.xcassets">
<FileRef
location = "group:Contents.json">
</FileRef>
</Group>
</Group>
<Group
location = "group:Styles"
name = "Styles">
<FileRef
location = "group:ImageStyle.swift">
</FileRef>
<FileRef
location = "group:TextStyle.swift">
</FileRef>
</Group>
<Group
location = "group:Components"
name = "Components">
<FileRef
location = "group:TabbarView.swift">
</FileRef>
</Group>
<Group
location = "group:api"
name = "api">
<FileRef
location = "group:Target.swift">
</FileRef>
<FileRef
location = "group:Req.swift">
</FileRef>
<FileRef
location = "group:Api.swift">
</FileRef>
</Group>
<Group
location = "group:Data"
name = "Data">
<FileRef
location = "group:CardData.swift">
</FileRef>
</Group>
<Group
location = "group:Views"
name = "Views">
<Group
location = "group:Home"
name = "Home">
<FileRef
location = "group:HomeView.swift">
</FileRef>
<Group
location = "group:Components"
name = "Components">
<FileRef
location = "group:FindView.swift">
</FileRef>
<FileRef
location = "group:CardView.swift">
</FileRef>
<FileRef
location = "group:VicinityView.swift">
</FileRef>
<FileRef
location = "group:FollowView.swift">
</FileRef>
<FileRef
location = "group:ArticleCardView.swift">
</FileRef>
<FileRef
location = "group:NavBarPopupView.swift">
</FileRef>
<FileRef
location = "group:NavBarView.swift">
</FileRef>
</Group>
</Group>
<Group
location = "group:Shopping"
name = "Shopping">
<FileRef
location = "group:ShoppingView.swift">
</FileRef>
</Group>
<Group
location = "group:TabbarLayout"
name = "TabbarLayout">
<FileRef
location = "group:TabbarLayoutView.swift">
</FileRef>
</Group>
<Group
location = "group:Record"
name = "Record">
<FileRef
location = "group:RecordView.swift">
</FileRef>
</Group>
<Group
location = "group:ArticleDetail"
name = "ArticleDetail">
<FileRef
location = "group:ArticleDetailView.swift">
</FileRef>
</Group>
<Group
location = "group:Message"
name = "Message">
<FileRef
location = "group:MessageDetail.swift">
</FileRef>
<FileRef
location = "group:MessageView.swift">
</FileRef>
</Group>
<Group
location = "group:Mine"
name = "Mine">
<FileRef
location = "group:MytargetStatus.swift">
</FileRef>
<FileRef
location = "group:TargetItamSwift.swift">
</FileRef>
<FileRef
location = "group:MineView.swift">
</FileRef>
<FileRef
location = "group:Login.swift">
</FileRef>
</Group>
</Group>
<FileRef
location = "group:ContentView.swift">
</FileRef>
</Group>
<Group
location = "group:Pods"
name = "Pods">
<FileRef
location = "group:Pods.xcodeproj">
</FileRef>
<Group
location = "group:Alamofire"
name = "Alamofire">
<FileRef
location = "group:LICENSE">
</FileRef>
<Group
location = "group:Source"
name = "Source">
<Group
location = "group:Core"
name = "Core">
<FileRef
location = "group:ParameterEncoder.swift">
</FileRef>
<FileRef
location = "group:Session.swift">
</FileRef>
<FileRef
location = "group:Protected.swift">
</FileRef>
<FileRef
location = "group:ParameterEncoding.swift">
</FileRef>
<FileRef
location = "group:AFError.swift">
</FileRef>
<FileRef
location = "group:DataStreamRequest.swift">
</FileRef>
<FileRef
location = "group:DownloadRequest.swift">
</FileRef>
<FileRef
location = "group:WebSocketRequest.swift">
</FileRef>
<FileRef
location = "group:HTTPMethod.swift">
</FileRef>
<FileRef
location = "group:URLConvertible+URLRequestConvertible.swift">
</FileRef>
<FileRef
location = "group:RequestTaskMap.swift">
</FileRef>
<FileRef
location = "group:Notifications.swift">
</FileRef>
<FileRef
location = "group:Response.swift">
</FileRef>
<FileRef
location = "group:Request.swift">
</FileRef>
<FileRef
location = "group:HTTPHeaders.swift">
</FileRef>
<FileRef
location = "group:DataRequest.swift">
</FileRef>
<FileRef
location = "group:UploadRequest.swift">
</FileRef>
<FileRef
location = "group:SessionDelegate.swift">
</FileRef>
</Group>
<Group
location = "group:Features"
name = "Features">
<FileRef
location = "group:MultipartFormData.swift">
</FileRef>
<FileRef
location = "group:RequestInterceptor.swift">
</FileRef>
<FileRef
location = "group:NetworkReachabilityManager.swift">
</FileRef>
<FileRef
location = "group:Concurrency.swift">
</FileRef>
<FileRef
location = "group:CachedResponseHandler.swift">
</FileRef>
<FileRef
location = "group:RetryPolicy.swift">
</FileRef>
<FileRef
location = "group:ServerTrustEvaluation.swift">
</FileRef>
<FileRef
location = "group:RedirectHandler.swift">
</FileRef>
<FileRef
location = "group:URLEncodedFormEncoder.swift">
</FileRef>
<FileRef
location = "group:AlamofireExtended.swift">
</FileRef>
<FileRef
location = "group:MultipartUpload.swift">
</FileRef>
<FileRef
location = "group:Validation.swift">
</FileRef>
<FileRef
location = "group:AuthenticationInterceptor.swift">
</FileRef>
<FileRef
location = "group:EventMonitor.swift">
</FileRef>
<FileRef
location = "group:Combine.swift">
</FileRef>
<FileRef
location = "group:RequestCompression.swift">
</FileRef>
<FileRef
location = "group:ResponseSerialization.swift">
</FileRef>
</Group>
<Group
location = "group:Extensions"
name = "Extensions">
<FileRef
location = "group:URLSessionConfiguration+Alamofire.swift">
</FileRef>
<FileRef
location = "group:OperationQueue+Alamofire.swift">
</FileRef>
<FileRef
location = "group:StringEncoding+Alamofire.swift">
</FileRef>
<FileRef
location = "group:DispatchQueue+Alamofire.swift">
</FileRef>
<FileRef
location = "group:URLRequest+Alamofire.swift">
</FileRef>
<FileRef
location = "group:Result+Alamofire.swift">
</FileRef>
</Group>
<FileRef
location = "group:Alamofire.swift">
</FileRef>
<FileRef
location = "group:PrivacyInfo.xcprivacy">
</FileRef>
</Group>
<FileRef
location = "group:README.md">
</FileRef>
</Group>
<Group
location = "group:Target Support Files"
name = "Target Support Files">
<Group
location = "group:Alamofire"
name = "Alamofire">
<FileRef
location = "group:Alamofire.release.xcconfig">
</FileRef>
<FileRef
location = "group:Alamofire-dummy.m">
</FileRef>
<FileRef
location = "group:Alamofire.debug.xcconfig">
</FileRef>
<FileRef
location = "group:Alamofire-umbrella.h">
</FileRef>
<FileRef
location = "group:Alamofire.modulemap">
</FileRef>
<FileRef
location = "group:ResourceBundle-Alamofire-Alamofire-Info.plist">
</FileRef>
<FileRef
location = "group:Alamofire-prefix.pch">
</FileRef>
</Group>
<Group
location = "group:Pods-copinism"
name = "Pods-copinism">
<FileRef
location = "group:Pods-copinism-resources-Debug-input-files.xcfilelist">
</FileRef>
<FileRef
location = "group:Pods-copinism.release.xcconfig">
</FileRef>
<FileRef
location = "group:Pods-copinism.debug.xcconfig">
</FileRef>
<FileRef
location = "group:Pods-copinism-resources-Release-output-files.xcfilelist">
</FileRef>
<FileRef
location = "group:Pods-copinism-dummy.m">
</FileRef>
<FileRef
location = "group:Pods-copinism-resources-Debug-output-files.xcfilelist">
</FileRef>
<FileRef
location = "group:Pods-copinism-acknowledgements.plist">
</FileRef>
<FileRef
location = "group:Pods-copinism-resources-Release-input-files.xcfilelist">
</FileRef>
<FileRef
location = "group:Pods-copinism-acknowledgements.markdown">
</FileRef>
<FileRef
location = "group:Pods-copinism-umbrella.h">
</FileRef>
<FileRef
location = "group:Pods-copinism.modulemap">
</FileRef>
<FileRef
location = "group:Pods-copinism-resources.sh">
</FileRef>
</Group>
<Group
location = "group:Kingfisher"
name = "Kingfisher">
<FileRef
location = "group:Kingfisher.modulemap">
</FileRef>
<FileRef
location = "group:Kingfisher-umbrella.h">
</FileRef>
<FileRef
location = "group:Kingfisher.release.xcconfig">
</FileRef>
<FileRef
location = "group:Kingfisher-prefix.pch">
</FileRef>
<FileRef
location = "group:Kingfisher-dummy.m">
</FileRef>
<FileRef
location = "group:Kingfisher.debug.xcconfig">
</FileRef>
</Group>
</Group>
<FileRef
location = "group:Manifest.lock">
</FileRef>
<Group
location = "group:Local Podspecs"
name = "Local Podspecs">
</Group>
<Group
location = "group:Headers"
name = "Headers">
<Group
location = "group:Public"
name = "Public">
<Group
location = "group:Alamofire"
name = "Alamofire">
<FileRef
location = "group:Alamofire-umbrella.h">
</FileRef>
<FileRef
location = "group:Alamofire.modulemap">
</FileRef>
</Group>
<Group
location = "group:Kingfisher"
name = "Kingfisher">
<FileRef
location = "group:Kingfisher.modulemap">
</FileRef>
<FileRef
location = "group:Kingfisher-umbrella.h">
</FileRef>
</Group>
</Group>
</Group>
<Group
location = "group:Kingfisher"
name = "Kingfisher">
<FileRef
location = "group:LICENSE">
</FileRef>
<FileRef
location = "group:README.md">
</FileRef>
<Group
location = "group:Sources"
name = "Sources">
<Group
location = "group:Cache"
name = "Cache">
<FileRef
location = "group:Storage.swift">
</FileRef>
<FileRef
location = "group:ImageCache.swift">
</FileRef>
<FileRef
location = "group:MemoryStorage.swift">
</FileRef>
<FileRef
location = "group:DiskStorage.swift">
</FileRef>
<FileRef
location = "group:CacheSerializer.swift">
</FileRef>
<FileRef
location = "group:FormatIndicatedCacheSerializer.swift">
</FileRef>
</Group>
<Group
location = "group:General"
name = "General">
<FileRef
location = "group:Kingfisher.swift">
</FileRef>
<FileRef
location = "group:KingfisherError.swift">
</FileRef>
<FileRef
location = "group:KF.swift">
</FileRef>
<Group
location = "group:ImageSource"
name = "ImageSource">
<FileRef
location = "group:Source.swift">
</FileRef>
<FileRef
location = "group:Resource.swift">
</FileRef>
<FileRef
location = "group:ImageDataProvider.swift">
</FileRef>
<FileRef
location = "group:AVAssetImageDataProvider.swift">
</FileRef>
</Group>
<FileRef
location = "group:KFOptionsSetter.swift">
</FileRef>
<FileRef
location = "group:KingfisherManager.swift">
</FileRef>
<FileRef
location = "group:KingfisherOptionsInfo.swift">
</FileRef>
</Group>
<Group
location = "group:Networking"
name = "Networking">
<FileRef
location = "group:ImageDownloaderDelegate.swift">
</FileRef>
<FileRef
location = "group:RequestModifier.swift">
</FileRef>
<FileRef
location = "group:ImageDataProcessor.swift">
</FileRef>
<FileRef
location = "group:SessionDataTask.swift">
</FileRef>
<FileRef
location = "group:RedirectHandler.swift">
</FileRef>
<FileRef
location = "group:ImageDownloader.swift">
</FileRef>
<FileRef
location = "group:AuthenticationChallengeResponsable.swift">
</FileRef>
<FileRef
location = "group:ImageModifier.swift">
</FileRef>
<FileRef
location = "group:RetryStrategy.swift">
</FileRef>
<FileRef
location = "group:SessionDelegate.swift">
</FileRef>
<FileRef
location = "group:ImagePrefetcher.swift">
</FileRef>
</Group>
<Group
location = "group:Image"
name = "Image">
<FileRef
location = "group:ImageProgressive.swift">
</FileRef>
<FileRef
location = "group:Image.swift">
</FileRef>
<FileRef
location = "group:ImageFormat.swift">
</FileRef>
<FileRef
location = "group:ImageDrawing.swift">
</FileRef>
<FileRef
location = "group:ImageTransition.swift">
</FileRef>
<FileRef
location = "group:ImageProcessor.swift">
</FileRef>
<FileRef
location = "group:GraphicsContext.swift">
</FileRef>
<FileRef
location = "group:Filter.swift">
</FileRef>
<FileRef
location = "group:Placeholder.swift">
</FileRef>
<FileRef
location = "group:GIFAnimatedImage.swift">
</FileRef>
</Group>
<Group
location = "group:Extensions"
name = "Extensions">
<FileRef
location = "group:TVMonogramView+Kingfisher.swift">
</FileRef>
<FileRef
location = "group:ImageView+Kingfisher.swift">
</FileRef>
<FileRef
location = "group:NSButton+Kingfisher.swift">
</FileRef>
<FileRef
location = "group:UIButton+Kingfisher.swift">
</FileRef>
<FileRef
location = "group:WKInterfaceImage+Kingfisher.swift">
</FileRef>
<FileRef
location = "group:NSTextAttachment+Kingfisher.swift">
</FileRef>
</Group>
<Group
location = "group:Views"
name = "Views">
<FileRef
location = "group:Indicator.swift">
</FileRef>
<FileRef
location = "group:AnimatedImageView.swift">
</FileRef>
</Group>
<Group
location = "group:SwiftUI"
name = "SwiftUI">
<FileRef
location = "group:ImageBinder.swift">
</FileRef>
<FileRef
location = "group:KFImage.swift">
</FileRef>
<FileRef
location = "group:KFImageOptions.swift">
</FileRef>
</Group>
<Group
location = "group:Utility"
name = "Utility">
<FileRef
location = "group:Runtime.swift">
</FileRef>
<FileRef
location = "group:CallbackQueue.swift">
</FileRef>
<FileRef
location = "group:Delegate.swift">
</FileRef>
<FileRef
location = "group:ExtensionHelpers.swift">
</FileRef>
<FileRef
location = "group:Result.swift">
</FileRef>
<FileRef
location = "group:SizeExtensions.swift">
</FileRef>
<FileRef
location = "group:Box.swift">
</FileRef>
<FileRef
location = "group:String+MD5.swift">
</FileRef>
</Group>
</Group>
</Group>
</Group>
<FileRef
location = "group:copinism.xcodeproj">
</FileRef>
<FileRef
location = "group:copinism.xcworkspace">
</FileRef>
<FileRef
location = "group:Podfile">
</FileRef>
<FileRef
location = "group:Podfile.lock">
</FileRef>
</Group>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>

123
copinism/AppDelegate.swift Normal file
View File

@ -0,0 +1,123 @@
//
// AppDelegate.swift
// copinism
//
// Created by on 2024/6/7.
//
import UIKit
import UserNotifications
import SwiftUI
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// UNUserNotificationCenter
UNUserNotificationCenter.current().delegate = self
//
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
if granted {
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
} else {
// 使
print("用户拒绝通知权限: \(String(describing: error))")
}
}
return true
}
//
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
let token = tokenParts.joined()
print("Device Token: \(token)")
sendDeviceTokenToServer(token: token)
}
//
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Failed to register: \(error)")
}
private func sendDeviceTokenToServer(token: String) {
let url = URL(string: "https://yourserver.com/api/deviceToken")!
let parameters: [String: Any] = [
"deviceToken": token,
"deviceType": "iOS"
]
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()
}
//
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
if #available(iOS 14.0, *) {
completionHandler([.banner, .sound, .badge])
} else {
completionHandler([.alert, .sound, .badge])
}
}
//
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
//
if response.actionIdentifier == UNNotificationDefaultActionIdentifier {
//
handleNotification(response.notification)
//
let message = Message(text: response.notification.request.content.body, sender: response.notification.request.content.title, timestamp: "" ,isSelf: true, avatar: "avatar")
sendNotification(for: message)
}
completionHandler()
}
private func handleNotification(_ notification: UNNotification) {
//
if let window = window {
// ContentView
let contentView = ContentView()
// SwiftUI UIViewController
let hostingController = UIHostingController(rootView: contentView)
// rootViewController
if let rootViewController = window.rootViewController {
rootViewController.present(hostingController, animated: true, completion: nil)
} else {
window.rootViewController = hostingController
window.makeKeyAndVisible()
}
}
}
//
private func sendNotification(for message: Message) {
let content = UNMutableNotificationContent()
content.title = message.sender
content.body = message.text
content.sound = UNNotificationSound.default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request)
}
}

View File

@ -4,6 +4,10 @@
<dict>
<key>CFBundleAllowMixedLocalizations</key>
<true/>
<key>CFBundleLocalizations</key>
<array>
<string>zh_CN</string>
</array>
<key>IDEPreferLogStreaming</key>
<true/>
<key>NSAppTransportSecurity</key>
@ -14,5 +18,11 @@
<key>NSLocalNotificationUsageDescription
NSLocalNotificationUsageDescription</key>
<string>需要通知权限以便发送新消息通知</string>
<key>NSUserNotificationUsageDescription</key>
<string>需要通知权限以便发送新消息通知</string>
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
</dict>
</plist>

View File

@ -1,8 +1,7 @@
import SwiftUI
import Alamofire
struct Message: Identifiable {
struct Message: Identifiable, Codable {
let id: UUID
let text: String
let sender: String
@ -10,8 +9,9 @@ struct Message: Identifiable {
let isSelf: Bool
let avatar: String
let num: Int
let image: Data?
init(id: UUID = UUID(), text: String, sender: String, timestamp: String, isSelf: Bool, avatar: String, num: Int = 0) {
init(id: UUID = UUID(), text: String = "", sender: String, timestamp: String, isSelf: Bool, avatar: String, num: Int = 0, image: UIImage? = nil) {
self.id = id
self.text = text
self.sender = sender
@ -19,18 +19,18 @@ struct Message: Identifiable {
self.isSelf = isSelf
self.avatar = avatar
self.num = num
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
@Environment(\.presentationMode) var presentationMode
var body: some View {
@ -57,7 +57,7 @@ struct MessageDetailView: View {
.font(.system(size: 18))
.foregroundColor(.black)
}
.opacity(0) //
.opacity(0)
}
.padding(.horizontal, 12)
.padding(.vertical, 8)
@ -69,11 +69,49 @@ struct MessageDetailView: View {
if !message.isSelf {
Image("avatar")
.CircleImage(size: 30)
if let imageData = message.image, let image = UIImage(data: imageData) {
ZStack {
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 200, height: 200)
.cornerRadius(10)
if uploadProgress < 1.0 {
ProgressView(value: uploadProgress)
.progressViewStyle(CircularProgressViewStyle())
.scaleEffect(2)
.frame(width: 50, height: 50)
.background(Color.white.opacity(0.7))
.cornerRadius(25)
}
}
} else {
TextBlackbble(message: message.text)
}
Spacer()
} else {
Spacer()
if let imageData = message.image, let image = UIImage(data: imageData) {
ZStack {
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 200, height: 200)
.cornerRadius(10)
if uploadProgress < 1.0 {
ProgressView(value: uploadProgress)
.progressViewStyle(CircularProgressViewStyle())
.scaleEffect(2)
.frame(width: 50, height: 50)
.background(Color.white.opacity(0.7))
.cornerRadius(25)
}
}
} else {
TextBubble(message: message.text)
}
Image("avatar")
.CircleImage(size: 30)
}
@ -88,7 +126,7 @@ struct MessageDetailView: View {
HStack {
Button(action: {
isDocumentPickerPresented = true
isImagePickerPresented = true
}) {
Image(systemName: "paperclip")
.font(.system(size: 18))
@ -120,13 +158,18 @@ struct MessageDetailView: View {
.padding(.horizontal, 12)
.padding(.bottom, 8)
.sheet(isPresented: $isImagePickerPresented) {
ImagePicker(selectedImage: $selectedImage)
ImagePicker(selectedImage: $selectedImage, onImagePicked: { image in
sendImageMessage(image: image)
})
}
.sheet(isPresented: $isDocumentPickerPresented) {
DocumentPicker(selectedFileURL: $selectedFileURL)
}
}
.navigationBarBackButtonHidden(true)
.onAppear {
loadMessages()
}
.onTapGesture {
hideKeyboard()
}
@ -135,9 +178,41 @@ struct MessageDetailView: View {
private func sendMessage() {
let newMessage = Message(text: replyText, sender: "", timestamp: currentTimestamp(), isSelf: true, avatar: "avatar")
messages.append(newMessage)
saveMessages()
replyText = ""
}
private func sendImageMessage(image: UIImage) {
let newMessage = Message(sender: "", timestamp: currentTimestamp(), isSelf: true, avatar: "avatar", image: image)
messages.append(newMessage)
//saveMessages()
uploadProgress = 0.0
if let imageData = image.jpegData(compressionQuality: 0.8) {
uploadFile(api: apiprefixV1+upload, file: imageData) { progress in
self.uploadProgress = progress
} completion: { (result: Result<HTTPBinResponse<Token>, AFError>) in
print(result)
}
}
}
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()
formatter.dateFormat = "h:mm a"
@ -177,23 +252,21 @@ struct TextBubble: View {
}
}
#Preview {
MessageDetailView()
}
// ImagePicker implementation
struct ImagePicker: UIViewControllerRepresentable {
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
let parent: ImagePicker
let onImagePicked: (UIImage) -> Void
init(parent: ImagePicker) {
init(parent: ImagePicker, onImagePicked: @escaping (UIImage) -> Void) {
self.parent = parent
self.onImagePicked = onImagePicked
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[.originalImage] as? UIImage {
parent.selectedImage = image
onImagePicked(image)
}
picker.dismiss(animated: true)
}
@ -204,9 +277,10 @@ struct ImagePicker: UIViewControllerRepresentable {
}
@Binding var selectedImage: UIImage?
let onImagePicked: (UIImage) -> Void
func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
Coordinator(parent: self, onImagePicked: onImagePicked)
}
func makeUIViewController(context: Context) -> UIImagePickerController {
@ -220,6 +294,8 @@ struct ImagePicker: UIViewControllerRepresentable {
// DocumentPicker implementation
struct DocumentPicker: UIViewControllerRepresentable {
@Binding var selectedFileURL: URL?
class Coordinator: NSObject, UIDocumentPickerDelegate {
let parent: DocumentPicker
@ -229,25 +305,28 @@ struct DocumentPicker: UIViewControllerRepresentable {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
parent.selectedFileURL = urls.first
controller.dismiss(animated: true)
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
controller.dismiss(animated: true)
parent.selectedFileURL = nil
}
}
@Binding var selectedFileURL: URL?
func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
}
func makeUIViewController(context: Context) -> UIDocumentPickerViewController {
let picker = UIDocumentPickerViewController(forOpeningContentTypes: [.item])
picker.delegate = context.coordinator
return picker
let controller = UIDocumentPickerViewController(forOpeningContentTypes: [.item])
controller.delegate = context.coordinator
return controller
}
func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: Context) {}
}
#Preview {
MessageDetailView()
}

View File

@ -299,7 +299,6 @@ struct MineView: View {
}
func getTarget(req: TargetMeReq){
print(req)
GetMeTargets(req: req){ (result: Result<HTTPBinResponse<TargetMeData>, AFError>) in
switch result {
case .success(let body):

View File

@ -11,13 +11,15 @@ import Foundation
let local = "http://127.0.0.1:9999"
let dev = "http://192.168.0.125:9999"
let dev = "http://192.168.1.5:9999"
let dev1 = "http://192.168.0.121:9999"
let pp = "http://43.136.169.205:9999"
let release = "https://api.flyaha.top"
let apiprefixV1 = release+"/api/v1"
let apiprefixV1 = pp+"/api/v1"
var loginApi = "/user/login"
@ -31,3 +33,5 @@ var targetApi = "/target/list"
var targeMetApi = "/target/me/list"
var upload = "/todo/message"

View File

@ -0,0 +1,8 @@
//
// MessageBack.swift
// copinism
//
// Created by on 2024/6/5.
//
import Foundation

View File

@ -9,6 +9,7 @@ import SwiftUI
@main
struct small_red_bookApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()

View File

@ -33,3 +33,23 @@ func makeAlamofireRequest<T: Decodable,Parame: Encodable>(api: String,
}
func uploadFile<T: Decodable>(api: String,file: Data,progressHandler: @escaping (Double) -> Void,completion: @escaping (Result<HTTPBinResponse<T>, AFError>) -> Void) {
var headers: HTTPHeaders = [
"Accept": "application/json",
"Content-Type":"application/octet-stream"
]
let loginStatus = checkLoginStatus()
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)
}
.validate().responseDecodable(of: HTTPBinResponse<T>.self) { response in
completion(response.result)
}
}