From 3541071eeed94e1673c2ec44c33820931c5e89e5 Mon Sep 17 00:00:00 2001 From: Joshua Liebowitz Date: Tue, 24 Aug 2021 12:56:37 -0700 Subject: [PATCH] Update inheritance, visibility, objc compatibility (#766) * initial work * Update inheritance, visibility, objc compatibility Also fix file headers. resolves #763 * Update for comments :rocket: --- Package.swift | 10 +- PublicSDKAPITester/RCOfferingAPI.m | 12 +- Purchases.xcodeproj/project.pbxproj | 2 +- .../ASIdentifierManagerProxy.swift | 8 +- .../Attribution/AdClientProxy.swift | 11 +- .../Attribution/AttributionData.swift | 10 +- .../Attribution/AttributionFetcher.swift | 18 +- .../Attribution/AttributionPoster.swift | 35 ++-- .../Attribution/AttributionTypeFactory.swift | 6 +- .../Attribution/TrackingManagerProxy.swift | 12 +- PurchasesCoreSwift/Caching/DeviceCache.swift | 62 +++---- .../Caching/InMemoryCachedObject.swift | 15 +- .../Date+Extensions.swift | 18 +- .../DateFormatter+Extensions.swift | 13 +- .../Dictionary+Extensions.swift | 11 +- .../Error+Extensions.swift | 4 +- .../Locale+Extensions.swift | 10 +- .../String+Extensions.swift | 1 - .../Identity/IdentityManager.swift | 28 ++- .../Identity/PurchaserInfoManager.swift | 36 ++-- .../IntroEligibilityCalculator.swift | 26 +-- .../BasicTypes/ASN1Container.swift | 25 ++- .../BasicTypes/ASN1ObjectIdentifier.swift | 15 +- .../BasicTypes/AppleReceipt.swift | 16 +- .../BasicTypes/InAppPurchase.swift | 19 +- .../Builders/ASN1ContainerBuilder.swift | 14 +- .../ASN1ObjectIdentifierBuilder.swift | 13 +- .../Builders/AppleReceiptBuilder.swift | 15 +- .../ArraySlice_UInt8+Extensions.swift | 22 ++- .../DataConverters/UInt8+Extensions.swift | 18 +- .../LocalReceiptParsing/ReceiptParser.swift | 30 ++-- .../ReceiptParsingError.swift | 13 +- PurchasesCoreSwift/Logging/LogIntent.swift | 14 +- PurchasesCoreSwift/Logging/Logger.swift | 35 ++-- .../Logging/Strings/AttributionStrings.swift | 15 +- .../Logging/Strings/ConfigureStrings.swift | 28 +-- .../Logging/Strings/NetworkStrings.swift | 12 +- .../Logging/Strings/OfferingStrings.swift | 12 +- .../Logging/Strings/PurchaseStrings.swift | 10 +- .../Strings/PurchaserInfoStrings.swift | 12 +- .../Logging/Strings/ReceiptStrings.swift | 16 +- .../Logging/Strings/RestoreStrings.swift | 12 +- .../Logging/Strings/Strings.swift | 19 +- PurchasesCoreSwift/Misc/DateExtensions.swift | 13 +- PurchasesCoreSwift/Misc/DateProvider.swift | 10 +- .../Misc/ISOPeriodFormatter.swift | 11 +- .../Misc/OperationDispatcher.swift | 17 +- PurchasesCoreSwift/Misc/SystemInfo.swift | 62 ++++--- PurchasesCoreSwift/Networking/Backend.swift | 31 ++-- .../Networking/ETagAndResponseWrapper.swift | 12 +- .../Networking/ETagManager.swift | 22 ++- .../Networking/HTTPClient.swift | 1 + .../Networking/HTTPRequest.swift | 10 +- .../Networking/HTTPResponse.swift | 10 +- .../Networking/HTTPStatusCodes.swift | 10 +- .../Public/EntitlementInfo.swift | 163 ++++++++++-------- .../Public/Error Handling/ErrorUtils.swift | 9 +- PurchasesCoreSwift/Public/Offering.swift | 14 +- PurchasesCoreSwift/Public/Offerings.swift | 11 +- PurchasesCoreSwift/Public/Purchases.swift | 2 +- .../{ => Public}/Transaction.swift | 14 +- .../Purchasing/OfferingsFactory.swift | 13 +- .../Purchasing/OfferingsManager.swift | 21 +-- .../Purchasing/ProductInfo.swift | 10 +- .../Purchasing/ProductInfoEnums.swift | 10 +- .../Purchasing/ProductInfoExtractor.swift | 10 +- .../Purchasing/ProductsManager.swift | 23 ++- .../Purchasing/ProductsRequestFactory.swift | 11 +- .../Purchasing/PromotionalOffer.swift | 10 +- .../Purchasing/PurchasesOrchestrator.swift | 77 ++++----- .../Purchasing/ReceiptFetcher.swift | 18 +- .../Purchasing/ReceiptRefreshPolicy.swift | 13 +- .../Purchasing/StoreKitRequestFetcher.swift | 23 ++- .../Purchasing/StoreKitWrapper.swift | 72 ++++---- .../Purchasing/TransactionsFactory.swift | 12 +- .../AttributionDataMigrator.swift | 12 +- .../SubscriberAttributes/AttributionKey.swift | 20 +++ .../SpecialSubscriberAttributes.swift | 10 +- .../SubscriberAttribute.swift | 57 +++--- .../SubscriberAttributesManager.swift | 57 +++--- .../Attribution/AttributionPosterTests.swift | 20 +-- .../NSDate+RCExtensionsTests.swift | 4 +- .../NSError+RCExtensionsTests.swift | 14 +- .../ArraySlice_UInt8+ExtensionsTests.swift | 6 +- .../Misc/SystemInfoTests.swift | 2 +- .../Mocks/MockReceiptFetcher.swift | 16 +- .../Purchasing/OfferingsManagerTests.swift | 2 +- .../Purchasing/PurchasesTests.swift | 12 +- .../BackendSubscriberAttributesTests.swift | 24 +-- .../SubscriberAttributeTests.swift | 2 +- 90 files changed, 1090 insertions(+), 646 deletions(-) rename PurchasesCoreSwift/{ => Public}/Transaction.swift (72%) diff --git a/Package.swift b/Package.swift index 879a27ae19..a255de1026 100644 --- a/Package.swift +++ b/Package.swift @@ -6,15 +6,7 @@ import class Foundation.ProcessInfo func resolveTargets() -> [Target] { let objcSources = ["Purchases/Info.plist", - "Purchases/Attribution", - "Purchases/Caching", - "Purchases/FoundationExtensions", - "Purchases/Networking", - "Purchases/Public", - "Purchases/ProtectedExtensions", - "Purchases/SubscriberAttributes", - "Purchases/SwiftObjcCompatibility", - "Purchases/Identity"] + "Purchases/Public"] let infoPlist = "Purchases/Info.plist" let baseTargets: [Target] = [ diff --git a/PublicSDKAPITester/RCOfferingAPI.m b/PublicSDKAPITester/RCOfferingAPI.m index bd7d145f79..a9a8995cc5 100644 --- a/PublicSDKAPITester/RCOfferingAPI.m +++ b/PublicSDKAPITester/RCOfferingAPI.m @@ -1,9 +1,15 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // RCOfferingAPI.m -// APITester // // Created by Joshua Liebowitz on 7/9/21. -// Copyright © 2021 Purchases. All rights reserved. // @import PurchasesCoreSwift; @@ -12,7 +18,7 @@ @implementation RCOfferingAPI + (void)checkAPI { - RCOffering *o = [[RCOffering alloc] initWithIdentifier:@"" serverDescription:@"" availablePackages:@[]]; + RCOffering *o = nil; // No public initializer. NSString *i = o.identifier; NSString *sd = o.serverDescription; NSArray *a = o.availablePackages; diff --git a/Purchases.xcodeproj/project.pbxproj b/Purchases.xcodeproj/project.pbxproj index b084fd8adc..d04318ce8f 100644 --- a/Purchases.xcodeproj/project.pbxproj +++ b/Purchases.xcodeproj/project.pbxproj @@ -643,7 +643,6 @@ F5E4383626B852D700841CC8 /* StoreKitExtensions */, 354895D0267AE32D001DC5B1 /* SubscriberAttributes */, B39E811B268E885900D31189 /* SubscriberAttributes */, - 37E355CBB3F3A31A32687B14 /* Transaction.swift */, ); path = PurchasesCoreSwift; sourceTree = ""; @@ -1139,6 +1138,7 @@ A56F9AB026990E9200AFC48F /* PurchaserInfo.swift */, B35042C326CDB79A00905B95 /* Purchases.swift */, B35042C526CDD3B100905B95 /* PurchasesDelegate.swift */, + 37E355CBB3F3A31A32687B14 /* Transaction.swift */, ); path = Public; sourceTree = ""; diff --git a/PurchasesCoreSwift/Attribution/ASIdentifierManagerProxy.swift b/PurchasesCoreSwift/Attribution/ASIdentifierManagerProxy.swift index f58f4e2d2a..65e403baf2 100644 --- a/PurchasesCoreSwift/Attribution/ASIdentifierManagerProxy.swift +++ b/PurchasesCoreSwift/Attribution/ASIdentifierManagerProxy.swift @@ -8,10 +8,8 @@ // https://opensource.org/licenses/MIT // // ASIdentifierManagerProxy.swift -// PurchasesCoreSwift // // Created by Juanpe Catalán on 14/7/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation @@ -29,9 +27,9 @@ class FakeASIdentifierManager: NSObject { } -// TODO (post-migration): Make all the things internal again if possible. Test if Objc can be removed. +// TODO: Test if Objc can be removed. @objc(RCASIdentifierManagerProxy) -public class ASIdentifierManagerProxy: NSObject { +class ASIdentifierManagerProxy: NSObject { static let mangledIdentifierClassName = "NFVqragvsvreZnantre" static let mangledIdentifierPropertyName = "nqiregvfvatVqragvsvre" @@ -44,7 +42,7 @@ public class ASIdentifierManagerProxy: NSObject { NSClassFromString(Self.mangledIdentifierClassName.rot13()) } - @objc public var adsIdentifier: UUID? { + @objc var adsIdentifier: UUID? { guard let classType: AnyClass = Self.identifierClass else { return nil } diff --git a/PurchasesCoreSwift/Attribution/AdClientProxy.swift b/PurchasesCoreSwift/Attribution/AdClientProxy.swift index 176ae788f8..5db949adb3 100644 --- a/PurchasesCoreSwift/Attribution/AdClientProxy.swift +++ b/PurchasesCoreSwift/Attribution/AdClientProxy.swift @@ -8,16 +8,13 @@ // https://opensource.org/licenses/MIT // // AdClientProxy.swift -// PurchasesCoreSwift // // Created by Juanpe Catalán on 14/7/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation -// TODO (post-migration): Make all the things internal again. -public typealias AttributionDetailsBlock = ([String: NSObject]?, Error?) -> Void +typealias AttributionDetailsBlock = ([String: NSObject]?, Error?) -> Void // We need this class to avoid Kid apps being rejected for getting idfa. It seems like App // Review uses some grep to find the class names, so we ended up creating a fake class that @@ -38,9 +35,9 @@ class FakeAdClient: NSObject { } -// TODO (post-migration): Make all the things internal again if possible. Test if Objc can be removed. +// TODO: Test if Objc can be removed. @objc(RCAdClientProxy) -public class AdClientProxy: NSObject { +class AdClientProxy: NSObject { private static let className = "ADClient" @@ -49,7 +46,7 @@ public class AdClientProxy: NSObject { } @objc(requestAttributionDetailsWithBlock:) - public func requestAttributionDetails(_ completionHandler: @escaping AttributionDetailsBlock) { + func requestAttributionDetails(_ completionHandler: @escaping AttributionDetailsBlock) { let client: AnyObject if let klass = Self.adClientClass, let clientClass = klass as AnyObject as? NSObjectProtocol { // This looks strange, but #selector() does fun things to create a selector. If the selector for the given diff --git a/PurchasesCoreSwift/Attribution/AttributionData.swift b/PurchasesCoreSwift/Attribution/AttributionData.swift index 893d42f556..202e5a2936 100644 --- a/PurchasesCoreSwift/Attribution/AttributionData.swift +++ b/PurchasesCoreSwift/Attribution/AttributionData.swift @@ -1,9 +1,15 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // AttributionData.swift -// PurchasesCoreSwift // // Created by Madeline Beyl on 7/7/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation diff --git a/PurchasesCoreSwift/Attribution/AttributionFetcher.swift b/PurchasesCoreSwift/Attribution/AttributionFetcher.swift index ce2e02fdf0..cab3f99362 100644 --- a/PurchasesCoreSwift/Attribution/AttributionFetcher.swift +++ b/PurchasesCoreSwift/Attribution/AttributionFetcher.swift @@ -1,6 +1,15 @@ // -// Created by Andrés Boedo on 4/8/21. -// Copyright (c) 2021 Purchases. All rights reserved. +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// AttributionFetcher.swift +// +// Created by Andrés Boedo on 4/8/21. // import Foundation @@ -17,8 +26,7 @@ enum AttributionFetcherError: Error { } -// TODO(post-migration): Make this internal -@objc(RCAttributionFetcher) public class AttributionFetcher: NSObject { +class AttributionFetcher { private let attributionFactory: AttributionTypeFactory private let systemInfo: SystemInfo @@ -29,7 +37,7 @@ enum AttributionFetcherError: Error { private let appTrackingTransparencyRequired = true #endif - @objc public init(attributionFactory: AttributionTypeFactory, systemInfo: SystemInfo) { + init(attributionFactory: AttributionTypeFactory, systemInfo: SystemInfo) { self.attributionFactory = attributionFactory self.systemInfo = systemInfo } diff --git a/PurchasesCoreSwift/Attribution/AttributionPoster.swift b/PurchasesCoreSwift/Attribution/AttributionPoster.swift index f1382e75db..f921725c22 100644 --- a/PurchasesCoreSwift/Attribution/AttributionPoster.swift +++ b/PurchasesCoreSwift/Attribution/AttributionPoster.swift @@ -13,8 +13,7 @@ import Foundation -// TODO(post-migration): Make this internal -@objc(RCAttributionPoster) public class AttributionPoster: NSObject { +class AttributionPoster { let deviceCache: DeviceCache let identityManager: IdentityManager @@ -24,11 +23,11 @@ import Foundation private static var postponedAttributionData: [AttributionData]? - @objc public init(deviceCache: DeviceCache, - identityManager: IdentityManager, - backend: Backend, - attributionFetcher: AttributionFetcher, - subscriberAttributesManager: SubscriberAttributesManager) { + init(deviceCache: DeviceCache, + identityManager: IdentityManager, + backend: Backend, + attributionFetcher: AttributionFetcher, + subscriberAttributesManager: SubscriberAttributesManager) { self.deviceCache = deviceCache self.identityManager = identityManager self.backend = backend @@ -36,10 +35,9 @@ import Foundation self.subscriberAttributesManager = subscriberAttributesManager } - @objc(postAttributionData:fromNetwork:forNetworkUserId:) - public func post(attributionData data: [String: Any], - fromNetwork network: AttributionNetwork, - forNetworkUserId networkUserId: String?) { + func post(attributionData data: [String: Any], + fromNetwork network: AttributionNetwork, + networkUserId: String?) { Logger.debug(Strings.attribution.instance_configured_posting_attribution) if data["rc_appsflyer_id"] != nil { Logger.warn(Strings.attribution.appsflyer_id_deprecated) @@ -105,7 +103,7 @@ import Foundation } } - @objc public func postAppleSearchAdsAttributionIfNeeded() { + func postAppleSearchAdsAttributionIfNeeded() { guard attributionFetcher.isAuthorizedToPostSearchAds else { return } @@ -129,11 +127,11 @@ import Foundation return } - self.post(attributionData: attributionDetails, fromNetwork: .appleSearchAds, forNetworkUserId: nil) + self.post(attributionData: attributionDetails, fromNetwork: .appleSearchAds, networkUserId: nil) } } - @objc public func postPostponedAttributionDataIfNeeded() { + func postPostponedAttributionDataIfNeeded() { guard let postponedAttributionData = Self.postponedAttributionData else { return } @@ -141,16 +139,15 @@ import Foundation for attributionData in postponedAttributionData { post(attributionData: attributionData.data, fromNetwork: attributionData.network, - forNetworkUserId: attributionData.networkUserId) + networkUserId: attributionData.networkUserId) } Self.postponedAttributionData = nil } - @objc(storePostponedAttributionData:fromNetwork:forNetworkUserId:) - public static func store(postponedAttributionData data: [String: Any], - fromNetwork network: AttributionNetwork, - forNetworkUserId networkUserID: String?) { + static func store(postponedAttributionData data: [String: Any], + fromNetwork network: AttributionNetwork, + forNetworkUserId networkUserID: String?) { Logger.debug(Strings.attribution.no_instance_configured_caching_attribution) var postponedData = postponedAttributionData ?? [] diff --git a/PurchasesCoreSwift/Attribution/AttributionTypeFactory.swift b/PurchasesCoreSwift/Attribution/AttributionTypeFactory.swift index 11659f0911..2a96bfc1cf 100644 --- a/PurchasesCoreSwift/Attribution/AttributionTypeFactory.swift +++ b/PurchasesCoreSwift/Attribution/AttributionTypeFactory.swift @@ -8,17 +8,13 @@ // https://opensource.org/licenses/MIT // // AttributionTypeFactory.swift -// PurchasesCoreSwift // // Created by Juanpe Catalán on 9/7/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation -// TODO (post-migration): change back to internal. -@objc(RCAttributionTypeFactory) -public class AttributionTypeFactory: NSObject { +class AttributionTypeFactory { func adClientProxy() -> AdClientProxy? { return AdClientProxy.adClientClass == nil ? nil : AdClientProxy() diff --git a/PurchasesCoreSwift/Attribution/TrackingManagerProxy.swift b/PurchasesCoreSwift/Attribution/TrackingManagerProxy.swift index f5b5465d51..fd74dc4287 100644 --- a/PurchasesCoreSwift/Attribution/TrackingManagerProxy.swift +++ b/PurchasesCoreSwift/Attribution/TrackingManagerProxy.swift @@ -8,16 +8,13 @@ // https://opensource.org/licenses/MIT // // TrackingManagerProxy.swift -// PurchasesCoreSwift // // Created by Juanpe Catalán on 14/7/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation -// TODO (post-migration): Make all the things internal again. -@objc public enum FakeTrackingManagerAuthorizationStatus: Int { +@objc enum FakeTrackingManagerAuthorizationStatus: Int { case notDetermined = 0 case restricted @@ -40,9 +37,8 @@ class FakeTrackingManager: NSObject { } -// TODO (post-migration): Make all the things internal again if possible. Test if Objc can be removed. -@objc(RCTrackingManagerProxy) -public class TrackingManagerProxy: NSObject { +// TODO: Test if Objc can be removed. +@objc class TrackingManagerProxy: NSObject { static let mangledTrackingClassName = "NGGenpxvatZnantre" static let mangledAuthStatusPropertyName = "genpxvatNhgubevmngvbaFgnghf" @@ -56,7 +52,7 @@ public class TrackingManagerProxy: NSObject { NSClassFromString(mangledTrackingClassName.rot13()) } - @objc public var authorizationStatusPropertyName: String { + @objc var authorizationStatusPropertyName: String { Self.mangledAuthStatusPropertyName.rot13() } diff --git a/PurchasesCoreSwift/Caching/DeviceCache.swift b/PurchasesCoreSwift/Caching/DeviceCache.swift index 13da945548..6cec677c85 100644 --- a/PurchasesCoreSwift/Caching/DeviceCache.swift +++ b/PurchasesCoreSwift/Caching/DeviceCache.swift @@ -1,16 +1,21 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // DeviceCache.swift -// PurchasesCoreSwift // // Created by Joshua Liebowitz on 7/13/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation // swiftlint:disable file_length -// TODO (post-migration) switch back to internal, along with most of the functions. -@objc(RCDeviceCache) public class DeviceCache: NSObject { +class DeviceCache { // Thread-safe, but don't call from anywhere inside this class. var cachedAppUserID: String? { readCache { self.threadUnsafeCachedAppUserID } } @@ -31,14 +36,14 @@ import Foundation private var appUserIDHasBeenSet: Bool = false private let assertionFunction: (String) -> Void - @objc convenience public init(userDefaults: UserDefaults = UserDefaults.standard) { + convenience init(userDefaults: UserDefaults = UserDefaults.standard) { self.init(userDefaults: userDefaults, offeringsCachedObject: nil, notificationCenter: nil) } - public init(userDefaults: UserDefaults = UserDefaults.standard, - offeringsCachedObject: InMemoryCachedObject? = InMemoryCachedObject(), - notificationCenter: NotificationCenter? = NotificationCenter.default, - assertionFunction: @escaping (String) -> Void = { fatalError($0) }) { + init(userDefaults: UserDefaults = UserDefaults.standard, + offeringsCachedObject: InMemoryCachedObject? = InMemoryCachedObject(), + notificationCenter: NotificationCenter? = NotificationCenter.default, + assertionFunction: @escaping (String) -> Void = { fatalError($0) }) { self.offeringsCachedObject = offeringsCachedObject ?? InMemoryCachedObject() self.notificationCenter = notificationCenter ?? NotificationCenter.default @@ -46,8 +51,6 @@ import Foundation self.assertionFunction = assertionFunction self.appUserIDHasBeenSet = userDefaults.string(forKey: CacheKeys.appUserDefaults) != nil - super.init() - self.notificationCenter.addObserver(self, selector: #selector(handleUserDefaultsChanged), name: UserDefaults.didChangeNotification, @@ -141,7 +144,6 @@ import Foundation } } - // TODO (post-migration): set this back to internal, it's only used during testing. func setPurchaserInfoCache(timestamp: Date, appUserID: String) { writeCache { self.threadUnsafeSetPurchaserInfoCache(timestamp: timestamp, appUserID: appUserID) @@ -166,7 +168,7 @@ import Foundation offeringsCachedObject.cache(instance: offerings) } - @objc public func isOfferingsCacheStale(isAppBackgrounded: Bool) -> Bool { + func isOfferingsCacheStale(isAppBackgrounded: Bool) -> Bool { let cacheDurationInSeconds = cacheDurationInSeconds(isAppBackgrounded: isAppBackgrounded) return offeringsCachedObject.isCacheStale(durationInSeconds: cacheDurationInSeconds) } @@ -346,9 +348,9 @@ import Foundation // All methods that modify or read from the UserDefaults data source but require external mechanisms for ensuring // mutual exclusion. -extension DeviceCache { +private extension DeviceCache { - private func threadUnsafeAppUserIDsWithLegacyAttributes() -> [String] { + func threadUnsafeAppUserIDsWithLegacyAttributes() -> [String] { var appUserIDsWithLegacyAttributes: [String] = [] let userDefaultsDict = userDefaults.dictionaryRepresentation() @@ -360,20 +362,20 @@ extension DeviceCache { return appUserIDsWithLegacyAttributes } - private var threadUnsafeStoredAttributesForAllUsers: [String: Any] { + var threadUnsafeStoredAttributesForAllUsers: [String: Any] { let attributes = userDefaults.dictionary(forKey: CacheKeys.subscriberAttributes) ?? [:] return attributes } - private func threadUnsafePurchaserInfoLastUpdated(appUserID: String) -> Date? { + func threadUnsafePurchaserInfoLastUpdated(appUserID: String) -> Date? { return userDefaults.object(forKey: CacheKeyBases.purchaserInfoLastUpdated + appUserID) as? Date } - private func threadUnsafeClearPurchaserInfoCacheTimestamp(appUserID: String) { + func threadUnsafeClearPurchaserInfoCacheTimestamp(appUserID: String) { userDefaults.removeObject(forKey: CacheKeyBases.purchaserInfoLastUpdated + appUserID) } - private func threadUnsafeUnsyncedAttributesByKey(appUserID: String) -> [String: SubscriberAttribute] { + func threadUnsafeUnsyncedAttributesByKey(appUserID: String) -> [String: SubscriberAttribute] { let allSubscriberAttributesByKey = threadUnsafeStoredSubscriberAttributes(appUserID: appUserID) var unsyncedAttributesByKey: [String: SubscriberAttribute] = [:] for attribute in allSubscriberAttributesByKey.values where !attribute.isSynced { @@ -382,19 +384,19 @@ extension DeviceCache { return unsyncedAttributesByKey } - private func threadUnsafeSetPurchaserInfoCache(timestamp: Date, appUserID: String) { + func threadUnsafeSetPurchaserInfoCache(timestamp: Date, appUserID: String) { userDefaults.setValue(timestamp, forKey: CacheKeyBases.purchaserInfoLastUpdated + appUserID) } - private func threadUnsafeSetPurchaserInfoCacheTimestampToNow(appUserID: String) { + func threadUnsafeSetPurchaserInfoCacheTimestampToNow(appUserID: String) { threadUnsafeSetPurchaserInfoCache(timestamp: Date(), appUserID: appUserID) } - private func threadUnsafeSubscriberAttributes(appUserID: String) -> [String: Any] { + func threadUnsafeSubscriberAttributes(appUserID: String) -> [String: Any] { return threadUnsafeStoredAttributesForAllUsers[appUserID] as? [String: Any] ?? [:] } - private func threadUnsafeStoredSubscriberAttributes(appUserID: String) -> [String: SubscriberAttribute] { + func threadUnsafeStoredSubscriberAttributes(appUserID: String) -> [String: SubscriberAttribute] { let allAttributesObjectsByKey = threadUnsafeSubscriberAttributes(appUserID: appUserID) var allSubscriberAttributesByKey: [String: SubscriberAttribute] = [:] for (key, attributeDict) in allAttributesObjectsByKey { @@ -408,7 +410,7 @@ extension DeviceCache { return allSubscriberAttributesByKey } - private func threadUnsafeMigrateSubscriberAttributes() { + func threadUnsafeMigrateSubscriberAttributes() { let appUserIDsWithLegacyAttributes = threadUnsafeAppUserIDsWithLegacyAttributes() var attributesInNewFormat = userDefaults.dictionary(forKey: CacheKeys.subscriberAttributes) ?? [:] for appUserID in appUserIDsWithLegacyAttributes { @@ -425,7 +427,7 @@ extension DeviceCache { userDefaults.setValue(attributesInNewFormat, forKey: CacheKeys.subscriberAttributes) } - private func threadUnsafeDeleteSyncedSubscriberAttributesForOtherUsers() { + func threadUnsafeDeleteSyncedSubscriberAttributesForOtherUsers() { let allStoredAttributes: [String: [String: Any]] = userDefaults.dictionary(forKey: CacheKeys.subscriberAttributes) as? [String: [String: Any]] ?? [:] @@ -457,21 +459,21 @@ extension DeviceCache { } -extension UserDefaults { +fileprivate extension UserDefaults { - fileprivate func setValue(_ value: Any?, forKey key: DeviceCache.CacheKeys) { + func setValue(_ value: Any?, forKey key: DeviceCache.CacheKeys) { self.setValue(value, forKey: key.rawValue) } - fileprivate func string(forKey defaultName: DeviceCache.CacheKeys) -> String? { + func string(forKey defaultName: DeviceCache.CacheKeys) -> String? { return self.string(forKey: defaultName.rawValue) } - fileprivate func removeObject(forKey defaultName: DeviceCache.CacheKeys) { + func removeObject(forKey defaultName: DeviceCache.CacheKeys) { removeObject(forKey: defaultName.rawValue) } - fileprivate func dictionary(forKey defaultName: DeviceCache.CacheKeys) -> [String: Any]? { + func dictionary(forKey defaultName: DeviceCache.CacheKeys) -> [String: Any]? { return dictionary(forKey: defaultName.rawValue) } diff --git a/PurchasesCoreSwift/Caching/InMemoryCachedObject.swift b/PurchasesCoreSwift/Caching/InMemoryCachedObject.swift index 13f534866e..5ce8c71988 100644 --- a/PurchasesCoreSwift/Caching/InMemoryCachedObject.swift +++ b/PurchasesCoreSwift/Caching/InMemoryCachedObject.swift @@ -1,13 +1,18 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // InMemoryCachedObject.swift -// PurchasesCoreSwift // // Created by Joshua Liebowitz on 7/13/21. -// Copyright © 2021 Purchases. All rights reserved. // -// TODO (post-migration) switch back to internal -public class InMemoryCachedObject { +class InMemoryCachedObject { var lastUpdatedAt: Date? { accessQueue.sync { @@ -19,8 +24,6 @@ public class InMemoryCachedObject { private var lastUpdated: Date? private var cachedObject: T? - public init() { } - func isCacheStale(durationInSeconds: Double) -> Bool { accessQueue.sync { guard let lastUpdated = lastUpdated else { diff --git a/PurchasesCoreSwift/FoundationExtensions/Date+Extensions.swift b/PurchasesCoreSwift/FoundationExtensions/Date+Extensions.swift index 3d78b48f85..efece45dbb 100644 --- a/PurchasesCoreSwift/FoundationExtensions/Date+Extensions.swift +++ b/PurchasesCoreSwift/FoundationExtensions/Date+Extensions.swift @@ -1,21 +1,31 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // Date+Extensions.swift -// PurchasesCoreSwift // // Created by Josh Holtz on 6/28/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation extension NSDate { - func rc_millisecondsSince1970AsUInt64() -> UInt64 { + + func millisecondsSince1970AsUInt64() -> UInt64 { return UInt64(self.timeIntervalSince1970 * 1000.0) } + } extension Date { - func rc_millisecondsSince1970AsUInt64() -> UInt64 { + + func millisecondsSince1970AsUInt64() -> UInt64 { return UInt64(self.timeIntervalSince1970 * 1000.0) } + } diff --git a/PurchasesCoreSwift/FoundationExtensions/DateFormatter+Extensions.swift b/PurchasesCoreSwift/FoundationExtensions/DateFormatter+Extensions.swift index dab5f21a72..1906f00e6c 100644 --- a/PurchasesCoreSwift/FoundationExtensions/DateFormatter+Extensions.swift +++ b/PurchasesCoreSwift/FoundationExtensions/DateFormatter+Extensions.swift @@ -1,6 +1,15 @@ // -// Created by Andrés Boedo on 7/29/20. -// Copyright (c) 2020 Purchases. All rights reserved. +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// DateFormatter+Extensions.swift +// +// Created by Andrés Boedo on 7/29/20. // import Foundation diff --git a/PurchasesCoreSwift/FoundationExtensions/Dictionary+Extensions.swift b/PurchasesCoreSwift/FoundationExtensions/Dictionary+Extensions.swift index 9693da76e1..961fc3c48e 100644 --- a/PurchasesCoreSwift/FoundationExtensions/Dictionary+Extensions.swift +++ b/PurchasesCoreSwift/FoundationExtensions/Dictionary+Extensions.swift @@ -1,9 +1,15 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // Dictionary+Extensions.swift -// PurchasesCoreSwift // // Created by César de la Vega on 7/21/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation @@ -62,4 +68,5 @@ extension Dictionary { static func + (lhs: [Key: Value], rhs: [Key: Value]) -> [Key: Value] { lhs.merging(rhs) } + } diff --git a/PurchasesCoreSwift/FoundationExtensions/Error+Extensions.swift b/PurchasesCoreSwift/FoundationExtensions/Error+Extensions.swift index db1f19dc44..44e97d0219 100644 --- a/PurchasesCoreSwift/FoundationExtensions/Error+Extensions.swift +++ b/PurchasesCoreSwift/FoundationExtensions/Error+Extensions.swift @@ -15,7 +15,7 @@ import Foundation extension NSError { - var rc_successfullySynced: Bool { + var successfullySynced: Bool { if code == ErrorCode.networkError.rawValue { return false } @@ -27,7 +27,7 @@ extension NSError { return false } - var rc_subscriberAttributesErrors: [String: String]? { + var subscriberAttributesErrors: [String: String]? { return userInfo[Backend.RCAttributeErrorsKey as String] as? [String: String] } diff --git a/PurchasesCoreSwift/FoundationExtensions/Locale+Extensions.swift b/PurchasesCoreSwift/FoundationExtensions/Locale+Extensions.swift index 4dea75e7df..c0ff1eefe6 100644 --- a/PurchasesCoreSwift/FoundationExtensions/Locale+Extensions.swift +++ b/PurchasesCoreSwift/FoundationExtensions/Locale+Extensions.swift @@ -1,9 +1,15 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // Locale+Extensions.swift -// PurchasesCoreSwift // // Created by Josh Holtz on 6/28/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation diff --git a/PurchasesCoreSwift/FoundationExtensions/String+Extensions.swift b/PurchasesCoreSwift/FoundationExtensions/String+Extensions.swift index bef182cffc..3c65660dee 100644 --- a/PurchasesCoreSwift/FoundationExtensions/String+Extensions.swift +++ b/PurchasesCoreSwift/FoundationExtensions/String+Extensions.swift @@ -8,7 +8,6 @@ // https://opensource.org/licenses/MIT // // String+Extensions.swift -// PurchasesCoreSwift // // Created by Juanpe Catalán on 9/7/21. // diff --git a/PurchasesCoreSwift/Identity/IdentityManager.swift b/PurchasesCoreSwift/Identity/IdentityManager.swift index 4895a5bd5e..947a42268f 100644 --- a/PurchasesCoreSwift/Identity/IdentityManager.swift +++ b/PurchasesCoreSwift/Identity/IdentityManager.swift @@ -13,12 +13,11 @@ import Foundation -// TODO (post-migration): Change back to internal, including all public properties. -@objc(RCIdentityManager) public class IdentityManager: NSObject { +class IdentityManager { static let anonymousRegex = #"\$RCAnonymousID:([a-z0-9]{32})$"# - @objc public var currentAppUserID: String { + var currentAppUserID: String { guard let appUserID = deviceCache.cachedAppUserID else { fatalError(Strings.identity.null_currentappuserid) } @@ -26,7 +25,7 @@ import Foundation return appUserID } - @objc public var currentUserIsAnonymous: Bool { + var currentUserIsAnonymous: Bool { let anonymousFoundRange = currentAppUserID.range(of: IdentityManager.anonymousRegex, options: .regularExpression) @@ -40,13 +39,13 @@ import Foundation private let backend: Backend private let purchaserInfoManager: PurchaserInfoManager - @objc public init(deviceCache: DeviceCache, backend: Backend, purchaserInfoManager: PurchaserInfoManager) { + init(deviceCache: DeviceCache, backend: Backend, purchaserInfoManager: PurchaserInfoManager) { self.deviceCache = deviceCache self.backend = backend self.purchaserInfoManager = purchaserInfoManager } - @objc public func configure(appUserID maybeAppUserID: String?) { + func configure(appUserID maybeAppUserID: String?) { let appUserID = maybeAppUserID ?? deviceCache.cachedAppUserID ?? deviceCache.cachedLegacyAppUserID @@ -57,7 +56,7 @@ import Foundation deviceCache.cleanupSubscriberAttributes() } - @objc public func logIn(appUserID: String, completion: @escaping (PurchaserInfo?, Bool, Error?) -> Void) { + func logIn(appUserID: String, completion: @escaping (PurchaserInfo?, Bool, Error?) -> Void) { let newAppUserID = appUserID.trimmingCharacters(in: .whitespacesAndNewlines) guard !newAppUserID.isEmpty else { Logger.error(Strings.identity.logging_in_with_nil_appuserid) @@ -85,7 +84,7 @@ import Foundation } } - @objc public func logOut(completion: (Error?) -> Void) { + func logOut(completion: (Error?) -> Void) { Logger.info(String(format: Strings.identity.log_out_called_for_user, currentAppUserID)) if currentUserIsAnonymous { @@ -98,19 +97,13 @@ import Foundation completion(nil) } - // TODO (post-migration): Change back to private. func generateRandomID() -> String { "$RCAnonymousID:\(UUID().uuidString.replacingOccurrences(of: "-", with: "").lowercased())" } -} - // MARK: Deprecated -// TODO: Migrate off these so we can mark them deprecated. -extension IdentityManager { - @objc(identifyAppUserID:completion:) - public func identify(appUserID: String, completion: @escaping (Error?) -> Void) { + func identify(appUserID: String, completion: @escaping (Error?) -> Void) { if currentUserIsAnonymous { Logger.user(String(format: Strings.identity.identifying_anon_id, currentAppUserID)) createAlias(appUserID: appUserID, completion: completion) @@ -121,8 +114,7 @@ extension IdentityManager { } } - @objc(createAliasForAppUserID:completion:) - public func createAlias(appUserID alias: String, completion: @escaping (Error?) -> Void) { + func createAlias(appUserID alias: String, completion: @escaping (Error?) -> Void) { guard !alias.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { completion(ErrorUtils.missingAppUserIDForAliasCreationError()) return @@ -137,7 +129,7 @@ extension IdentityManager { } } - @objc public func resetAppUserID() { + func resetAppUserID() { deviceCache.clearCaches(oldAppUserID: currentAppUserID, andSaveWithNewUserID: generateRandomID()) deviceCache.clearLatestNetworkAndAdvertisingIdsSent(appUserID: currentAppUserID) backend.clearCaches() diff --git a/PurchasesCoreSwift/Identity/PurchaserInfoManager.swift b/PurchasesCoreSwift/Identity/PurchaserInfoManager.swift index 9c8758a105..6a89f76317 100644 --- a/PurchasesCoreSwift/Identity/PurchaserInfoManager.swift +++ b/PurchasesCoreSwift/Identity/PurchaserInfoManager.swift @@ -13,17 +13,15 @@ import Foundation -@objc(RCPurchaserInfoManagerDelegate) public protocol PurchaserInfoManagerDelegate: NSObjectProtocol { +protocol PurchaserInfoManagerDelegate: AnyObject { - @objc(purchaserInfoManagerDidReceiveUpdatedPurchaserInfo:) func purchaserInfoManagerDidReceiveUpdated(purchaserInfo: PurchaserInfo) } -// TODO (post-migration) make all the things internal, including the protocol. -@objc(RCPurchaserInfoManager) public class PurchaserInfoManager: NSObject { +class PurchaserInfoManager { - @objc public weak var delegate: PurchaserInfoManagerDelegate? + weak var delegate: PurchaserInfoManagerDelegate? private(set) var lastSentPurchaserInfo: PurchaserInfo? private let operationDispatcher: OperationDispatcher @@ -32,19 +30,19 @@ import Foundation private let systemInfo: SystemInfo private let purchaserInfoCacheLock = NSRecursiveLock() - @objc public init(operationDispatcher: OperationDispatcher, - deviceCache: DeviceCache, - backend: Backend, - systemInfo: SystemInfo) { + init(operationDispatcher: OperationDispatcher, + deviceCache: DeviceCache, + backend: Backend, + systemInfo: SystemInfo) { self.operationDispatcher = operationDispatcher self.deviceCache = deviceCache self.backend = backend self.systemInfo = systemInfo } - @objc public func fetchAndCachePurchaserInfo(appUserID: String, - isAppBackgrounded: Bool, - completion maybeCompletion: ReceivePurchaserInfoBlock?) { + func fetchAndCachePurchaserInfo(appUserID: String, + isAppBackgrounded: Bool, + completion maybeCompletion: ReceivePurchaserInfoBlock?) { deviceCache.setCacheTimestampToNowToPreventConcurrentPurchaserInfoUpdates(appUserID: appUserID) operationDispatcher.dispatchOnWorkerThread(withRandomDelay: isAppBackgrounded) { self.backend.getSubscriberData(appUserID: appUserID) { maybePurchaserInfo, maybeError in @@ -67,9 +65,9 @@ import Foundation } } - @objc public func fetchAndCachePurchaserInfoIfStale(appUserID: String, - isAppBackgrounded: Bool, - completion: ReceivePurchaserInfoBlock?) { + func fetchAndCachePurchaserInfoIfStale(appUserID: String, + isAppBackgrounded: Bool, + completion: ReceivePurchaserInfoBlock?) { let maybeCachedPurchaserInfo = cachedPurchaserInfo(appUserID: appUserID) let isCacheStale = deviceCache.isPurchaserInfoCacheStale(appUserID: appUserID, isAppBackgrounded: isAppBackgrounded) @@ -90,8 +88,7 @@ import Foundation } } - @objc(sendCachedPurchaserInfoIfAvailableForAppUserID:) - public func sendCachedPurchaserInfoIfAvailable(appUserID: String) { + func sendCachedPurchaserInfoIfAvailable(appUserID: String) { guard let info = cachedPurchaserInfo(appUserID: appUserID) else { return } @@ -99,8 +96,7 @@ import Foundation sendUpdateIfChanged(purchaserInfo: info) } - @objc(purchaserInfoWithAppUserID:completionBlock:) - public func purchaserInfo(appUserID: String, completion maybeCompletion: ReceivePurchaserInfoBlock?) { + func purchaserInfo(appUserID: String, completion maybeCompletion: ReceivePurchaserInfoBlock?) { let maybeInfoFromCache = cachedPurchaserInfo(appUserID: appUserID) var completionCalled = false @@ -156,7 +152,7 @@ import Foundation } } - @objc public func clearPurchaserInfoCache(forAppUserID appUserID: String) { + func clearPurchaserInfoCache(forAppUserID appUserID: String) { purchaserInfoCacheLock.lock() deviceCache.clearPurchaserInfoCache(appUserID: appUserID) lastSentPurchaserInfo = nil diff --git a/PurchasesCoreSwift/IntroEligibilityCalculator.swift b/PurchasesCoreSwift/IntroEligibilityCalculator.swift index ab09ae78a6..8fc9fe31b0 100644 --- a/PurchasesCoreSwift/IntroEligibilityCalculator.swift +++ b/PurchasesCoreSwift/IntroEligibilityCalculator.swift @@ -1,31 +1,32 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // IntroEligibilityCalculator.swift -// Purchases // // Created by Andrés Boedo on 7/14/20. -// Copyright © 2020 Purchases. All rights reserved. // import Foundation import StoreKit -@objc(RCIntroEligibilityCalculator) public class IntroEligibilityCalculator: NSObject { +class IntroEligibilityCalculator { + private let productsManager: ProductsManager private let receiptParser: ReceiptParser - @objc public override init() { - self.productsManager = ProductsManager() - self.receiptParser = ReceiptParser() - } - - internal init(productsManager: ProductsManager, - receiptParser: ReceiptParser) { + init(productsManager: ProductsManager = ProductsManager(), receiptParser: ReceiptParser = ReceiptParser()) { self.productsManager = productsManager self.receiptParser = receiptParser } @available(iOS 12.0, macOS 10.14, macCatalyst 13.0, tvOS 12.0, watchOS 6.2, *) - @objc public func checkTrialOrIntroductoryPriceEligibility( + func checkTrialOrIntroductoryPriceEligibility( with receiptData: Data, productIdentifiers candidateProductIdentifiers: Set, completion: @escaping ([String: NSNumber], Error?) -> Void) { @@ -93,10 +94,13 @@ private extension IntroEligibilityCalculator { } return result } + } extension IntroEligibilityStatus { + func toNSNumber() -> NSNumber { return self.rawValue as NSNumber } + } diff --git a/PurchasesCoreSwift/LocalReceiptParsing/BasicTypes/ASN1Container.swift b/PurchasesCoreSwift/LocalReceiptParsing/BasicTypes/ASN1Container.swift index ae9b1ad9f8..5b207b578f 100644 --- a/PurchasesCoreSwift/LocalReceiptParsing/BasicTypes/ASN1Container.swift +++ b/PurchasesCoreSwift/LocalReceiptParsing/BasicTypes/ASN1Container.swift @@ -1,15 +1,27 @@ // -// Created by Andrés Boedo on 7/28/20. -// Copyright (c) 2020 Purchases. All rights reserved. +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// ASN1Container.swift +// +// Created by Andrés Boedo on 7/28/20. // import Foundation enum ASN1Class: UInt8 { + case universal, application, contextSpecific, `private` + } enum ASN1Identifier: UInt8, CaseIterable { + case endOfContent = 0 case boolean = 1 case integer = 2 @@ -39,24 +51,31 @@ enum ASN1Identifier: UInt8, CaseIterable { case universalString = 28 case characterString = 29 case bmpString = 30 + } enum ASN1EncodingType: UInt8 { + case primitive, constructed + } struct ASN1Length: Equatable { + let value: Int let bytesUsedForLength: Int + } struct ASN1Container: Equatable { + let containerClass: ASN1Class let containerIdentifier: ASN1Identifier let encodingType: ASN1EncodingType let length: ASN1Length let internalPayload: ArraySlice let bytesUsedForIdentifier = 1 - var totalBytesUsed: Int { return bytesUsedForIdentifier + length.value + length.bytesUsedForLength } + var totalBytesUsed: Int { bytesUsedForIdentifier + length.value + length.bytesUsedForLength } let internalContainers: [ASN1Container] + } diff --git a/PurchasesCoreSwift/LocalReceiptParsing/BasicTypes/ASN1ObjectIdentifier.swift b/PurchasesCoreSwift/LocalReceiptParsing/BasicTypes/ASN1ObjectIdentifier.swift index 08590896ec..27611e1022 100644 --- a/PurchasesCoreSwift/LocalReceiptParsing/BasicTypes/ASN1ObjectIdentifier.swift +++ b/PurchasesCoreSwift/LocalReceiptParsing/BasicTypes/ASN1ObjectIdentifier.swift @@ -1,16 +1,27 @@ // -// Created by Andrés Boedo on 7/29/20. -// Copyright (c) 2020 Purchases. All rights reserved. +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// ASN1ObjectIdentifier.swift +// +// Created by Andrés Boedo on 7/29/20. // import Foundation // http://www.umich.edu/~x509/ssleay/asn1-oids.html enum ASN1ObjectIdentifier: String { + case data = "1.2.840.113549.1.7.1" case signedData = "1.2.840.113549.1.7.2" case envelopedData = "1.2.840.113549.1.7.3" case signedAndEnvelopedData = "1.2.840.113549.1.7.4" case digestedData = "1.2.840.113549.1.7.5" case encryptedData = "1.2.840.113549.1.7.6" + } diff --git a/PurchasesCoreSwift/LocalReceiptParsing/BasicTypes/AppleReceipt.swift b/PurchasesCoreSwift/LocalReceiptParsing/BasicTypes/AppleReceipt.swift index c13bede4c5..d9e728ce93 100644 --- a/PurchasesCoreSwift/LocalReceiptParsing/BasicTypes/AppleReceipt.swift +++ b/PurchasesCoreSwift/LocalReceiptParsing/BasicTypes/AppleReceipt.swift @@ -1,21 +1,30 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // AppleReceipt.swift -// Purchases // // Created by Andrés Boedo on 7/22/20. -// Copyright © 2020 Purchases. All rights reserved. // import Foundation // https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html struct ReceiptAttribute { + let type: ReceiptAttributeType let version: Int let value: String + } enum ReceiptAttributeType: Int { + case bundleId = 2, applicationVersion = 3, opaqueValue = 4, @@ -24,9 +33,11 @@ enum ReceiptAttributeType: Int { inAppPurchase = 17, originalApplicationVersion = 19, expirationDate = 21 + } struct AppleReceipt: Equatable { + let bundleId: String let applicationVersion: String let originalApplicationVersion: String @@ -59,4 +70,5 @@ struct AppleReceipt: Equatable { var description: String { return String(describing: self.asDict) } + } diff --git a/PurchasesCoreSwift/LocalReceiptParsing/BasicTypes/InAppPurchase.swift b/PurchasesCoreSwift/LocalReceiptParsing/BasicTypes/InAppPurchase.swift index 3f88582457..f29c648900 100644 --- a/PurchasesCoreSwift/LocalReceiptParsing/BasicTypes/InAppPurchase.swift +++ b/PurchasesCoreSwift/LocalReceiptParsing/BasicTypes/InAppPurchase.swift @@ -1,12 +1,22 @@ // -// Created by Andrés Boedo on 7/29/20. -// Copyright (c) 2020 Purchases. All rights reserved. +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// InAppPurchase.swift +// +// Created by Andrés Boedo on 7/29/20. // import Foundation // https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html enum InAppPurchaseAttributeType: Int { + case quantity = 1701, productId = 1702, transactionId = 1703, @@ -20,17 +30,21 @@ enum InAppPurchaseAttributeType: Int { isInTrialPeriod = 1713, isInIntroOfferPeriod = 1719, promotionalOfferIdentifier = 1721 + } enum InAppPurchaseProductType: Int { + case unknown = -1, nonConsumable, consumable, nonRenewingSubscription, autoRenewableSubscription + } struct InAppPurchase: Equatable { + let quantity: Int let productId: String let transactionId: String @@ -66,4 +80,5 @@ struct InAppPurchase: Equatable { var description: String { return String(describing: self.asDict) } + } diff --git a/PurchasesCoreSwift/LocalReceiptParsing/Builders/ASN1ContainerBuilder.swift b/PurchasesCoreSwift/LocalReceiptParsing/Builders/ASN1ContainerBuilder.swift index e199543e99..fb00b892e5 100644 --- a/PurchasesCoreSwift/LocalReceiptParsing/Builders/ASN1ContainerBuilder.swift +++ b/PurchasesCoreSwift/LocalReceiptParsing/Builders/ASN1ContainerBuilder.swift @@ -1,6 +1,15 @@ // -// Created by Andrés Boedo on 7/29/20. -// Copyright (c) 2020 Purchases. All rights reserved. +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// ASN1ContainerBuilder.swift +// +// Created by Andrés Boedo on 7/29/20. // import Foundation @@ -95,4 +104,5 @@ private extension ASN1ContainerBuilder { return ASN1Length(value: lengthValue, bytesUsedForLength: bytesUsedForLength) } } + } diff --git a/PurchasesCoreSwift/LocalReceiptParsing/Builders/ASN1ObjectIdentifierBuilder.swift b/PurchasesCoreSwift/LocalReceiptParsing/Builders/ASN1ObjectIdentifierBuilder.swift index b91cb27661..42df0ebfb1 100644 --- a/PurchasesCoreSwift/LocalReceiptParsing/Builders/ASN1ObjectIdentifierBuilder.swift +++ b/PurchasesCoreSwift/LocalReceiptParsing/Builders/ASN1ObjectIdentifierBuilder.swift @@ -1,6 +1,15 @@ // -// Created by Andrés Boedo on 7/28/20. -// Copyright (c) 2020 Purchases. All rights reserved. +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// ASN1ObjectIdentifierBuilder.swift +// +// Created by Andrés Boedo on 7/28/20. // import Foundation diff --git a/PurchasesCoreSwift/LocalReceiptParsing/Builders/AppleReceiptBuilder.swift b/PurchasesCoreSwift/LocalReceiptParsing/Builders/AppleReceiptBuilder.swift index 559319428b..73da69014d 100644 --- a/PurchasesCoreSwift/LocalReceiptParsing/Builders/AppleReceiptBuilder.swift +++ b/PurchasesCoreSwift/LocalReceiptParsing/Builders/AppleReceiptBuilder.swift @@ -1,11 +1,21 @@ // -// Created by Andrés Boedo on 7/29/20. -// Copyright (c) 2020 Purchases. All rights reserved. +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// AppleReceiptBuilder.swift +// +// Created by Andrés Boedo on 7/29/20. // import Foundation class AppleReceiptBuilder { + private let containerBuilder: ASN1ContainerBuilder private let inAppPurchaseBuilder: InAppPurchaseBuilder @@ -92,4 +102,5 @@ class AppleReceiptBuilder { inAppPurchases: inAppPurchases) return receipt } + } diff --git a/PurchasesCoreSwift/LocalReceiptParsing/DataConverters/ArraySlice_UInt8+Extensions.swift b/PurchasesCoreSwift/LocalReceiptParsing/DataConverters/ArraySlice_UInt8+Extensions.swift index 859fedf09a..1511656e45 100644 --- a/PurchasesCoreSwift/LocalReceiptParsing/DataConverters/ArraySlice_UInt8+Extensions.swift +++ b/PurchasesCoreSwift/LocalReceiptParsing/DataConverters/ArraySlice_UInt8+Extensions.swift @@ -1,12 +1,22 @@ // -// Created by Andrés Boedo on 7/29/20. -// Copyright (c) 2020 Purchases. All rights reserved. +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// ArraySlice_UInt8+Extensions.swift +// +// Created by Andrés Boedo on 7/29/20. // import Foundation extension ArraySlice where Element == UInt8 { - func toUInt() -> UInt64 { + + func toUInt64() -> UInt64 { let array = Array(self) var result: UInt64 = 0 for idx in 0..<(array.count) { @@ -17,15 +27,15 @@ extension ArraySlice where Element == UInt8 { } func toInt() -> Int { - return Int(self.toUInt()) + return Int(self.toUInt64()) } func toInt64() -> Int64 { - return Int64(self.toUInt()) + return Int64(self.toUInt64()) } func toBool() -> Bool { - return self.toUInt() == 1 + return self.toUInt64() == 1 } func toString() -> String? { diff --git a/PurchasesCoreSwift/LocalReceiptParsing/DataConverters/UInt8+Extensions.swift b/PurchasesCoreSwift/LocalReceiptParsing/DataConverters/UInt8+Extensions.swift index 3db6e707dc..b04338e166 100644 --- a/PurchasesCoreSwift/LocalReceiptParsing/DataConverters/UInt8+Extensions.swift +++ b/PurchasesCoreSwift/LocalReceiptParsing/DataConverters/UInt8+Extensions.swift @@ -1,9 +1,15 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // UInt8+Extensions.swift -// Purchases // // Created by Andrés Boedo on 7/24/20. -// Copyright © 2020 Purchases. All rights reserved. // import Foundation @@ -19,7 +25,8 @@ enum BitShiftError: Error { } extension BitShiftError: CustomStringConvertible { - public var description: String { + + var description: String { switch self { case .invalidIndex(let index): return "invalid index: \(index)" @@ -31,9 +38,11 @@ extension BitShiftError: CustomStringConvertible { return "unhandled range" } } + } extension UInt8 { + func bitAtIndex(_ index: UInt8) throws -> UInt8 { guard index <= 7 else { throw BitShiftError.invalidIndex(index) } let shifted = self >> (7 - index) @@ -49,9 +58,11 @@ extension UInt8 { let mask = try maskForRange(range) return shifted & mask } + } private extension UInt8 { + func maskForRange(_ range: UInt8) throws -> UInt8 { guard 0 <= range && range <= 8 else { throw BitShiftError.rangeLargerThanByte } switch range { @@ -67,4 +78,5 @@ private extension UInt8 { throw BitShiftError.unhandledRange } } + } diff --git a/PurchasesCoreSwift/LocalReceiptParsing/ReceiptParser.swift b/PurchasesCoreSwift/LocalReceiptParsing/ReceiptParser.swift index f277f8bc92..beb4f29322 100644 --- a/PurchasesCoreSwift/LocalReceiptParsing/ReceiptParser.swift +++ b/PurchasesCoreSwift/LocalReceiptParsing/ReceiptParser.swift @@ -1,34 +1,34 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // ReceiptParser.swift -// Purchases // // Created by Andrés Boedo on 7/22/20. -// Copyright © 2020 Purchases. All rights reserved. // import Foundation -@objc(RCReceiptParser) public class ReceiptParser: NSObject { +class ReceiptParser { + private let objectIdentifierBuilder: ASN1ObjectIdentifierBuilder private let containerBuilder: ASN1ContainerBuilder private let receiptBuilder: AppleReceiptBuilder - @objc public convenience override init() { - self.init(objectIdentifierBuilder: ASN1ObjectIdentifierBuilder(), - containerBuilder: ASN1ContainerBuilder(), - receiptBuilder: AppleReceiptBuilder()) - } - - init(objectIdentifierBuilder: ASN1ObjectIdentifierBuilder, - containerBuilder: ASN1ContainerBuilder, - receiptBuilder: AppleReceiptBuilder) { + init(objectIdentifierBuilder: ASN1ObjectIdentifierBuilder = ASN1ObjectIdentifierBuilder(), + containerBuilder: ASN1ContainerBuilder = ASN1ContainerBuilder(), + receiptBuilder: AppleReceiptBuilder = AppleReceiptBuilder()) { self.objectIdentifierBuilder = objectIdentifierBuilder self.containerBuilder = containerBuilder self.receiptBuilder = receiptBuilder - super.init() } - @objc public func receiptHasTransactions(receiptData: Data) -> Bool { + func receiptHasTransactions(receiptData: Data) -> Bool { Logger.info(Strings.receipt.parsing_receipt) if let receipt = try? parse(from: receiptData) { return receipt.inAppPurchases.count > 0 @@ -54,6 +54,7 @@ import Foundation } private extension ReceiptParser { + func findASN1Container(withObjectId objectId: ASN1ObjectIdentifier, inContainer container: ASN1Container) throws -> ASN1Container? { if container.encodingType == .constructed { @@ -75,4 +76,5 @@ private extension ReceiptParser { } return nil } + } diff --git a/PurchasesCoreSwift/LocalReceiptParsing/ReceiptParsingError.swift b/PurchasesCoreSwift/LocalReceiptParsing/ReceiptParsingError.swift index 67116a34b3..0021507e73 100644 --- a/PurchasesCoreSwift/LocalReceiptParsing/ReceiptParsingError.swift +++ b/PurchasesCoreSwift/LocalReceiptParsing/ReceiptParsingError.swift @@ -1,23 +1,33 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // ReceiptParsingError.swift // Purchases // // Created by Andrés Boedo on 7/30/20. -// Copyright © 2020 Purchases. All rights reserved. // import Foundation enum ReceiptReadingError: Error, Equatable { + case missingReceipt, emptyReceipt, dataObjectIdentifierMissing, asn1ParsingError(description: String), receiptParsingError, inAppPurchaseParsingError + } extension ReceiptReadingError: LocalizedError { + public var errorDescription: String? { switch self { case .missingReceipt: @@ -34,4 +44,5 @@ extension ReceiptReadingError: LocalizedError { return "Error while parsing in-app purchase. One or more attributes are missing or in the wrong format." } } + } diff --git a/PurchasesCoreSwift/Logging/LogIntent.swift b/PurchasesCoreSwift/Logging/LogIntent.swift index fa5a632613..32076194dc 100644 --- a/PurchasesCoreSwift/Logging/LogIntent.swift +++ b/PurchasesCoreSwift/Logging/LogIntent.swift @@ -1,14 +1,21 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // LogIntent.swift -// PurchasesCoreSwift // // Created by Tina Nguyen on 12/08/20. -// Copyright © 2020 Purchases. All rights reserved. // import Foundation -@objc(RCLogIntent) public enum LogIntent: Int { +enum LogIntent { + case appleError case info case purchase @@ -30,4 +37,5 @@ import Foundation case .warning: return "⚠️" } } + } diff --git a/PurchasesCoreSwift/Logging/Logger.swift b/PurchasesCoreSwift/Logging/Logger.swift index b49b2556c0..5ce645d338 100644 --- a/PurchasesCoreSwift/Logging/Logger.swift +++ b/PurchasesCoreSwift/Logging/Logger.swift @@ -1,14 +1,21 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // Logger.swift -// PurchasesCoreSwift // // Created by Andrés Boedo on 11/13/20. -// Copyright © 2020 Purchases. All rights reserved. // import Foundation @objc(RCLogLevel) public enum LogLevel: Int { + case debug, info, warn, error func description() -> String { @@ -21,42 +28,45 @@ import Foundation } } -@objc(RCLog) public class Logger: NSObject { - @objc public static var logLevel: LogLevel = .info - @objc public static var logHandler: (LogLevel, String) -> Void = { level, message in +class Logger { + + static var logLevel: LogLevel = .info + static var logHandler: (LogLevel, String) -> Void = { level, message in NSLog("[\(frameworkDescription)] - \(level.description()): \(message)") } private static let frameworkDescription = "Purchases" - @objc public static func log(level: LogLevel, message: String) { + static func log(level: LogLevel, message: String) { guard self.logLevel.rawValue <= level.rawValue else { return } logHandler(level, message) } - @objc public static func log(level: LogLevel, intent: LogIntent, message: String) { + static func log(level: LogLevel, intent: LogIntent, message: String) { let messageWithPrefix = "\(intent.suffix) \(message)" Logger.log(level: level, message: messageWithPrefix) } - @objc public static func debug(_ message: String) { + static func debug(_ message: String) { log(level: .debug, intent: .info, message: message) } - @objc public static func info(_ message: String) { + static func info(_ message: String) { log(level: .info, intent: .info, message: message) } - @objc public static func warn(_ message: String) { + static func warn(_ message: String) { log(level: .warn, intent: .warning, message: message) } - @objc public static func error(_ message: String) { + static func error(_ message: String) { log(level: .error, intent: .rcError, message: message) } + } -@objc public extension Logger { +extension Logger { + static func appleError(_ message: String) { log(level: .error, intent: .appleError, message: message) } @@ -80,4 +90,5 @@ import Foundation static func user(_ message: String) { log(level: .debug, intent: .user, message: message) } + } diff --git a/PurchasesCoreSwift/Logging/Strings/AttributionStrings.swift b/PurchasesCoreSwift/Logging/Strings/AttributionStrings.swift index 84d8b1298e..e5b400569d 100644 --- a/PurchasesCoreSwift/Logging/Strings/AttributionStrings.swift +++ b/PurchasesCoreSwift/Logging/Strings/AttributionStrings.swift @@ -1,15 +1,22 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // AttributionStrings.swift -// PurchasesCoreSwift // // Created by Andrés Boedo on 9/14/20. -// Copyright © 2020 Purchases. All rights reserved. // import Foundation // swiftlint:disable identifier_name -@objc(RCAttributionStrings) public class AttributionStrings: NSObject { +class AttributionStrings { + var appsflyer_id_deprecated: String { "The parameter key rc_appsflyer_id is deprecated. Pass networkUserId to addAttribution instead." } @@ -52,7 +59,7 @@ import Foundation "Will automatically retry if authorization gets granted." } var skip_same_attributes: String { "Attribution data is the same as latest. Skipping." } - @objc public var subscriber_attributes_error: String { "Subscriber attributes errors: %@" } + var subscriber_attributes_error: String { "Subscriber attributes errors: %@" } var unsynced_attributes_count: String { "Found %lu unsynced attributes for App User ID: %@" } var unsynced_attributes: String { "Unsynced attributes: %@" } var attribute_set_locally: String { "Attribute set locally: %@. It will be synced to the backend" + diff --git a/PurchasesCoreSwift/Logging/Strings/ConfigureStrings.swift b/PurchasesCoreSwift/Logging/Strings/ConfigureStrings.swift index b2cdbc3929..e5af1f69de 100644 --- a/PurchasesCoreSwift/Logging/Strings/ConfigureStrings.swift +++ b/PurchasesCoreSwift/Logging/Strings/ConfigureStrings.swift @@ -1,29 +1,37 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // ConfigureStrings.swift -// PurchasesCoreSwift // // Created by Tina Nguyen on 12/11/20. -// Copyright © 2020 Purchases. All rights reserved. // import Foundation // swiftlint:disable identifier_name -@objc(RCConfigureStrings) public class ConfigureStrings: NSObject { +class ConfigureStrings { + var adsupport_not_imported: String { "AdSupport framework not imported. Attribution data incomplete." } - @objc public var application_active: String { "applicationDidBecomeActive" } + var application_active: String { "applicationDidBecomeActive" } var configuring_purchases_proxy_url_set: String { "Purchases is being configured using a proxy for RevenueCat with URL: %@" } - @objc public var debug_enabled: String { "Debug logging enabled" } - @objc public var delegate_set: String { "Delegate set" } - @objc public var purchase_instance_already_set: String { + var debug_enabled: String { "Debug logging enabled" } + var delegate_set: String { "Delegate set" } + var purchase_instance_already_set: String { "Purchases instance already set. Did you mean to configure two Purchases objects?" } - @objc public var initial_app_user_id: String { "Initial App User ID - %@" } - @objc public var no_singleton_instance: String { + var initial_app_user_id: String { "Initial App User ID - %@" } + var no_singleton_instance: String { "There is no singleton instance. Make sure you configure Purchases before trying to get the default instance." + " More info here: https://errors.rev.cat/configuring-sdk" } - @objc public var sdk_version: String { "SDK Version - %@" } + var sdk_version: String { "SDK Version - %@" } + } diff --git a/PurchasesCoreSwift/Logging/Strings/NetworkStrings.swift b/PurchasesCoreSwift/Logging/Strings/NetworkStrings.swift index eb4323c625..dc8ebb914a 100644 --- a/PurchasesCoreSwift/Logging/Strings/NetworkStrings.swift +++ b/PurchasesCoreSwift/Logging/Strings/NetworkStrings.swift @@ -1,15 +1,22 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // NetworkStrings.swift -// PurchasesCoreSwift // // Created by Tina Nguyen on 12/11/20. -// Copyright © 2020 Purchases. All rights reserved. // import Foundation // swiftlint:disable identifier_name class NetworkStrings { + var api_request_completed: String { "API request completed with status: %@ %@ %d" } var api_request_started: String { "API request started: %@ %@" } var creating_json_error: String { "Error creating request with JSON body: %@ ; error: %@" } @@ -35,4 +42,5 @@ class NetworkStrings { We can't find the cached response, but call has already been retried. Returning result from backend %@. """ } + } diff --git a/PurchasesCoreSwift/Logging/Strings/OfferingStrings.swift b/PurchasesCoreSwift/Logging/Strings/OfferingStrings.swift index 5585da6ce9..da1e5b088b 100644 --- a/PurchasesCoreSwift/Logging/Strings/OfferingStrings.swift +++ b/PurchasesCoreSwift/Logging/Strings/OfferingStrings.swift @@ -1,15 +1,22 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // OfferingStrings.swift -// PurchasesCoreSwift // // Created by Tina Nguyen on 12/11/20. -// Copyright © 2020 Purchases. All rights reserved. // import Foundation // swiftlint:disable identifier_name class OfferingStrings { + var cannot_find_product_configuration_error: String { "Could not find SKProduct for %@ " + "\nThere is a problem with your configuration in App Store Connect. " + "\nMore info here: https://errors.rev.cat/configuring-products"} @@ -36,4 +43,5 @@ class OfferingStrings { var skproductsrequest_did_finish: String { "SKProductsRequest did finish" } var skproductsrequest_received_response: String { "SKProductsRequest request received response" } var vending_offerings_cache: String { "Vending Offerings from cache" } + } diff --git a/PurchasesCoreSwift/Logging/Strings/PurchaseStrings.swift b/PurchasesCoreSwift/Logging/Strings/PurchaseStrings.swift index a8b9ce1f1b..addba5741a 100644 --- a/PurchasesCoreSwift/Logging/Strings/PurchaseStrings.swift +++ b/PurchasesCoreSwift/Logging/Strings/PurchaseStrings.swift @@ -1,9 +1,15 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // PurchaseStrings.swift -// PurchasesCoreSwift // // Created by Tina Nguyen on 12/11/20. -// Copyright © 2020 Purchases. All rights reserved. // import Foundation diff --git a/PurchasesCoreSwift/Logging/Strings/PurchaserInfoStrings.swift b/PurchasesCoreSwift/Logging/Strings/PurchaserInfoStrings.swift index 5f3a3d5e4e..358d867326 100644 --- a/PurchasesCoreSwift/Logging/Strings/PurchaserInfoStrings.swift +++ b/PurchasesCoreSwift/Logging/Strings/PurchaserInfoStrings.swift @@ -1,15 +1,22 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // PurchaserInfoStrings.swift -// PurchasesCoreSwift // // Created by Tina Nguyen on 12/11/20. -// Copyright © 2020 Purchases. All rights reserved. // import Foundation // swiftlint:disable identifier_name class PurchaserInfoStrings { + var checking_intro_eligibility_locally_error: String { "Couldn't check intro eligibility locally, error: %@" } var checking_intro_eligibility_locally_result: String { "Local intro eligibility computed locally. Result: %@" } var checking_intro_eligibility_locally: String { "Attempting to check intro eligibility locally" } @@ -26,4 +33,5 @@ class PurchaserInfoStrings { var sending_latest_purchaserinfo_to_delegate: String { "Sending latest PurchaserInfo to delegate." } var sending_updated_purchaserinfo_to_delegate: String { "Sending updated PurchaserInfo to delegate." } var vending_cache: String { "Vending PurchaserInfo from cache." } + } diff --git a/PurchasesCoreSwift/Logging/Strings/ReceiptStrings.swift b/PurchasesCoreSwift/Logging/Strings/ReceiptStrings.swift index 5027fd3619..98595a541e 100644 --- a/PurchasesCoreSwift/Logging/Strings/ReceiptStrings.swift +++ b/PurchasesCoreSwift/Logging/Strings/ReceiptStrings.swift @@ -1,15 +1,22 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // ReceiptStrings.swift -// PurchasesCoreSwift // // Created by Tina Nguyen on 12/11/20. -// Copyright © 2020 Purchases. All rights reserved. // import Foundation // swiftlint:disable identifier_name -@objc(RCReceiptStrings) public class ReceiptStrings: NSObject { +class ReceiptStrings { + var data_object_identifer_not_found_receipt: String { "The data object identifier couldn't be found " + "on the receipt." } var force_refreshing_receipt: String { "Force refreshing the receipt to get latest transactions " + @@ -20,7 +27,7 @@ import Foundation var no_sandbox_receipt_restore: String { "App running in sandbox without a receipt file. Restoring " + "transactions won't work until a purchase is made to generate a receipt. This should not happen in " + "production unless user is logged out of Apple account." } - @objc public var parse_receipt_locally_error: String { "There was an error when trying to parse the receipt " + + var parse_receipt_locally_error: String { "There was an error when trying to parse the receipt " + "locally, details: %@" } var parsing_receipt_failed: String { "%@-%@: Could not parse receipt, conservatively returning true" } var parsing_receipt_success: String { "Receipt parsed successfully" } @@ -30,4 +37,5 @@ import Foundation "Apple account." } var unknown_backend_error: String { "Unexpected backend error when posting receipt. Make sure you " + "are on latest SDK version and let us know if problem persists." } + } diff --git a/PurchasesCoreSwift/Logging/Strings/RestoreStrings.swift b/PurchasesCoreSwift/Logging/Strings/RestoreStrings.swift index e57eaa79bd..038b7d17b0 100644 --- a/PurchasesCoreSwift/Logging/Strings/RestoreStrings.swift +++ b/PurchasesCoreSwift/Logging/Strings/RestoreStrings.swift @@ -1,17 +1,25 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // RestoreStrings.swift -// PurchasesCoreSwift // // Created by Tina Nguyen on 12/11/20. -// Copyright © 2020 Purchases. All rights reserved. // import Foundation // swiftlint:disable identifier_name class RestoreStrings { + var restoretransactions_called_with_allow_sharing_appstore_account_false_warning: String { "allowSharingAppStoreAccount is set to false and restoreTransactions has been called. Are you sure you want " + "to do this?" } + } diff --git a/PurchasesCoreSwift/Logging/Strings/Strings.swift b/PurchasesCoreSwift/Logging/Strings/Strings.swift index d050cf73a4..662e5cbacf 100644 --- a/PurchasesCoreSwift/Logging/Strings/Strings.swift +++ b/PurchasesCoreSwift/Logging/Strings/Strings.swift @@ -1,18 +1,27 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // Created by Andrés Boedo on 9/14/20. -// Copyright (c) 2020 Purchases. All rights reserved. // import Foundation -@objc(RCStrings) public class Strings: NSObject { - @objc public static let attribution = AttributionStrings() - @objc public static let configure = ConfigureStrings() +class Strings { + + static let attribution = AttributionStrings() + static let configure = ConfigureStrings() static let identity = IdentityStrings() static let network = NetworkStrings() static let offering = OfferingStrings() static let purchase = PurchaseStrings() static let purchaserInfo = PurchaserInfoStrings() - @objc public static let receipt = ReceiptStrings() + static let receipt = ReceiptStrings() static let restore = RestoreStrings() + } diff --git a/PurchasesCoreSwift/Misc/DateExtensions.swift b/PurchasesCoreSwift/Misc/DateExtensions.swift index 1cc8a11142..d13192a05c 100644 --- a/PurchasesCoreSwift/Misc/DateExtensions.swift +++ b/PurchasesCoreSwift/Misc/DateExtensions.swift @@ -1,6 +1,13 @@ // -// Created by Andrés Boedo on 8/7/20. -// Copyright (c) 2020 Purchases. All rights reserved. +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Created by Andrés Boedo on 8/7/20. // import Foundation @@ -13,7 +20,7 @@ enum DateExtensionsError: Error { extension DateExtensionsError: CustomStringConvertible { - public var description: String { + var description: String { switch self { case .invalidDateComponents(let dateComponents): return "invalid date components: \(dateComponents.description)" diff --git a/PurchasesCoreSwift/Misc/DateProvider.swift b/PurchasesCoreSwift/Misc/DateProvider.swift index 35f2a16061..16d85fe281 100644 --- a/PurchasesCoreSwift/Misc/DateProvider.swift +++ b/PurchasesCoreSwift/Misc/DateProvider.swift @@ -1,9 +1,15 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // DateProvider.swift -// PurchasesCoreSwift // // Created by Josh Holtz on 6/28/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation diff --git a/PurchasesCoreSwift/Misc/ISOPeriodFormatter.swift b/PurchasesCoreSwift/Misc/ISOPeriodFormatter.swift index df6ebc7d30..5f416ae4b0 100644 --- a/PurchasesCoreSwift/Misc/ISOPeriodFormatter.swift +++ b/PurchasesCoreSwift/Misc/ISOPeriodFormatter.swift @@ -1,9 +1,15 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // ISOPeriodFormatter.swift -// PurchasesCoreSwift // // Created by Joshua Liebowitz on 6/30/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation @@ -33,4 +39,5 @@ class ISOPeriodFormatter { fatalError("New SKProduct.PeriodUnit \(unit) unaccounted for") } } + } diff --git a/PurchasesCoreSwift/Misc/OperationDispatcher.swift b/PurchasesCoreSwift/Misc/OperationDispatcher.swift index 7f99aa5530..862cafe6f2 100644 --- a/PurchasesCoreSwift/Misc/OperationDispatcher.swift +++ b/PurchasesCoreSwift/Misc/OperationDispatcher.swift @@ -1,21 +1,27 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // OperationDispatcher.swift -// Purchases // // Created by Andrés Boedo on 8/5/20. -// Copyright © 2020 Purchases. All rights reserved. // import Foundation -@objc(RCOperationDispatcher) public class OperationDispatcher: NSObject { +class OperationDispatcher { private let mainQueue = DispatchQueue.main private let workerQueue = DispatchQueue(label: "OperationDispatcherWorkerQueue") private let httpQueue = DispatchQueue(label: "HTTPClientQueue") private let maxJitterInSeconds: Double = 5 - @objc public func dispatchOnMainThread(_ block: @escaping () -> Void) { + func dispatchOnMainThread(_ block: @escaping () -> Void) { if Thread.isMainThread { block() } else { @@ -23,8 +29,7 @@ import Foundation } } - @objc public func dispatchOnWorkerThread(withRandomDelay: Bool = false, - block: @escaping () -> Void) { + func dispatchOnWorkerThread(withRandomDelay: Bool = false, block: @escaping () -> Void) { if withRandomDelay { let delay = Double.random(in: 0.. Void) { + func isApplicationBackgrounded(completion: @escaping (Bool) -> Void) { DispatchQueue.main.async { completion(self.isApplicationBackgrounded) } } - @objc open func isOperatingSystemAtLeastVersion(_ version: OperatingSystemVersion) -> Bool { + func isOperatingSystemAtLeastVersion(_ version: OperatingSystemVersion) -> Bool { return ProcessInfo.processInfo.isOperatingSystemAtLeast(version) } } -@objc public extension SystemInfo { +extension SystemInfo { static var applicationDidBecomeActiveNotification: Notification.Name { #if os(iOS) || os(tvOS) diff --git a/PurchasesCoreSwift/Networking/Backend.swift b/PurchasesCoreSwift/Networking/Backend.swift index 7f0760c1d7..3c99b6a0c1 100644 --- a/PurchasesCoreSwift/Networking/Backend.swift +++ b/PurchasesCoreSwift/Networking/Backend.swift @@ -13,15 +13,14 @@ import Foundation -public typealias SubscriberAttributeDict = [String: SubscriberAttribute] -public typealias BackendPurchaserInfoResponseHandler = (PurchaserInfo?, Error?) -> Void -public typealias IntroEligibilityResponseHandler = ([String: IntroEligibility], Error?) -> Void -public typealias OfferingsResponseHandler = ([String: Any]?, Error?) -> Void -public typealias OfferSigningResponseHandler = (String?, String?, UUID?, NSNumber?, Error?) -> Void +typealias SubscriberAttributeDict = [String: SubscriberAttribute] +typealias BackendPurchaserInfoResponseHandler = (PurchaserInfo?, Error?) -> Void +typealias IntroEligibilityResponseHandler = ([String: IntroEligibility], Error?) -> Void +typealias OfferingsResponseHandler = ([String: Any]?, Error?) -> Void +typealias OfferSigningResponseHandler = (String?, String?, UUID?, NSNumber?, Error?) -> Void // swiftlint:disable type_body_length file_length -// TODO(post-migration): Make this internal again, and all the other things too -@objc(RCBackend) public class Backend: NSObject { +class Backend { static let RCSuccessfullySyncedKey: NSError.UserInfoKey = "rc_successfullySynced" static let RCAttributeErrorsKey = "attribute_errors" @@ -37,11 +36,10 @@ public typealias OfferSigningResponseHandler = (String?, String?, UUID?, NSNumbe private var authHeaders: [String: String] { return ["Authorization": "Bearer \(self.apiKey)"] } - @objc(initWithAPIKey:systemInfo:eTagManager:operationDispatcher:) - public convenience init(apiKey: String, - systemInfo: SystemInfo, - eTagManager: ETagManager, - operationDispatcher: OperationDispatcher) { + convenience init(apiKey: String, + systemInfo: SystemInfo, + eTagManager: ETagManager, + operationDispatcher: OperationDispatcher) { let httpClient = HTTPClient(systemInfo: systemInfo, eTagManager: eTagManager, operationDispatcher: operationDispatcher) @@ -329,11 +327,10 @@ public typealias OfferSigningResponseHandler = (String?, String?, UUID?, NSNumbe } } - @objc(getIntroEligibilityForAppUserID:receiptData:productIdentifiers:completion:) - public func getIntroEligibility(appUserID: String, - receiptData: Data, - productIdentifiers: [String], - completion: @escaping IntroEligibilityResponseHandler) { + func getIntroEligibility(appUserID: String, + receiptData: Data, + productIdentifiers: [String], + completion: @escaping IntroEligibilityResponseHandler) { guard productIdentifiers.count > 0 else { completion([:], nil) return diff --git a/PurchasesCoreSwift/Networking/ETagAndResponseWrapper.swift b/PurchasesCoreSwift/Networking/ETagAndResponseWrapper.swift index c478e48fbc..dab03fb059 100644 --- a/PurchasesCoreSwift/Networking/ETagAndResponseWrapper.swift +++ b/PurchasesCoreSwift/Networking/ETagAndResponseWrapper.swift @@ -1,9 +1,15 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // ETagAndResponseWrapper.swift -// PurchasesCoreSwift // // Created by César de la Vega on 6/11/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation @@ -36,6 +42,7 @@ struct ETagAndResponseWrapper { } extension ETagAndResponseWrapper { + init?(with data: Data) { guard let dictionary = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] else { @@ -54,4 +61,5 @@ extension ETagAndResponseWrapper { } self.init(eTag: eTag, statusCode: statusCode, jsonObject: jsonObject) } + } diff --git a/PurchasesCoreSwift/Networking/ETagManager.swift b/PurchasesCoreSwift/Networking/ETagManager.swift index 7f793d932c..7b5c1ba926 100644 --- a/PurchasesCoreSwift/Networking/ETagManager.swift +++ b/PurchasesCoreSwift/Networking/ETagManager.swift @@ -1,11 +1,20 @@ // -// Created by César de la Vega on 4/16/21. +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// ETagManager.swift +// +// Created by César de la Vega on 4/16/21. // import Foundation -// TODO:(post-migration): make internal -@objc(RCETagManager) public class ETagManager: NSObject { +class ETagManager { static let eTagHeaderName = "X-RevenueCat-ETag" @@ -13,7 +22,7 @@ import Foundation private let userDefaults: UserDefaults - @objc public override init() { + init() { self.userDefaults = UserDefaults(suiteName: ETagManager.suiteName) ?? UserDefaults.standard } @@ -63,7 +72,7 @@ import Foundation } func clearCaches() { - self.userDefaults.removePersistentDomain(forName: ETagManager.suiteName) + userDefaults.removePersistentDomain(forName: ETagManager.suiteName) } } @@ -114,11 +123,12 @@ private extension ETagManager { return request.url?.absoluteString } - private static let suiteNameBase: String = "revenuecat.etags" + static let suiteNameBase: String = "revenuecat.etags" static var suiteName: String { guard let bundleID = Bundle.main.bundleIdentifier else { return suiteNameBase } return bundleID + ".\(suiteNameBase)" } + } diff --git a/PurchasesCoreSwift/Networking/HTTPClient.swift b/PurchasesCoreSwift/Networking/HTTPClient.swift index 46317eca32..7d831505c1 100644 --- a/PurchasesCoreSwift/Networking/HTTPClient.swift +++ b/PurchasesCoreSwift/Networking/HTTPClient.swift @@ -320,4 +320,5 @@ private extension HTTPClient { } return urlRequest } + } diff --git a/PurchasesCoreSwift/Networking/HTTPRequest.swift b/PurchasesCoreSwift/Networking/HTTPRequest.swift index a7ccf20fde..35511bd5ba 100644 --- a/PurchasesCoreSwift/Networking/HTTPRequest.swift +++ b/PurchasesCoreSwift/Networking/HTTPRequest.swift @@ -1,9 +1,15 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // HTTPRequest.swift -// PurchasesCoreSwift // // Created by Juanpe Catalán on 8/7/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation diff --git a/PurchasesCoreSwift/Networking/HTTPResponse.swift b/PurchasesCoreSwift/Networking/HTTPResponse.swift index 14c2c3b5f7..ef1b91af2e 100644 --- a/PurchasesCoreSwift/Networking/HTTPResponse.swift +++ b/PurchasesCoreSwift/Networking/HTTPResponse.swift @@ -1,9 +1,15 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // HTTPResponse.swift -// PurchasesCoreSwift // // Created by César de la Vega on 4/19/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation diff --git a/PurchasesCoreSwift/Networking/HTTPStatusCodes.swift b/PurchasesCoreSwift/Networking/HTTPStatusCodes.swift index 66d84c700c..ba7c1e86d8 100644 --- a/PurchasesCoreSwift/Networking/HTTPStatusCodes.swift +++ b/PurchasesCoreSwift/Networking/HTTPStatusCodes.swift @@ -1,9 +1,15 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // HTTPStatusCodes.swift -// PurchasesCoreSwift // // Created by César de la Vega on 4/19/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation diff --git a/PurchasesCoreSwift/Public/EntitlementInfo.swift b/PurchasesCoreSwift/Public/EntitlementInfo.swift index 461052203f..1cae96822c 100644 --- a/PurchasesCoreSwift/Public/EntitlementInfo.swift +++ b/PurchasesCoreSwift/Public/EntitlementInfo.swift @@ -1,9 +1,15 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // EntitlementInfo.swift -// PurchasesCoreSwift // // Created by Joshua Liebowitz on 6/25/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation @@ -128,7 +134,6 @@ import Foundation dateFormatter: .iso8601SecondsDateFormatter) } - // TODO(post-migration): Make this internal // TODO(cleanup): Codable init(entitlementId: String, entitlementData: [String: Any], @@ -175,73 +180,6 @@ import Foundation billingIssueDetectedAt: billingIssueDetectedAt) } - private class func isDateActive(expirationDate: Date?, forRequestDate requestDate: Date?) -> Bool { - guard let expirationDate = expirationDate else { - return true - } - - let referenceDate: Date = requestDate ?? Date.init() - return expirationDate.timeIntervalSince(referenceDate) > 0 - } - - private class func parseOwnershipType(ownershipType: String?) -> PurchaseOwnershipType { - switch ownershipType { - case nil: - return .purchased - case "PURCHASED": - return .purchased - case "FAMILY_SHARED": - return .familyShared - default: - // TODO(post-migration check): Logging? - return .unknown - } - } - - private class func parsePeriodType(periodType: String?) -> PeriodType { - switch periodType { - case "normal": - return .normal - case "intro": - return .intro - case "trial": - return .trial - default: - // TODO(post-migration check): Also handles nil. - return .normal - } - } - - private class func parseStore(store: String?) -> Store { - switch store { - case "app_store": - return .appStore - case "mac_app_store": - return .macAppStore - case "play_store": - return .playStore - case "stripe": - return .stripe - case "promotional": - return .promotional - default: - // TODO(post-migration check): Logging? - return .unknownStore - } - } - - private class func willRenewWithExpirationDate(expirationDate: Date?, - store: Store, - unsubscribeDetectedAt: Date?, - billingIssueDetectedAt: Date?) -> Bool { - let isPromo = store == .promotional - let isLifetime = expirationDate == nil - let hasUnsubscribed = unsubscribeDetectedAt != nil - let hasBillingIssues = billingIssueDetectedAt != nil - - return !(isPromo || isLifetime || hasUnsubscribed || hasBillingIssues) - } - public override var description: String { return """ <\(String(describing: EntitlementInfo.self)): " @@ -331,4 +269,89 @@ import Foundation hash = hash * 31 + UInt(self.ownershipType.hashValue) return Int(hash) } + +} + +private extension EntitlementInfo { + + class func isDateActive(expirationDate: Date?, forRequestDate requestDate: Date?) -> Bool { + guard let expirationDate = expirationDate else { + return true + } + + let referenceDate: Date = requestDate ?? Date.init() + return expirationDate.timeIntervalSince(referenceDate) > 0 + } + + class func parseOwnershipType(ownershipType: String?) -> PurchaseOwnershipType { + guard let ownershipType = ownershipType else { + // TODO: should this be a warning? + return .purchased + } + + switch ownershipType { + case "PURCHASED": + return .purchased + case "FAMILY_SHARED": + return .familyShared + default: + Logger.warn("received unknown ownershipType: \(ownershipType)") + return .unknown + } + } + + class func parsePeriodType(periodType: String?) -> PeriodType { + guard let periodType = periodType else { + Logger.warn("nil periodType found during parsePeriodType") + return .normal + } + + switch periodType { + case "normal": + return .normal + case "intro": + return .intro + case "trial": + return .trial + default: + Logger.warn("received unknown periodType: \(periodType)") + return .normal + } + } + + class func parseStore(store: String?) -> Store { + guard let store = store else { + Logger.warn("nil store found during parseStore") + return .unknownStore + } + + switch store { + case "app_store": + return .appStore + case "mac_app_store": + return .macAppStore + case "play_store": + return .playStore + case "stripe": + return .stripe + case "promotional": + return .promotional + default: + Logger.warn("received unknown store: \(store)") + return .unknownStore + } + } + + class func willRenewWithExpirationDate(expirationDate: Date?, + store: Store, + unsubscribeDetectedAt: Date?, + billingIssueDetectedAt: Date?) -> Bool { + let isPromo = store == .promotional + let isLifetime = expirationDate == nil + let hasUnsubscribed = unsubscribeDetectedAt != nil + let hasBillingIssues = billingIssueDetectedAt != nil + + return !(isPromo || isLifetime || hasUnsubscribed || hasBillingIssues) + } + } diff --git a/PurchasesCoreSwift/Public/Error Handling/ErrorUtils.swift b/PurchasesCoreSwift/Public/Error Handling/ErrorUtils.swift index eb21433eb7..b8c97ec262 100644 --- a/PurchasesCoreSwift/Public/Error Handling/ErrorUtils.swift +++ b/PurchasesCoreSwift/Public/Error Handling/ErrorUtils.swift @@ -223,12 +223,11 @@ private extension SKError { } -// TODO(post migration): Change back to internal -public extension ErrorUtils { +extension ErrorUtils { - @objc static func backendError(withBackendCode backendCode: NSNumber?, - backendMessage: String?, - extraUserInfo: [NSError.UserInfoKey: Any]? = nil) -> Error { + static func backendError(withBackendCode backendCode: NSNumber?, + backendMessage: String?, + extraUserInfo: [NSError.UserInfoKey: Any]? = nil) -> Error { let errorCode: ErrorCode if let maybeBackendCode = backendCode, let backendErrorCode = BackendErrorCode.init(rawValue: maybeBackendCode.intValue) { diff --git a/PurchasesCoreSwift/Public/Offering.swift b/PurchasesCoreSwift/Public/Offering.swift index ead7ed11a3..ea020828cf 100644 --- a/PurchasesCoreSwift/Public/Offering.swift +++ b/PurchasesCoreSwift/Public/Offering.swift @@ -1,14 +1,21 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // Offering.swift -// PurchasesCoreSwift // // Created by Joshua Liebowitz on 7/9/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation @objc(RCOffering) public class Offering: NSObject { + /** Unique identifier defined in RevenueCat dashboard. */ @@ -59,8 +66,7 @@ import Foundation */ @objc private(set) public var weekly: Package? - // TODO(post-migration): Change back to internal instead of public - @objc public init(identifier: String, serverDescription: String, availablePackages: [Package]) { + init(identifier: String, serverDescription: String, availablePackages: [Package]) { self.identifier = identifier self.serverDescription = serverDescription self.availablePackages = availablePackages diff --git a/PurchasesCoreSwift/Public/Offerings.swift b/PurchasesCoreSwift/Public/Offerings.swift index dd75b581ea..4172c9b6ba 100644 --- a/PurchasesCoreSwift/Public/Offerings.swift +++ b/PurchasesCoreSwift/Public/Offerings.swift @@ -1,9 +1,15 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // Offerings.swift -// PurchasesCoreSwift // // Created by Joshua Liebowitz on 7/12/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation @@ -51,7 +57,6 @@ import Foundation return offering(identifier: key) } - // TODO (Post-migration): Remove @objc and make it internal again. @objc public init(offerings: [String: Offering], currentOfferingID: String?) { all = offerings self.currentOfferingID = currentOfferingID diff --git a/PurchasesCoreSwift/Public/Purchases.swift b/PurchasesCoreSwift/Public/Purchases.swift index 7558d9553c..4251bd3b00 100644 --- a/PurchasesCoreSwift/Public/Purchases.swift +++ b/PurchasesCoreSwift/Public/Purchases.swift @@ -622,7 +622,7 @@ extension Purchases { private func post(attributionData data: [String: Any], fromNetwork network: AttributionNetwork, forNetworkUserId networkUserId: String?) { - attributionPoster.post(attributionData: data, fromNetwork: network, forNetworkUserId: networkUserId) + attributionPoster.post(attributionData: data, fromNetwork: network, networkUserId: networkUserId) } private func postAppleSearchAddsAttributionCollectionIfNeeded() { diff --git a/PurchasesCoreSwift/Transaction.swift b/PurchasesCoreSwift/Public/Transaction.swift similarity index 72% rename from PurchasesCoreSwift/Transaction.swift rename to PurchasesCoreSwift/Public/Transaction.swift index b7609b649d..6d7811bbfb 100644 --- a/PurchasesCoreSwift/Transaction.swift +++ b/PurchasesCoreSwift/Public/Transaction.swift @@ -1,9 +1,15 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // Transaction.swift -// Purchases // // Created by RevenueCat. -// Copyright © 2020 Purchases. All rights reserved. // import Foundation @@ -18,10 +24,9 @@ import Foundation self.revenueCatId = transactionId self.productId = productId self.purchaseDate = purchaseDate - super.init() } - init?(with serverResponse: [String: Any], productId: String, dateFormatter: DateFormatter) { + @objc public init?(with serverResponse: [String: Any], productId: String, dateFormatter: DateFormatter) { guard let revenueCatId = serverResponse["id"] as? String, let dateString = serverResponse["purchase_date"] as? String, let purchaseDate = dateFormatter.date(fromString: dateString) else { @@ -33,7 +38,6 @@ import Foundation self.revenueCatId = revenueCatId self.purchaseDate = purchaseDate self.productId = productId - super.init() } } diff --git a/PurchasesCoreSwift/Purchasing/OfferingsFactory.swift b/PurchasesCoreSwift/Purchasing/OfferingsFactory.swift index 07fe362353..0963d807dc 100644 --- a/PurchasesCoreSwift/Purchasing/OfferingsFactory.swift +++ b/PurchasesCoreSwift/Purchasing/OfferingsFactory.swift @@ -1,16 +1,21 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // OfferingsFactory.swift -// PurchasesCoreSwift // // Created by César de la Vega on 7/13/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation import StoreKit -// TODO (Post-migration): Remove @objc and make it internal again. -@objc(RCOfferingsFactory) public class OfferingsFactory: NSObject { +class OfferingsFactory { func createOfferings(withProducts products: [String: SKProduct], data: [String: Any]) -> Offerings? { diff --git a/PurchasesCoreSwift/Purchasing/OfferingsManager.swift b/PurchasesCoreSwift/Purchasing/OfferingsManager.swift index cefdee0596..722736cf4c 100644 --- a/PurchasesCoreSwift/Purchasing/OfferingsManager.swift +++ b/PurchasesCoreSwift/Purchasing/OfferingsManager.swift @@ -14,8 +14,7 @@ import Foundation import StoreKit -// TODO (post-migration): Make all the things internal again. -@objc(RCOfferingsManager) public class OfferingsManager: NSObject { +class OfferingsManager { private let deviceCache: DeviceCache private let operationDispatcher: OperationDispatcher @@ -24,12 +23,12 @@ import StoreKit private let offeringsFactory: OfferingsFactory private let productsManager: ProductsManager - @objc public init(deviceCache: DeviceCache, - operationDispatcher: OperationDispatcher, - systemInfo: SystemInfo, - backend: Backend, - offeringsFactory: OfferingsFactory, - productsManager: ProductsManager) { + init(deviceCache: DeviceCache, + operationDispatcher: OperationDispatcher, + systemInfo: SystemInfo, + backend: Backend, + offeringsFactory: OfferingsFactory, + productsManager: ProductsManager) { self.deviceCache = deviceCache self.operationDispatcher = operationDispatcher self.systemInfo = systemInfo @@ -38,11 +37,6 @@ import StoreKit self.productsManager = productsManager } -} - -public extension OfferingsManager { - - @objc(offeringsWithAppUserID:completionBlock:) func offerings(appUserID: String, completion: ReceiveOfferingsBlock?) { guard let cachedOfferings = deviceCache.cachedOfferings else { Logger.debug(Strings.offering.no_cached_offerings_fetching_from_network) @@ -74,7 +68,6 @@ public extension OfferingsManager { } } - @objc(updateOfferingsCacheWithAppUserID:isAppBackgrounded:completion:) func updateOfferingsCache(appUserID: String, isAppBackgrounded: Bool, completion: ReceiveOfferingsBlock?) { deviceCache.setOfferingsCacheTimestampToNow() operationDispatcher.dispatchOnWorkerThread(withRandomDelay: isAppBackgrounded) { diff --git a/PurchasesCoreSwift/Purchasing/ProductInfo.swift b/PurchasesCoreSwift/Purchasing/ProductInfo.swift index 61cf3f8833..19fb566de4 100644 --- a/PurchasesCoreSwift/Purchasing/ProductInfo.swift +++ b/PurchasesCoreSwift/Purchasing/ProductInfo.swift @@ -1,9 +1,15 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // ProductInfo.swift -// PurchasesCoreSwift // // Created by Joshua Liebowitz on 7/2/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation diff --git a/PurchasesCoreSwift/Purchasing/ProductInfoEnums.swift b/PurchasesCoreSwift/Purchasing/ProductInfoEnums.swift index 0ef0a43e8f..c7e5769165 100644 --- a/PurchasesCoreSwift/Purchasing/ProductInfoEnums.swift +++ b/PurchasesCoreSwift/Purchasing/ProductInfoEnums.swift @@ -1,9 +1,15 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // ProductInfoEnums.swift -// PurchasesCoreSwift // // Created by Joshua Liebowitz on 7/2/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation diff --git a/PurchasesCoreSwift/Purchasing/ProductInfoExtractor.swift b/PurchasesCoreSwift/Purchasing/ProductInfoExtractor.swift index b505ec7f0b..861a6f0294 100644 --- a/PurchasesCoreSwift/Purchasing/ProductInfoExtractor.swift +++ b/PurchasesCoreSwift/Purchasing/ProductInfoExtractor.swift @@ -1,9 +1,15 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // ProductInfoExtractor.swift -// PurchasesCoreSwift // // Created by Juanpe Catalán on 8/7/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation diff --git a/PurchasesCoreSwift/Purchasing/ProductsManager.swift b/PurchasesCoreSwift/Purchasing/ProductsManager.swift index 4fed770e9f..48588fb909 100644 --- a/PurchasesCoreSwift/Purchasing/ProductsManager.swift +++ b/PurchasesCoreSwift/Purchasing/ProductsManager.swift @@ -1,24 +1,29 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // ProductsManager.swift -// Purchases // // Created by Andrés Boedo on 7/14/20. -// Copyright © 2020 Purchases. All rights reserved. // import Foundation import StoreKit -// TODO: make internal -@objc(RCProductsManager) public class ProductsManager: NSObject { - private let productsRequestFactory: ProductsRequestFactory +class ProductsManager: NSObject { + private let productsRequestFactory: ProductsRequestFactory private var cachedProductsByIdentifier: [String: SKProduct] = [:] private let queue = DispatchQueue(label: "ProductsManager") private var productsByRequests: [SKRequest: Set] = [:] private var completionHandlers: [Set: [(Set) -> Void]] = [:] - @objc public init(productsRequestFactory: ProductsRequestFactory = ProductsRequestFactory()) { + init(productsRequestFactory: ProductsRequestFactory = ProductsRequestFactory()) { self.productsRequestFactory = productsRequestFactory } @@ -64,7 +69,7 @@ import StoreKit extension ProductsManager: SKProductsRequestDelegate { - public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { + func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { queue.async { [self] in Logger.rcSuccess(Strings.network.skproductsrequest_received_response) guard let requestProducts = self.productsByRequests[request] else { @@ -86,12 +91,12 @@ extension ProductsManager: SKProductsRequestDelegate { } } - public func requestDidFinish(_ request: SKRequest) { + func requestDidFinish(_ request: SKRequest) { Logger.rcSuccess(Strings.network.skproductsrequest_finished) request.cancel() } - public func request(_ request: SKRequest, didFailWithError error: Error) { + func request(_ request: SKRequest, didFailWithError error: Error) { queue.async { [self] in Logger.appleError(String(format: Strings.network.skproductsrequest_failed, error.localizedDescription)) guard let products = self.productsByRequests[request] else { diff --git a/PurchasesCoreSwift/Purchasing/ProductsRequestFactory.swift b/PurchasesCoreSwift/Purchasing/ProductsRequestFactory.swift index 6c33882ab7..6483b99fc6 100644 --- a/PurchasesCoreSwift/Purchasing/ProductsRequestFactory.swift +++ b/PurchasesCoreSwift/Purchasing/ProductsRequestFactory.swift @@ -1,13 +1,20 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // Created by Andrés Boedo on 8/12/20. -// Copyright (c) 2020 Purchases. All rights reserved. // import Foundation import StoreKit // todo: make internal -@objc(RCProductsRequestFactory) public class ProductsRequestFactory: NSObject { +class ProductsRequestFactory { func request(productIdentifiers: Set) -> SKProductsRequest { return SKProductsRequest(productIdentifiers: productIdentifiers) diff --git a/PurchasesCoreSwift/Purchasing/PromotionalOffer.swift b/PurchasesCoreSwift/Purchasing/PromotionalOffer.swift index 1b9aa31be3..fbbd252216 100644 --- a/PurchasesCoreSwift/Purchasing/PromotionalOffer.swift +++ b/PurchasesCoreSwift/Purchasing/PromotionalOffer.swift @@ -1,9 +1,15 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // PromotionalOffer.swift -// PurchasesCoreSwift // // Created by Joshua Liebowitz on 7/2/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation diff --git a/PurchasesCoreSwift/Purchasing/PurchasesOrchestrator.swift b/PurchasesCoreSwift/Purchasing/PurchasesOrchestrator.swift index 9844ddf01c..4bdff0fb72 100644 --- a/PurchasesCoreSwift/Purchasing/PurchasesOrchestrator.swift +++ b/PurchasesCoreSwift/Purchasing/PurchasesOrchestrator.swift @@ -14,17 +14,16 @@ import Foundation import StoreKit -@objc(RCPurchasesOrchestratorDelegate) public protocol PurchasesOrchestratorDelegate { +@objc protocol PurchasesOrchestratorDelegate { func shouldPurchasePromoProduct(_ product: SKProduct, defermentBlock: @escaping DeferredPromotionalPurchaseBlock) } -// Todo(post-migration): make internal -@objc(RCPurchasesOrchestrator) public class PurchasesOrchestrator: NSObject { +class PurchasesOrchestrator { - @objc public var finishTransactions: Bool { systemInfo.finishTransactions } - @objc public var allowSharingAppStoreAccount: Bool { + var finishTransactions: Bool { systemInfo.finishTransactions } + var allowSharingAppStoreAccount: Bool { get { return maybeAllowSharingAppStoreAccount ?? identityManager.currentUserIsAnonymous } @@ -33,13 +32,12 @@ import StoreKit } } - @objc public weak var maybeDelegate: PurchasesOrchestratorDelegate? + @objc weak var maybeDelegate: PurchasesOrchestratorDelegate? private var maybeAllowSharingAppStoreAccount: Bool? private var presentedOfferingIDsByProductID: [String: String] = [:] private var purchaseCompleteCallbacksByProductID: [String: PurchaseCompletedBlock] = [:] - // todo: remove explicit unwrap once nullability in identityManager is updated private var appUserID: String { identityManager.currentAppUserID } private var unsyncedAttributes: SubscriberAttributeDict { subscriberAttributesManager.unsyncedAttributesByKey(appUserID: self.appUserID) @@ -58,17 +56,17 @@ import StoreKit private let deviceCache: DeviceCache private let lock = NSRecursiveLock() - @objc public init(productsManager: ProductsManager, - storeKitWrapper: StoreKitWrapper, - systemInfo: SystemInfo, - subscriberAttributesManager: SubscriberAttributesManager, - operationDispatcher: OperationDispatcher, - receiptFetcher: ReceiptFetcher, - purchaserInfoManager: PurchaserInfoManager, - backend: Backend, - identityManager: IdentityManager, - receiptParser: ReceiptParser, - deviceCache: DeviceCache) { + init(productsManager: ProductsManager, + storeKitWrapper: StoreKitWrapper, + systemInfo: SystemInfo, + subscriberAttributesManager: SubscriberAttributesManager, + operationDispatcher: OperationDispatcher, + receiptFetcher: ReceiptFetcher, + purchaserInfoManager: PurchaserInfoManager, + backend: Backend, + identityManager: IdentityManager, + receiptParser: ReceiptParser, + deviceCache: DeviceCache) { self.productsManager = productsManager self.storeKitWrapper = storeKitWrapper self.systemInfo = systemInfo @@ -82,18 +80,17 @@ import StoreKit self.deviceCache = deviceCache } - @objc public func restoreTransactions(completion maybeCompletion: ((PurchaserInfo?, Error?) -> Void)?) { + func restoreTransactions(completion maybeCompletion: ((PurchaserInfo?, Error?) -> Void)?) { syncPurchases(receiptRefreshPolicy: .always, isRestore: true, maybeCompletion: maybeCompletion) } - @objc public func syncPurchases(completion maybeCompletion: ((PurchaserInfo?, Error?) -> Void)? = nil) { + func syncPurchases(completion maybeCompletion: ((PurchaserInfo?, Error?) -> Void)? = nil) { syncPurchases(receiptRefreshPolicy: .never, isRestore: allowSharingAppStoreAccount, maybeCompletion: maybeCompletion) } - @objc public func products(withIdentifiers identifiers: [String], - completion: @escaping ([SKProduct]) -> Void) { + func products(withIdentifiers identifiers: [String], completion: @escaping ([SKProduct]) -> Void) { let productIdentifiersSet = Set(identifiers) guard !productIdentifiersSet.isEmpty else { operationDispatcher.dispatchOnMainThread { completion([]) } @@ -108,9 +105,9 @@ import StoreKit } @available(iOS 12.2, macOS 10.14.4, watchOS 6.2, macCatalyst 13.0, tvOS 12.2, *) - @objc public func paymentDiscount(forProductDiscount productDiscount: SKProductDiscount, - product: SKProduct, - completion: @escaping (SKPaymentDiscount?, Error?) -> Void) { + func paymentDiscount(forProductDiscount productDiscount: SKProductDiscount, + product: SKProduct, + completion: @escaping (SKPaymentDiscount?, Error?) -> Void) { guard let discountIdentifier = productDiscount.identifier else { completion(nil, ErrorUtils.productDiscountMissingIdentifierError()) return @@ -160,11 +157,10 @@ import StoreKit } } - @objc(purchaseProduct:payment:presentedOfferingIdentifier:completion:) - public func purchase(product: SKProduct, - payment: SKMutablePayment, - presentedOfferingIdentifier maybePresentedOfferingIdentifier: String?, - completion: @escaping PurchaseCompletedBlock) { + func purchase(product: SKProduct, + payment: SKMutablePayment, + presentedOfferingIdentifier maybePresentedOfferingIdentifier: String?, + completion: @escaping PurchaseCompletedBlock) { Logger.debug(String(format: "Make purchase called: %@", #function)) guard let productIdentifier = extractProductIdentifier(fromProduct: product, orPayment: payment) else { Logger.error(Strings.purchase.could_not_purchase_product_id_not_found) @@ -211,8 +207,7 @@ import StoreKit extension PurchasesOrchestrator: StoreKitWrapperDelegate { - public func storeKitWrapper(_ storeKitWrapper: StoreKitWrapper, - updatedTransaction transaction: SKPaymentTransaction) { + func storeKitWrapper(_ storeKitWrapper: StoreKitWrapper, updatedTransaction transaction: SKPaymentTransaction) { switch transaction.transactionState { case .restored, // for observer mode .purchased: @@ -228,15 +223,15 @@ extension PurchasesOrchestrator: StoreKitWrapperDelegate { } } - public func storeKitWrapper(_ storeKitWrapper: StoreKitWrapper, - removedTransaction transaction: SKPaymentTransaction) { + func storeKitWrapper(_ storeKitWrapper: StoreKitWrapper, + removedTransaction transaction: SKPaymentTransaction) { // todo: remove // unused for now } - public func storeKitWrapper(_ storeKitWrapper: StoreKitWrapper, - shouldAddStorePayment payment: SKPayment, - for product: SKProduct) -> Bool { + func storeKitWrapper(_ storeKitWrapper: StoreKitWrapper, + shouldAddStorePayment payment: SKPayment, + for product: SKProduct) -> Bool { productsManager.cacheProduct(product) guard let delegate = maybeDelegate else { return false } @@ -249,8 +244,8 @@ extension PurchasesOrchestrator: StoreKitWrapperDelegate { return false } - public func storeKitWrapper(_ storeKitWrapper: StoreKitWrapper, - didRevokeEntitlementsForProductIdentifiers productIdentifiers: [String]) { + func storeKitWrapper(_ storeKitWrapper: StoreKitWrapper, + didRevokeEntitlementsForProductIdentifiers productIdentifiers: [String]) { Logger.debug(String(format: Strings.purchase.entitlements_revoked_syncing_purchases, productIdentifiers)) syncPurchases { _, _ in Logger.debug(Strings.purchase.purchases_synced) @@ -406,10 +401,10 @@ private extension PurchasesOrchestrator { func markSyncedIfNeeded(subscriberAttributes: SubscriberAttributeDict?, appUserID: String, maybeError: Error?) { if let error = maybeError as NSError? { - if !error.rc_successfullySynced { + if !error.successfullySynced { return } - let attributeErrors = (error.rc_subscriberAttributesErrors?.debugDescription ?? "None") as NSString + let attributeErrors = (error.subscriberAttributesErrors?.debugDescription ?? "None") as NSString Logger.error(String(format: Strings.attribution.subscriber_attributes_error, attributeErrors)) } diff --git a/PurchasesCoreSwift/Purchasing/ReceiptFetcher.swift b/PurchasesCoreSwift/Purchasing/ReceiptFetcher.swift index f0ad44e39b..d9742a7def 100644 --- a/PurchasesCoreSwift/Purchasing/ReceiptFetcher.swift +++ b/PurchasesCoreSwift/Purchasing/ReceiptFetcher.swift @@ -1,20 +1,25 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // ReceiptFetcher.swift -// Purchases // // Created by Javier de Martín Gil on 8/7/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation -// TODO: Make internal after migration to Swift is complete -@objc(RCReceiptFetcher) public class ReceiptFetcher: NSObject { +class ReceiptFetcher { private let requestFetcher: StoreKitRequestFetcher private let receiptBundle: Bundle - @objc public convenience init(requestFetcher: StoreKitRequestFetcher) { + convenience init(requestFetcher: StoreKitRequestFetcher) { self.init(requestFetcher: requestFetcher, bundle: .main) } @@ -23,8 +28,7 @@ import Foundation self.receiptBundle = bundle } - @objc public func receiptData(refreshPolicy: ReceiptRefreshPolicy, - completion: @escaping (Data?) -> Void) { + func receiptData(refreshPolicy: ReceiptRefreshPolicy, completion: @escaping (Data?) -> Void) { if refreshPolicy == .always { Logger.debug(String(format: Strings.receipt.force_refreshing_receipt)) refreshReceipt(completion) diff --git a/PurchasesCoreSwift/Purchasing/ReceiptRefreshPolicy.swift b/PurchasesCoreSwift/Purchasing/ReceiptRefreshPolicy.swift index ace7904423..f72af899c9 100644 --- a/PurchasesCoreSwift/Purchasing/ReceiptRefreshPolicy.swift +++ b/PurchasesCoreSwift/Purchasing/ReceiptRefreshPolicy.swift @@ -1,15 +1,20 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // ReceiptRefreshPolicy.swift -// PurchasesCoreSwift // // Created by Juanpe Catalán on 7/7/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation -// TODO(Post-migration): switch this back to internal -@objc(RCReceiptRefreshPolicy) public enum ReceiptRefreshPolicy: Int { +enum ReceiptRefreshPolicy: Int { case always = 0 case onlyIfEmpty diff --git a/PurchasesCoreSwift/Purchasing/StoreKitRequestFetcher.swift b/PurchasesCoreSwift/Purchasing/StoreKitRequestFetcher.swift index 01184b7c1f..182c2e174c 100644 --- a/PurchasesCoreSwift/Purchasing/StoreKitRequestFetcher.swift +++ b/PurchasesCoreSwift/Purchasing/StoreKitRequestFetcher.swift @@ -1,31 +1,37 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // StoreKitRequestFetcher.swift -// PurchasesCoreSwift // // Created by Andrés Boedo on 6/29/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation import StoreKit -// todo: make internal -@objc(RCReceiptRefreshRequestFactory) public class ReceiptRefreshRequestFactory: NSObject { +class ReceiptRefreshRequestFactory { func receiptRefreshRequest() -> SKReceiptRefreshRequest { return SKReceiptRefreshRequest() } + } -// todo: make internal -@objc(RCStoreKitRequestFetcher) public class StoreKitRequestFetcher: NSObject { +class StoreKitRequestFetcher: NSObject { + private let requestFactory: ReceiptRefreshRequestFactory private var receiptRefreshRequest: SKRequest? private var receiptRefreshCompletionHandlers: [() -> Void] private let operationDispatcher: OperationDispatcher - @objc public init(requestFactory: ReceiptRefreshRequestFactory = ReceiptRefreshRequestFactory(), - operationDispatcher: OperationDispatcher) { + init(requestFactory: ReceiptRefreshRequestFactory = ReceiptRefreshRequestFactory(), + operationDispatcher: OperationDispatcher) { self.requestFactory = requestFactory self.operationDispatcher = operationDispatcher receiptRefreshRequest = nil @@ -43,6 +49,7 @@ import StoreKit } } } + } extension StoreKitRequestFetcher: SKRequestDelegate { diff --git a/PurchasesCoreSwift/Purchasing/StoreKitWrapper.swift b/PurchasesCoreSwift/Purchasing/StoreKitWrapper.swift index 4730491772..0bce6ee0f1 100644 --- a/PurchasesCoreSwift/Purchasing/StoreKitWrapper.swift +++ b/PurchasesCoreSwift/Purchasing/StoreKitWrapper.swift @@ -1,35 +1,40 @@ -// RCStoreKitWrapper.m -// Purchases +// +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// RCStoreKitWrapper.swift // // Created by RevenueCat. -// Copyright © 2021 RevenueCat. All rights reserved. // + import StoreKit -// todo: make internal -@objc(RCStoreKitWrapperDelegate) public protocol StoreKitWrapperDelegate: AnyObject { - @objc func storeKitWrapper(_ storeKitWrapper: StoreKitWrapper, - updatedTransaction transaction: SKPaymentTransaction) +protocol StoreKitWrapperDelegate: AnyObject { - @objc func storeKitWrapper(_ storeKitWrapper: StoreKitWrapper, - removedTransaction transaction: SKPaymentTransaction) + func storeKitWrapper(_ storeKitWrapper: StoreKitWrapper, updatedTransaction transaction: SKPaymentTransaction) + + func storeKitWrapper(_ storeKitWrapper: StoreKitWrapper, removedTransaction transaction: SKPaymentTransaction) - @objc(storeKitWrapper:shouldAddStorePayment:forProduct:) func storeKitWrapper(_ storeKitWrapper: StoreKitWrapper, shouldAddStorePayment payment: SKPayment, for product: SKProduct) -> Bool - @objc func storeKitWrapper(_ storeKitWrapper: StoreKitWrapper, - didRevokeEntitlementsForProductIdentifiers productIdentifiers: [String]) + func storeKitWrapper(_ storeKitWrapper: StoreKitWrapper, + didRevokeEntitlementsForProductIdentifiers productIdentifiers: [String]) + } -// todo: make internal -@objc(RCStoreKitWrapper) public class StoreKitWrapper: NSObject, SKPaymentTransactionObserver { +class StoreKitWrapper: NSObject, SKPaymentTransactionObserver { @available(macOS 10.14, macCatalyst 13.0, *) - @objc public static var simulatesAskToBuyInSandbox = false + static var simulatesAskToBuyInSandbox = false - @objc public weak var delegate: StoreKitWrapperDelegate? { + weak var delegate: StoreKitWrapperDelegate? { didSet { if delegate != nil { paymentQueue.add(self) @@ -41,11 +46,11 @@ import StoreKit private var paymentQueue: SKPaymentQueue - @objc public init(paymentQueue: SKPaymentQueue) { + init(paymentQueue: SKPaymentQueue) { self.paymentQueue = paymentQueue } - @objc override public convenience init() { + override convenience init() { self.init(paymentQueue: .default()) } @@ -53,11 +58,11 @@ import StoreKit paymentQueue.remove(self) } - @objc(addPayment:) public func add(_ payment: SKPayment) { + func add(_ payment: SKPayment) { paymentQueue.add(payment) } - @objc public func finishTransaction(_ transaction: SKPaymentTransaction) { + func finishTransaction(_ transaction: SKPaymentTransaction) { Logger.purchase(String(format: Strings.purchase.finishing_transaction, transaction.payment.productIdentifier, transaction.transactionIdentifier ?? "", @@ -70,12 +75,12 @@ import StoreKit @available(macOS, unavailable) @available(tvOS, unavailable) @available(watchOS, unavailable) - @objc public func presentCodeRedemptionSheet() { + func presentCodeRedemptionSheet() { Logger.debug(Strings.purchase.presenting_code_redemption_sheet) paymentQueue.presentCodeRedemptionSheet() } - @objc public func payment(withProduct product: SKProduct) -> SKMutablePayment { + func payment(withProduct product: SKProduct) -> SKMutablePayment { let payment = SKMutablePayment(product: product) // todo: check that it's fine to omit tvOS and iOS since the relevant methods exist in targets lower than ours if #available(macOS 10.14, watchOS 6.2, macCatalyst 13.0, *) { @@ -85,7 +90,7 @@ import StoreKit } @available(iOS 12.2, macOS 10.14.4, watchOS 6.2, macCatalyst 13.0, tvOS 12.2, *) - @objc public func payment(withProduct product: SKProduct, discount: SKPaymentDiscount) -> SKMutablePayment { + func payment(withProduct product: SKProduct, discount: SKPaymentDiscount) -> SKMutablePayment { let payment = self.payment(withProduct: product) payment.paymentDiscount = discount return payment @@ -95,8 +100,7 @@ import StoreKit extension StoreKitWrapper: SKPaymentQueueDelegate { - public func paymentQueue(_ queue: SKPaymentQueue, - updatedTransactions transactions: [SKPaymentTransaction]) { + func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { for transaction in transactions { Logger.debug(String(format: Strings.purchase.paymentqueue_updatedtransaction, transaction.payment.productIdentifier, @@ -109,8 +113,7 @@ extension StoreKitWrapper: SKPaymentQueueDelegate { } // Sent when transactions are removed from the queue (via finishTransaction:). - public func paymentQueue(_ queue: SKPaymentQueue, - removedTransactions transactions: [SKPaymentTransaction]) { + func paymentQueue(_ queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction]) { for transaction in transactions { Logger.debug(String(format: Strings.purchase.paymentqueue_removedtransaction, transaction.payment.productIdentifier, @@ -126,22 +129,19 @@ extension StoreKitWrapper: SKPaymentQueueDelegate { // Sent when a user initiated an in-app purchase from the App Store. @available(iOS 11.0, macOS 11.0, macCatalyst 14.0, tvOS 11.0, *) @available(watchOS, unavailable) - public func paymentQueue( - _ queue: SKPaymentQueue, - shouldAddStorePayment payment: SKPayment, - for product: SKProduct - ) -> Bool { + func paymentQueue(_ queue: SKPaymentQueue, + shouldAddStorePayment payment: SKPayment, + for product: SKProduct) -> Bool { return delegate?.storeKitWrapper(self, shouldAddStorePayment: payment, for: product) ?? false } // Sent when access to a family shared subscription is revoked from a family member or canceled the subscription. @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) - public func paymentQueue( - _ queue: SKPaymentQueue, - didRevokeEntitlementsForProductIdentifiers productIdentifiers: [String] - ) { + func paymentQueue(_ queue: SKPaymentQueue, + didRevokeEntitlementsForProductIdentifiers productIdentifiers: [String]) { Logger.debug(String(format: Strings.purchase.paymentqueue_revoked_entitlements_for_product_identifiers, productIdentifiers)) delegate?.storeKitWrapper(self, didRevokeEntitlementsForProductIdentifiers: productIdentifiers) } + } diff --git a/PurchasesCoreSwift/Purchasing/TransactionsFactory.swift b/PurchasesCoreSwift/Purchasing/TransactionsFactory.swift index afffa70635..01f4fba435 100644 --- a/PurchasesCoreSwift/Purchasing/TransactionsFactory.swift +++ b/PurchasesCoreSwift/Purchasing/TransactionsFactory.swift @@ -1,9 +1,15 @@ // -// PurchaserInfoHelper.swift -// Purchases +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// TransactionsFactory.swift // // Created by RevenueCat. -// Copyright © 2020 Purchases. All rights reserved. // import Foundation diff --git a/PurchasesCoreSwift/SubscriberAttributes/AttributionDataMigrator.swift b/PurchasesCoreSwift/SubscriberAttributes/AttributionDataMigrator.swift index 76eb45af39..b38a7684fa 100644 --- a/PurchasesCoreSwift/SubscriberAttributes/AttributionDataMigrator.swift +++ b/PurchasesCoreSwift/SubscriberAttributes/AttributionDataMigrator.swift @@ -1,14 +1,20 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // AttributionDataMigrator.swift -// PurchasesCoreSwift // // Created by César de la Vega on 6/16/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation -@objc(RCAttributionDataMigrator) public class AttributionDataMigrator: NSObject { +class AttributionDataMigrator { func convertToSubscriberAttributes(attributionData: [String: Any], network: Int) -> [String: String] { let network = AttributionNetwork(rawValue: network) diff --git a/PurchasesCoreSwift/SubscriberAttributes/AttributionKey.swift b/PurchasesCoreSwift/SubscriberAttributes/AttributionKey.swift index d12e316499..860021d349 100644 --- a/PurchasesCoreSwift/SubscriberAttributes/AttributionKey.swift +++ b/PurchasesCoreSwift/SubscriberAttributes/AttributionKey.swift @@ -1,3 +1,15 @@ +// +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// AttributionKey.swift +// + import Foundation // swiftlint:disable identifier_name @@ -10,14 +22,17 @@ enum AttributionKey: String { networkID = "rc_attribution_network_id" enum Adjust: String { + case id = "adid", network = "network", campaign = "campaign", adGroup = "adgroup", creative = "creative" + } enum AppsFlyer: String { + case id = "rc_appsflyer_id", campaign = "campaign", channel = "af_channel", @@ -29,15 +44,20 @@ enum AttributionKey: String { adId = "ad_id", dataKey = "data", statusKey = "status" + } enum Branch: String { + case campaign, channel + } enum MParticle: String { + case id = "mpid" + } } diff --git a/PurchasesCoreSwift/SubscriberAttributes/SpecialSubscriberAttributes.swift b/PurchasesCoreSwift/SubscriberAttributes/SpecialSubscriberAttributes.swift index b81897429a..a45cd27956 100644 --- a/PurchasesCoreSwift/SubscriberAttributes/SpecialSubscriberAttributes.swift +++ b/PurchasesCoreSwift/SubscriberAttributes/SpecialSubscriberAttributes.swift @@ -1,9 +1,15 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // SpecialSubscriberAttributes.swift -// PurchasesCoreSwift // // Created by César de la Vega on 6/17/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation diff --git a/PurchasesCoreSwift/SubscriberAttributes/SubscriberAttribute.swift b/PurchasesCoreSwift/SubscriberAttributes/SubscriberAttribute.swift index b84673943d..1e184073cc 100644 --- a/PurchasesCoreSwift/SubscriberAttributes/SubscriberAttribute.swift +++ b/PurchasesCoreSwift/SubscriberAttributes/SubscriberAttribute.swift @@ -1,36 +1,42 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // SubscriberAttribute.swift -// PurchasesCoreSwift // // Created by Joshua Liebowitz on 7/1/21. -// Copyright © 2021 Purchases. All rights reserved. // import Foundation -@objc(RCSubscriberAttribute) public class SubscriberAttribute: NSObject { +class SubscriberAttribute { + static private let backendValueKey = "value" static private let backendTimestampKey = "updated_at_ms" - // TODO (Post-migration): remove public. static let keyKey = "key" static let valueKey = "value" static let setTimeKey = "setTime" static let isSyncedKey = "isSynced" - let setTime: Date - @objc public let key: String - @objc public let value: String - @objc public var isSynced: Bool + let setTime: Date + let key: String + let value: String + var isSynced: Bool - @objc required public init(withKey key: String, value: String?, isSynced: Bool, setTime: Date) { + required init(withKey key: String, value: String?, isSynced: Bool, setTime: Date) { self.key = key self.value = value ?? "" self.isSynced = isSynced self.setTime = setTime } - @objc convenience public init(withKey: String, value: String?) { + convenience init(withKey: String, value: String?) { self.init(withKey: withKey, value: value, dateProvider: DateProvider()) } @@ -38,10 +44,6 @@ import Foundation self.init(withKey: key, value: value, isSynced: false, setTime: dateProvider.now()) } - private override init() { - fatalError("Init not supported from here") - } - func asDictionary() -> [String: NSObject] { return [Self.keyKey: self.key as NSString, Self.valueKey: self.value as NSString, @@ -50,32 +52,37 @@ import Foundation } func asBackendDictionary() -> [String: Any] { - let timestamp = self.setTime.rc_millisecondsSince1970AsUInt64() + let timestamp = self.setTime.millisecondsSince1970AsUInt64() return [Self.backendValueKey: self.value, Self.backendTimestampKey: timestamp] } - public override func isEqual(_ object: Any?) -> Bool { - guard let attribute = object as? SubscriberAttribute else { - return false - } +} + +extension SubscriberAttribute: Equatable { - if self === attribute { + static func == (lhs: SubscriberAttribute, rhs: SubscriberAttribute) -> Bool { + if lhs === rhs { return true - } else if self.key != attribute.key { + } else if lhs.key != rhs.key { return false - } else if self.value != attribute.value { + } else if lhs.value != rhs.value { return false - } else if self.setTime != attribute.setTime { + } else if lhs.setTime != rhs.setTime { return false - } else if self.isSynced != attribute.isSynced { + } else if lhs.isSynced != rhs.isSynced { return false } return true } - public override var description: String { +} + +extension SubscriberAttribute: CustomStringConvertible { + + var description: String { return "Subscriber attribute: key: \(self.key) value: \(self.value) setTime: \(self.setTime)" } + } diff --git a/PurchasesCoreSwift/SubscriberAttributes/SubscriberAttributesManager.swift b/PurchasesCoreSwift/SubscriberAttributes/SubscriberAttributesManager.swift index 6d2132d10f..9f361ddecd 100644 --- a/PurchasesCoreSwift/SubscriberAttributes/SubscriberAttributesManager.swift +++ b/PurchasesCoreSwift/SubscriberAttributes/SubscriberAttributesManager.swift @@ -13,8 +13,7 @@ import Foundation -// TODO after migration make class and all methods internal -@objc(RCSubscriberAttributesManager) public class SubscriberAttributesManager: NSObject { +class SubscriberAttributesManager { private let backend: Backend private let deviceCache: DeviceCache @@ -22,115 +21,115 @@ import Foundation private let attributionDataMigrator: AttributionDataMigrator private let lock = NSRecursiveLock() - @objc public init(backend: Backend, - deviceCache: DeviceCache, - attributionFetcher: AttributionFetcher, - attributionDataMigrator: AttributionDataMigrator) { + init(backend: Backend, + deviceCache: DeviceCache, + attributionFetcher: AttributionFetcher, + attributionDataMigrator: AttributionDataMigrator) { self.backend = backend self.deviceCache = deviceCache self.attributionFetcher = attributionFetcher self.attributionDataMigrator = attributionDataMigrator } - @objc public func setAttributes(_ attributes: [String: String], appUserID: String) { + func setAttributes(_ attributes: [String: String], appUserID: String) { logAttributionMethodCalled(functionName: #function) for (key, value) in attributes { setAttribute(key: key, value: value, appUserID: appUserID) } } - @objc public func setEmail(_ maybeEmail: String?, appUserID: String) { + func setEmail(_ maybeEmail: String?, appUserID: String) { logAttributionMethodCalled(functionName: #function) setAttribute(key: SpecialSubscriberAttributes.email, value: maybeEmail, appUserID: appUserID) } - @objc public func setPhoneNumber(_ maybePhoneNumber: String?, appUserID: String) { + func setPhoneNumber(_ maybePhoneNumber: String?, appUserID: String) { logAttributionMethodCalled(functionName: #function) setAttribute(key: SpecialSubscriberAttributes.phoneNumber, value: maybePhoneNumber, appUserID: appUserID) } - @objc public func setDisplayName(_ maybeDisplayName: String?, appUserID: String) { + func setDisplayName(_ maybeDisplayName: String?, appUserID: String) { logAttributionMethodCalled(functionName: #function) setAttribute(key: SpecialSubscriberAttributes.displayName, value: maybeDisplayName, appUserID: appUserID) } - @objc public func setPushToken(_ maybePushToken: Data?, appUserID: String) { + func setPushToken(_ maybePushToken: Data?, appUserID: String) { logAttributionMethodCalled(functionName: #function) let maybePushTokenString = maybePushToken?.rc_asString setPushTokenString(maybePushTokenString, appUserID: appUserID) } - @objc public func setPushTokenString(_ maybePushTokenString: String?, appUserID: String) { + func setPushTokenString(_ maybePushTokenString: String?, appUserID: String) { logAttributionMethodCalled(functionName: #function) setAttribute(key: SpecialSubscriberAttributes.pushToken, value: maybePushTokenString, appUserID: appUserID) } - @objc public func setAdjustID(_ maybeAdjustID: String?, appUserID: String) { + func setAdjustID(_ maybeAdjustID: String?, appUserID: String) { logAttributionMethodCalled(functionName: #function) setAttributionID(networkID: maybeAdjustID, networkKey: SpecialSubscriberAttributes.adjustID, appUserID: appUserID) } - @objc public func setAppsflyerID(_ maybeAppsflyerID: String?, appUserID: String) { + func setAppsflyerID(_ maybeAppsflyerID: String?, appUserID: String) { logAttributionMethodCalled(functionName: #function) setAttributionID(networkID: maybeAppsflyerID, networkKey: SpecialSubscriberAttributes.appsFlyerID, appUserID: appUserID) } - @objc public func setFBAnonymousID(_ maybeFBAnonymousID: String?, appUserID: String) { + func setFBAnonymousID(_ maybeFBAnonymousID: String?, appUserID: String) { logAttributionMethodCalled(functionName: #function) setAttributionID(networkID: maybeFBAnonymousID, networkKey: SpecialSubscriberAttributes.fBAnonID, appUserID: appUserID) } - @objc public func setMparticleID(_ maybeMparticleID: String?, appUserID: String) { + func setMparticleID(_ maybeMparticleID: String?, appUserID: String) { logAttributionMethodCalled(functionName: #function) setAttributionID(networkID: maybeMparticleID, networkKey: SpecialSubscriberAttributes.mpParticleID, appUserID: appUserID) } - @objc public func setOnesignalID(_ maybeOnesignalID: String?, appUserID: String) { + func setOnesignalID(_ maybeOnesignalID: String?, appUserID: String) { logAttributionMethodCalled(functionName: #function) setAttributionID(networkID: maybeOnesignalID, networkKey: SpecialSubscriberAttributes.oneSignalID, appUserID: appUserID) } - @objc public func setMediaSource(_ maybeMediaSource: String?, appUserID: String) { + func setMediaSource(_ maybeMediaSource: String?, appUserID: String) { logAttributionMethodCalled(functionName: #function) setAttribute(key: SpecialSubscriberAttributes.mediaSource, value: maybeMediaSource, appUserID: appUserID) } - @objc public func setCampaign(_ maybeCampaign: String?, appUserID: String) { + func setCampaign(_ maybeCampaign: String?, appUserID: String) { logAttributionMethodCalled(functionName: #function) setAttribute(key: SpecialSubscriberAttributes.campaign, value: maybeCampaign, appUserID: appUserID) } - @objc public func setAdGroup(_ maybeAdGroup: String?, appUserID: String) { + func setAdGroup(_ maybeAdGroup: String?, appUserID: String) { logAttributionMethodCalled(functionName: #function) setAttribute(key: SpecialSubscriberAttributes.adGroup, value: maybeAdGroup, appUserID: appUserID) } - @objc public func setAd(_ maybeAd: String?, appUserID: String) { + func setAd(_ maybeAd: String?, appUserID: String) { logAttributionMethodCalled(functionName: #function) setAttribute(key: SpecialSubscriberAttributes.ad, value: maybeAd, appUserID: appUserID) } - @objc public func setKeyword(_ maybeKeyword: String?, appUserID: String) { + func setKeyword(_ maybeKeyword: String?, appUserID: String) { logAttributionMethodCalled(functionName: #function) setAttribute(key: SpecialSubscriberAttributes.keyword, value: maybeKeyword, appUserID: appUserID) } - @objc public func setCreative(_ maybeCreative: String?, appUserID: String) { + func setCreative(_ maybeCreative: String?, appUserID: String) { logAttributionMethodCalled(functionName: #function) setAttribute(key: SpecialSubscriberAttributes.creative, value: maybeCreative, appUserID: appUserID) } - @objc public func collectDeviceIdentifiers(forAppUserID appUserID: String) { + func collectDeviceIdentifiers(forAppUserID appUserID: String) { logAttributionMethodCalled(functionName: #function) let identifierForAdvertisers = attributionFetcher.identifierForAdvertisers let identifierForVendor = attributionFetcher.identifierForVendor @@ -140,7 +139,7 @@ import Foundation setAttribute(key: SpecialSubscriberAttributes.ip, value: "true", appUserID: appUserID) } - @objc public func syncAttributesForAllUsers(currentAppUserID: String) { + func syncAttributesForAllUsers(currentAppUserID: String) { let unsyncedAttributesForAllUsers = unsyncedAttributesByKeyForAllUsers() for (syncingAppUserId, attributes) in unsyncedAttributesForAllUsers { @@ -152,7 +151,7 @@ import Foundation } } - @objc public func handleAttributesSynced(syncingAppUserId: String, currentAppUserId: String, error: Error?) { + func handleAttributesSynced(syncingAppUserId: String, currentAppUserId: String, error: Error?) { if error == nil { Logger.rcSuccess(String(format: Strings.attribution.attributes_sync_success, syncingAppUserId)) if syncingAppUserId != currentAppUserId { @@ -182,7 +181,7 @@ import Foundation return deviceCache.unsyncedAttributesForAllUsers() } - @objc public func markAttributesAsSynced(_ maybeAttributesToSync: SubscriberAttributeDict?, appUserID: String) { + func markAttributesAsSynced(_ maybeAttributesToSync: SubscriberAttributeDict?, appUserID: String) { guard let attributesToSync = maybeAttributesToSync, !attributesToSync.isEmpty else { return @@ -234,7 +233,7 @@ private extension SubscriberAttributesManager { completion: @escaping (Error?) -> Void) { backend.post(subscriberAttributes: attributes, appUserID: appUserID) { error in let receivedNSError = error as NSError? - let didBackendReceiveValues = receivedNSError?.rc_successfullySynced ?? true + let didBackendReceiveValues = receivedNSError?.successfullySynced ?? true if didBackendReceiveValues { self.markAttributesAsSynced(attributes, appUserID: appUserID) @@ -245,7 +244,7 @@ private extension SubscriberAttributesManager { func storeAttributeLocally(key: String, value: String, appUserID: String) { let subscriberAttribute = SubscriberAttribute.init(withKey: key, value: value) - Logger.debug(String(format: Strings.attribution.attribute_set_locally, subscriberAttribute)) + Logger.debug(String(format: Strings.attribution.attribute_set_locally, subscriberAttribute.description)) deviceCache.store(subscriberAttribute: subscriberAttribute, appUserID: appUserID) } diff --git a/PurchasesCoreSwiftTests/Attribution/AttributionPosterTests.swift b/PurchasesCoreSwiftTests/Attribution/AttributionPosterTests.swift index a77fce4110..2409aab620 100644 --- a/PurchasesCoreSwiftTests/Attribution/AttributionPosterTests.swift +++ b/PurchasesCoreSwiftTests/Attribution/AttributionPosterTests.swift @@ -78,13 +78,13 @@ class AttributionPosterTests: XCTestCase { backend.stubbedPostAttributionDataCompletionResult = (nil, ()) attributionPoster.post(attributionData: ["something": "here"], fromNetwork: .adjust, - forNetworkUserId: userID) + networkUserId: userID) expect(self.backend.invokedPostAttributionDataCount) == 0 expect(self.subscriberAttributesManager.invokedConvertAttributionDataAndSetCount) == 1 attributionPoster.post(attributionData: ["something": "else"], fromNetwork: .adjust, - forNetworkUserId: userID) + networkUserId: userID) expect(self.backend.invokedPostAttributionDataCount) == 0 expect(self.subscriberAttributesManager.invokedConvertAttributionDataAndSetCount) == 1 @@ -96,13 +96,13 @@ class AttributionPosterTests: XCTestCase { attributionPoster.post(attributionData: ["something": "here"], fromNetwork: .appleSearchAds, - forNetworkUserId: userID) + networkUserId: userID) expect(self.backend.invokedPostAttributionDataCount) == 1 expect(self.subscriberAttributesManager.invokedConvertAttributionDataAndSetCount) == 0 attributionPoster.post(attributionData: ["something": "else"], fromNetwork: .appleSearchAds, - forNetworkUserId: userID) + networkUserId: userID) expect(self.backend.invokedPostAttributionDataCount) == 1 expect(self.subscriberAttributesManager.invokedConvertAttributionDataAndSetCount) == 0 @@ -114,13 +114,13 @@ class AttributionPosterTests: XCTestCase { attributionPoster.post(attributionData: ["something": "here"], fromNetwork: .adjust, - forNetworkUserId: userID) + networkUserId: userID) expect(self.backend.invokedPostAttributionDataCount) == 0 expect(self.subscriberAttributesManager.invokedConvertAttributionDataAndSetCount) == 1 attributionPoster.post(attributionData: ["something": "else"], fromNetwork: .facebook, - forNetworkUserId: userID) + networkUserId: userID) expect(self.backend.invokedPostAttributionDataCount) == 0 expect(self.subscriberAttributesManager.invokedConvertAttributionDataAndSetCount) == 2 @@ -131,13 +131,13 @@ class AttributionPosterTests: XCTestCase { attributionPoster.post(attributionData: ["something": "here"], fromNetwork: .adjust, - forNetworkUserId: "attributionUser1") + networkUserId: "attributionUser1") expect(self.backend.invokedPostAttributionDataCount) == 0 expect(self.subscriberAttributesManager.invokedConvertAttributionDataAndSetCount) == 1 attributionPoster.post(attributionData: ["something": "else"], fromNetwork: .adjust, - forNetworkUserId: "attributionUser2") + networkUserId: "attributionUser2") expect(self.backend.invokedPostAttributionDataCount) == 0 expect(self.subscriberAttributesManager.invokedConvertAttributionDataAndSetCount) == 2 @@ -148,13 +148,13 @@ class AttributionPosterTests: XCTestCase { attributionPoster.post(attributionData: ["something": "here"], fromNetwork: .appleSearchAds, - forNetworkUserId: "attributionUser1") + networkUserId: "attributionUser1") expect(self.backend.invokedPostAttributionDataCount) == 1 expect(self.subscriberAttributesManager.invokedConvertAttributionDataAndSetCount) == 0 attributionPoster.post(attributionData: ["something": "else"], fromNetwork: .appleSearchAds, - forNetworkUserId: "attributionUser2") + networkUserId: "attributionUser2") expect(self.backend.invokedPostAttributionDataCount) == 2 expect(self.subscriberAttributesManager.invokedConvertAttributionDataAndSetCount) == 0 diff --git a/PurchasesCoreSwiftTests/FoundationExtensions/NSDate+RCExtensionsTests.swift b/PurchasesCoreSwiftTests/FoundationExtensions/NSDate+RCExtensionsTests.swift index 1644fcf327..ba3d4416ae 100644 --- a/PurchasesCoreSwiftTests/FoundationExtensions/NSDate+RCExtensionsTests.swift +++ b/PurchasesCoreSwiftTests/FoundationExtensions/NSDate+RCExtensionsTests.swift @@ -11,13 +11,13 @@ import XCTest class NSDateExtensionsTests: XCTestCase { func testMillisecondsSince1970ConvertsCorrectlyWithCurrentTime() { let date = NSDate() - expect(date.rc_millisecondsSince1970AsUInt64()) == (UInt64)(date.timeIntervalSince1970 * 1000) + expect(date.millisecondsSince1970AsUInt64()) == (UInt64)(date.timeIntervalSince1970 * 1000) } func testMillisecondsSince1970ConvertsCorrectlyWithFixedTime() { let secondsSince1970: TimeInterval = 1619555571.0 let millisecondsSince1970UInt64: UInt64 = 1619555571000 let date = NSDate(timeIntervalSince1970: secondsSince1970) - expect(date.rc_millisecondsSince1970AsUInt64()) == millisecondsSince1970UInt64 + expect(date.millisecondsSince1970AsUInt64()) == millisecondsSince1970UInt64 } } diff --git a/PurchasesCoreSwiftTests/FoundationExtensions/NSError+RCExtensionsTests.swift b/PurchasesCoreSwiftTests/FoundationExtensions/NSError+RCExtensionsTests.swift index 5a997f19e4..679eecca34 100644 --- a/PurchasesCoreSwiftTests/FoundationExtensions/NSError+RCExtensionsTests.swift +++ b/PurchasesCoreSwiftTests/FoundationExtensions/NSError+RCExtensionsTests.swift @@ -13,31 +13,31 @@ class NSErrorRCExtensionsTests: XCTestCase { func testSuccessfullySyncedFalseIfCodeIsNetworkError() { let errorCode = ErrorCode.networkError.rawValue let error = NSError(domain: RCPurchasesErrorCodeDomain, code: errorCode, userInfo: [:]) - expect(error.rc_successfullySynced) == false + expect(error.successfullySynced) == false } func testSuccessfullySyncedFalseIfNotShouldMarkSynced() { let errorCode = ErrorCode.purchaseNotAllowedError.rawValue let error = NSError(domain: RCPurchasesErrorCodeDomain, code: errorCode, userInfo: [Backend.RCSuccessfullySyncedKey as String: false]) - expect(error.rc_successfullySynced) == false + expect(error.successfullySynced) == false } func testSuccessfullySyncedFalseIfShouldMarkSyncedNotPresent() { let errorCode = ErrorCode.purchaseNotAllowedError.rawValue let error = NSError(domain: RCPurchasesErrorCodeDomain, code: errorCode, userInfo: [:]) - expect(error.rc_successfullySynced) == false + expect(error.successfullySynced) == false } func testSuccessfullySyncedTrueIfShouldMarkSynced() { let errorCode = ErrorCode.purchaseNotAllowedError.rawValue let error = NSError(domain: RCPurchasesErrorCodeDomain, code: errorCode, userInfo: [Backend.RCSuccessfullySyncedKey as String: true]) - expect(error.rc_successfullySynced) == true + expect(error.successfullySynced) == true } func testSubscriberAttributesErrorsNilIfNoAttributesErrors() { let errorCode = ErrorCode.purchaseNotAllowedError.rawValue let error = NSError(domain: RCPurchasesErrorCodeDomain, code: errorCode, userInfo: [Backend.RCSuccessfullySyncedKey as String: true]) - expect(error.rc_subscriberAttributesErrors).to(beNil()) + expect(error.subscriberAttributesErrors).to(beNil()) } func testSubscriberAttributesErrorsReturnsAttributesErrorsInUserInfo() { @@ -47,7 +47,7 @@ class NSErrorRCExtensionsTests: XCTestCase { let error = NSError(domain: RCPurchasesErrorCodeDomain, code: errorCode, userInfo: [Backend.RCAttributeErrorsKey as String: attributeErrors]) - expect(error.rc_subscriberAttributesErrors).toNot(beNil()) - expect(error.rc_subscriberAttributesErrors) == attributeErrors + expect(error.subscriberAttributesErrors).toNot(beNil()) + expect(error.subscriberAttributesErrors) == attributeErrors } } diff --git a/PurchasesCoreSwiftTests/LocalReceiptParsing/DataConverters/ArraySlice_UInt8+ExtensionsTests.swift b/PurchasesCoreSwiftTests/LocalReceiptParsing/DataConverters/ArraySlice_UInt8+ExtensionsTests.swift index 5667ee596e..9dff876604 100644 --- a/PurchasesCoreSwiftTests/LocalReceiptParsing/DataConverters/ArraySlice_UInt8+ExtensionsTests.swift +++ b/PurchasesCoreSwiftTests/LocalReceiptParsing/DataConverters/ArraySlice_UInt8+ExtensionsTests.swift @@ -6,12 +6,12 @@ import Nimble class ArraySliceUInt8ExtensionsTests: XCTestCase { func testToUIntReturnsCorrectValue() { var arraySlice = ArraySlice([UInt8(0b10000000), UInt8(0b10000000)]) - expect(arraySlice.toUInt()) == 0b10000000_10000000 + expect(arraySlice.toUInt64()) == 0b10000000_10000000 arraySlice = ArraySlice([UInt8(0b1), UInt8(0b01), UInt8(0b10)]) - expect(arraySlice.toUInt()) == 0b00000001_00000001_00000010 + expect(arraySlice.toUInt64()) == 0b00000001_00000001_00000010 arraySlice = ArraySlice([UInt8(0b10010100)]) - expect(arraySlice.toUInt()) == 0b10010100 + expect(arraySlice.toUInt64()) == 0b10010100 } } diff --git a/PurchasesCoreSwiftTests/Misc/SystemInfoTests.swift b/PurchasesCoreSwiftTests/Misc/SystemInfoTests.swift index 135923e4c9..57719b3144 100644 --- a/PurchasesCoreSwiftTests/Misc/SystemInfoTests.swift +++ b/PurchasesCoreSwiftTests/Misc/SystemInfoTests.swift @@ -1,7 +1,7 @@ import XCTest import Nimble -import PurchasesCoreSwift +@testable import PurchasesCoreSwift class SystemInfoTests: XCTestCase { func testproxyURL() { diff --git a/PurchasesCoreSwiftTests/Mocks/MockReceiptFetcher.swift b/PurchasesCoreSwiftTests/Mocks/MockReceiptFetcher.swift index 85c99ccf6c..b756732cbc 100644 --- a/PurchasesCoreSwiftTests/Mocks/MockReceiptFetcher.swift +++ b/PurchasesCoreSwiftTests/Mocks/MockReceiptFetcher.swift @@ -1,11 +1,19 @@ // -// Created by RevenueCat on 3/2/20. -// Copyright (c) 2020 Purchases. All rights reserved. +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// MockReceiptFetcher.swift // @testable import PurchasesCoreSwift class MockReceiptFetcher: ReceiptFetcher { + var receiptDataCalled = false var shouldReturnReceipt = true var shouldReturnZeroBytesReceipt = false @@ -16,8 +24,7 @@ class MockReceiptFetcher: ReceiptFetcher { self.init(requestFetcher: requestFetcher, bundle: .main) } - @objc override public func receiptData(refreshPolicy: ReceiptRefreshPolicy, - completion: @escaping ((Data?) -> Void)) { + override func receiptData(refreshPolicy: ReceiptRefreshPolicy, completion: @escaping ((Data?) -> Void)) { receiptDataReceivedRefreshPolicy = refreshPolicy receiptDataCalled = true receiptDataTimesCalled += 1 @@ -31,4 +38,5 @@ class MockReceiptFetcher: ReceiptFetcher { completion(nil) } } + } diff --git a/PurchasesCoreSwiftTests/Purchasing/OfferingsManagerTests.swift b/PurchasesCoreSwiftTests/Purchasing/OfferingsManagerTests.swift index 1d4ba78559..88d06987cf 100644 --- a/PurchasesCoreSwiftTests/Purchasing/OfferingsManagerTests.swift +++ b/PurchasesCoreSwiftTests/Purchasing/OfferingsManagerTests.swift @@ -14,7 +14,7 @@ import XCTest import Nimble import StoreKit -import PurchasesCoreSwift +@testable import PurchasesCoreSwift // TODO (post-migration): Move this test case to the new framework target (PurchasesCoreSwift). class OfferingsManagerTests: XCTestCase { diff --git a/PurchasesCoreSwiftTests/Purchasing/PurchasesTests.swift b/PurchasesCoreSwiftTests/Purchasing/PurchasesTests.swift index 605c9e02af..c17c2a6564 100644 --- a/PurchasesCoreSwiftTests/Purchasing/PurchasesTests.swift +++ b/PurchasesCoreSwiftTests/Purchasing/PurchasesTests.swift @@ -1,6 +1,13 @@ // +// Copyright RevenueCat Inc. All Rights Reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// // Created by RevenueCat. -// Copyright © 2019 RevenueCat. All rights reserved. // import Nimble @@ -334,7 +341,7 @@ class PurchasesTests: XCTestCase { Purchases.notConfiguredAssertionFunction = { assertionHappened.fulfill() } - let purchases = Purchases.shared + _ = Purchases.shared wait(for: [assertionHappened], timeout: TimeInterval(1)) #else @@ -2802,6 +2809,7 @@ class PurchasesTests: XCTestCase { } } + @available(iOS, deprecated: 0.1) // Ignore deprecation warnings func testSetDebugLogsEnabledSetsTheCorrectValue() { Logger.logLevel = .warn diff --git a/PurchasesCoreSwiftTests/SubscriberAttributes/BackendSubscriberAttributesTests.swift b/PurchasesCoreSwiftTests/SubscriberAttributes/BackendSubscriberAttributesTests.swift index 7a8eb4483c..aaa16e6e52 100644 --- a/PurchasesCoreSwiftTests/SubscriberAttributes/BackendSubscriberAttributesTests.swift +++ b/PurchasesCoreSwiftTests/SubscriberAttributes/BackendSubscriberAttributesTests.swift @@ -74,11 +74,11 @@ class BackendSubscriberAttributesTests: XCTestCase { let expectedBody: [String: [String: NSObject]] = [ "attributes": [ subscriberAttribute1.key: [ - "updated_at_ms": (subscriberAttribute1.setTime as NSDate).rc_millisecondsSince1970AsUInt64(), + "updated_at_ms": (subscriberAttribute1.setTime as NSDate).millisecondsSince1970AsUInt64(), "value": subscriberAttribute1.value ] as NSObject, subscriberAttribute2.key: [ - "updated_at_ms": (subscriberAttribute2.setTime as NSDate).rc_millisecondsSince1970AsUInt64(), + "updated_at_ms": (subscriberAttribute2.setTime as NSDate).millisecondsSince1970AsUInt64(), "value": subscriberAttribute2.value ] as NSObject, ] @@ -130,7 +130,7 @@ class BackendSubscriberAttributesTests: XCTestCase { let receivedNSError = receivedError! as NSError expect(receivedNSError.code) == ErrorCode.networkError.rawValue - expect(receivedNSError.rc_successfullySynced) == false + expect(receivedNSError.successfullySynced) == false } func testPostSubscriberAttributesCallsCompletionWithErrorInBackendErrorCase() { @@ -157,7 +157,7 @@ class BackendSubscriberAttributesTests: XCTestCase { let receivedNSError = receivedError! as NSError expect(receivedNSError.code) == ErrorCode.unknownBackendError.rawValue - expect(receivedNSError.rc_successfullySynced) == false + expect(receivedNSError.successfullySynced) == false expect(receivedNSError.userInfo[Backend.RCSuccessfullySyncedKey as String]).toNot(beNil()) expect((receivedNSError.userInfo[Backend.RCSuccessfullySyncedKey as String] as! NSNumber).boolValue) == false } @@ -223,7 +223,7 @@ class BackendSubscriberAttributesTests: XCTestCase { let receivedNSError = receivedError! as NSError expect(receivedNSError.code) == ErrorCode.unknownBackendError.rawValue - expect(receivedNSError.rc_successfullySynced) == true + expect(receivedNSError.successfullySynced) == true expect(receivedNSError.userInfo[Backend.RCSuccessfullySyncedKey as String]).toNot(beNil()) expect((receivedNSError.userInfo[Backend.RCSuccessfullySyncedKey as String] as! NSNumber).boolValue) == true } @@ -263,7 +263,7 @@ class BackendSubscriberAttributesTests: XCTestCase { let receivedNSError = receivedError! as NSError expect(receivedNSError.code) == ErrorCode.unknownBackendError.rawValue - expect(receivedNSError.rc_successfullySynced) == false + expect(receivedNSError.successfullySynced) == false expect(receivedNSError.userInfo[Backend.RCSuccessfullySyncedKey as String]).toNot(beNil()) let code = receivedNSError.userInfo[Backend.RCSuccessfullySyncedKey as String] as! NSNumber @@ -302,11 +302,11 @@ class BackendSubscriberAttributesTests: XCTestCase { let expectedBody: [String: NSObject] = [ subscriberAttribute1.key: [ - "updated_at_ms": (subscriberAttribute1.setTime as NSDate).rc_millisecondsSince1970AsUInt64(), + "updated_at_ms": (subscriberAttribute1.setTime as NSDate).millisecondsSince1970AsUInt64(), "value": subscriberAttribute1.value ] as NSObject, subscriberAttribute2.key: [ - "updated_at_ms": (subscriberAttribute2.setTime as NSDate).rc_millisecondsSince1970AsUInt64(), + "updated_at_ms": (subscriberAttribute2.setTime as NSDate).millisecondsSince1970AsUInt64(), "value": subscriberAttribute2.value ] as NSObject ] @@ -371,8 +371,8 @@ class BackendSubscriberAttributesTests: XCTestCase { expect(receivedError).toNot(beNil()) guard let nonNilReceivedError = receivedError else { fatalError() } - expect(nonNilReceivedError.rc_successfullySynced) == true - expect(nonNilReceivedError.rc_subscriberAttributesErrors) + expect(nonNilReceivedError.successfullySynced) == true + expect(nonNilReceivedError.subscriberAttributesErrors) == attributeErrors[Backend.RCAttributeErrorsKey] } @@ -408,8 +408,8 @@ class BackendSubscriberAttributesTests: XCTestCase { expect(receivedError).toNot(beNil()) guard let nonNilReceivedError = receivedError else { fatalError() } - expect(nonNilReceivedError.rc_successfullySynced) == true - expect(nonNilReceivedError.rc_subscriberAttributesErrors) + expect(nonNilReceivedError.successfullySynced) == true + expect(nonNilReceivedError.subscriberAttributesErrors) == attributeErrors[Backend.RCAttributeErrorsKey] } diff --git a/PurchasesCoreSwiftTests/SubscriberAttributes/SubscriberAttributeTests.swift b/PurchasesCoreSwiftTests/SubscriberAttributes/SubscriberAttributeTests.swift index b6ad78dfc5..82a8272991 100644 --- a/PurchasesCoreSwiftTests/SubscriberAttributes/SubscriberAttributeTests.swift +++ b/PurchasesCoreSwiftTests/SubscriberAttributes/SubscriberAttributeTests.swift @@ -55,6 +55,6 @@ class SubscriberAttributeTests: XCTestCase { expect(receivedDictionary["value"] as? String) == value let updatedAtEpoch = (receivedDictionary["updated_at_ms"] as! NSNumber).uint64Value - expect(updatedAtEpoch) == (now as NSDate).rc_millisecondsSince1970AsUInt64() + expect(updatedAtEpoch) == (now as NSDate).millisecondsSince1970AsUInt64() } }