ios - 如何正确防止分析数据的数据丢失?
问题描述
我目前正在编写一个分析系统。目前,它在 RAM 中缓存事件。当应用程序关闭时,它通过 NSUserDefaults (iOS) 和 SharedPreferences (Android) 以 JSON 格式写入文件系统。当应用程序打开时读取此数据。
它还会每 N 秒发送一次或在事件数量达到 20 时发送。发送成功时,它会删除从 RAM 发送的所有事件。
这有一些明显的缺陷:当应用程序崩溃时,N 秒内的所有数据都会丢失。当无法访问服务器(例如,因为服务器已关闭)并且应用程序崩溃时,甚至会丢失更多数据。
我的问题是:当服务器关闭或无法访问时,如何提高数据的“安全性”并防止大量数据丢失?
这是我当前的代码(删除了不重要的部分)
import Foundation
class BackendTrackingHandler : TrackingHandler {
static let KEY_CACHE_EVENT = "TrackingCache"
private static let SEND_INTERVAL:TimeInterval = 10
var cachedEvents: [TrackingEvent] = []
var temporaryCachedEvents: [TrackingEvent] = []
var prefix: String
var endpoint: String
var timer : Timer?
//whether we currently wait for a response
var isSending: Bool = false
override init() {
//init
readCachedEventsFromDisk()
timer = Timer.scheduledTimer(timeInterval: BackendTrackingHandler.SEND_INTERVAL, target: self, selector: #selector(send), userInfo: nil, repeats: true)
}
override func trackEvent(_ event: TrackingEvent) {
cachedEvents.append(event)
if((cachedEvents.count) >= 20) {
send()
}
}
@objc func send() {
if((cachedEvents.count) < 1) {
return
}
if(isSending) {
return
}
isSending = true
let enc = JSONEncoder()
enc.outputFormatting = .prettyPrinted
let data = try! enc.encode(cachedEvents)
// Constructring Request here
let session = URLSession.shared
//while the request is on the way, we can trigger new events. Make a temporary copy
temporaryCachedEvents = cachedEvents
let taksID = UIApplication.shared.beginBackgroundTask()
let task = session.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) -> Void in
if(error != nil)
{
self.isSending = false
UIApplication.shared.endBackgroundTask(taksID)
}else {
let httpResponse = response as! HTTPURLResponse
if(httpResponse.statusCode >= 200 && httpResponse.statusCode <= 299) {
//success, Data was sent so we can create a new cached event
//remove all events we already sent
self.cachedEvents = self.cachedEvents.filter{!self.temporaryCachedEvents.contains($0)}
self.isSending = false
UIApplication.shared.endBackgroundTask(taksID)
}else {
self.isSending = false
UIApplication.shared.endBackgroundTask(taksID)
}
}
}
task.resume()
}
func readCachedEventsFromDisk() {
let dec = JSONDecoder()
guard let data = UserDefaults.standard.data(forKey: BackendTrackingHandler.KEY_CACHE_EVENT) else {
cachedEvents = []
return
}
do {
cachedEvents = try dec.decode([TrackingEvent].self, from: data)
} catch {
cachedEvents = []
}
}
func writeCachedEventsToDisk() {
let enc = JSONEncoder()
let data = try! enc.encode(cachedEvents)
UserDefaults.standard.set(data, forKey: BackendTrackingHandler.KEY_CACHE_EVENT)
}
override func onApplicationBecomeActive() {
}
override func onApplicationBecomeInactive() {
let taskID = UIApplication.shared.beginBackgroundTask()
writeCachedEventsToDisk()
UIApplication.shared.endBackgroundTask(taskID)
}
}
€dit:
TrackingEvent
是一个在多个TrackingHandler
s 之间共享的结构。还有一个额外的FirebaseTrackingHandler
,旨在与我们自己的分析系统并排运行。
解决方案
我认为最简单的方法是编写“Property Wrapper”,cachedEvents
这样它就可以直接访问 UserDefaults,看起来这个操作并不那么麻烦。
第二种方式 - 如果您非常关心性能,您可以每隔 N 秒/分钟左右将缓存保存到 UserDefaults。但是,它不会使您的系统防弹