diff --git a/Runtime/GuruConsent/Bridge.meta b/Runtime/GuruConsent/Bridge.meta deleted file mode 100644 index bc56537..0000000 --- a/Runtime/GuruConsent/Bridge.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: b88f29431cdd47f9bc1945ddd590c4df -timeCreated: 1717035321 \ No newline at end of file diff --git a/Runtime/GuruConsent/Bridge/Plugins.meta b/Runtime/GuruConsent/Bridge/Plugins.meta deleted file mode 100644 index 0ab4f91..0000000 --- a/Runtime/GuruConsent/Bridge/Plugins.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: c1d92ec4f77c4f179f05f8a7be17de86 -timeCreated: 1717035338 \ No newline at end of file diff --git a/Runtime/GuruConsent/Bridge/Plugins/iOS.meta b/Runtime/GuruConsent/Bridge/Plugins/iOS.meta deleted file mode 100644 index 11b1c1c..0000000 --- a/Runtime/GuruConsent/Bridge/Plugins/iOS.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 4e5722a54ec94d72a304d981db2426f7 -timeCreated: 1717035343 \ No newline at end of file diff --git a/Runtime/GuruConsent/Plugins/Android/GuruConsent-1.0.0.aar b/Runtime/GuruConsent/Plugins/Android/GuruConsent-1.0.0.aar index d24ab95..d78c3c0 100644 Binary files a/Runtime/GuruConsent/Plugins/Android/GuruConsent-1.0.0.aar and b/Runtime/GuruConsent/Plugins/Android/GuruConsent-1.0.0.aar differ diff --git a/Runtime/GuruConsent/Plugins/iOS/GuruConsent.meta b/Runtime/GuruConsent/Plugins/iOS/GuruConsent.meta new file mode 100644 index 0000000..7eca1c5 --- /dev/null +++ b/Runtime/GuruConsent/Plugins/iOS/GuruConsent.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 78bd1ca99d374a1f9f31be5cbf38b03c +timeCreated: 1717118600 \ No newline at end of file diff --git a/Runtime/GuruConsent/Plugins/iOS/GuruConsent.podspec b/Runtime/GuruConsent/Plugins/iOS/GuruConsent.podspec index d5dd05d..e255532 100644 --- a/Runtime/GuruConsent/Plugins/iOS/GuruConsent.podspec +++ b/Runtime/GuruConsent/Plugins/iOS/GuruConsent.podspec @@ -22,7 +22,7 @@ Pod::Spec.new do |s| s.swift_version = '5.0' s.ios.deployment_target = '12.0' - s.source_files = "Sources/**/*.swift" + s.source_files = "GuruConsent/Sources/**/*.swift" s.requires_arc = true @@ -34,7 +34,7 @@ Pod::Spec.new do |s| s.subspec 'Privacy' do |ss| ss.resource_bundles = { - s.name => 'Resources/PrivacyInfo.xcprivacy' + s.name => 'GuruConsent/Resources/PrivacyInfo.xcprivacy' } end end diff --git a/Runtime/GuruConsent/Plugins/iOS/GuruConsent/LICENSE b/Runtime/GuruConsent/Plugins/iOS/GuruConsent/LICENSE new file mode 100644 index 0000000..a40c2c1 --- /dev/null +++ b/Runtime/GuruConsent/Plugins/iOS/GuruConsent/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 CastBox Dev Team + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Runtime/GuruConsent/Plugins/iOS/LICENSE.meta b/Runtime/GuruConsent/Plugins/iOS/GuruConsent/LICENSE.meta similarity index 100% rename from Runtime/GuruConsent/Plugins/iOS/LICENSE.meta rename to Runtime/GuruConsent/Plugins/iOS/GuruConsent/LICENSE.meta diff --git a/Runtime/GuruConsent/Plugins/iOS/GuruConsent/README.md b/Runtime/GuruConsent/Plugins/iOS/GuruConsent/README.md new file mode 100644 index 0000000..ec9e6d6 --- /dev/null +++ b/Runtime/GuruConsent/Plugins/iOS/GuruConsent/README.md @@ -0,0 +1,282 @@ +# GuruConsent-iOS + +![Swift](https://img.shields.io/badge/Swift-5.0-orange.svg)  + +## 特性 + +- [x] 支持系统ATT权限引导弹窗. +- [x] 支持多国语言显示. +- [x] 支持调试EEA地理设置等. +- [x] 支持结果状态回调. + +## 准备 + +将应用 ID 添加到 Info.plist 中: + +``` +GADApplicationIdentifier +YOUR-APP-ID +``` + +将ATT跟踪权限添加到 Info.plist 中: + +``` +NSUserTrackingUsageDescription +This identifier will be used to deliver personalized ads to you. +``` + +## 安装 + +GuruConsent 仅支持CocoaPods. + +**CocoaPods - Podfile** + +```ruby +source 'git@github.com:castbox/GuruSpecs.git' + +pod 'GuruConsent' +``` + +## 使用 + +首先导入framework: + +Swift: + +```swift +import GuruConsent +``` + +Objective-C: + +```objc +#import +``` + +下面是一些简单示例. 支持所有设备和模拟器: + +### 方式一: 自动 + +如果满足显示条件 自动显示弹窗 具有一定的延迟效果 但具体显示时机不定, 受网络影响. + +__建议在应用启动后 延后一些调用开始 请务必确保首次启动后已授权网络请求权限再调用__ + +Swift: + +```swift +// 开始请求 +GuruConsent.start(from: controller) { result in + switch result { + case .success(let status): + if #available(iOS 14, *) { + print("ATT 结果: \(ATTrackingManager.trackingAuthorizationStatus)") + } + print("GDPR 结果: \(status)") + + case .failure(let error): + print("失败: \(error)") + } +} +``` + +Objective-C: + +```objc +// 开始请求 +[GuruConsent startFrom:self success:^(enum GuruConsentGDPRStatus status) { + + if (@available(iOS 14, *)) { + NSLog(@"ATT 结果: %lu", (unsigned long)ATTrackingManager.trackingAuthorizationStatus); + } + + switch (status) { + case GuruConsentGDPRStatusUnknown: + + break; + + case GuruConsentGDPRStatusRequired: + + break; + + case GuruConsentGDPRStatusNotRequired: + + break; + + case GuruConsentGDPRStatusObtained: + + break; + + default: + break; + } + NSLog(@"GDPR 结果: %ld", (long)status); + +} failure:^(NSError * _Nonnull error) { + NSLog(@"失败: %@", error); +}]; +``` + + +### 方式二: 手动 + +先调用准备, 准备完成后在合适的时机手动调用弹窗显示. + +__建议在应用启动后 延后一些调用准备 请务必确保首次启动后已授权网络请求权限再调用__ + +Swift: + +```swift +// 准备 +GuruConsent.prepare { result in + switch result { + case .success(let status): + print("GDPR 结果: \(status)") + + case .failure(let error): + print("失败: \(error)") + } +} + + +// 显示 请确保status为.required 否则无法显示 +GuruConsent.present(from: self) { result in + switch result { + case .success(let status): + if #available(iOS 14, *) { + print("ATT 结果: \(ATTrackingManager.trackingAuthorizationStatus)") + } + print("GDPR 结果: \(status)") + + case .failure(let error): + print("失败: \(error)") + } +} +``` + +Objective-C: + +```objc +// 准备 +[GuruConsent prepareWithSuccess:^(enum GuruConsentGDPRStatus status) { + + switch (status) { + case GuruConsentGDPRStatusUnknown: + + break; + + case GuruConsentGDPRStatusRequired: + + break; + + case GuruConsentGDPRStatusNotRequired: + + break; + + case GuruConsentGDPRStatusObtained: + + break; + + default: + break; + } + NSLog(@"GDPR 结果: %ld", (long)status); + +} failure:^(NSError * _Nonnull error) { + NSLog(@"失败: %@", error); +}]; + + +// 显示 请确保status为.required 否则无法显示 +[GuruConsent presentFrom:self success:^(enum GuruConsentGDPRStatus status) { + + if (@available(iOS 14, *)) { + NSLog(@"ATT 结果: %lu", (unsigned long)ATTrackingManager.trackingAuthorizationStatus); + } + + switch (status) { + case GuruConsentGDPRStatusUnknown: + + break; + + case GuruConsentGDPRStatusRequired: + + break; + + case GuruConsentGDPRStatusNotRequired: + + break; + + case GuruConsentGDPRStatusObtained: + + break; + + default: + break; + } + NSLog(@"GDPR 结果: %ld", (long)status); + +} failure:^(NSError * _Nonnull error) { + NSLog(@"%@", error); +}]; +``` + + +### 调试设置 + +`testDeviceIdentifiers`获取方式: 当传入空置, 运行调用`GuruConsent.start(from:)` Xcode控制台会输出如下: + +``` + To enable debug mode for this device, set: UMPDebugSettings.testDeviceIdentifiers = @[ @"8C5E8576-5090-4C41-8FC4-A5A80FF77D9E" ]; +``` +将控制台的`8C5E8576-5090-4C41-8FC4-A5A80FF77D9E` 复制粘贴到代码中, 再次运行即可进行调试. + +Swift: + +```swift +// 设置调试配置 +let debug = GuruConsent.DebugSettings() +debug.testDeviceIdentifiers = ["8C5E8576-5090-4C41-8FC4-A5A80FF77D9E"] +debug.geography = .EEA +GuruConsent.debug = debug +``` + +Objective-C: + +```objc +// 设置调试配置 +GuruConsentDebugSettings *debug = [[GuruConsentDebugSettings alloc] init]; +debug.testDeviceIdentifiers = @[@"8C5E8576-5090-4C41-8FC4-A5A80FF77D9E"]; +debug.geography = GuruConsentDebugSettingsGeographyEEA; +GuruConsent.debug = debug; +``` + +重置状态 + +```swift +GuruConsent.reset() +``` + +## 运行 + +### 未授权过ATT权限 (非EEA地区): +![IMG_4886](https://user-images.githubusercontent.com/13112992/201629493-3b95e3e8-ca02-41b6-9a64-1acd11ea4261.PNG) + +点击`Continue`按钮弹出ATT授权弹窗 + +![IMG_4887](https://user-images.githubusercontent.com/13112992/201629676-3ca39406-513a-46ec-b79e-60df4fd7cc88.PNG) + +### EEA地区: +![IMG_4888](https://user-images.githubusercontent.com/13112992/201629612-b736c439-54fe-4c59-97ee-71a6a449f23a.PNG) + +未授权过ATT权限 点击同意等按钮弹出ATT授权弹窗 + +![Simulator Screen Shot - iPhone 14 Pro - 2022-11-14 at 16 40 48](https://user-images.githubusercontent.com/13112992/201629990-ec3776b2-6bd0-4c3e-aba4-48f093216e11.png) + + +## 参考 + +[官方文档](https://developers.google.com/admob/ump/ios/quick-start) + +## 协议 + +GuruConsent 使用 MIT 协议. 有关更多信息,请参阅 [LICENSE](LICENSE) 文件. diff --git a/Runtime/GuruConsent/Plugins/iOS/README.md.meta b/Runtime/GuruConsent/Plugins/iOS/GuruConsent/README.md.meta similarity index 100% rename from Runtime/GuruConsent/Plugins/iOS/README.md.meta rename to Runtime/GuruConsent/Plugins/iOS/GuruConsent/README.md.meta diff --git a/Runtime/GuruConsent/Plugins/iOS/Resources.meta b/Runtime/GuruConsent/Plugins/iOS/GuruConsent/Resources.meta similarity index 100% rename from Runtime/GuruConsent/Plugins/iOS/Resources.meta rename to Runtime/GuruConsent/Plugins/iOS/GuruConsent/Resources.meta diff --git a/Runtime/GuruConsent/Plugins/iOS/GuruConsent/Resources/PrivacyInfo.xcprivacy b/Runtime/GuruConsent/Plugins/iOS/GuruConsent/Resources/PrivacyInfo.xcprivacy new file mode 100755 index 0000000..a20ab95 --- /dev/null +++ b/Runtime/GuruConsent/Plugins/iOS/GuruConsent/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1,60 @@ + + + + + NSPrivacyCollectedDataTypes + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeCoarseLocation + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypePerformanceData + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeProductInteraction + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + + + NSPrivacyTrackingDomains + + NSPrivacyTracking + + + diff --git a/Runtime/GuruConsent/Plugins/iOS/GuruConsent/Resources/PrivacyInfo.xcprivacy.meta b/Runtime/GuruConsent/Plugins/iOS/GuruConsent/Resources/PrivacyInfo.xcprivacy.meta new file mode 100644 index 0000000..3399c07 --- /dev/null +++ b/Runtime/GuruConsent/Plugins/iOS/GuruConsent/Resources/PrivacyInfo.xcprivacy.meta @@ -0,0 +1,85 @@ +fileFormatVersion: 2 +guid: 71f33675e1c99450686a549ba5f04505 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + - first: + tvOS: tvOS + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/GuruConsent/Plugins/iOS/Sources.meta b/Runtime/GuruConsent/Plugins/iOS/GuruConsent/Sources.meta similarity index 100% rename from Runtime/GuruConsent/Plugins/iOS/Sources.meta rename to Runtime/GuruConsent/Plugins/iOS/GuruConsent/Sources.meta diff --git a/Runtime/GuruConsent/Plugins/iOS/GuruConsent/Sources/GuruConsent.GDPR.swift b/Runtime/GuruConsent/Plugins/iOS/GuruConsent/Sources/GuruConsent.GDPR.swift new file mode 100644 index 0000000..2e0028f --- /dev/null +++ b/Runtime/GuruConsent/Plugins/iOS/GuruConsent/Sources/GuruConsent.GDPR.swift @@ -0,0 +1,121 @@ +// +// GuruConsent.GDPR.swift +// GuruConsent +// +// Created by 李响 on 2022/11/11. +// + +import UIKit +import UserMessagingPlatform + +public extension GuruConsent { + + /// 当前状态 + @objc + public static var status: GDPRStatus { + return .init( + rawValue: UMPConsentInformation.sharedInstance.consentStatus.rawValue + ) ?? .unknown + } + + /// 是否可以请求广告 (status为.obtained或.notRequired) + @objc + public static var canRequestAds: Bool { + return UMPConsentInformation.sharedInstance.canRequestAds + } + + /// 隐私选项状态 + /// 初始状态为.unknown 如果status为.notRequired 该状态也是.notRequired + /// status为.obtained(弹窗同意后) 会变成.required, 代表可以让用户修改之前同意的隐私选项 + /// 调用`func openPrivacyOptions(from: with:)`方法可以再次打开弹窗 + /// 弹窗第二次打开后 用户可以重新勾选隐私选项并同意, 操作完成后状态会变为.unknown + @objc + public static var privacyOptionsRequirementStatus: GDPRPrivacyOptionsRequirementStatus { + return .init( + rawValue: UMPConsentInformation.sharedInstance.privacyOptionsRequirementStatus.rawValue + ) ?? .unknown + } + + private static var form: UMPConsentForm? + + internal static func request(with completion: @escaping ((Swift.Result) -> Void)) { + let parameters = UMPRequestParameters() + // 设置未满同意年龄的标签。此处false表示用户达到年龄 + parameters.tagForUnderAgeOfConsent = tagForUnderAgeOfConsent + // 设置调试设置 + if let debug = GuruConsent.debug { + let debugSettings = UMPDebugSettings() + debugSettings.testDeviceIdentifiers = debug.testDeviceIdentifiers + debugSettings.geography = .init(rawValue: debug.geography.rawValue) ?? .disabled + parameters.debugSettings = debugSettings + } + + // 请求最新同意信息 + UMPConsentInformation.sharedInstance.requestConsentInfoUpdate( + with: parameters, + completionHandler: { error in + if let error = error { + // 请求同意信息失败 + completion(.failure(error)) + + } else { + let status = UMPConsentInformation.sharedInstance.formStatus + completion(.success(.init(rawValue: status.rawValue) ?? .unknown)) + } + } + ) + } + + static func loadForm(with completion: @escaping ((Swift.Result) -> Void)) { + // 加载表单 + UMPConsentForm.load { form, error in + if let error = error { + // 表单加载失败 + completion(.failure(error)) + + } else { + self.form = form + let status = UMPConsentInformation.sharedInstance.consentStatus + completion(.success(.init(rawValue: status.rawValue) ?? .unknown)) + } + } + } + + static func openForm(from controller: UIViewController, with completion: @escaping ((Swift.Result) -> Void)) { + guard let form = form else { + completion(.failure(NSError(domain: "Form Empty.", code: -1))) + return + } + // 打开弹窗 + form.present( + from: controller, + completionHandler: { error in + if let error = error { + // 弹窗失败 + completion(.failure(error)) + + } else { + // 是否已同意 + completion(.success(status)) + } + } + ) + } + + /// 重置 + @objc + public static func reset() { + UMPConsentInformation.sharedInstance.reset() + } + + /// 打开隐私选项弹窗 (privacyOptionsRequirementStatus必须为.required) + /// - Parameters: + /// - controller: 视图控制器 + /// - completion: 完成回调 + @objc + public static func openPrivacyOptions(from controller: UIViewController, with completion: @escaping ((Error?) -> Void)) { + UMPConsentForm.presentPrivacyOptionsForm(from: controller) { error in + completion(error) + } + } +} diff --git a/Runtime/GuruConsent/Plugins/iOS/GuruConsent/Sources/GuruConsent.GDPR.swift.meta b/Runtime/GuruConsent/Plugins/iOS/GuruConsent/Sources/GuruConsent.GDPR.swift.meta new file mode 100644 index 0000000..48688a8 --- /dev/null +++ b/Runtime/GuruConsent/Plugins/iOS/GuruConsent/Sources/GuruConsent.GDPR.swift.meta @@ -0,0 +1,49 @@ +fileFormatVersion: 2 +guid: 604558f3813844f3eaa13f84fde7e522 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 0 + settings: {} + - first: + tvOS: tvOS + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/GuruConsent/Plugins/iOS/GuruConsent/Sources/GuruConsent.swift b/Runtime/GuruConsent/Plugins/iOS/GuruConsent/Sources/GuruConsent.swift new file mode 100644 index 0000000..f294437 --- /dev/null +++ b/Runtime/GuruConsent/Plugins/iOS/GuruConsent/Sources/GuruConsent.swift @@ -0,0 +1,204 @@ +// +// GuruConsent.swift +// GuruConsent +// +// Created by 李响 on 2022/11/11. +// + +import UIKit + +@objc +public class GuruConsent: NSObject { + + /// CDPR状态 + @objc(GuruConsentGDPRStatus) + public enum GDPRStatus: Int { + case unknown ///< Unknown consent status. + case required ///< User consent required but not yet obtained. + case notRequired ///< Consent not required. + case obtained ///< User consent obtained, personalized vs non-personalized undefined. + } + + /// CDPR表单状态 + internal enum GDPRFormStatus: Int { + case unknown + case available + case unavailable + } + + /// GDPR隐私选项所需状态 + @objc(GuruConsentGDPRPrivacyOptionsRequirementStatus) + public enum GDPRPrivacyOptionsRequirementStatus: Int { + case unknown ///< Requirement unknown. + case required ///< A way must be provided for the user to modify their privacy options. + case notRequired ///< User does not need to modify their privacy options. Either consent is not required, or the consent type does not require modification. + } + + /// 调试设置 + @objc(GuruConsentDebugSettings) + public class DebugSettings: NSObject { + + @objc(GuruConsentDebugSettingsGeography) + public enum Geography: Int { + case disabled + case EEA + case notEEA + } + + /// 测试设备ID + @objc + public var testDeviceIdentifiers: [String] + /// 地理位置 + @objc + public var geography: Geography + + @objc + public override init() { + testDeviceIdentifiers = [] + geography = .disabled + } + } + + /// 调试设置 + @objc + public static var debug: DebugSettings? + + /// 设置未满同意年龄的标签 默认为false 表示用户达到年龄 + @objc + public static var tagForUnderAgeOfConsent: Bool = false + + /// 是否已同意 (status为.obtained) + @objc + public static var isObtained: Bool { + return status == .obtained + } + + /// 开始 OC + /// ATT未授权过(非EEA地区) 会弹出ATT引导弹窗 在点击继续按钮时弹出ATT权限弹窗 + /// ATT未授权过(EEA地区) 会弹出GDPR弹窗 在点击同意时弹出ATT权限弹窗 + /// - Parameters: + /// - controller: 视图控制器 + /// - completion: 完成回调 + @objc + public static func start(from controller: UIViewController, success: @escaping ((GDPRStatus) -> Void), failure: @escaping (Error) -> Void) { + start(from: controller) { result in + switch result { + case .success(let value): + success(value) + + case .failure(let error): + failure(error) + } + } + } + + /// 开始 Swift + /// ATT未授权过(非EEA地区) 会弹出ATT引导弹窗 在点击继续按钮时弹出ATT权限弹窗 + /// ATT未授权过(EEA地区) 会弹出GDPR弹窗 在点击同意时弹出ATT权限弹窗 + /// - Parameters: + /// - controller: 视图控制器 + /// - completion: 完成回调 + public static func start(from controller: UIViewController, with completion: @escaping ((Swift.Result) -> Void)) { + request { result in + switch result { + case .success(let value): + if value == .available { + // 加载表单 + loadForm { result in + switch result { + case .success(let value): + switch value { + case .required: + // 打开表单 + openForm(from: controller, with: completion) + + default: + completion(.success(value)) + } + + case .failure(let error): + // 表单加载失败 + completion(.failure(error)) + } + } + + } else { + // 无表单需要加载 + completion(.success(.notRequired)) + } + + case .failure(let error): + // 请求同意信息失败 + completion(.failure(error)) + } + } + } + + /// ---------------------------------------- + + @objc + public static func prepare(success: @escaping ((GDPRStatus) -> Void), failure: @escaping (Error) -> Void) { + prepare { result in + switch result { + case .success(let value): + success(value) + + case .failure(let error): + failure(error) + } + } + } + + @objc + public static func present(from controller: UIViewController, success: @escaping ((GDPRStatus) -> Void), failure: @escaping (Error) -> Void) { + present(from: controller) { result in + switch result { + case .success(let value): + success(value) + + case .failure(let error): + failure(error) + } + } + } + + /// 预加载 (需在加载成功后 手动调用打开表单) + /// - Parameter completion: 完成回调 + public static func prepare(with completion: @escaping ((Swift.Result) -> Void)) { + request { result in + switch result { + case .success(let value): + if value == .available { + // 加载表单 + loadForm { result in + switch result { + case .success(let value): + // 表单加载成功 + completion(.success(value)) + + case .failure(let error): + // 表单加载失败 + completion(.failure(error)) + } + } + + } else { + // 无表单需要加载 + completion(.success(.notRequired)) + } + + case .failure(let error): + // 请求同意信息失败 + completion(.failure(error)) + } + } + } + + /// 打开表单 请确保status为.required 否则无效 + /// - Parameters: + /// - controller: 视图控制器 + /// - completion: 完成回调 + public static func present(from controller: UIViewController, with completion: @escaping ((Swift.Result) -> Void)) { + openForm(from: controller, with: completion) + } +} diff --git a/Runtime/GuruConsent/Plugins/iOS/GuruConsent/Sources/GuruConsent.swift.meta b/Runtime/GuruConsent/Plugins/iOS/GuruConsent/Sources/GuruConsent.swift.meta new file mode 100644 index 0000000..2c848da --- /dev/null +++ b/Runtime/GuruConsent/Plugins/iOS/GuruConsent/Sources/GuruConsent.swift.meta @@ -0,0 +1,49 @@ +fileFormatVersion: 2 +guid: 0b11b5b1fa30a432eaf1b02602717dc9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 0 + settings: {} + - first: + tvOS: tvOS + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/GuruConsent/Plugins/iOS/LICENSE b/Runtime/GuruConsent/Plugins/iOS/LICENSE deleted file mode 120000 index b012b3a..0000000 --- a/Runtime/GuruConsent/Plugins/iOS/LICENSE +++ /dev/null @@ -1 +0,0 @@ -/Users/huyfuei/Workspace/Castbox/SDK/Native/GuruConsent-iOS/LICENSE \ No newline at end of file diff --git a/Runtime/GuruConsent/Plugins/iOS/README.md b/Runtime/GuruConsent/Plugins/iOS/README.md deleted file mode 120000 index a167ee3..0000000 --- a/Runtime/GuruConsent/Plugins/iOS/README.md +++ /dev/null @@ -1 +0,0 @@ -/Users/huyfuei/Workspace/Castbox/SDK/Native/GuruConsent-iOS/README.md \ No newline at end of file diff --git a/Runtime/GuruConsent/Plugins/iOS/Resources b/Runtime/GuruConsent/Plugins/iOS/Resources deleted file mode 120000 index 9464a8b..0000000 --- a/Runtime/GuruConsent/Plugins/iOS/Resources +++ /dev/null @@ -1 +0,0 @@ -/Users/huyfuei/Workspace/Castbox/SDK/Native/GuruConsent-iOS/Resources \ No newline at end of file diff --git a/Runtime/GuruConsent/Plugins/iOS/Sources b/Runtime/GuruConsent/Plugins/iOS/Sources deleted file mode 120000 index ea570b1..0000000 --- a/Runtime/GuruConsent/Plugins/iOS/Sources +++ /dev/null @@ -1 +0,0 @@ -/Users/huyfuei/Workspace/Castbox/SDK/Native/GuruConsent-iOS/Sources \ No newline at end of file diff --git a/Runtime/GuruConsent/Bridge/Plugins/iOS/U3DConsent.mm b/Runtime/GuruConsent/Plugins/iOS/U3DConsent.mm similarity index 100% rename from Runtime/GuruConsent/Bridge/Plugins/iOS/U3DConsent.mm rename to Runtime/GuruConsent/Plugins/iOS/U3DConsent.mm diff --git a/Runtime/GuruConsent/Bridge/Plugins/iOS/U3DConsent.mm.meta b/Runtime/GuruConsent/Plugins/iOS/U3DConsent.mm.meta similarity index 100% rename from Runtime/GuruConsent/Bridge/Plugins/iOS/U3DConsent.mm.meta rename to Runtime/GuruConsent/Plugins/iOS/U3DConsent.mm.meta diff --git a/Runtime/GuruConsent/README.md b/Runtime/GuruConsent/README.md index 151741a..ccf0730 100644 --- a/Runtime/GuruConsent/README.md +++ b/Runtime/GuruConsent/README.md @@ -1,16 +1,23 @@ # Guru Unity Consent -## Version 1.0.8 +## 1.0.9 +* 更新 iOS 库版为本地 Pod 库, 直接引用源码 `1.4.6` +> 78dfe631fc97da53023577a7fcebb71400c3c873 +* Anroid 库同步 github 仓库最新版本 `1.0.0` +> f8355aecfb36132c252d216331ad6d8f2c4b6363 + +## 1.0.8 * 更新 GuruConsent 的 iOS 库版本至 `1.4.6` * 更新 iOS 库添加 Privacy Policy 的配置项 * 更新 iOS 依赖的 cocospods 库地址 -## Version 1.0.7 + +## 1.0.7 * 更新 Consent 针对 Json 参数的回调和解析逻辑. -## Version 1.0.6 +## 1.0.6 * 更新了Google DMA 合规策略, 请查询详细的 [Google Ads Consent Mode DMA合规 技术需求文档](https://docs.google.com/document/d/1p7ad-W6XnqPjMgFkvoVf1Yylsogm_PykD9_nauInVoI/edit#heading=h.42lwhi7yczmk) * 已集成了中台的 `dma_gg` 点位 * 云控可控变量 `dma_map_rule`, `dma_country_check` 两个参数来改变 TFC 策略以及开启地区检测 @@ -18,12 +25,12 @@ - ![](Docs/tcf_map.png) > 已知问题: 基于用户选择结果的 Purpose 在 iOS 上会产生乱码, 因此针对返回空串或非 "0","1" 的结果处理为非EEA地区的用户, 即不上报 DMA 数据 -## Version 1.0.3 +## 1.0.3 * 更新了SDKCallback对象的名称和逻辑, 同SDK本体进行区分 -## Version 1.0.2 +## 1.0.2 - 注意本插件库依赖 `LitJson` 用于解析Json格式数据. 请确保项目内引入此库. - 使用了 *EDM* 插件实现自动依赖注入. diff --git a/Runtime/GuruConsent/Runtime/Script/Consent/GuruConsent.cs b/Runtime/GuruConsent/Runtime/Script/Consent/GuruConsent.cs index a5e5a19..4c9ae94 100644 --- a/Runtime/GuruConsent/Runtime/Script/Consent/GuruConsent.cs +++ b/Runtime/GuruConsent/Runtime/Script/Consent/GuruConsent.cs @@ -14,7 +14,7 @@ namespace Guru public class GuruConsent { // Guru Consent Version - public static string Version = "1.0.8"; + public static string Version = "1.0.9"; public static string Tag = "[GuruConsent]"; #region 公用接口 @@ -56,7 +56,7 @@ namespace Guru string deviceId = "", int debugGeography = -1, string dmaMapRule = "", bool enableCountryCheck = false) { - Debug.Log($"{Tag} --- GuruConsent::StartConsent - deviceId:[{deviceId}] debugGeography:[{debugGeography}] dmaMapRule:[{dmaMapRule}] enableCountryCheck:[{enableCountryCheck}]"); + Debug.Log($"{Tag} --- GuruConsent::StartConsent [{Version}] - deviceId:[{deviceId}] debugGeography:[{debugGeography}] dmaMapRule:[{dmaMapRule}] enableCountryCheck:[{enableCountryCheck}]"); _dmaMapRule = dmaMapRule; _enableCountryCheck = enableCountryCheck;