// // YXTrackingSQLiteManager.swift // YXTrackingSDK // // Created by 1234 on 2026/2/6. // import UIKit import SQLite3 class YXTrackingSQLiteManager: NSObject { // 添加跟踪数据到数据库 class func addTrackingData(data: EventRequestResult) { // 创建数据库连接 let dbPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! let path = URL(fileURLWithPath: dbPath).appendingPathComponent("tracking.sqlite").path var db: OpaquePointer? = nil if sqlite3_open(path, &db) != SQLITE_OK { YXTracking.shared.error(message: "SDK sqlite open failed!"); return } YXTracking.shared.success(message: "SDK sqlite open success!"); // 创建表 let createTableSQL = """ CREATE TABLE IF NOT EXISTS tracking_data ( id INTEGER PRIMARY KEY AUTOINCREMENT, system_code TEXT, event_type TEXT, user_info BLOB, client_type INT, client_timestamp INT64, timestamp TEXT, device_info BLOB, event_params BLOB, custom_tags BLOB ) """ if sqlite3_exec(db, createTableSQL, nil, nil, nil) != SQLITE_OK { YXTracking.shared.error(message: "SDK sqlite create table failed!"); return } YXTracking.shared.success(message: "SDK sqlite create table success!"); // 插入数据 let encoder = JSONEncoder() let insertSQL = "INSERT INTO tracking_data (system_code, event_type, user_info, client_type, client_timestamp, timestamp, device_info, event_params, custom_tags) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" var statement: OpaquePointer? = nil if sqlite3_prepare_v2(db, insertSQL, -1, &statement, nil) == SQLITE_OK { let system_code = data.system_code ?? "" let event_type = data.eventType ?? "" let user_info = (data.userInfo != nil) ? try! encoder.encode(data.userInfo) : nil let client_type = data.clientType ?? 0 let client_timestamp = data.clientTimestamp ?? 0 let timestamp = data.timestamp ?? "" let device_info = try! encoder.encode(data.deviceInfo) let event_params = try! encoder.encode(data.eventParams) let custom_tags = try! JSONSerialization.data(withJSONObject: data.customTags ?? [], options: []) if sqlite3_bind_text(statement, 1, system_code, -1, nil) != SQLITE_OK || sqlite3_bind_text(statement, 2, event_type, -1, nil) != SQLITE_OK || ((data.userInfo != nil) ? (sqlite3_bind_blob(statement, 3, user_info! as CFTypeRef as? UnsafeRawPointer, Int32(user_info!.count), nil) != SQLITE_OK) : (sqlite3_bind_null(statement, 3) != SQLITE_OK)) || sqlite3_bind_int(statement, 4, client_type) != SQLITE_OK || sqlite3_bind_int64(statement, 5, client_timestamp) != SQLITE_OK || sqlite3_bind_text(statement, 6, timestamp, -1, nil) != SQLITE_OK || sqlite3_bind_blob(statement, 7, device_info as CFTypeRef as? UnsafeRawPointer, Int32(device_info.count), nil) != SQLITE_OK || sqlite3_bind_blob(statement, 8, event_params as CFTypeRef as? UnsafeRawPointer, Int32(event_params.count), nil) != SQLITE_OK || sqlite3_bind_blob(statement, 9, custom_tags as CFTypeRef as? UnsafeRawPointer, Int32(custom_tags.count), nil) != SQLITE_OK { YXTracking.shared.error(message: "SDK sqlite bind parameter failed!"); return } else if sqlite3_step(statement) != SQLITE_DONE { YXTracking.shared.error(message: "SDK sqlite insert data failed!"); return } YXTracking.shared.success(message: "SDK sqlite bind parameter success!"); } else { YXTracking.shared.error(message: "SDK sqlite preparing to insert data failed!"); return } YXTracking.shared.success(message: "SDK sqlite insert data success!"); // 关闭数据库连接 do { sqlite3_close(db) } } // 读取跟踪数据 class func readTrackingData() -> [EventRequestResult] { var trackingData: [EventRequestResult] = [] // 创建数据库连接 let dbPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! let path = URL(fileURLWithPath: dbPath).appendingPathComponent("tracking.sqlite").path var db: OpaquePointer? = nil if sqlite3_open(path, &db) != SQLITE_OK { YXTracking.shared.error(message: "SDK sqlite open failed!"); return trackingData } // 查询数据 let decoder = JSONDecoder() let querySQL = "SELECT system_code, event_type, user_info, client_type, client_timestamp, timestamp, device_info, event_params, custom_tags FROM tracking_data" var statement: OpaquePointer? = nil if sqlite3_prepare_v2(db, querySQL, -1, &statement, nil) == SQLITE_OK { while (sqlite3_step(statement) == SQLITE_ROW) { // 获取数据并转换为EventRequestResult let system_code = String(cString: sqlite3_column_text(statement, 1)) let event_type = String(cString: sqlite3_column_text(statement, 2)) var user_info = UserInfo(userId: nil, userName: nil, account: nil) if sqlite3_column_type(statement, 3) != SQLITE_NULL { if let user_info_blob = sqlite3_column_blob(statement, 3) { let user_info_blob_length = sqlite3_column_bytes(statement, 3) let data = Data(bytes: user_info_blob, count: Int(user_info_blob_length)) user_info = try! decoder.decode(UserInfo.self, from: data) } } let client_type = sqlite3_column_int(statement, 4) let client_timestamp = sqlite3_column_int64(statement, 5) let timestamp = String(cString: sqlite3_column_text(statement, 6)) var device_info = DeviceInfo(os: nil, model: nil, screenResolution: nil) if sqlite3_column_type(statement, 7) != SQLITE_NULL { if let device_info_blob = sqlite3_column_blob(statement, 7) { let device_info_blob_length = sqlite3_column_bytes(statement, 7) let data = Data(bytes: device_info_blob, count: Int(device_info_blob_length)) device_info = try! decoder.decode(DeviceInfo.self, from: data) } } var event_params = EventParams(page: nil, buttonId: nil, url: nil) if sqlite3_column_type(statement, 8) != SQLITE_NULL { if let event_params_blob = sqlite3_column_blob(statement, 8) { let event_params_blob_length = sqlite3_column_bytes(statement, 8) let data = Data(bytes: event_params_blob, count: Int(event_params_blob_length)) event_params = try! decoder.decode(EventParams.self, from: data) } } var custom_tags = Array() if sqlite3_column_type(statement, 9) != SQLITE_NULL { if let custom_tags_blob = sqlite3_column_blob(statement, 9) { let custom_tags_blob_length = sqlite3_column_bytes(statement, 9) let data = Data(bytes: custom_tags_blob, count: Int(custom_tags_blob_length)) custom_tags = try! decoder.decode([String].self, from: data) } } let event = EventRequestResult(system_code: system_code, eventType: event_type, userInfo: user_info, clientType: client_type, clientTimestamp: client_timestamp, timestamp: timestamp, deviceInfo: device_info, eventParams: event_params, customTags: custom_tags) trackingData.append(event) } sqlite3_finalize(statement) } else { YXTracking.shared.error(message: "SDK sqlite read data failed!"); } // 关闭数据库连接 do { sqlite3_close(db) } return trackingData } // 删除跟踪数据 class func deleteTrackingData() { // 创建数据库连接 let dbPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! let path = URL(fileURLWithPath: dbPath).appendingPathComponent("tracking.sqlite").path var db: OpaquePointer? = nil if sqlite3_open(path, &db) != SQLITE_OK { YXTracking.shared.error(message: "SDK sqlite open failed!"); return } // 删除数据 let deleteSQL = "DELETE FROM tracking_data" if sqlite3_exec(db, deleteSQL, nil, nil, nil) != SQLITE_OK { YXTracking.shared.error(message: "SDK sqlite delete data failed!"); } // 关闭数据库连接 do { sqlite3_close(db) } } // 获取数据库缓存条数 class func getTrackingDataCount() -> Int { // 创建数据库连接 let dbPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! let path = URL(fileURLWithPath: dbPath).appendingPathComponent("tracking.sqlite").path var db: OpaquePointer? = nil if sqlite3_open(path, &db) != SQLITE_OK { YXTracking.shared.error(message: "SDK sqlite open failed!"); return -1 } // 查询数据 var dataCount = -1 let querySQL = "SELECT COUNT(*) FROM tracking_data" var statement: OpaquePointer? = nil if sqlite3_prepare_v2(db, querySQL, -1, &statement, nil) == SQLITE_OK { sqlite3_step(statement) dataCount = Int(sqlite3_column_int64(statement, 0)) sqlite3_finalize(statement) } else { YXTracking.shared.error(message: "SDK sqlite read data failed!"); } // 关闭数据库连接 do { sqlite3_close(db) } return dataCount } }