diff --git a/AffiseAttributionLib.podspec b/AffiseAttributionLib.podspec index c17f961..73c3bda 100644 --- a/AffiseAttributionLib.podspec +++ b/AffiseAttributionLib.podspec @@ -5,7 +5,7 @@ Pod::Spec.new do |spec| spec.name = "AffiseAttributionLib" - spec.version = ENV['LIB_VERSION'] || "1.6.12" + spec.version = ENV['LIB_VERSION'] || "1.6.13" spec.summary = "Affise Attribution iOS library" spec.description = "Affise SDK is a software you can use to collect app usage statistics, device identifiers, deeplink usage, track install referrer." spec.homepage = "https://github.com/affise/sdk-ios" diff --git a/AffiseAttributionLib/Classes/Affise.swift b/AffiseAttributionLib/Classes/Affise.swift index 48551b9..15f537a 100644 --- a/AffiseAttributionLib/Classes/Affise.swift +++ b/AffiseAttributionLib/Classes/Affise.swift @@ -192,7 +192,7 @@ public final class Affise: NSObject { * Get module status */ @objc - public static func getStatus(_ module: AffiseModules, _ onComplete: @escaping (_ data: [AffiseKeyValue]) -> Void) { + public static func getStatus(_ module: AffiseModules, _ onComplete: @escaping OnKeyValueCallback) { api?.moduleManager.status(module, onComplete) } @@ -215,4 +215,27 @@ public final class Affise: NSObject { internal static func sendInternalEvent(_ event: InternalEvent) { api?.storeInternalEventUseCase.storeInternalEvent(event: event) } + + public class Debug { + /** + * Won't work on Production + * + * Validate credentials + */ + @objc + public static func validate(_ callback: @escaping DebugOnValidateCallback) { + api?.debugValidateUseCase.validate(callback) + } + + /** + * Won't work on Production + * + * Show request-response data + */ + @objc + public static func network(_ callback: @escaping DebugOnNetworkCallback) { + api?.debugNetworkUseCase.onRequest(callback) + } + } + } diff --git a/AffiseAttributionLib/Classes/AffiseApi.swift b/AffiseAttributionLib/Classes/AffiseApi.swift index 74d0f75..dfe4a80 100644 --- a/AffiseAttributionLib/Classes/AffiseApi.swift +++ b/AffiseAttributionLib/Classes/AffiseApi.swift @@ -18,7 +18,9 @@ internal protocol AffiseApi { var preferencesUseCase: PreferencesUseCase {get} var postBackModelFactory: PostBackModelFactory {get} var moduleManager: AffiseModuleManager {get} - var storeInternalEventUseCase:StoreInternalEventUseCase {get} + var storeInternalEventUseCase: StoreInternalEventUseCase {get} + var debugValidateUseCase: DebugValidateUseCase {get} + var debugNetworkUseCase: DebugNetworkUseCase {get} func isInitialized() -> Bool } diff --git a/AffiseAttributionLib/Classes/AffiseComponent.swift b/AffiseAttributionLib/Classes/AffiseComponent.swift index c325292..5586d9a 100644 --- a/AffiseAttributionLib/Classes/AffiseComponent.swift +++ b/AffiseAttributionLib/Classes/AffiseComponent.swift @@ -108,10 +108,10 @@ internal class AffiseComponent: AffiseApi { logsManager: logsManager, isFirstForUserUseCase: isFirstForUserUseCase ) - lazy var storeInternalEventUseCase:StoreInternalEventUseCase = StoreInternalEventUseCaseImpl(repository: internalEventsRepository) - lazy var storeLogsUseCase:StoreLogsUseCase = StoreLogsUseCaseImpl(repository: logsRepository) - lazy var isFirstForUserStorage:IsFirstForUserStorage = IsFirstForUserStorageImpl(logsManager: logsManager, fileManager: fileManager) - lazy var isFirstForUserUseCase:IsFirstForUserUseCase = IsFirstForUserUseCaseImpl(isFirstForUserStorage: isFirstForUserStorage) + lazy var storeInternalEventUseCase: StoreInternalEventUseCase = StoreInternalEventUseCaseImpl(repository: internalEventsRepository) + lazy var storeLogsUseCase: StoreLogsUseCase = StoreLogsUseCaseImpl(repository: logsRepository) + lazy var isFirstForUserStorage: IsFirstForUserStorage = IsFirstForUserStorageImpl(logsManager: logsManager, fileManager: fileManager) + lazy var isFirstForUserUseCase: IsFirstForUserUseCase = IsFirstForUserUseCaseImpl(isFirstForUserStorage: isFirstForUserStorage) lazy var initPropertiesStorage: InitPropertiesStorage = InitPropertiesStorageImpl() lazy var setPropertiesWhenInitUseCase: SetPropertiesWhenAppInitializedUseCase = SetPropertiesWhenAppInitializedUseCaseImpl( storage: initPropertiesStorage @@ -138,6 +138,7 @@ internal class AffiseComponent: AffiseApi { ) lazy var stringToSha256Converter: StringToSHA256Converter = StringToSHA256Converter() + lazy var providersToJsonStringConverter: ProvidersToJsonStringConverter = ProvidersToJsonStringConverter() lazy var deviceUseCase: DeviceUseCase = DeviceUseCaseImpl() /** @@ -163,4 +164,16 @@ internal class AffiseComponent: AffiseApi { logsManager: logsManager, postBackModelFactory: postBackModelFactory ) + + lazy var debugValidateUseCase: DebugValidateUseCase = DebugValidateUseCaseImpl( + initProperties: initProperties, + postBackModelFactory: postBackModelFactory, + logsManager: logsManager, + networkService: networkService, + converter: providersToJsonStringConverter + ) + lazy var debugNetworkUseCase: DebugNetworkUseCase = DebugNetworkUseCaseImpl( + initProperties: initProperties, + networkService: networkService + ) } diff --git a/AffiseAttributionLib/Classes/AffiseShared.swift b/AffiseAttributionLib/Classes/AffiseShared.swift index 38ca2a6..b3eb0da 100644 --- a/AffiseAttributionLib/Classes/AffiseShared.swift +++ b/AffiseAttributionLib/Classes/AffiseShared.swift @@ -169,7 +169,7 @@ public class AffiseShared: NSObject { * Get module status */ @available(*, deprecated, message: "use Affise.getStatus(_ module: _ onComplete:)") - public func getStatus(_ module: AffiseModules, onComplete: @escaping (_ data: [AffiseKeyValue]) -> Void) { + public func getStatus(_ module: AffiseModules, onComplete: @escaping OnKeyValueCallback) { Affise.getStatus(module, onComplete) } diff --git a/AffiseModule/Status/Classes/converter/ProvidersToJsonStringConverter.swift b/AffiseAttributionLib/Classes/converter/ProvidersToJsonStringConverter.swift similarity index 51% rename from AffiseModule/Status/Classes/converter/ProvidersToJsonStringConverter.swift rename to AffiseAttributionLib/Classes/converter/ProvidersToJsonStringConverter.swift index e80ca29..6d6fc81 100644 --- a/AffiseModule/Status/Classes/converter/ProvidersToJsonStringConverter.swift +++ b/AffiseAttributionLib/Classes/converter/ProvidersToJsonStringConverter.swift @@ -1,12 +1,12 @@ import AffiseAttributionLib -class ProvidersToJsonStringConverter: Converter { +public class ProvidersToJsonStringConverter: Converter { - typealias T = [Provider] - typealias R = String + public typealias T = [Provider] + public typealias R = String - func convert(from: [Provider]) -> String { + public func convert(from: [Provider]) -> String { let result = from.mapProviders().map {(key, value) in (key.provider, value) } diff --git a/AffiseAttributionLib/Classes/debug/network/DebugNetworkUseCase.swift b/AffiseAttributionLib/Classes/debug/network/DebugNetworkUseCase.swift new file mode 100644 index 0000000..4c40e05 --- /dev/null +++ b/AffiseAttributionLib/Classes/debug/network/DebugNetworkUseCase.swift @@ -0,0 +1,3 @@ +protocol DebugNetworkUseCase { + func onRequest(_ onComplete: @escaping DebugOnNetworkCallback) +} diff --git a/AffiseAttributionLib/Classes/debug/network/DebugNetworkUseCaseImpl.swift b/AffiseAttributionLib/Classes/debug/network/DebugNetworkUseCaseImpl.swift new file mode 100644 index 0000000..f78c781 --- /dev/null +++ b/AffiseAttributionLib/Classes/debug/network/DebugNetworkUseCaseImpl.swift @@ -0,0 +1,22 @@ +class DebugNetworkUseCaseImpl { + let initProperties: AffiseInitProperties + let networkService: NetworkService + + init( + initProperties: AffiseInitProperties, + networkService: NetworkService + ) { + self.initProperties = initProperties + self.networkService = networkService + } +} + +extension DebugNetworkUseCaseImpl : DebugNetworkUseCase { + + func onRequest(_ onDebug: @escaping DebugOnNetworkCallback) { + if initProperties.isProduction == true { + return + } + networkService.setDebug(onDebug) + } +} diff --git a/AffiseAttributionLib/Classes/debug/network/DebugOnNetworkCallback.swift b/AffiseAttributionLib/Classes/debug/network/DebugOnNetworkCallback.swift new file mode 100644 index 0000000..6dfb772 --- /dev/null +++ b/AffiseAttributionLib/Classes/debug/network/DebugOnNetworkCallback.swift @@ -0,0 +1 @@ +public typealias DebugOnNetworkCallback = (_ request: HttpRequest, _ response: HttpResponse) -> Void diff --git a/AffiseAttributionLib/Classes/debug/validate/DebugOnValidateCallback.swift b/AffiseAttributionLib/Classes/debug/validate/DebugOnValidateCallback.swift new file mode 100644 index 0000000..a7c06ef --- /dev/null +++ b/AffiseAttributionLib/Classes/debug/validate/DebugOnValidateCallback.swift @@ -0,0 +1 @@ +public typealias DebugOnValidateCallback = (_ status: ValidationStatus) -> Void diff --git a/AffiseAttributionLib/Classes/debug/validate/DebugValidateUseCase.swift b/AffiseAttributionLib/Classes/debug/validate/DebugValidateUseCase.swift new file mode 100644 index 0000000..a66b1e1 --- /dev/null +++ b/AffiseAttributionLib/Classes/debug/validate/DebugValidateUseCase.swift @@ -0,0 +1,3 @@ +protocol DebugValidateUseCase { + func validate(_ onComplete: @escaping DebugOnValidateCallback) +} \ No newline at end of file diff --git a/AffiseAttributionLib/Classes/debug/validate/DebugValidateUseCaseImpl.swift b/AffiseAttributionLib/Classes/debug/validate/DebugValidateUseCaseImpl.swift new file mode 100644 index 0000000..b87c47b --- /dev/null +++ b/AffiseAttributionLib/Classes/debug/validate/DebugValidateUseCaseImpl.swift @@ -0,0 +1,117 @@ +class DebugValidateUseCaseImpl { + + private static let TIME_DELAY_SENDING: TimeInterval = 5 + private let ATTEMPTS_TO_SEND = 2 + private let TIMEOUT_SEND: TimeInterval = 30 + + let KEY = "message" + let INVALID_APP_ID = "Invalid affise_app_id" + let INVALID_CHECK_SUM = "Failed to get application or check sum" + let PACKAGE_NAME_NOT_FOUND = "Package name not found" + + let URL = "https://tracking.affattr.com/postback/validate" + + let initProperties: AffiseInitProperties + let logsManager: LogsManager + let converter: ProvidersToJsonStringConverter + let networkService: NetworkService + let providers: [Provider] + + init( + initProperties: AffiseInitProperties, + postBackModelFactory: PostBackModelFactory, + logsManager: LogsManager, + networkService: NetworkService, + converter: ProvidersToJsonStringConverter + ) { + self.initProperties = initProperties + self.logsManager = logsManager + self.networkService = networkService + self.converter = converter + + self.providers = postBackModelFactory.getRequestProviders() + } + + private func createRequest() -> HttpResponse { + guard let httpsUrl = URL.toURL() else { return HttpResponse(0, "", nil) } + + //Create request + return networkService.executeRequest( + httpsUrl: httpsUrl, + method: .POST, + data: converter.convert(from: providers).toData(), + timeout: TIMEOUT_SEND, + headers: CloudConfig.headers + ) + } + + private func getValidationStatus(_ response: HttpResponse) -> ValidationStatus? { // wtf server? + if response.code == 0 { + return ValidationStatus.NETWORK_ERROR + } + + do { + //Create json + let dict = try JSONSerialization.jsonObject(with: (response.body ?? "{}").data(using: .utf8)!, options: .mutableContainers) as! [String: Any?] + let message: String = dict[KEY] as? String ?? "" + + if message.caseInsensitiveCompare(INVALID_APP_ID) == .orderedSame { + return ValidationStatus.INVALID_APP_ID + } + if message.caseInsensitiveCompare(PACKAGE_NAME_NOT_FOUND) == .orderedSame { + return ValidationStatus.PACKAGE_NAME_NOT_FOUND + } + if message.caseInsensitiveCompare(INVALID_CHECK_SUM) == .orderedSame { + return ValidationStatus.INVALID_SECRET_KEY + } + } catch { + } + + if response.code == 200 { + return ValidationStatus.VALID + } + return nil + } +} + +extension DebugValidateUseCaseImpl: DebugValidateUseCase { + + func validate(_ onComplete: @escaping DebugOnValidateCallback) { + if initProperties.isProduction == true { + onComplete(.NOT_WORKING_ON_PRODUCTION) + return + } + + DispatchQueue.global(qos:.background).async { [weak self] in + self?.sendWithRepeat(onComplete) { + Thread.sleep(forTimeInterval: DebugValidateUseCaseImpl.TIME_DELAY_SENDING) + } + } + } + + func sendWithRepeat( _ onComplete: @escaping DebugOnValidateCallback, onFailedAttempt: () -> Void) { + //attempts to send + var attempts = ATTEMPTS_TO_SEND + + //Send or not + var send = false + + //While has attempts and not send + while (attempts != 0 && !send) { + let status = getValidationStatus(createRequest()) + if let status = status { + onComplete(status) + //Send is ok + send = true + } else { + attempts = attempts - 1 + //Check attempts + if (attempts == 0) { + onComplete(ValidationStatus.NETWORK_ERROR) + } else { + onFailedAttempt() + } + } + } + } +} diff --git a/AffiseAttributionLib/Classes/debug/validate/ValidationStatus.swift b/AffiseAttributionLib/Classes/debug/validate/ValidationStatus.swift new file mode 100644 index 0000000..b200eca --- /dev/null +++ b/AffiseAttributionLib/Classes/debug/validate/ValidationStatus.swift @@ -0,0 +1,31 @@ +@objc +public enum ValidationStatus: Int { + case VALID + case INVALID_APP_ID + case INVALID_SECRET_KEY + case PACKAGE_NAME_NOT_FOUND + case NOT_WORKING_ON_PRODUCTION + case NETWORK_ERROR + case UNKNOWN_ERROR + + public var status: String { + switch self { + case .VALID: return "valid" + case .INVALID_APP_ID: return "invalid_app_id" + case .INVALID_SECRET_KEY: return "invalid_secret_key" + case .PACKAGE_NAME_NOT_FOUND: return "package_name_not_found" + case .NOT_WORKING_ON_PRODUCTION: return "not_working_on_production" + case .NETWORK_ERROR: return "network_error" + case .UNKNOWN_ERROR: return "unknown_error" + } + } +} + +extension ValidationStatus: CaseIterable, CustomStringConvertible { + public var description: String { status } + + public static func from(_ name: String?) -> ValidationStatus? { + if name == nil { return nil } + return allCases.first { name == $0.status } + } +} diff --git a/AffiseAttributionLib/Classes/errors/Errors.swift b/AffiseAttributionLib/Classes/errors/Errors.swift index 3f734ed..06acbb6 100644 --- a/AffiseAttributionLib/Classes/errors/Errors.swift +++ b/AffiseAttributionLib/Classes/errors/Errors.swift @@ -1,11 +1,3 @@ -// -// Errors.swift -// app -// -// Created by Sergey Korney -// - - public enum AffiseError: Error { case cloud(url: String, error: Error, attempts: Int, retry: Bool) case network(status: Int, message: String?) @@ -13,3 +5,25 @@ public enum AffiseError: Error { case trackingDisabledException case backgroundTrackingDisabledException } + +extension AffiseError : LocalizedError { + + private var localized: String? { + switch self { + case .cloud(url: let url, error: let error, attempts: let attempts, retry: let retry): + return NSLocalizedString("AffiseError.cloud(url=\(url), error=\(error.localizedDescription), attempts=\(attempts), retry=\(retry))", comment: "") + case .network(status: let status, message: let message): + return NSLocalizedString("AffiseError.network(status=\(status), message=\(message ?? ""))", comment: "") + case .offlineModeEnabled: + return NSLocalizedString("AffiseError.offlineModeEnabled", comment: "") + case .trackingDisabledException: + return NSLocalizedString("AffiseError.trackingDisabledException", comment: "") + case .backgroundTrackingDisabledException: + return NSLocalizedString("AffiseError.backgroundTrackingDisabledException", comment: "") + } + } + + public var errorDescription: String? { + return localized?.toJsonGuardString() + } +} diff --git a/AffiseAttributionLib/Classes/events/cusom/UserCustomEvent.swift b/AffiseAttributionLib/Classes/events/custom/UserCustomEvent.swift similarity index 100% rename from AffiseAttributionLib/Classes/events/cusom/UserCustomEvent.swift rename to AffiseAttributionLib/Classes/events/custom/UserCustomEvent.swift diff --git a/AffiseAttributionLib/Classes/events/cusom/UserCustomSubscriptionEvent.swift b/AffiseAttributionLib/Classes/events/custom/UserCustomSubscriptionEvent.swift similarity index 100% rename from AffiseAttributionLib/Classes/events/cusom/UserCustomSubscriptionEvent.swift rename to AffiseAttributionLib/Classes/events/custom/UserCustomSubscriptionEvent.swift diff --git a/AffiseAttributionLib/Classes/events/predefined/ContactEvent.swift b/AffiseAttributionLib/Classes/events/predefined/ContactEvent.swift index 6d747fc..492b2fe 100644 --- a/AffiseAttributionLib/Classes/events/predefined/ContactEvent.swift +++ b/AffiseAttributionLib/Classes/events/predefined/ContactEvent.swift @@ -1,8 +1,6 @@ /** * Event Contact * - * @property userData any custom data. - * @property timeStampMillis the timestamp event in milliseconds. */ @objc public class ContactEvent : NativeEvent { diff --git a/AffiseAttributionLib/Classes/init/AffiseInitProperties.swift b/AffiseAttributionLib/Classes/init/AffiseInitProperties.swift index e6142ca..7565069 100644 --- a/AffiseAttributionLib/Classes/init/AffiseInitProperties.swift +++ b/AffiseAttributionLib/Classes/init/AffiseInitProperties.swift @@ -35,8 +35,8 @@ public class AffiseInitProperties: NSObject { @objc public convenience init( - affiseAppId: String?, - secretKey: String? = nil + affiseAppId: String, + secretKey: String ) { self.init( affiseAppId: affiseAppId, @@ -48,21 +48,19 @@ public class AffiseInitProperties: NSObject { ) } - // public init(affiseAppId: String?, - // partParamName: String? = nil, - // partParamNameToken: String? = nil, - // appToken: String? = nil, - // isProduction: Bool = true, - // secretKey: String? = nil, - // flags: [AffiseFlag] - // ) { - - // self.affiseAppId = affiseAppId - // self.partParamName = partParamName - // self.partParamNameToken = partParamNameToken - // self.appToken = appToken - // self.isProduction = isProduction - // self.secretKey = secretKey - // self.flags = flags - // } + @objc + public convenience init( + affiseAppId: String, + secretKey: String, + isProduction: Bool + ) { + self.init( + affiseAppId: affiseAppId, + partParamName: nil, + partParamNameToken: nil, + appToken: nil, + isProduction: isProduction, + secretKey: secretKey + ) + } } diff --git a/AffiseAttributionLib/Classes/InternalCrossPlatform.swift b/AffiseAttributionLib/Classes/internal/platform/InternalCrossPlatform.swift similarity index 81% rename from AffiseAttributionLib/Classes/InternalCrossPlatform.swift rename to AffiseAttributionLib/Classes/internal/platform/InternalCrossPlatform.swift index 92e91b7..b8db569 100644 --- a/AffiseAttributionLib/Classes/InternalCrossPlatform.swift +++ b/AffiseAttributionLib/Classes/internal/platform/InternalCrossPlatform.swift @@ -13,14 +13,14 @@ public class InternalCrossPlatform { } public static func react() { - SdkPlatform.react() + InternalSdkPlatform.react() } public static func flutter() { - SdkPlatform.flutter() + InternalSdkPlatform.flutter() } public static func unity() { - SdkPlatform.unity() + InternalSdkPlatform.unity() } } diff --git a/AffiseAttributionLib/Classes/platform/SdkPlatform.swift b/AffiseAttributionLib/Classes/internal/platform/InternalSdkPlatform.swift similarity index 92% rename from AffiseAttributionLib/Classes/platform/SdkPlatform.swift rename to AffiseAttributionLib/Classes/internal/platform/InternalSdkPlatform.swift index f29eb92..8a5705c 100644 --- a/AffiseAttributionLib/Classes/platform/SdkPlatform.swift +++ b/AffiseAttributionLib/Classes/internal/platform/InternalSdkPlatform.swift @@ -1,4 +1,4 @@ -internal class SdkPlatform { +internal class InternalSdkPlatform { static let IOS = "ios" static private let REACT = "react" static private let FLUTTER = "flutter" diff --git a/AffiseAttributionLib/Classes/logs/LogsManagerImpl.swift b/AffiseAttributionLib/Classes/logs/LogsManagerImpl.swift index e54ba5d..e32d0d6 100644 --- a/AffiseAttributionLib/Classes/logs/LogsManagerImpl.swift +++ b/AffiseAttributionLib/Classes/logs/LogsManagerImpl.swift @@ -29,7 +29,7 @@ extension LogsManagerImpl: LogsManager { default: log = [ - ("network_error", error.localizedDescription.replacingOccurrences(of: "\"", with: "\\\"")) + ("network_error", error.localizedDescription.toJsonGuardString()) ] } @@ -46,7 +46,7 @@ extension LogsManagerImpl: LogsManager { //Create DevicedataLog storeLog( event: AffiseLog.DevicedataLog( - value: error.localizedDescription.replacingOccurrences(of: "\"", with: "\\\"") + value: error.localizedDescription.toJsonGuardString() ) ) } @@ -58,7 +58,7 @@ extension LogsManagerImpl: LogsManager { //Create UserdataLog storeLog( event: AffiseLog.UserdataLog( - value: error.localizedDescription.replacingOccurrences(of: "\"", with: "\\\"") + value: error.localizedDescription.toJsonGuardString() ) ) } @@ -70,7 +70,7 @@ extension LogsManagerImpl: LogsManager { //Create SdkLog storeLog( event: AffiseLog.SdkLog( - value: error.localizedDescription.replacingOccurrences(of: "\"", with: "\\\"") + value: error.localizedDescription.toJsonGuardString() ) ) } diff --git a/AffiseAttributionLib/Classes/modules/AffiseModule.swift b/AffiseAttributionLib/Classes/modules/AffiseModule.swift index 3fbaf61..57be865 100644 --- a/AffiseAttributionLib/Classes/modules/AffiseModule.swift +++ b/AffiseAttributionLib/Classes/modules/AffiseModule.swift @@ -24,7 +24,7 @@ open class AffiseModule { return [] } - open func status(_ onComplete: @escaping (_ data: [AffiseKeyValue]) -> Void) { + open func status(_ onComplete: @escaping OnKeyValueCallback) { onComplete([AffiseKeyValue("state", "enabled")]) } @@ -35,4 +35,12 @@ open class AffiseModule { public func getProvider() -> T? { baseProviders.getProvider() } + + public func getProviders(_ types: [Provider.Type]) -> [Provider] { + return baseProviders.getProviders(types) + } + + public func getRequestProviders() -> [Provider] { + return baseProviders.getRequestProviders() + } } diff --git a/AffiseAttributionLib/Classes/modules/AffiseModuleManager.swift b/AffiseAttributionLib/Classes/modules/AffiseModuleManager.swift index 2f27380..32dc306 100644 --- a/AffiseAttributionLib/Classes/modules/AffiseModuleManager.swift +++ b/AffiseAttributionLib/Classes/modules/AffiseModuleManager.swift @@ -34,7 +34,7 @@ internal class AffiseModuleManager { } } - func status(_ module: AffiseModules, _ onComplete: @escaping (_ data: [AffiseKeyValue]) -> Void) { + func status(_ module: AffiseModules, _ onComplete: @escaping OnKeyValueCallback) { modules[module]?.status(onComplete) } diff --git a/AffiseAttributionLib/Classes/modules/OnKeyValueCallback.swift b/AffiseAttributionLib/Classes/modules/OnKeyValueCallback.swift new file mode 100644 index 0000000..d31fd19 --- /dev/null +++ b/AffiseAttributionLib/Classes/modules/OnKeyValueCallback.swift @@ -0,0 +1 @@ +public typealias OnKeyValueCallback = (_ data: [AffiseKeyValue]) -> Void \ No newline at end of file diff --git a/AffiseAttributionLib/Classes/network/CloudConfig.swift b/AffiseAttributionLib/Classes/network/CloudConfig.swift index d5e1c7e..cf5101c 100644 --- a/AffiseAttributionLib/Classes/network/CloudConfig.swift +++ b/AffiseAttributionLib/Classes/network/CloudConfig.swift @@ -1,17 +1,13 @@ -// -// CloudConfig.swift -// app -// -// Created by Sergey Korney -// - - -struct CloudConfig { +public struct CloudConfig { /** * Urls for send data */ static let urls: Array = [ - "https://tracking.affattr.com/postback" + "https://tracking.affattr.com/postback" + ] + + public static let headers: [String:String] = [ + "Content-Type" : "application/json; charset=utf-8" ] } diff --git a/AffiseAttributionLib/Classes/network/CloudRepositoryImpl.swift b/AffiseAttributionLib/Classes/network/CloudRepositoryImpl.swift index c87b55a..6e357cb 100644 --- a/AffiseAttributionLib/Classes/network/CloudRepositoryImpl.swift +++ b/AffiseAttributionLib/Classes/network/CloudRepositoryImpl.swift @@ -1,11 +1,3 @@ -// -// CloudRepositoryImpl.swift -// app -// -// Created by Sergey Korney -// - - internal class CloudRepositoryImpl { private let ATTEMPTS_TO_SEND = 3 @@ -27,27 +19,18 @@ internal class CloudRepositoryImpl { /** * Send [data] to [url] */ - private func createRequest(url: String, data: Array) throws { - - let httpsUrl = URL(string: url)! - - let jsonString = converter.convert(from: data) - let sendData = jsonString.data(using: .utf8)! + private func createRequest(url: String, data: [PostBackModel]) -> HttpResponse { + guard let httpsUrl = url.toURL() else { return HttpResponse(0, "", nil) } // let sendData = try JSONSerialization.data(withJSONObject: dictArray, options: .prettyPrinted) //Create request - let (data, response) = try networkService.executeRequest(httpsUrl: httpsUrl, - method: .POST, - data: sendData, - timeout: TIMEOUT_SEND, - headers: createHeaders()) - if (response.statusCode != 200) { - var message: String? - if let data = data { - message = String.init(data: data, encoding: .utf8) - } - throw AffiseError.network(status: response.statusCode, message: message) - } + return networkService.executeRequest( + httpsUrl: httpsUrl, + method: .POST, + data: converter.convert(from: data).toData(), + timeout: TIMEOUT_SEND, + headers: createHeaders() + ) } /** @@ -77,16 +60,17 @@ extension CloudRepositoryImpl: CloudRepository { //While has attempts and not send while (attempts != 0 && !send) { - do { - //Create request - try createRequest(url: url, data: data) - + //Create request + let response = createRequest(url: url, data: data) + + if isHttpValid(response.code) { //Send is ok send = true - } catch { + } else { attempts = attempts - 1 //Check attempts if (attempts == 0) { + let error = AffiseError.network(status: response.code, message: response.body) //Add throwable throw AffiseError.cloud(url: url, error: error, attempts: ATTEMPTS_TO_SEND, retry: true) } diff --git a/AffiseAttributionLib/Classes/network/HttpRequest.swift b/AffiseAttributionLib/Classes/network/HttpRequest.swift new file mode 100644 index 0000000..cb93ec8 --- /dev/null +++ b/AffiseAttributionLib/Classes/network/HttpRequest.swift @@ -0,0 +1,18 @@ +@objc +public class HttpRequest: NSObject { + public let url: URL + public let method: NetworkServiceMethod + public let headers: [String: String] + public let body: String? + + public init(_ url: URL, _ method: NetworkServiceMethod, _ headers: [String: String], _ body: String? = nil) { + self.url = url + self.method = method + self.headers = headers + self.body = body + } + + public override var description: String { + return "HttpRequest(url=\(url.absoluteString), method=\(method), headers=\(headers), body=\(body ?? ""))" + } +} diff --git a/AffiseAttributionLib/Classes/network/HttpResponse.swift b/AffiseAttributionLib/Classes/network/HttpResponse.swift new file mode 100644 index 0000000..20c8d42 --- /dev/null +++ b/AffiseAttributionLib/Classes/network/HttpResponse.swift @@ -0,0 +1,16 @@ +@objc +public class HttpResponse: NSObject { + public let code: Int + public let message: String + public let body: String? + + public init(_ code: Int, _ message: String, _ body: String? = nil) { + self.code = code + self.message = message + self.body = body + } + + public override var description: String { + return "HttpResponse(code=\(code), message=\(message), body=\(body ?? ""))" + } +} diff --git a/AffiseAttributionLib/Classes/network/NetworkService.swift b/AffiseAttributionLib/Classes/network/NetworkService.swift index 068c81a..d0176ea 100644 --- a/AffiseAttributionLib/Classes/network/NetworkService.swift +++ b/AffiseAttributionLib/Classes/network/NetworkService.swift @@ -1,11 +1,3 @@ -// -// NetworkService.swift -// app -// -// Created by Sergey Korney -// - - /** * Method of connection */ @@ -16,15 +8,19 @@ public enum NetworkServiceMethod: String { public protocol NetworkService { + func setDebug(_ debugRequest: @escaping DebugOnNetworkCallback) + /** * Create request and execute result * Executes [method] on url [httpsUrl] with body of [data] and [headers] * * @return string representation of request */ - func executeRequest(httpsUrl: URL, - method: NetworkServiceMethod, - data: Data, - timeout: TimeInterval, - headers: Dictionary) throws -> (Data?, HTTPURLResponse) + func executeRequest( + httpsUrl: URL, + method: NetworkServiceMethod, + data: Data?, + timeout: TimeInterval, + headers: [String:String] + ) -> HttpResponse } diff --git a/AffiseAttributionLib/Classes/network/NetworkServiceImpl.swift b/AffiseAttributionLib/Classes/network/NetworkServiceImpl.swift index 2aae812..0fcd54a 100644 --- a/AffiseAttributionLib/Classes/network/NetworkServiceImpl.swift +++ b/AffiseAttributionLib/Classes/network/NetworkServiceImpl.swift @@ -1,13 +1,6 @@ -// -// NetworkServiceImpl.swift -// app -// -// Created by Sergey Korney -// - - public class NetworkServiceImpl { private let urlSession: URLSession + private var debugRequest: DebugOnNetworkCallback? init(urlSession: URLSession) { self.urlSession = urlSession; @@ -15,17 +8,27 @@ public class NetworkServiceImpl { } extension NetworkServiceImpl: NetworkService { - public func executeRequest(httpsUrl: URL, - method: NetworkServiceMethod, - data: Data, - timeout: TimeInterval, - headers: Dictionary) throws -> (Data?, HTTPURLResponse) { + + public func setDebug(_ debugRequest: @escaping DebugOnNetworkCallback) { + self.debugRequest = debugRequest + } + + public func executeRequest( + httpsUrl: URL, + method: NetworkServiceMethod, + data: Data?, + timeout: TimeInterval, + headers: [String:String] + ) -> HttpResponse { + var responseCode: Int = 0 + var responseMessage: String = "" + var responseBody: String? = nil var request = URLRequest.init(url: httpsUrl) request.httpMethod = method.rawValue request.httpShouldHandleCookies = false; request.httpBody = data - let contentLength = "\(data.count)" + let contentLength = "\(data?.count ?? 0)" request.addValue(contentLength, forHTTPHeaderField: "Content-Length") request.timeoutInterval = timeout @@ -33,18 +36,14 @@ extension NetworkServiceImpl: NetworkService { request.addValue(value, forHTTPHeaderField: key) } request.addValue("must-revalidate, no-store, no-cache", forHTTPHeaderField: "Cache-Control") - - var result: Data? - var response: URLResponse? - var error: Error? - + var complete: Bool = false - let task = urlSession.dataTask(with: request) { data, resp, err in - result = data - response = resp - error = err - + let task = urlSession.dataTask(with: request) { responseData, response, error in + responseBody = responseData?.toString() + responseCode = (response as? HTTPURLResponse)?.statusCode ?? 0 + responseMessage = error?.localizedDescription ?? "" + complete = true } task.resume() @@ -52,11 +51,11 @@ extension NetworkServiceImpl: NetworkService { while !complete { Thread.sleep(forTimeInterval: 1) } - - if let error = error { - throw error + + let httpResponse = HttpResponse(responseCode, responseMessage, responseBody) + if let debugRequest = debugRequest { + debugRequest(HttpRequest(httpsUrl, method, headers, data?.toString()), httpResponse) } - - return (result, response as! HTTPURLResponse) + return httpResponse } } diff --git a/AffiseAttributionLib/Classes/parameters/providers/HardwareNameProvider.swift b/AffiseAttributionLib/Classes/parameters/providers/HardwareNameProvider.swift index 9d47032..864cad9 100644 --- a/AffiseAttributionLib/Classes/parameters/providers/HardwareNameProvider.swift +++ b/AffiseAttributionLib/Classes/parameters/providers/HardwareNameProvider.swift @@ -1,11 +1,3 @@ -// -// HardwareNameProvider.swift -// AffiseAttributionLib -// -// Created by Sergey Korney -// - - /** * Provider for parameter [ProviderType.HARDWARE_NAME] */ diff --git a/AffiseAttributionLib/Classes/parameters/providers/PlatformNameProvider.swift b/AffiseAttributionLib/Classes/parameters/providers/PlatformNameProvider.swift index 63f4c94..f3d1f39 100644 --- a/AffiseAttributionLib/Classes/parameters/providers/PlatformNameProvider.swift +++ b/AffiseAttributionLib/Classes/parameters/providers/PlatformNameProvider.swift @@ -4,7 +4,7 @@ class PlatformNameProvider: StringPropertyProvider { override func provide() -> String? { - return SdkPlatform.IOS + return InternalSdkPlatform.IOS } public override func getOrder() -> Float { diff --git a/AffiseAttributionLib/Classes/parameters/providers/SdkPlatformNameProvider.swift b/AffiseAttributionLib/Classes/parameters/providers/SdkPlatformNameProvider.swift index 8d3f393..d880145 100644 --- a/AffiseAttributionLib/Classes/parameters/providers/SdkPlatformNameProvider.swift +++ b/AffiseAttributionLib/Classes/parameters/providers/SdkPlatformNameProvider.swift @@ -4,7 +4,7 @@ class SdkPlatformNameProvider: StringPropertyProvider { override func provide() -> String? { - return SdkPlatform.info + return InternalSdkPlatform.info } public override func getOrder() -> Float { diff --git a/AffiseAttributionLib/Classes/parameters/providers/base/Provider+Extension.swift b/AffiseAttributionLib/Classes/parameters/providers/base/Provider+Extension.swift index 437dc98..6d875ac 100644 --- a/AffiseAttributionLib/Classes/parameters/providers/base/Provider+Extension.swift +++ b/AffiseAttributionLib/Classes/parameters/providers/base/Provider+Extension.swift @@ -18,6 +18,23 @@ public extension Array where Element == Provider { } return result } + + func getProviders(_ types: [Provider.Type]) -> [Provider] { + return self.filter { p in + types.contains(where: { type(of: p) == $0 }) + } + } + + func getRequestProviders() -> [Provider] { + return getProviders([ + CreatedTimeProvider.self, + AffiseAppIdProvider.self, + AffisePackageAppNameProvider.self, + AffAppTokenPropertyProvider.self, + AffiseDeviceIdProvider.self, + RandomUserIdProvider.self + ]) + } } internal extension Provider { diff --git a/AffiseAttributionLib/Classes/parameters/providers/factory/PostBackModelFactory.swift b/AffiseAttributionLib/Classes/parameters/providers/factory/PostBackModelFactory.swift index 765f065..113f38c 100644 --- a/AffiseAttributionLib/Classes/parameters/providers/factory/PostBackModelFactory.swift +++ b/AffiseAttributionLib/Classes/parameters/providers/factory/PostBackModelFactory.swift @@ -1,11 +1,3 @@ -// -// PostBackModelFactory.swift -// app -// -// Created by Sergey Korney -// - - internal class PostBackModelFactory { private var providers: [Provider] @@ -49,4 +41,12 @@ internal class PostBackModelFactory { func getProviders() -> [Provider] { return providers } + + func getProviders(_ types: [Provider.Type]) -> [Provider] { + return providers.getProviders(types) + } + + func getRequestProviders() -> [Provider] { + return providers.getRequestProviders() + } } diff --git a/AffiseAttributionLib/Classes/parameters/providers/init/AffSDKVersionProvider.swift b/AffiseAttributionLib/Classes/parameters/providers/init/AffSDKVersionProvider.swift index b69f9c2..fb13c47 100644 --- a/AffiseAttributionLib/Classes/parameters/providers/init/AffSDKVersionProvider.swift +++ b/AffiseAttributionLib/Classes/parameters/providers/init/AffSDKVersionProvider.swift @@ -5,7 +5,7 @@ class AffSDKVersionProvider: StringPropertyProvider { override func provide() -> String? { - return "1.6.12" + return "1.6.13" } public override func getOrder() -> Float { diff --git a/AffiseAttributionLib/Classes/parameters/providers/init/AffisePackageAppNameProvider.swift b/AffiseAttributionLib/Classes/parameters/providers/init/AffisePackageAppNameProvider.swift index 071ff0f..70a12fd 100644 --- a/AffiseAttributionLib/Classes/parameters/providers/init/AffisePackageAppNameProvider.swift +++ b/AffiseAttributionLib/Classes/parameters/providers/init/AffisePackageAppNameProvider.swift @@ -1,4 +1,3 @@ - /** * Provider for parameter [ProviderType.AFFISE_PKG_APP_NAME] * diff --git a/AffiseAttributionLib/Classes/utils/HttpUtils.swift b/AffiseAttributionLib/Classes/utils/HttpUtils.swift new file mode 100644 index 0000000..b6f8dd2 --- /dev/null +++ b/AffiseAttributionLib/Classes/utils/HttpUtils.swift @@ -0,0 +1,3 @@ +public func isHttpValid(_ responseCode: Int) -> Bool { + return (200...399).contains(responseCode) +} diff --git a/AffiseAttributionLib/Classes/utils/StringUtils.swift b/AffiseAttributionLib/Classes/utils/StringUtils.swift new file mode 100644 index 0000000..d391378 --- /dev/null +++ b/AffiseAttributionLib/Classes/utils/StringUtils.swift @@ -0,0 +1,27 @@ +public func isNullOrBlank(_ text: String?) -> Bool { + guard let text = text else { + return true + } + return text.isEmpty +} + +public extension Data { + func toString() -> String? { + return String.init(data: self, encoding: .utf8) + } +} + +public extension String { + func toData() -> Data? { + return self.data(using: .utf8) + } + + func toURL() -> URL? { + return Foundation.URL(string: self) + } + + + func toJsonGuardString() -> String { + return self.replacingOccurrences(of: "\"", with: "\\\"") + } +} diff --git a/AffiseInternal.podspec b/AffiseInternal.podspec index d809079..ca714f6 100644 --- a/AffiseInternal.podspec +++ b/AffiseInternal.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |spec| spec.name = "AffiseInternal" - spec.version = ENV['LIB_VERSION'] || "1.6.12" + spec.version = ENV['LIB_VERSION'] || "1.6.13" spec.summary = "Affise Internal library" spec.description = "Affise Internal wrapper library for crossplatform" spec.homepage = "https://github.com/affise/sdk-ios" diff --git a/AffiseInternal/Classes/AffiseApiMethod.swift b/AffiseInternal/Classes/AffiseApiMethod.swift index b00aaaa..b97f5f0 100644 --- a/AffiseInternal/Classes/AffiseApiMethod.swift +++ b/AffiseInternal/Classes/AffiseApiMethod.swift @@ -21,12 +21,17 @@ public enum AffiseApiMethod: Int { case GET_RANDOM_USER_ID case GET_RANDOM_DEVICE_ID case GET_PROVIDERS + // callback case GET_REFERRER_CALLBACK case GET_REFERRER_VALUE_CALLBACK case GET_STATUS_CALLBACK case REGISTER_DEEPLINK_CALLBACK case SKAD_REGISTER_ERROR_CALLBACK - case SKAD_POSTBACK_ERROR_CALLBACK + case SKAD_POSTBACK_ERROR_CALLBACK + // debug + case DEBUG_VALIDATE_CALLBACK + case DEBUG_NETWORK_CALLBACK + // builder case AFFISE_BUILDER var value: String { @@ -58,6 +63,8 @@ public enum AffiseApiMethod: Int { case .REGISTER_DEEPLINK_CALLBACK: return "register_deeplink_callback" case .SKAD_REGISTER_ERROR_CALLBACK: return "skad_register_error_callback" case .SKAD_POSTBACK_ERROR_CALLBACK: return "skad_postback_error_callback" + case .DEBUG_VALIDATE_CALLBACK: return "debug_validate_callback" + case .DEBUG_NETWORK_CALLBACK: return "debug_network_callback" case .AFFISE_BUILDER: return "affise_builder" } } diff --git a/AffiseInternal/Classes/AffiseApiWrapper.swift b/AffiseInternal/Classes/AffiseApiWrapper.swift index 7c8c5b5..e30705e 100644 --- a/AffiseInternal/Classes/AffiseApiWrapper.swift +++ b/AffiseInternal/Classes/AffiseApiWrapper.swift @@ -7,7 +7,8 @@ public class AffiseApiWrapper: NSObject { private let factory = AffiseEventFactory() private let affiseBuilder = AffiseBuilder() - private var callback: ((_ api: String, _ data: [String: Any?]) -> Void)? = nil + public typealias OnCallback = (_ api: String, _ data: [String: Any?]) -> Void + private var callback: OnCallback? = nil private var app: UIApplication? = nil private var launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil @@ -89,6 +90,8 @@ public class AffiseApiWrapper: NSObject { case .REGISTER_DEEPLINK_CALLBACK: callRegisterDeeplinkCallback(api, map: map, result: result) case .SKAD_REGISTER_ERROR_CALLBACK: callSkadRegisterErrorCallback(api, map: map, result: result) case .SKAD_POSTBACK_ERROR_CALLBACK: callSkadPostbackErrorCallback(api, map: map, result: result) + case .DEBUG_VALIDATE_CALLBACK: callDebugValidateCallback(api, map: map, result: result) + case .DEBUG_NETWORK_CALLBACK: callDebugNetworkCallback(api, map: map, result: result) case .AFFISE_BUILDER: callAffiseBuilder(api, map: map, result: result) default: @@ -330,7 +333,43 @@ public class AffiseApiWrapper: NSObject { self.callback?(api.method, data) } } - + + private func callDebugValidateCallback(_ api: AffiseApiMethod, map: [String: Any?], result: AffiseResult?) { + guard let uuid: String = map.opt(UUID) else { + result?.error("api [\(api.method)]: no valid Callback UUID") + return + } + + Affise.Debug.validate { status in + let data: [String: Any?] = [ + self.UUID: uuid, + api.method: status.status, + ] + self.callback?(api.method, data) + } + } + + private func callDebugNetworkCallback(_ api: AffiseApiMethod, map: [String: Any?], result: AffiseResult?) { + Affise.Debug.network { request, response in + let data: [String: Any?] = [ + api.method: [ + "request": [ + "method": request.method.rawValue, + "url": request.url.absoluteString, + "headers": request.headers, + "body": "\(request.body?.toJsonGuardString() ?? "")", + ], + "response": [ + "code": response.code, + "message": response.message, + "body": "\(response.body?.toJsonGuardString() ?? "")", + ], + ] + ] + self.callback?(api.method, data) + } + } + private func callAffiseBuilder(_ api: AffiseApiMethod, map: [String: Any?], result: AffiseResult?) { affiseBuilder.call(api, map, result) } diff --git a/AffiseModule.podspec b/AffiseModule.podspec index 4b6f0e7..bce885f 100644 --- a/AffiseModule.podspec +++ b/AffiseModule.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = "AffiseModule" - s.version = ENV["LIB_VERSION"] || "1.6.12" + s.version = ENV["LIB_VERSION"] || "1.6.13" s.summary = "Affise Modules" s.description = "Affise module collection" s.homepage = "https://github.com/affise/sdk-ios" diff --git a/AffiseModule/Status/Classes/StatusModule.swift b/AffiseModule/Status/Classes/StatusModule.swift index 4169978..2f9ee68 100644 --- a/AffiseModule/Status/Classes/StatusModule.swift +++ b/AffiseModule/Status/Classes/StatusModule.swift @@ -6,37 +6,22 @@ class StatusModule: AffiseModule { private var checkStatusUseCase: CheckStatusUseCase? = nil private lazy var stringToKeyValueConverter: StringToKeyValueConverter = StringToKeyValueConverter() - private lazy var providersToJsonStringConverter: ProvidersToJsonStringConverter = ProvidersToJsonStringConverter() override func initialize() { - - let createdTimeProvider: CreatedTimeProvider? = getProvider() - let affiseAppIdProvider: AffiseAppIdProvider? = getProvider() - let affisePackageAppNameProvider: AffisePackageAppNameProvider? = getProvider() - let affAppTokenPropertyProvider: AffAppTokenPropertyProvider? = getProvider() - let affiseDeviceIdProvider: AffiseDeviceIdProvider? = getProvider() - let randomUserIdProvider: RandomUserIdProvider? = getProvider() - - let providers: [Provider?] = [ - createdTimeProvider, - affiseAppIdProvider, - affisePackageAppNameProvider, - affAppTokenPropertyProvider, - affiseDeviceIdProvider, - randomUserIdProvider, - ] - + guard let providersToJsonStringConverter: ProvidersToJsonStringConverter = get() else { return } + guard let networkService: NetworkService = get() else { return } + checkStatusUseCase = CheckStatusUseCaseImpl( + affiseModule: self, logsManager: logsManager, - networkService: get(), - providers: providers.compactMap { $0 }, + networkService: networkService, converter: providersToJsonStringConverter, keyValueConverter: stringToKeyValueConverter ) } - override func status(_ onComplete: @escaping ([AffiseKeyValue]) -> Void) { - checkStatusUseCase?.send(onComplete) + override func status(_ onComplete: @escaping OnKeyValueCallback) { + checkStatusUseCase?.send(onComplete) ?? onComplete([]) } } diff --git a/AffiseModule/Status/Classes/usecase/CheckStatusUseCase.swift b/AffiseModule/Status/Classes/usecase/CheckStatusUseCase.swift index 186cd24..c7c0d1b 100644 --- a/AffiseModule/Status/Classes/usecase/CheckStatusUseCase.swift +++ b/AffiseModule/Status/Classes/usecase/CheckStatusUseCase.swift @@ -3,5 +3,5 @@ import AffiseAttributionLib protocol CheckStatusUseCase { - func send(_ onComplete: @escaping (_ data: [AffiseKeyValue]) -> Void) + func send(_ onComplete: @escaping OnKeyValueCallback) } diff --git a/AffiseModule/Status/Classes/usecase/CheckStatusUseCaseImpl.swift b/AffiseModule/Status/Classes/usecase/CheckStatusUseCaseImpl.swift index 8e19eee..928d635 100644 --- a/AffiseModule/Status/Classes/usecase/CheckStatusUseCaseImpl.swift +++ b/AffiseModule/Status/Classes/usecase/CheckStatusUseCaseImpl.swift @@ -11,13 +11,13 @@ internal class CheckStatusUseCaseImpl { let logsManager: LogsManager let converter: ProvidersToJsonStringConverter let keyValueConverter: StringToKeyValueConverter - let networkService: NetworkService? + let networkService: NetworkService let providers: [Provider] init( + affiseModule: AffiseModule, logsManager: LogsManager, - networkService: NetworkService?, - providers: [Provider], + networkService: NetworkService, converter: ProvidersToJsonStringConverter, keyValueConverter: StringToKeyValueConverter ) { @@ -25,49 +25,26 @@ internal class CheckStatusUseCaseImpl { self.converter = converter self.keyValueConverter = keyValueConverter self.networkService = networkService - self.providers = providers + self.providers = affiseModule.getRequestProviders() } - private func createRequest(url: String, data: [Provider]) throws -> String? { - guard let networkService = networkService else { - return nil - } - - let httpsUrl = Foundation.URL(string: url)! - - let jsonString = converter.convert(from: data) - let sendData = jsonString.data(using: .utf8)! + private func createRequest() -> HttpResponse { + guard let httpsUrl = URL.toURL() else { return HttpResponse(0, "", nil) } //Create request - let (data, response) = try networkService.executeRequest( + return networkService.executeRequest( httpsUrl: httpsUrl, method: .POST, - data: sendData, + data: converter.convert(from: providers).toData(), timeout: TIMEOUT_SEND, - headers: createHeaders() + headers: CloudConfig.headers ) - - var result: String? - if let data = data { - result = String.init(data: data, encoding: .utf8) - } - - if (response.statusCode != 200) { - throw AffiseError.network(status: response.statusCode, message: result) - } - return result - } - - private func createHeaders() -> Dictionary { - return [ - "Content-Type" : "application/json; charset=utf-8" - ] } } extension CheckStatusUseCaseImpl: CheckStatusUseCase { - func send(_ onComplete: @escaping (_ data: [AffiseKeyValue]) -> Void) { + func send(_ onComplete: @escaping OnKeyValueCallback) { DispatchQueue.global(qos:.background).async { [weak self] in self?.sendWithRepeat(onComplete) { Thread.sleep(forTimeInterval: CheckStatusUseCaseImpl.TIME_DELAY_SENDING) @@ -75,7 +52,7 @@ extension CheckStatusUseCaseImpl: CheckStatusUseCase { } } - func sendWithRepeat( _ onComplete: @escaping (_ data: [AffiseKeyValue]) -> Void, onFailedAttempt: () -> Void) { + func sendWithRepeat( _ onComplete: @escaping OnKeyValueCallback, onFailedAttempt: () -> Void) { //attempts to send var attempts = ATTEMPTS_TO_SEND @@ -84,17 +61,18 @@ extension CheckStatusUseCaseImpl: CheckStatusUseCase { //While has attempts and not send while (attempts != 0 && !send) { - do { - let response = try createRequest(url: URL, data: providers) - onComplete(keyValueConverter.convert(from: response)) + let response = createRequest() + if isHttpValid(response.code) { + onComplete(keyValueConverter.convert(from: response.body)) //Send is ok send = true - } catch { + } else { attempts = attempts - 1 //Check attempts if (attempts == 0) { onComplete([]) + let error = AffiseError.network(status: response.code, message: response.body) //Log error logsManager.addSdkError(error: AffiseError.cloud(url: URL, error: error, attempts: ATTEMPTS_TO_SEND, retry: true)) } else { diff --git a/AffiseSKAdNetwork.podspec b/AffiseSKAdNetwork.podspec index 9263453..19cafe7 100644 --- a/AffiseSKAdNetwork.podspec +++ b/AffiseSKAdNetwork.podspec @@ -5,7 +5,7 @@ Pod::Spec.new do |spec| spec.name = "AffiseSKAdNetwork" - spec.version = ENV['LIB_VERSION'] || "1.6.12" + spec.version = ENV['LIB_VERSION'] || "1.6.13" spec.summary = "AffiseSKAdNetwork iOS library" spec.description = "Affise library for StoreKit Ad Network (SKAdNetwork)" spec.homepage = "https://github.com/affise/sdk-ios" diff --git a/README.md b/README.md index dd113c4..f2fdb18 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ | Pod | Version | | ---- |:-------:| -| `AffiseAttributionLib` | [`1.6.12`](https://github.com/CocoaPods/Specs/tree/master/Specs/a/9/3/AffiseAttributionLib) | -| `AffiseSKAdNetwork` | [`1.6.12`](https://github.com/CocoaPods/Specs/tree/master/Specs/3/6/f/AffiseSKAdNetwork) | -| `AffiseModule/Status` | [`1.6.12`](https://github.com/CocoaPods/Specs/tree/master/Specs/0/3/d/AffiseModule/) | +| `AffiseAttributionLib` | [`1.6.13`](https://github.com/CocoaPods/Specs/tree/master/Specs/a/9/3/AffiseAttributionLib) | +| `AffiseSKAdNetwork` | [`1.6.13`](https://github.com/CocoaPods/Specs/tree/master/Specs/3/6/f/AffiseSKAdNetwork) | +| `AffiseModule/Status` | [`1.6.13`](https://github.com/CocoaPods/Specs/tree/master/Specs/0/3/d/AffiseModule/) | - [Affise Attribution iOS Library](#affise-attribution-ios-library) - [Description](#description) @@ -45,7 +45,9 @@ - [ironSource](#ironsource) - [Custom](#custom) - [ConversionId](#conversionid) - +- [Debug](#debug) + - [Validate credentials](#validate-credentials) + # Description Affise SDK is a software you can use to collect app usage statistics, device identifiers, deeplink usage, track install @@ -61,18 +63,18 @@ To add the SDK using Cocoapods, specify the version you want to use in your Podf ```ruby # Affise SDK library -pod 'AffiseAttributionLib', '~> 1.6.12' +pod 'AffiseAttributionLib', '~> 1.6.13' # Affise module -pod 'AffiseModule/Status', '~> 1.6.12' +pod 'AffiseModule/Status', '~> 1.6.13' ``` Get source directly from GitHub ```ruby # Affise SDK library -pod 'AffiseAttributionLib', :git => 'https://github.com/affise/sdk-ios.git', :tag => '1.6.12' +pod 'AffiseAttributionLib', :git => 'https://github.com/affise/sdk-ios.git', :tag => '1.6.13' # Affise module -pod 'AffiseModule/Status', :git => 'https://github.com/affise/sdk-ios.git', :tag => '1.6.12' +pod 'AffiseModule/Status', :git => 'https://github.com/affise/sdk-ios.git', :tag => '1.6.13' ``` ### Initialize @@ -142,14 +144,14 @@ To add the SDK using Cocoapods, specify the version you want to use in your Podf ```ruby # Wrapper for StoreKit Ad Network -pod 'AffiseSKAdNetwork', '~> 1.6.12' +pod 'AffiseSKAdNetwork', '~> 1.6.13' ``` Get source directly from GitHub ```ruby # Wrapper for StoreKit Ad Network -pod 'AffiseSKAdNetwork', :git => 'https://github.com/affise/sdk-ios.git', :tag => '1.6.12' +pod 'AffiseSKAdNetwork', :git => 'https://github.com/affise/sdk-ios.git', :tag => '1.6.13' ``` For `swift` use: @@ -631,17 +633,15 @@ Affise.addPushToken(token) To integrate deeplink support You can find out how to set up deeplinks in the [official documentation](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app). -Register deeplink callback right after Affise.init(..) +Register deeplink callback right after `Affise.init(..)` ```swift Affise.init(..) Affise.registerDeeplinkCallback { url in let component = URLComponents(string: url.absoluteString)! - let screen = component.queryItems?.first(where: {$0.name == "screen"})?.value - if let screen = screen, screen == "special_offer" { - // open special offer activity - } else { - // open another activity + let screen = component.queryItems?.first(where: {$0.name == ""})?.value + if let screen = screen, screen == "" { + // handle value } } ``` @@ -995,3 +995,35 @@ let conversionId = event.customPredefined().conversionId("ORDER_ID", "PRODUCT_ID Affise.sendEvent(event) ``` + +# Debug + +## Validate credentials + +> **Warning** +> Debug methods WON'T work on Production + +Validate your credentials by receiving `ValidationStatus` values: + +- `VALID` - your credentials are valid +- `INVALID_APP_ID` - your app id is not valid +- `INVALID_SECRET_KEY` - your SDK secretKey is not valid +- `PACKAGE_NAME_NOT_FOUND` - your application package name not found +- `NOT_WORKING_ON_PRODUCTION` - you using debug method on production +- `NETWORK_ERROR` - network or server not available (for example `Airoplane mode` is active) + +```swift +Affise.load(app: application, + initProperties: AffiseInitProperties( + affiseAppId: "Your appId", + secretKey: "Your secretKey", + isProduction: false, //Set Production to false to enable debug methods + ), + launchOptions: launchOptions +) + + +Affise.Debug.validate { status in + // Handle validation status +} +``` diff --git a/example/app/app/AppDelegate.swift b/example/app/app/AppDelegate.swift index 7319d22..3a87d7e 100644 --- a/example/app/app/AppDelegate.swift +++ b/example/app/app/AppDelegate.swift @@ -1,10 +1,3 @@ -// -// AppDelegate.swift -// app -// -// Created by Sergey Korney -// - import UIKit import AffiseAttributionLib import AffiseSKAdNetwork @@ -17,34 +10,49 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. + + // Initialize https://github.com/affise/sdk-ios#initialize let properties = AffiseInitProperties( affiseAppId: "129", - secretKey: "93a40b54-6f12-443f-a250-ebf67c5ee4d2" + secretKey: "93a40b54-6f12-443f-a250-ebf67c5ee4d2", + isProduction: false //Set Production to false to enable debug methods ) Affise.load(app: application, initProperties: properties, launchOptions: launchOptions) + // Deeplinks https://github.com/affise/sdk-ios#deeplinks Affise.registerDeeplinkCallback { url in let component = URLComponents(string: url.absoluteString)! let screen = component.queryItems?.first(where: {$0.name == "screen"})?.value if let screen = screen, screen == "special_offer" { - // open special offer activity - } else { - // open another activity + // handle value } } + // StoreKit Ad Network https://github.com/affise/sdk-ios#storekit-ad-network // AffiseSKAd.register { error in -// // Handle error +// // handle error // } -// + + // StoreKit Ad Network https://github.com/affise/sdk-ios#storekit-ad-network // AffiseSKAd.updatePostbackConversionValue(fineValue: 1, coarseValue: CoarseConversionValue.medium) { error in // // handle error // } - + // Get module state https://github.com/affise/sdk-ios#get-module-state // Affise.getStatus(AffiseModules.Status) { result in // // handle status // } + + // Debug: Validate credentials https://github.com/affise/sdk-ios#validate-credentials + Affise.Debug.validate { status in + debugPrint("Affise: validate = \(status)") + } + + // Debug: network request/response + Affise.Debug.network { (request, response) in + debugPrint("Affise: \(request)") + debugPrint("Affise: \(response)") + } return true }