Music_Player3/relax.offline.mp3.music/MP/Common/Tool(工具封装)/MP_LuxServerManager.swift

307 lines
11 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// MP_LuxServerManager.swift
// relax.offline.mp3.music
//
// Created by Mr.Zhou on 2024/9/10.
//
import UIKit
import Alamofire
import Security
import AdSupport
///
class MP_LuxServerManager: NSObject {
//
static let shared = MP_LuxServerManager()
///keychain
private let service = "relax.offline.mp3.music.deviceIdentifier"
//MARK: - URL
///
private let baseUrl:String = "https://openapi.lux-ad.com"
///
private let activeUrl:String = "/statistic/appdatacollection/saveAppData"
///
private let errorUrl:String = "/statistic/applogscollection/save"
//MARK: -
///UUID使ID
private var uuID:String{
get{
return getDeviceUUID()
}
}
///IDID
private var userID:String{
get{
return getUserID()
}
}
///IDFA广ID(/0ID)
private let IDFAID:String = ASIdentifierManager.shared().advertisingIdentifier.uuidString
///
private let channel:String = "ios"
///
private let pkgName:String = "relax.offline.mp3.music"
///
private var eventName:String {
get{
return getEventName()
}
}
///
private var timestamp:Int64 {
get{
//
let now = Date()
//,
let currentTimestampInMillisInt:Int64 = Int64(now.timeIntervalSince1970 * 1000)
return currentTimestampInMillisInt
}
}
///
private var deviceVersion: String {
return UIDevice.current.modelName
}
///
private var osVersion:String {
return UIDevice.current.systemVersion
}
//MARK: -
///
private lazy var LuxSession:Session = {
let configuration = URLSessionConfiguration.af.default
///
configuration.timeoutIntervalForRequest = 20
configuration.timeoutIntervalForResource = 20
//
configuration.networkServiceType = .default
let seesion = Alamofire.Session(configuration: configuration, interceptor: MP_CustomRetrier())
return seesion
}()
override init() {
super.init()
}
///
/**
{
"eventName": "app_open",
"timestamp": 1711522237247,
"uuid": "c1c4c016cd6276b41e2447653fe5a9b2",
"app_version": "1.5.4",
"channel": "google",
"country": "cn",
"device": "android",
"language": "zh",
"pkgName": "com.master.ae.safevpn",
"userId": "55a7fa0c-be84-44b9-a73e-a4256dbb30ee",
"adId": "995772a9-7748-418e-bef4-10ca1ad1abe1",
"createdOn": "2022-03-10T12:15:50.000Z",
"data": {
"property1": {},
"property2": {}
}
}**/
//MARK: -
///
func upDateOpenActiveEventTask() {
//
guard let url = URL(string: baseUrl+activeUrl) else {
return
}
//
let parameters:[String:Any] = [
"userId":userID,
"ad_id":IDFAID,
"uuid":uuID,
"device":deviceVersion,
"appVersion":app_Version,
"language":Language_first_local,
"channel":channel,
"pkgName":pkgName,
"eventName":eventName,
"country":Language_first_local,
"dataStr":"Active",
"timestamp":timestamp
]
postUpDateOpenActiveEvent(url, parameters: parameters){
[weak self] statu in
guard let self = self, statu == true else {return}
//
var loads = loadPendingActivities()
guard loads.isEmpty == false else {return}
var indicesToRemove: [Int] = []
let dispatchGroup = DispatchGroup()
for (index, item) in loads.enumerated() {
dispatchGroup.enter()
postUpDateOpenActiveEvent(url, parameters: item, isRepeat: true) { [weak self] statu in
guard let self = self, statu == true else {
dispatchGroup.leave()
return
}
//
indicesToRemove.append(index)
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: .main) {
[weak self] in
guard let self = self else {return}
//
for index in indicesToRemove.sorted(by: >) {
loads.remove(at: index)
}
reloadActivities(loads)
}
}
}
//
private func postUpDateOpenActiveEvent(_ url:URL, parameters:[String:Any], isRepeat:Bool = false, completion:((Bool) -> Void)?) {
LuxSession.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default).responseDecodable(of: JsonActive.self) { [weak self] (response) in
guard let self = self else {return}
switch response.result {
case .success(let value):
print("成功报活:\(value)")
completion?(true)
case .failure(let error):
print("报活失败,失败错误:\(error.localizedDescription)")
if isRepeat == false {
//
saveActivity(parameters)
}
completion?(false)
}
}
}
//
private func saveActivity(_ parameters:[String:Any]) {
var pendingActivities = loadPendingActivities()
pendingActivities.append(parameters)
if let encoded = try? JSONSerialization.data(withJSONObject: pendingActivities, options: []) {
UserDefaults.standard.set(encoded, forKey: "relax.offline.mp3.music.Activities")
}
}
//
private func reloadActivities(_ loads:[[String:Any]]) {
if let encoded = try? JSONSerialization.data(withJSONObject: loads, options: []) {
UserDefaults.standard.set(encoded, forKey: "relax.offline.mp3.music.Activities")
}
}
//
private func loadPendingActivities() -> [[String:Any]] {
if let savedData = UserDefaults.standard.data(forKey: "relax.offline.mp3.music.Activities"),
let decoded = try? JSONSerialization.jsonObject(with: savedData, options: []) as? [[String: Any]] {
return decoded
}
return []
}
//MARK: - ID
///ID
private func getDeviceUUID() -> String {
if let uuid = loadUUIDFromKeychain() {
return uuid
} else {
let newUUID = UUID().uuidString
saveUUIDToKeychain(uuid: newUUID)
return newUUID
}
}
/// Keychain UUID
private func loadUUIDFromKeychain() -> String? {
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne]
var dataTypeRef: AnyObject? = nil
let status = SecItemCopyMatching(query as CFDictionary, &dataTypeRef)
if status == errSecSuccess {
if let data = dataTypeRef as? Data,
let uuid = String(data: data, encoding: .utf8) {
return uuid
}
}
return nil
}
/// UUID Keychain
private func saveUUIDToKeychain(uuid: String) {
if let data = uuid.data(using: .utf8) {
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecValueData as String: data]
SecItemAdd(query as CFDictionary, nil)
}
}
//MARK: - ID
///ID
private func getUserID() -> String {
//UserdefaultsuserID
if let userID = UserDefaults.standard.string(forKey: "relax.offline.mp3.music.userIdentifier") {
return userID
}else {
let newUUID = UIDevice.current.identifierForVendor?.uuidString ?? UUID().uuidString
//UserDefaults
UserDefaults.standard.set(newUUID, forKey: "relax.offline.mp3.music.userIdentifier")
return newUUID
}
}
///
private func getEventName() -> String {
//
if UserDefaults.standard.string(forKey: "relax.offline.mp3.music.firstEvent") != nil {
return "app_open"
}else {
UserDefaults.standard.set("first_open", forKey: "relax.offline.mp3.music.firstEvent")
return "first_open"
}
}
//MARK: -
//
func updateErrorLogEventTask(_ level:ErrorLevel, title:String, message:String) {
//
guard let url = URL(string: baseUrl+errorUrl) else {
return
}
//
let parameters:[String:Any] = [
"pkgName":pkgName,
"appVersion":app_Version,
"os":osVersion,
"device":deviceVersion,
"Level":level.rawValue,
"title":title,
"message":message,
]
}
}
///
struct JsonActive:Codable {
let message:String?
let status:String?
enum CodingKeys: String, CodingKey {
case message = "message"
case status = "status"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
message = try values.decodeIfPresent(String.self, forKey: .message)
status = try values.decodeIfPresent(String.self, forKey: .status)
}
}
///
enum ErrorLevel:Int {
case debug = 1
case info = 2
case warn = 3
case error = 4
case crash = 5
}