@onekeyfe/react-native-cloud-kit-module 1.1.19 → 1.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CloudKitModule.podspec +1 -0
- package/ios/CloudKitModule.swift +37 -2
- package/package.json +1 -1
package/CloudKitModule.podspec
CHANGED
package/ios/CloudKitModule.swift
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import CloudKit
|
|
3
3
|
import NitroModules
|
|
4
|
+
import ReactNativeNativeLogger
|
|
4
5
|
|
|
5
6
|
class CloudKitModule: HybridCloudKitModuleSpec {
|
|
6
7
|
|
|
@@ -8,11 +9,31 @@ class CloudKitModule: HybridCloudKitModuleSpec {
|
|
|
8
9
|
private let container = CKContainer.default()
|
|
9
10
|
private lazy var database = container.privateCloudDatabase
|
|
10
11
|
|
|
12
|
+
// MARK: - Input Validation
|
|
13
|
+
|
|
14
|
+
private static let recordIDMaxLength = 255
|
|
15
|
+
private static let recordTypePattern = try! NSRegularExpression(pattern: "^[a-zA-Z][a-zA-Z0-9_]{0,254}$")
|
|
16
|
+
private static let queryResultsLimit = 200
|
|
17
|
+
|
|
18
|
+
private func validateRecordID(_ recordID: String) throws {
|
|
19
|
+
guard !recordID.isEmpty, recordID.count <= Self.recordIDMaxLength else {
|
|
20
|
+
throw NSError(domain: "CloudKitModule", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid record ID"])
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private func validateRecordType(_ recordType: String) throws {
|
|
25
|
+
let range = NSRange(recordType.startIndex..., in: recordType)
|
|
26
|
+
guard Self.recordTypePattern.firstMatch(in: recordType, range: range) != nil else {
|
|
27
|
+
throw NSError(domain: "CloudKitModule", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid record type"])
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
11
31
|
// MARK: - Check Availability
|
|
12
32
|
|
|
13
33
|
public func isAvailable() throws -> Promise<Bool> {
|
|
14
34
|
return Promise.async {
|
|
15
35
|
let status = try await self.container.accountStatus()
|
|
36
|
+
OneKeyLog.info("CloudKit", "Account status: \(status.rawValue), available: \(status == .available)")
|
|
16
37
|
return status == .available
|
|
17
38
|
}
|
|
18
39
|
}
|
|
@@ -28,6 +49,7 @@ class CloudKitModule: HybridCloudKitModuleSpec {
|
|
|
28
49
|
let userRecordID = try await self.container.userRecordID()
|
|
29
50
|
userId = userRecordID.recordName
|
|
30
51
|
} catch {
|
|
52
|
+
OneKeyLog.warn("CloudKit", "Failed to get user record ID: \(error.localizedDescription)")
|
|
31
53
|
userId = nil
|
|
32
54
|
}
|
|
33
55
|
}
|
|
@@ -50,7 +72,10 @@ class CloudKitModule: HybridCloudKitModuleSpec {
|
|
|
50
72
|
// MARK: - Save Record
|
|
51
73
|
|
|
52
74
|
public func saveRecord(params: SaveRecordParams) throws -> Promise<SaveRecordResult> {
|
|
75
|
+
try validateRecordID(params.recordID)
|
|
76
|
+
try validateRecordType(params.recordType)
|
|
53
77
|
return Promise.async {
|
|
78
|
+
OneKeyLog.debug("CloudKit", "Saving record: \(params.recordID), type: \(params.recordType)")
|
|
54
79
|
let ckRecordID = CKRecord.ID(recordName: params.recordID)
|
|
55
80
|
let recordToSave: CKRecord
|
|
56
81
|
do {
|
|
@@ -65,6 +90,7 @@ class CloudKitModule: HybridCloudKitModuleSpec {
|
|
|
65
90
|
recordToSave[CloudKitConstants.recordDataField] = params.data as CKRecordValue
|
|
66
91
|
recordToSave[CloudKitConstants.recordMetaField] = params.meta as CKRecordValue
|
|
67
92
|
let savedRecord = try await self.database.save(recordToSave)
|
|
93
|
+
OneKeyLog.debug("CloudKit", "Record saved: \(savedRecord.recordID.recordName)")
|
|
68
94
|
let createdAt = Int64((savedRecord.creationDate?.timeIntervalSince1970 ?? 0) * 1000)
|
|
69
95
|
let result = SaveRecordResult(
|
|
70
96
|
recordID: savedRecord.recordID.recordName,
|
|
@@ -77,11 +103,13 @@ class CloudKitModule: HybridCloudKitModuleSpec {
|
|
|
77
103
|
// MARK: - Fetch Record
|
|
78
104
|
|
|
79
105
|
public func fetchRecord(params: FetchRecordParams) throws -> Promise<Variant_NullType_RecordResult> {
|
|
106
|
+
try validateRecordID(params.recordID)
|
|
80
107
|
return Promise.async {
|
|
81
108
|
let ckRecordID = CKRecord.ID(recordName: params.recordID)
|
|
82
109
|
|
|
83
110
|
do {
|
|
84
111
|
let record = try await self.database.record(for: ckRecordID)
|
|
112
|
+
OneKeyLog.debug("CloudKit", "Record fetched: \(params.recordID)")
|
|
85
113
|
|
|
86
114
|
let data = record[CloudKitConstants.recordDataField] as? String ?? ""
|
|
87
115
|
let meta = record[CloudKitConstants.recordMetaField] as? String ?? ""
|
|
@@ -97,6 +125,7 @@ class CloudKitModule: HybridCloudKitModuleSpec {
|
|
|
97
125
|
modifiedAt: Double(modifiedAt)
|
|
98
126
|
))
|
|
99
127
|
} catch let error as CKError where error.code == .unknownItem {
|
|
128
|
+
OneKeyLog.debug("CloudKit", "Record not found: \(params.recordID)")
|
|
100
129
|
return Variant_NullType_RecordResult.first(NullType.null)
|
|
101
130
|
}
|
|
102
131
|
}
|
|
@@ -105,14 +134,16 @@ class CloudKitModule: HybridCloudKitModuleSpec {
|
|
|
105
134
|
// MARK: - Delete Record
|
|
106
135
|
|
|
107
136
|
public func deleteRecord(params: DeleteRecordParams) throws -> Promise<Void> {
|
|
137
|
+
try validateRecordID(params.recordID)
|
|
108
138
|
return Promise.async {
|
|
109
139
|
let ckRecordID = CKRecord.ID(recordName: params.recordID)
|
|
110
140
|
|
|
111
141
|
do {
|
|
112
142
|
_ = try await self.database.deleteRecord(withID: ckRecordID)
|
|
143
|
+
OneKeyLog.debug("CloudKit", "Record deleted: \(params.recordID)")
|
|
113
144
|
return Void()
|
|
114
145
|
} catch let error as CKError where error.code == .unknownItem {
|
|
115
|
-
|
|
146
|
+
OneKeyLog.debug("CloudKit", "Record not found for delete (OK): \(params.recordID)")
|
|
116
147
|
return Void()
|
|
117
148
|
}
|
|
118
149
|
}
|
|
@@ -121,6 +152,7 @@ class CloudKitModule: HybridCloudKitModuleSpec {
|
|
|
121
152
|
// MARK: - Record Exists
|
|
122
153
|
|
|
123
154
|
public func recordExists(params: RecordExistsParams) throws -> Promise<Bool> {
|
|
155
|
+
try validateRecordID(params.recordID)
|
|
124
156
|
return Promise.async {
|
|
125
157
|
let ckRecordID = CKRecord.ID(recordName: params.recordID)
|
|
126
158
|
|
|
@@ -136,6 +168,7 @@ class CloudKitModule: HybridCloudKitModuleSpec {
|
|
|
136
168
|
// MARK: - Query Records
|
|
137
169
|
|
|
138
170
|
public func queryRecords(params: QueryRecordsParams) throws -> Promise<QueryRecordsResult> {
|
|
171
|
+
try validateRecordType(params.recordType)
|
|
139
172
|
return Promise.async {
|
|
140
173
|
let predicate = NSPredicate(value: true)
|
|
141
174
|
let query = CKQuery(recordType: params.recordType, predicate: predicate)
|
|
@@ -145,7 +178,7 @@ class CloudKitModule: HybridCloudKitModuleSpec {
|
|
|
145
178
|
var results: [RecordResult] = []
|
|
146
179
|
let operation = CKQueryOperation(query: query)
|
|
147
180
|
operation.desiredKeys = [CloudKitConstants.recordMetaField]
|
|
148
|
-
|
|
181
|
+
operation.resultsLimit = Self.queryResultsLimit
|
|
149
182
|
|
|
150
183
|
operation.recordMatchedBlock = { _, result in
|
|
151
184
|
switch result {
|
|
@@ -172,9 +205,11 @@ class CloudKitModule: HybridCloudKitModuleSpec {
|
|
|
172
205
|
case .success:
|
|
173
206
|
// Sort by modification time descending to return latest first
|
|
174
207
|
let sorted = results.sorted { $0.modifiedAt > $1.modifiedAt }
|
|
208
|
+
OneKeyLog.info("CloudKit", "Query returned \(sorted.count) records for type: \(params.recordType)")
|
|
175
209
|
let queryResult = QueryRecordsResult(records: sorted)
|
|
176
210
|
continuation.resume(returning: queryResult)
|
|
177
211
|
case .failure(let error):
|
|
212
|
+
OneKeyLog.error("CloudKit", "Query failed for type \(params.recordType): \(error.localizedDescription)")
|
|
178
213
|
continuation.resume(throwing: error)
|
|
179
214
|
}
|
|
180
215
|
}
|