feat: update

This commit is contained in:
zhangmin 2026-02-10 17:19:20 +08:00
parent d7ebceabeb
commit c4dbf83e7d
12 changed files with 906 additions and 23 deletions

View File

@ -0,0 +1,415 @@
//
// YXTracking.swift
// YXTrackingSDK
//
// Created by 1234 on 2026/1/27.
//
import UIKit
/// SDK
/// 1SDK
/// 2/20600
/// 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<String>?, 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<String, Any>,
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 failerror 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")
// eventJSON
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 listJSON
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)")
}
}

View File

@ -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 = "<group>"; };
7C84FBDC2F35D11600A0FC52 /* YXTrackingSQLiteManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YXTrackingSQLiteManager.swift; sourceTree = "<group>"; };
/* 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 */;

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2610"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7C1AA95E2F288F6300382E10"
BuildableName = "YXTrackingSDK.framework"
BlueprintName = "YXTrackingSDK"
ReferencedContainer = "container:YXTrackingSDK.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7C1AA95E2F288F6300382E10"
BuildableName = "YXTrackingSDK.framework"
BlueprintName = "YXTrackingSDK"
ReferencedContainer = "container:YXTrackingSDK.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -9,6 +9,19 @@
<key>orderHint</key>
<integer>0</integer>
</dict>
<key>YXTrackingSDKAggregate.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>7C1AA95E2F288F6300382E10</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@ -1,12 +0,0 @@
//
// YXTracking.swift
// YXTrackingSDK
//
// Created by 1234 on 2026/1/27.
//
import UIKit
class YXTracking: NSObject {
}

View File

@ -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? // 1Android 2iOS 3PC 4H5
var clientTimestamp: Int64? // ISO8601
var timestamp: String? // ISO8601
var deviceInfo: DeviceInfo? //
var eventParams: EventParams? //
var customTags: Array<String>? //
}
///
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? // IDdim_system.system_id
var event_code: String? // button_clickpage_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? // IDdim_system.system_id
var tag_name: String? // promotion_idsource_channel
var description: String? //
var is_required: Bool? //
var system_name: String? //
}
///
public struct SystemAllDimInfoResponseResult: Codable {
var systemInfo: SystemInfo? //
var systemEventTypes: Array<SystemEventType>? //
var SystemCustonTas: Array<SystemCustonTag>? //
}

View File

@ -1,9 +0,0 @@
//
// YXTrackingSDK.swift
// YXTrackingSDK
//
// Created by 1234 on 2026/1/27.
//
import Foundation

View File

@ -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<String>()
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
}
}