From c4dbf83e7dbd1db6c814da767f3ebccaa7df7940 Mon Sep 17 00:00:00 2001 From: zhangmin <410260571@qq.com> Date: Tue, 10 Feb 2026 17:19:20 +0800 Subject: [PATCH] feat: update --- YXTrackingSDK/YXTracking.swift | 415 ++++++++++++++++++ .../YXTrackingSDK.xcodeproj/project.pbxproj | 121 ++++- .../contents.xcworkspacedata | 0 .../UserInterfaceState.xcuserstate | Bin 0 -> 141978 bytes .../xcschemes/YXTrackingSDK.xcscheme | 67 +++ .../xcschemes/xcschememanagement.plist | 13 + YXTrackingSDK/YXTrackingSDK/YXTracking.swift | 12 - .../YXTrackingSDK.docc/YXTrackingSDK.md | 0 .../YXTrackingSDK/YXTrackingSDK.swift | 85 ++++ .../UserInterfaceState.xcuserstate | Bin 6770 -> 0 bytes .../YXTrackingSDK/YXTrackingSDK.swift | 9 - YXTrackingSDK/YXTrackingSQLiteManager.swift | 207 +++++++++ 12 files changed, 906 insertions(+), 23 deletions(-) create mode 100644 YXTrackingSDK/YXTracking.swift rename YXTrackingSDK/{YXTrackingSDK => }/YXTrackingSDK.xcodeproj/project.pbxproj (65%) rename YXTrackingSDK/{YXTrackingSDK => }/YXTrackingSDK.xcodeproj/project.xcworkspace/contents.xcworkspacedata (100%) create mode 100644 YXTrackingSDK/YXTrackingSDK.xcodeproj/project.xcworkspace/xcuserdata/a1234.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 YXTrackingSDK/YXTrackingSDK.xcodeproj/xcshareddata/xcschemes/YXTrackingSDK.xcscheme rename YXTrackingSDK/{YXTrackingSDK => }/YXTrackingSDK.xcodeproj/xcuserdata/a1234.xcuserdatad/xcschemes/xcschememanagement.plist (56%) delete mode 100644 YXTrackingSDK/YXTrackingSDK/YXTracking.swift rename YXTrackingSDK/YXTrackingSDK/{YXTrackingSDK => }/YXTrackingSDK.docc/YXTrackingSDK.md (100%) create mode 100644 YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.swift delete mode 100644 YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.xcodeproj/project.xcworkspace/xcuserdata/a1234.xcuserdatad/UserInterfaceState.xcuserstate delete mode 100644 YXTrackingSDK/YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.swift diff --git a/YXTrackingSDK/YXTracking.swift b/YXTrackingSDK/YXTracking.swift new file mode 100644 index 0000000..e8ebb17 --- /dev/null +++ b/YXTrackingSDK/YXTracking.swift @@ -0,0 +1,415 @@ +// +// YXTracking.swift +// YXTrackingSDK +// +// Created by 1234 on 2026/1/27. +// + +import UIKit + +/// 远轩埋点SDK实现类 四种上报机制 +/// 1、初始化时触发:用户在初始化SDK时,会将缓存的事件数据上报。 +/// 2、定时/定量触发:数据条数达到阈值(默认20条)或定时器到期(默认600秒)时会将缓存的事件数据上报。 +/// 3、事件触发:用户在添加事件时可选择立即上报。 +/// 4、应用进入后台和即将被系统终止时触发:用户在应用内停留一段时间后,手动切换到其他应用或桌面时,或即将被系统终止时,会将缓存的事件数据上报。 +public class YXTracking: NSObject { + + static let shared = YXTracking() + + private var host: String = "" // 主机地址 + + private var cacheCount: Int = 20 // 缓存数量 + + private var pushInterval: Int = 600 // 上报周期(单位:秒) + + private var enableLog: Bool = false // 是否开启调试日志 + + private var eventRequestResult: EventRequestResult = EventRequestResult() // 事件请求参数 + + private var systemAllDimInfoResponseResult: SystemAllDimInfoResponseResult = SystemAllDimInfoResponseResult() // 系统所有维度信息 + + /// 初始化SDK + /// - Parameters: + /// - host: 主机地址 + /// - systemCode: 系统编码 + /// - cacheCount: 缓存数量(可选) + /// - pushInterval: 上报周期(可选) + /// - enableLog: 是否开启调试日志(可选) + /// - Returns: 无返回值 + public class func initWithSystemCode(host: String, systemCode: String, cacheCount: Int?, pushInterval: Int?, enableLog: Bool?) { + // 校验主机地址是否为空 + if host.isEmpty { + YXTracking.shared.error(message: "SDK config failed, host is empty!"); + return; + } + // 校验系统编码是否为空 + if systemCode.isEmpty { + YXTracking.shared.error(message: "SDK config failed, system code is empty!"); + return; + } + + // 设置主机地址和缓存数量、上报周期、是否开启调试日志 + YXTracking.shared.host = host + if cacheCount != nil { + YXTracking.shared.cacheCount = cacheCount! + } + if pushInterval != nil { + YXTracking.shared.cacheCount = pushInterval! + } + if enableLog != nil { + YXTracking.shared.enableLog = enableLog! + } + + // 打印日志信息 + YXTracking.shared.log(message: "SDK config success!"); + + // 添加应用进入后台和即将被系统终止的通知监听 应用进入后台时或即将被系统终止时触发缓存事件推送 + YXTracking.shared.addEnterBackgroundNotificationObserver() + YXTracking.shared.addWillTerminateNotificationObserver() + + // 重置事件请求参数中的系统编码和客户端类型 + YXTracking.shared.eventRequestResult.system_code = systemCode + YXTracking.shared.eventRequestResult.clientType = 2 + + // 获取设备信息 + YXTracking.shared.getDeviceInfo() + + // 获取系统所有维度信息 + YXTracking.shared.getSystemAllDimInfo() + } + + /// 添加事件 + /// - Parameters: + /// - eventType: 事件类型 + /// - page: 事件页面controller名称 + /// - buttonId: 事件按钮名称 + /// - url: 事件action + /// - customTags: 自定义标签数组(可选) + /// - pushNow: 是否立即推送 + /// - Returns: 无返回值 + public class func addEvent(eventType: String, page: String, buttonId: String, url: String, customTags: Array?, pushNow: Bool) { + // 校验事件类型是否在系统维度信息中存在 + if let systemEventTypes = YXTracking.shared.systemAllDimInfoResponseResult.systemEventTypes, + systemEventTypes.count > 0 { + guard !eventType.isEmpty else { + YXTracking.shared.error(message: "SDK push event error, event type is empty!"); + return + } + + if !systemEventTypes.contains(where: { $0.event_code == eventType }) { + YXTracking.shared.error(message: "SDK push event error, event type not in systemEventTypes!"); + return; + } + } else { + YXTracking.shared.error(message: "SDK push event error, systemEventTypes is empty!"); + return + } + + // 重置事件请求参数中的事件类型 + YXTracking.shared.eventRequestResult.eventType = eventType + + // 重置事件请求参数中的事件参数 + let eventParams = EventParams(page: page, buttonId: buttonId, url: url) + YXTracking.shared.eventRequestResult.eventParams = eventParams + + // 重置事件请求参数中的自定义标签 + YXTracking.shared.eventRequestResult.customTags = customTags + + // 重置事件请求参数中的时间戳 + YXTracking.shared.getTimestamp() + + // 打印日志信息 + YXTracking.shared.log(message: "SDK add event success\nEvent Request Parameter:\n\(YXTracking.shared.eventRequestResult)"); + + // 缓存事件数据或立即推送事件数据 + if pushNow { + YXTracking.shared.pushEvent(event: YXTracking.shared.eventRequestResult) + } else { + YXTrackingSQLiteManager.addTrackingData(data: YXTracking.shared.eventRequestResult) + YXTracking.shared.checkEventCount() + } + } + + /// 设置用户信息 + /// - Parameters: + /// - userInfo: 用户信息 + /// - Returns: 无返回值 + public class func setUserInfo(userId: Int64, userName: String, account: String) { + let userInfo = UserInfo(userId: userId, userName: userName, account: account) + YXTracking.shared.eventRequestResult.userInfo = userInfo + + YXTracking.shared.log(message: "SDK config user info success\nUser info:\n\(userInfo)"); + } + + /// 清空用户信息 + public class func clearUserInfo() { + YXTracking.shared.eventRequestResult.userInfo = nil + + YXTracking.shared.log(message: "SDK clear user info success!"); + } + + /// 获取设备信息 + func getDeviceInfo() { + let device = UIDevice.current + + let os = "\(device.systemName) \(device.systemVersion)" + + let localizedModel = device.localizedModel + + let screenWidthPoints = UIScreen.main.bounds.width + let screenHeightPoints = UIScreen.main.bounds.height + let scale = UIScreen.main.scale + let screenWidthPixels = screenWidthPoints * scale + let screenHeightPixels = screenHeightPoints * scale + let screenResolution = "\(Int(screenWidthPixels))x\(Int(screenHeightPixels))" + + let deviceInfo = DeviceInfo(os: os, model: localizedModel, screenResolution: screenResolution) + YXTracking.shared.eventRequestResult.deviceInfo = deviceInfo + + YXTracking.shared.debug(message: "SDK config device info success\nDevice info:\n\(deviceInfo)"); + } + + /// 获取时间戳 + func getTimestamp() { + let currentDate = Date() + + let unixTimestamp = currentDate.timeIntervalSince1970 + let milliseconds = Int64(unixTimestamp * 1000) + + let customFormatter = ISO8601DateFormatter() + customFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] + customFormatter.timeZone = TimeZone.current + let isoStringWithMilliseconds = customFormatter.string(from: currentDate) + + YXTracking.shared.eventRequestResult.clientTimestamp = milliseconds + YXTracking.shared.eventRequestResult.timestamp = isoStringWithMilliseconds + } + + /// 判断数据库数量是否达到缓存上限,如已达到,则推送缓存事件 + func checkEventCount() { + let count = YXTrackingSQLiteManager.getTrackingDataCount() + if count >= cacheCount { + YXTracking.pushEventList() + } + } + + /// 开启定时推送本地缓存的事件日志列表 + func startThreadPool() { + let queue = DispatchQueue(label: "com.yx.tracking", qos: .userInitiated) + queue.async { [self] in + while true { + YXTracking.pushEventList() + sleep(UInt32(pushInterval)) + } + } + } + + /// 根据系统编码获取系统所有维度信息 + func getSystemAllDimInfo() { + let systemCode = YXTracking.shared.eventRequestResult.system_code ?? "" + guard !systemCode.isEmpty else { return } + + let urlString = "\(host)/api/ExternalEventlogs/GetSystemAllDimInfo?systemCode=\(systemCode)" + guard let url = URL(string: urlString) else { + return + } + + let task = URLSession.shared.dataTask(with: url) { [self] data, response, error in + if let error = error { + YXTracking.shared.error(message: error.localizedDescription) + return + } + + guard let data = data else { + return + } + + let json = try? JSONSerialization.jsonObject(with: data, options: []) + if let dictionary = json as? [String: Any] { + if let code = dictionary["code"] as? Int, code == 200 { + if let result = dictionary["data"] as? Dictionary, + let resultData = try? JSONSerialization.data(withJSONObject: result) { + do { + let resultModel = try JSONDecoder().decode(SystemAllDimInfoResponseResult.self, from: resultData) + systemAllDimInfoResponseResult = resultModel + + YXTracking.shared.log(message: "SDK init success\nSystem All Dim Info:\n\(systemAllDimInfoResponseResult)"); + + // 初始化成功后 推送本地缓存的事件日志列表 + YXTracking.pushEventList() + + // 开启线程池,定时推送本地缓存的事件日志列表 + YXTracking.shared.startThreadPool() + } catch { + YXTracking.shared.error(message: "SDK init fail!"); + } + } else { + YXTracking.shared.error(message: "无法创建Data对象!") + } + } else { + YXTracking.shared.error(message: "SDK init fail,error code: \(dictionary["code"] ?? "未知")") + } + } + } + task.resume() + } + + /// 推送单条日志 + /// - Parameters: + /// - event: 事件对象 + /// - Returns: 无返回值 + func pushEvent(event: EventRequestResult) { + let urlString = "\(host)/api/ExternalEventlogs/AddEventLog" + guard let url = URL(string: urlString) else { + return + } + + // 创建URL请求 + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + + // 将event对象转换为JSON数据 + let jsonEncoder = JSONEncoder() + do { + let jsonData = try jsonEncoder.encode(event) + request.httpBody = jsonData + } catch { + YXTracking.shared.error(message: "SDK push event error encoding\n\(error)"); + return + } + + // 发送请求 + let task = URLSession.shared.dataTask(with: request) { data, response, error in + if let error = error { + YXTracking.shared.error(message: "SDK push event error sending\n\(error)"); + return + } + + guard let data = data else { + return + } + + let json = try? JSONSerialization.jsonObject(with: data, options: []) + if let dictionary = json as? [String: Any] { + if let code = dictionary["code"] as? Int, code == 200 { + YXTracking.shared.success(message: "SDK push event success Response:\n\(String(data: data, encoding: .utf8) ?? "")"); + } else { + YXTrackingSQLiteManager.addTrackingData(data: YXTracking.shared.eventRequestResult) + YXTracking.shared.error(message: "SDK push event fail Response:\n\(String(data: data, encoding: .utf8) ?? "")"); + } + } + } + + task.resume() + } + + /// 推送多条日志 + public class func pushEventList() { + let count = YXTrackingSQLiteManager.getTrackingDataCount() + guard count > 0 else { return } + + let urlString = "\(YXTracking.shared.host)/api/ExternalEventlogs/AddEventListLog" + guard let url = URL(string: urlString) else { + return + } + + // 创建URL请求 + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + + // 读取数据库数据 + let eventList = YXTrackingSQLiteManager.readTrackingData() + var event: [EventRequestResult] = [] + for item in eventList { + event.append(item) + } + + // 将event list对象转换为JSON数据 + let jsonEncoder = JSONEncoder() + do { + let jsonData = try jsonEncoder.encode(event) + request.httpBody = jsonData + } catch { + YXTracking.shared.error(message: "SDK push event list error encoding\n\(error)"); + return + } + + // 发送请求 + let task = URLSession.shared.dataTask(with: request) { data, response, error in + if let error = error { + YXTracking.shared.error(message: "SDK push event list error sending\n\(error)"); + return + } + + guard let data = data else { + return + } + + let json = try? JSONSerialization.jsonObject(with: data, options: []) + if let dictionary = json as? [String: Any] { + if let code = dictionary["code"] as? Int, code == 200 { + YXTrackingSQLiteManager.deleteTrackingData() + YXTracking.shared.success(message: "SDK push event list success Response:\n\(String(data: data, encoding: .utf8) ?? "")"); + } else { + YXTracking.shared.error(message: "SDK push event fail Response:\n\(String(data: data, encoding: .utf8) ?? "")"); + } + } + } + + task.resume() + } + + // 监听应用切换到后台 + func addEnterBackgroundNotificationObserver() { + NotificationCenter.default.addObserver(self, selector: #selector(onPushEventList), name: UIApplication.didEnterBackgroundNotification, object: nil) + } + + // 监听系统即将终止应用程序 + func addWillTerminateNotificationObserver() { + NotificationCenter.default.addObserver(self, selector: #selector(onPushEventList), name: UIApplication.willTerminateNotification, object: nil) + } + + // 监听回调:推送事件列表 + @objc func onPushEventList() { + YXTracking.pushEventList() + } + +} + +/// 扩展 +extension YXTracking { + + /// 日志打印 + func log(message: String) { + guard YXTracking.shared.enableLog else { return } + print("ℹ️ [YXTracking LOG] \(message)") + } + + func debug(message: String) { + guard YXTracking.shared.enableLog else { return } + print("🐛 [YXTracking DEBUG] \(message)") + } + + func warning(message: String) { + guard YXTracking.shared.enableLog else { return } + print("⚠️ [YXTracking WARNING] \(message)") + } + + func success(message: String) { + guard YXTracking.shared.enableLog else { return } + print("✅ [YXTracking SUCCESS] \(message)") + } + + func error(message: String) { + guard YXTracking.shared.enableLog else { return } + print("❌ [YXTracking ERROR] \(message)") + } + + func fatal(message: String) { + guard YXTracking.shared.enableLog else { return } + print("💀 [YXTracking FATAL] \(message)") + } + +} diff --git a/YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.xcodeproj/project.pbxproj b/YXTrackingSDK/YXTrackingSDK.xcodeproj/project.pbxproj similarity index 65% rename from YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.xcodeproj/project.pbxproj rename to YXTrackingSDK/YXTrackingSDK.xcodeproj/project.pbxproj index 3e95475..25ab89c 100644 --- a/YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.xcodeproj/project.pbxproj +++ b/YXTrackingSDK/YXTrackingSDK.xcodeproj/project.pbxproj @@ -6,8 +6,42 @@ objectVersion = 77; objects = { +/* Begin PBXAggregateTarget section */ + 7C1AA9712F288FED00382E10 /* YXTrackingSDKAggregate */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 7C1AA9722F288FED00382E10 /* Build configuration list for PBXAggregateTarget "YXTrackingSDKAggregate" */; + buildPhases = ( + 7C1AA9772F28900800382E10 /* ShellScript */, + ); + dependencies = ( + 7C1AA9762F288FFF00382E10 /* PBXTargetDependency */, + ); + name = YXTrackingSDKAggregate; + packageProductDependencies = ( + ); + productName = YXTrackingSDKAggregate; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 7C1AA9792F2892D700382E10 /* YXTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C1AA9782F2892D700382E10 /* YXTracking.swift */; }; + 7C84FBDD2F35D11600A0FC52 /* YXTrackingSQLiteManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C84FBDC2F35D11600A0FC52 /* YXTrackingSQLiteManager.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 7C1AA9752F288FFF00382E10 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7C1AA9562F288F6300382E10 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7C1AA95E2F288F6300382E10; + remoteInfo = YXTrackingSDK; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXFileReference section */ 7C1AA95F2F288F6300382E10 /* YXTrackingSDK.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = YXTrackingSDK.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 7C1AA9782F2892D700382E10 /* YXTracking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YXTracking.swift; sourceTree = ""; }; + 7C84FBDC2F35D11600A0FC52 /* YXTrackingSQLiteManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YXTrackingSQLiteManager.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ @@ -32,6 +66,8 @@ 7C1AA9552F288F6300382E10 = { isa = PBXGroup; children = ( + 7C1AA9782F2892D700382E10 /* YXTracking.swift */, + 7C84FBDC2F35D11600A0FC52 /* YXTrackingSQLiteManager.swift */, 7C1AA9612F288F6300382E10 /* YXTrackingSDK */, 7C1AA9602F288F6300382E10 /* Products */, ); @@ -94,6 +130,9 @@ 7C1AA95E2F288F6300382E10 = { CreatedOnToolsVersion = 26.1.1; }; + 7C1AA9712F288FED00382E10 = { + CreatedOnToolsVersion = 26.1.1; + }; }; }; buildConfigurationList = 7C1AA9592F288F6300382E10 /* Build configuration list for PBXProject "YXTrackingSDK" */; @@ -111,6 +150,7 @@ projectRoot = ""; targets = ( 7C1AA95E2F288F6300382E10 /* YXTrackingSDK */, + 7C1AA9712F288FED00382E10 /* YXTrackingSDKAggregate */, ); }; /* End PBXProject section */ @@ -125,16 +165,46 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 7C1AA9772F28900800382E10 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n#!/bin/sh\n#要build的target名\nTARGET_NAME=${PROJECT_NAME}\nif [[ $1 ]]\nthen\nTARGET_NAME=$1\nfi\nUNIVERSAL_OUTPUT_FOLDER=\"${SRCROOT}/${PROJECT_NAME}/\"\n \n#创建输出目录,并删除之前的framework文件\nmkdir -p \"${UNIVERSAL_OUTPUT_FOLDER}\"\nrm -rf \"${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework\"\n \n#分别编译模拟器和真机的Framework\nxcodebuild -target \"${TARGET_NAME}\" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR=\"${BUILD_DIR}\" BUILD_ROOT=\"${BUILD_ROOT}\" clean build\nxcodebuild -target \"${TARGET_NAME}\" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphonesimulator BUILD_DIR=\"${BUILD_DIR}\" BUILD_ROOT=\"${BUILD_ROOT}\" clean build\n \n#拷贝framework到univer目录\ncp -R \"${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework\" \"${UNIVERSAL_OUTPUT_FOLDER}\"\n \nlipo \"${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework/${TARGET_NAME}\" -remove arm64 -output \"${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework/${TARGET_NAME}\"\n \n \n#合并framework,输出最终的framework到build目录\nlipo -create -output \"${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework/${TARGET_NAME}\" \"${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework/${TARGET_NAME}\" \"${BUILD_DIR}/${CONFIGURATION}-iphoneos/${TARGET_NAME}.framework/${TARGET_NAME}\"\n \n#删除编译之后生成的无关的配置文件\ndir_path=\"${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework/\"\nfor file in ls $dir_path\ndo\nif [[ ${file} =~ \".xcconfig\" ]]\nthen\nrm -f \"${dir_path}/${file}\"\nfi\ndone\n#判断build文件夹是否存在,存在则删除\nif [ -d \"${SRCROOT}/build\" ]\nthen\nrm -rf \"${SRCROOT}/build\"\nfi\nrm -rf \"${BUILD_DIR}/${CONFIGURATION}-iphonesimulator\" \"${BUILD_DIR}/${CONFIGURATION}-iphoneos\"\n#打开合并后的文件夹\nopen \"${UNIVERSAL_OUTPUT_FOLDER}\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 7C1AA95B2F288F6300382E10 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7C1AA9792F2892D700382E10 /* YXTracking.swift in Sources */, + 7C84FBDD2F35D11600A0FC52 /* YXTrackingSQLiteManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 7C1AA9762F288FFF00382E10 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7C1AA95E2F288F6300382E10 /* YXTrackingSDK */; + targetProxy = 7C1AA9752F288FFF00382E10 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin XCBuildConfiguration section */ 7C1AA9662F288F6300382E10 /* Debug */ = { isa = XCBuildConfiguration; @@ -269,26 +339,36 @@ BUILD_LIBRARY_FOR_DISTRIBUTION = YES; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; DEVELOPMENT_TEAM = Z778GC45N8; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_MODULE_VERIFIER = YES; GENERATE_INFOPLIST_FILE = YES; + GENERATE_PRELINK_OBJECT_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.0; + MACH_O_TYPE = staticlib; + MARKETING_VERSION = 1.0.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + OTHER_LDFLAGS = "-ObjC"; + OTHER_LIBTOOLFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = com.yuanxuan.YXTrackingSDK; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_INSTALL_MODULE = YES; @@ -305,26 +385,36 @@ BUILD_LIBRARY_FOR_DISTRIBUTION = YES; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; DEVELOPMENT_TEAM = Z778GC45N8; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_MODULE_VERIFIER = YES; GENERATE_INFOPLIST_FILE = YES; + GENERATE_PRELINK_OBJECT_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.0; + MACH_O_TYPE = staticlib; + MARKETING_VERSION = 1.0.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + OTHER_LDFLAGS = "-ObjC"; + OTHER_LIBTOOLFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = com.yuanxuan.YXTrackingSDK; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_INSTALL_MODULE = YES; @@ -335,6 +425,24 @@ }; name = Release; }; + 7C1AA9732F288FED00382E10 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = Z778GC45N8; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 7C1AA9742F288FED00382E10 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = Z778GC45N8; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -356,6 +464,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 7C1AA9722F288FED00382E10 /* Build configuration list for PBXAggregateTarget "YXTrackingSDKAggregate" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7C1AA9732F288FED00382E10 /* Debug */, + 7C1AA9742F288FED00382E10 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 7C1AA9562F288F6300382E10 /* Project object */; diff --git a/YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/YXTrackingSDK/YXTrackingSDK.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to YXTrackingSDK/YXTrackingSDK.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/YXTrackingSDK/YXTrackingSDK.xcodeproj/project.xcworkspace/xcuserdata/a1234.xcuserdatad/UserInterfaceState.xcuserstate b/YXTrackingSDK/YXTrackingSDK.xcodeproj/project.xcworkspace/xcuserdata/a1234.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..b90b5ed0f530d617bb29b965d2ee1b017675c839 GIT binary patch literal 141978 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�nmNr3ZMmO1=@iQpc{Z5Dh~z@1qe6-I2t$(I1xAn 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�q9^q8L~81zpC?+g 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 literal 0 HcmV?d00001 diff --git a/YXTrackingSDK/YXTrackingSDK.xcodeproj/xcshareddata/xcschemes/YXTrackingSDK.xcscheme b/YXTrackingSDK/YXTrackingSDK.xcodeproj/xcshareddata/xcschemes/YXTrackingSDK.xcscheme new file mode 100644 index 0000000..1a25797 --- /dev/null +++ b/YXTrackingSDK/YXTrackingSDK.xcodeproj/xcshareddata/xcschemes/YXTrackingSDK.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.xcodeproj/xcuserdata/a1234.xcuserdatad/xcschemes/xcschememanagement.plist b/YXTrackingSDK/YXTrackingSDK.xcodeproj/xcuserdata/a1234.xcuserdatad/xcschemes/xcschememanagement.plist similarity index 56% rename from YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.xcodeproj/xcuserdata/a1234.xcuserdatad/xcschemes/xcschememanagement.plist rename to YXTrackingSDK/YXTrackingSDK.xcodeproj/xcuserdata/a1234.xcuserdatad/xcschemes/xcschememanagement.plist index c00689f..cca050e 100644 --- a/YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.xcodeproj/xcuserdata/a1234.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/YXTrackingSDK/YXTrackingSDK.xcodeproj/xcuserdata/a1234.xcuserdatad/xcschemes/xcschememanagement.plist @@ -9,6 +9,19 @@ orderHint 0 + YXTrackingSDKAggregate.xcscheme_^#shared#^_ + + orderHint + 1 + + + SuppressBuildableAutocreation + + 7C1AA95E2F288F6300382E10 + + primary + + diff --git a/YXTrackingSDK/YXTrackingSDK/YXTracking.swift b/YXTrackingSDK/YXTrackingSDK/YXTracking.swift deleted file mode 100644 index 8bb1d4b..0000000 --- a/YXTrackingSDK/YXTrackingSDK/YXTracking.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// YXTracking.swift -// YXTrackingSDK -// -// Created by 1234 on 2026/1/27. -// - -import UIKit - -class YXTracking: NSObject { - -} diff --git a/YXTrackingSDK/YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.docc/YXTrackingSDK.md b/YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.docc/YXTrackingSDK.md similarity index 100% rename from YXTrackingSDK/YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.docc/YXTrackingSDK.md rename to YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.docc/YXTrackingSDK.md diff --git a/YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.swift b/YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.swift new file mode 100644 index 0000000..be048e8 --- /dev/null +++ b/YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.swift @@ -0,0 +1,85 @@ +// +// YXTrackingSDK.swift +// YXTrackingSDK +// +// Created by 1234 on 2026/1/27. +// + +import Foundation + +/// 定义一个结构体来存储用户信息 +public struct UserInfo: Codable { + var userId: Int64? // 用户ID + var userName: String? // 用户名称 + var account: String? // 账号 +} + +/// 定义一个结构体来存储事件参数 +public struct EventParams: Codable { + var page: String? // 事件页面controller名称 + var buttonId: String? // 事件按钮名称 + var url: String? // 事件action +} + +/// 定义一个结构体来存储设备信息 +public struct DeviceInfo: Codable { + var os: String? // 操作系统 + var model: String? // 设备型号 + var screenResolution: String? // 屏幕分辨率 +} + +/// 定义一个结构体来存储事件请求参数 +public struct EventRequestResult: Codable { + var system_code: String? // 系统id(关联 dim_system.system_code) + var eventType: String? // 事件类型(关联 dim_event_type.event_code) + var userInfo: UserInfo? // 用户信息 + var clientType: Int32? // 数据埋点 客户端类型枚举 1、Android 2、iOS 3、PC 4、H5 + var clientTimestamp: Int64? // 事件发生时间(ISO8601格式) 毫秒时间戳 + var timestamp: String? // 事件发生时间(ISO8601格式) + var deviceInfo: DeviceInfo? // 设备信息 + var eventParams: EventParams? // 事件参数 + var customTags: Array? // 自定义标签(可选) +} + +/// 定义一个结构体来存储系统信息 +public struct SystemInfo: Codable { + var id: Int64? // ID + var createTime: String? // 创建时间 + var modifyTime: String? // 修改时间 + var system_code: String? // 系统唯一标识 + var description: String? // 系统描述 +} + +/// 定义一个结构体来存储接入系统事件类型信息 +public struct SystemEventType: Codable { + var id: Int64? // ID + var createTime: String? // 创建时间 + var modifyTime: String? // 修改时间 + var system_id: Int64? // 所属系统ID(关联dim_system.system_id) + var event_code: String? // 事件编码(如button_click、page_view) + var event_name: String? // 事件名称(如按钮点击、页面访问) + var description: String? // 事件描述 + var system_name: String? // 所属系统名称 +} + + + +/// 定义一个结构体来存储系统自定义标签信息 +public struct SystemCustonTag: Codable { + var id: Int64? // ID + var createTime: String? // 创建时间 + var modifyTime: String? // 修改时间 + var system_id: Int64? // 所属系统ID(关联dim_system.system_id) + var tag_name: String? // 标签名称(如promotion_id、source_channel) + var description: String? // 标签描述 + var is_required: Bool? // 是否为必传标签 + var system_name: String? // 所属系统 +} + +/// 定义一个结构体来存储系统所有维度信息响应结果 +public struct SystemAllDimInfoResponseResult: Codable { + var systemInfo: SystemInfo? // 接入系统 + var systemEventTypes: Array? // 接入系统事件类型 + var SystemCustonTas: Array? // 接入系统自定义标签 +} + diff --git a/YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.xcodeproj/project.xcworkspace/xcuserdata/a1234.xcuserdatad/UserInterfaceState.xcuserstate b/YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.xcodeproj/project.xcworkspace/xcuserdata/a1234.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index 2ab321ef2b0c8fcf1632371a7ba94590b79544ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6770 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 r`I_^9^N{mR=iAPs&X1kPoS!+5JHK;&ucTB0>Z diff --git a/YXTrackingSDK/YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.swift b/YXTrackingSDK/YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.swift deleted file mode 100644 index b4a628f..0000000 --- a/YXTrackingSDK/YXTrackingSDK/YXTrackingSDK/YXTrackingSDK.swift +++ /dev/null @@ -1,9 +0,0 @@ -// -// YXTrackingSDK.swift -// YXTrackingSDK -// -// Created by 1234 on 2026/1/27. -// - -import Foundation - diff --git a/YXTrackingSDK/YXTrackingSQLiteManager.swift b/YXTrackingSDK/YXTrackingSQLiteManager.swift index 17e80a6..32a3c86 100644 --- a/YXTrackingSDK/YXTrackingSQLiteManager.swift +++ b/YXTrackingSDK/YXTrackingSQLiteManager.swift @@ -6,7 +6,214 @@ // 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 + } }