Skip to content

Commit

Permalink
新增: 消息本地缓存 / 国际化(英文/中文) / 其它细节优化
Browse files Browse the repository at this point in the history
  • Loading branch information
Hext123 committed Jan 15, 2022
1 parent 2e7bfe9 commit c5bd3b2
Show file tree
Hide file tree
Showing 32 changed files with 673 additions and 170 deletions.
2 changes: 1 addition & 1 deletion ios/PushDeer-iOS/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def commonPods
pod 'SDWebImageSwiftUI', '~> 2.0.2'
pod 'KRProgressHUD', '~> 3.4.7'

# pod 'WoodPeckeriOS', :configurations => ['Debug']
# pod 'WoodPeckeriOS', :configurations => ['Debug']
end

target 'PushDeer' do
Expand Down
2 changes: 1 addition & 1 deletion ios/PushDeer-iOS/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@ SPEC CHECKSUMS:
SDWebImage: 4dc3e42d9ec0c1028b960a33ac6b637bb432207b
SDWebImageSwiftUI: 8a3923c95108312b03a599ec1498754af55a6819

PODFILE CHECKSUM: e462e86a9cce18b92c573f662ef405e7091cd912
PODFILE CHECKSUM: 06aae1de50f9c1a188e69787835ec8718dd7d543

COCOAPODS: 1.11.2
95 changes: 91 additions & 4 deletions ios/PushDeer-iOS/PushDeer.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1320"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "52B8CF63277E0B44004CB680"
BuildableName = "PushDeerClip.app"
BlueprintName = "PushDeerClip"
ReferencedContainer = "container:PushDeer.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "52B8CF63277E0B44004CB680"
BuildableName = "PushDeerClip.app"
BlueprintName = "PushDeerClip"
ReferencedContainer = "container:PushDeer.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "_XCAppClipURL"
value = "https://example.com"
isEnabled = "NO">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "52B8CF63277E0B44004CB680"
BuildableName = "PushDeerClip.app"
BlueprintName = "PushDeerClip"
ReferencedContainer = "container:PushDeer.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
5 changes: 5 additions & 0 deletions ios/PushDeer-iOS/PushDeer/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDele

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -> UNNotificationPresentationOptions {
print("willPresent:", notification.request.content.userInfo)
Task {
// 收到推送后, 刷新本地消息列表
let messageItems = try await HttpRequest.getMessages().messages
try MessageModel.saveAndUpdate(messageItems: messageItems)
}
return [.sound, .list, .banner]
}

Expand Down
6 changes: 6 additions & 0 deletions ios/PushDeer-iOS/PushDeer/Common/HToast.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ struct HToast {
static func showSuccess(_ msg: String?) {
KRProgressHUD.showSuccess(withMessage: msg)
}
static func showInfo(_ msg: String?) {
KRProgressHUD.showInfo(withMessage: msg)
}
static func showWarning(_ msg: String?) {
KRProgressHUD.showWarning(withMessage: msg)
}
static func showError(_ msg: String?) {
KRProgressHUD.showError(withMessage: msg)
}
Expand Down
87 changes: 87 additions & 0 deletions ios/PushDeer-iOS/PushDeer/Model/MessageModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//
// MessageModel.swift
// PushDeer
//
// Created by HEXT on 2022/1/15.
//

import Foundation
import CoreData

extension MessageModel {
convenience init(id: Int64, uid: String, text: String, desp: String, type: String, pushkey_name: String, created_at: String, context: NSManagedObjectContext = PersistenceController.shared.container.viewContext) {
self.init(context: context)
self.id = id
self.uid = uid
self.text = text
self.desp = desp
self.type = type
self.pushkey_name = pushkey_name
self.created_at = created_at
}
convenience init(messageItem: MessageItem, context: NSManagedObjectContext = PersistenceController.shared.container.viewContext) {
self.init(
id: Int64(messageItem.id),
uid: messageItem.uid,
text: messageItem.text,
desp: messageItem.desp,
type: messageItem.type,
pushkey_name: messageItem.pushkey_name,
created_at: messageItem.created_at,
context: context)
}

var createdDateStr: String {
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ"
let createdDate = dateFormatter.date(from: self.created_at ?? "") ?? Date()
let timeInterval = -createdDate.timeIntervalSinceNow
let minute = Int(floor(timeInterval / 60))
if minute == 0 {
return "刚刚"
} else if minute <= 30 {
return "\(minute)分钟前"
} else if Calendar.current.isDateInToday(createdDate) {
dateFormatter.dateFormat = "HH:mm:ss"
} else {
dateFormatter.dateFormat = "yyyy/MM/dd HH:mm:ss"
}
return dateFormatter.string(from: createdDate)
}

static let _viewContext = PersistenceController.shared.container.viewContext
static let _fetchRequest = MessageModel.fetchRequest()

/// 持久化保存和更新
static func saveAndUpdate(messageItems: [MessageItem]) throws -> Void {
try messageItems.forEach(saveAndUpdate)
}

/// 持久化保存和更新
static func saveAndUpdate(messageItem: MessageItem) throws -> Void {
_fetchRequest.predicate = NSPredicate(format: "id = \(messageItem.id)")
let models = try _viewContext.fetch(_fetchRequest)
if models.isEmpty {
// 如果本地不存在, 就构建一个新的放进 context
_ = MessageModel(messageItem: messageItem, context: _viewContext)
} else {
// 如果存在, 就更新第一个, 删除其它重复的
models.enumerated().forEach { element in
let messageModel = element.element
let index = element.offset
if index == 0 {
messageModel.id = Int64(messageItem.id);
messageModel.uid = messageItem.uid;
messageModel.text = messageItem.text;
messageModel.desp = messageItem.desp;
messageModel.type = messageItem.type;
messageModel.pushkey_name = messageItem.pushkey_name;
messageModel.created_at = messageItem.created_at;
} else {
_viewContext.delete(messageModel)
}
}
}
// 保存 context 中的所有改动
try _viewContext.save()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21C52" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="MessageModel" representedClassName="MessageModel" syncable="YES" codeGenerationType="class">
<attribute name="created_at" optional="YES" attributeType="String"/>
<attribute name="desp" optional="YES" attributeType="String"/>
<attribute name="id" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="pushkey_name" optional="YES" attributeType="String"/>
<attribute name="text" optional="YES" attributeType="String"/>
<attribute name="type" optional="YES" attributeType="String"/>
<attribute name="uid" optional="YES" attributeType="String"/>
</entity>
<elements>
<element name="MessageModel" positionX="-45" positionY="1" width="128" height="148"/>
</elements>
</model>
18 changes: 3 additions & 15 deletions ios/PushDeer-iOS/PushDeer/Model/Result.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ struct UserInfoContent: Codable{
struct DeviceItem: Codable, Identifiable{
let id: Int
let uid: String
let name: String
var name: String
let type: String
let device_id: String
let is_clip: Int
Expand All @@ -50,7 +50,7 @@ struct KeyContent: Codable{

struct KeyItem: Codable, Identifiable{
let id: Int
let name: String
var name: String
let uid: String
let key: String
let created_at: String
Expand All @@ -66,6 +66,7 @@ struct MessageItem: Codable, Identifiable{
let text: String
let desp: String
let type: String
let pushkey_name: String
let created_at: String
}

Expand All @@ -87,16 +88,3 @@ extension KeyItem {
return dateFormatter.string(from: createdDate ?? Date())
}
}

extension MessageItem {
var createdDateStr: String {
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ"
let createdDate = dateFormatter.date(from: self.created_at) ?? Date()
if Calendar.current.isDateInToday(createdDate) {
dateFormatter.dateFormat = "HH:mm:ss"
} else {
dateFormatter.dateFormat = "yyyy/MM/dd HH:mm:ss"
}
return dateFormatter.string(from: createdDate)
}
}
2 changes: 2 additions & 0 deletions ios/PushDeer-iOS/PushDeer/PushDeer.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.personal-information.photos-library</key>
<true/>
</dict>
</plist>
6 changes: 5 additions & 1 deletion ios/PushDeer-iOS/PushDeer/PushDeerApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ import SwiftUI
@main
struct PushDeerApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
let store = AppState.shared
let persistenceController = PersistenceController.shared

var body: some Scene {
WindowGroup {
ContentView().environmentObject(AppState.shared)
ContentView()
.environmentObject(store)
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}
4 changes: 2 additions & 2 deletions ios/PushDeer-iOS/PushDeer/Service/AppState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class AppState: ObservableObject {
/// key 列表
@Published var keys: [KeyItem] = []
/// 消息列表
@Published var messages: [MessageItem] = []
// @Published var messages: [MessageItem] = []
/// 选中的 tab 下标
@Published var tabSelectedIndex: Int {
didSet {
Expand Down Expand Up @@ -85,7 +85,7 @@ class AppState: ObservableObject {
print(error)
}
// 登录失败
throw NSError(domain: "登录失败", code: -1, userInfo: nil)
throw NSError(domain: NSLocalizedString("登录失败", comment: "AppleId登录失败时提示"), code: -1, userInfo: nil)
}

}
8 changes: 5 additions & 3 deletions ios/PushDeer-iOS/PushDeer/Service/HttpRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ struct HttpRequest {
continuation.resume(returning: content)
} else if result.code == 80403 {
AppState.shared.token = ""
continuation.resume(throwing: NSError(domain: result.error ?? "接口报错", code: result.code, userInfo: nil))
continuation.resume(throwing: NSError(domain: result.error ?? NSLocalizedString("登录过期", comment: "token失效时提示"), code: result.code, userInfo: nil))
} else {
continuation.resume(throwing: NSError(domain: result.error ?? "接口报错", code: result.code, userInfo: nil))
continuation.resume(throwing: NSError(domain: result.error ?? NSLocalizedString("接口报错", comment: "接口报错时提示"), code: result.code, userInfo: nil))
}
} catch {
print(error)
Expand Down Expand Up @@ -66,7 +66,9 @@ struct HttpRequest {
static func rmDevice(id: Int) async throws -> ActionContent {
return try await request(.rmDevice(token: AppState.shared.token, id: id), resultType: ActionContent.self)
}

static func renameDevice(id: Int, name: String) async throws -> ActionContent {
return try await request(.renameDevice(token: AppState.shared.token, id: id, name: name), resultType: ActionContent.self)
}
static func getDevices() async throws -> DeviceContent {
return try await request(.getDevices(token: AppState.shared.token), resultType: DeviceContent.self)
}
Expand Down
39 changes: 39 additions & 0 deletions ios/PushDeer-iOS/PushDeer/Service/Persistence.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// Persistence.swift
// PushDeer
//
// Created by HEXT on 2022/1/14.
//

import CoreData

struct PersistenceController {

static let shared = PersistenceController()

let container: NSPersistentContainer

init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "PushDeerData")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
}
}
Loading

0 comments on commit c5bd3b2

Please sign in to comment.