From 014bc07ce3a838bb6216c5a57add385ff0cbb25b Mon Sep 17 00:00:00 2001 From: Joshua Liebowitz Date: Wed, 25 Aug 2021 16:08:49 -0700 Subject: [PATCH] Enable swiftlint (#782) * Enable swiftlint Enable swiftlint, fix warnings. * Update PurchasesCoreSwift/Public/Purchases.swift Co-authored-by: Cesar de la Vega Co-authored-by: Cesar de la Vega --- .swiftlint.yml | 12 ++--- Package.swift | 2 +- .../ASIdentifierManagerProxy.swift | 4 +- .../Attribution/AdClientProxy.swift | 4 +- .../Attribution/AttributionFetcher.swift | 3 +- .../Attribution/AttributionPoster.swift | 2 + .../Attribution/TrackingManagerProxy.swift | 1 - .../Data+Extensions.swift | 8 +-- .../String+Extensions.swift | 6 +-- PurchasesCoreSwift/Networking/Backend.swift | 15 +++--- .../Networking/HTTPClient.swift | 18 ++++--- .../Public/EntitlementInfo.swift | 2 + .../Error Handling/BackendErrorCode.swift | 4 +- .../Public/Error Handling/ErrorUtils.swift | 49 ++++++++++--------- .../Public/IntroEligibility.swift | 10 ++-- PurchasesCoreSwift/Public/Package.swift | 12 ++++- PurchasesCoreSwift/Public/PurchaserInfo.swift | 15 +++--- PurchasesCoreSwift/Public/Purchases.swift | 35 +++++++++++-- .../Purchasing/OfferingsManager.swift | 7 ++- .../Purchasing/ProductInfo.swift | 3 +- .../Purchasing/PromotionalOffer.swift | 3 +- .../Purchasing/PurchasesOrchestrator.swift | 8 ++- .../Purchasing/ReceiptFetcher.swift | 8 +-- .../SubscriberAttributesManager.swift | 2 +- .../NSData+RCExtensionsTests.swift | 6 +-- .../Purchasing/PurchaserInfoTests.swift | 10 +++- .../Purchasing/PurchasesTests.swift | 2 +- .../PurchasesSubscriberAttributesTests.swift | 4 +- .../SubscriberAttributesManagerTests.swift | 6 +-- SwiftStyleGuide.swift | 1 + 30 files changed, 162 insertions(+), 100 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index d4c3a0fc40..bea1924638 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -9,17 +9,11 @@ excluded: - PublicSDKAPITester - vendor - scan_derived_data -disabled_rules: - - trailing_comma - - todo - - cyclomatic_complexity - - function_body_length - - identifier_name - - line_length + opt_in_rules: - sorted_imports - # TODO: enable this rule after the swift migration is finished. - # - missing_docs + - missing_docs + identifier_name: max_length: warning: 60 diff --git a/Package.swift b/Package.swift index a255de1026..135551679b 100644 --- a/Package.swift +++ b/Package.swift @@ -33,7 +33,7 @@ let package = Package( ], products: [ .library(name: "Purchases", - targets: ["Purchases"]), + targets: ["Purchases"]) ], dependencies: [], targets: resolveTargets() diff --git a/PurchasesCoreSwift/Attribution/ASIdentifierManagerProxy.swift b/PurchasesCoreSwift/Attribution/ASIdentifierManagerProxy.swift index 65e403baf2..229f8c4698 100644 --- a/PurchasesCoreSwift/Attribution/ASIdentifierManagerProxy.swift +++ b/PurchasesCoreSwift/Attribution/ASIdentifierManagerProxy.swift @@ -27,9 +27,7 @@ class FakeASIdentifierManager: NSObject { } -// TODO: Test if Objc can be removed. -@objc(RCASIdentifierManagerProxy) -class ASIdentifierManagerProxy: NSObject { +@objc class ASIdentifierManagerProxy: NSObject { static let mangledIdentifierClassName = "NFVqragvsvreZnantre" static let mangledIdentifierPropertyName = "nqiregvfvatVqragvsvre" diff --git a/PurchasesCoreSwift/Attribution/AdClientProxy.swift b/PurchasesCoreSwift/Attribution/AdClientProxy.swift index 5db949adb3..2a71c415e7 100644 --- a/PurchasesCoreSwift/Attribution/AdClientProxy.swift +++ b/PurchasesCoreSwift/Attribution/AdClientProxy.swift @@ -35,9 +35,7 @@ class FakeAdClient: NSObject { } -// TODO: Test if Objc can be removed. -@objc(RCAdClientProxy) -class AdClientProxy: NSObject { +@objc class AdClientProxy: NSObject { private static let className = "ADClient" diff --git a/PurchasesCoreSwift/Attribution/AttributionFetcher.swift b/PurchasesCoreSwift/Attribution/AttributionFetcher.swift index cab3f99362..46a1118b8f 100644 --- a/PurchasesCoreSwift/Attribution/AttributionFetcher.swift +++ b/PurchasesCoreSwift/Attribution/AttributionFetcher.swift @@ -114,7 +114,8 @@ private extension AttributionFetcher { let minimumOSVersionRequiringAuthorization = OperatingSystemVersion(majorVersion: 14, minorVersion: 5, patchVersion: 0) - let needsTrackingAuthorization = systemInfo.isOperatingSystemAtLeastVersion(minimumOSVersionRequiringAuthorization) + let needsTrackingAuthorization = systemInfo + .isOperatingSystemAtLeastVersion(minimumOSVersionRequiringAuthorization) guard let trackingManagerProxy = attributionFactory.atTrackingProxy() else { if needsTrackingAuthorization { diff --git a/PurchasesCoreSwift/Attribution/AttributionPoster.swift b/PurchasesCoreSwift/Attribution/AttributionPoster.swift index 7150db3a8e..be2d8c2776 100644 --- a/PurchasesCoreSwift/Attribution/AttributionPoster.swift +++ b/PurchasesCoreSwift/Attribution/AttributionPoster.swift @@ -35,6 +35,7 @@ class AttributionPoster { self.subscriberAttributesManager = subscriberAttributesManager } + // swiftlint:disable function_body_length func post(attributionData data: [String: Any], fromNetwork network: AttributionNetwork, networkUserId: String?) { @@ -100,6 +101,7 @@ class AttributionPoster { } } } + // swiftlint:enable function_body_length func postAppleSearchAdsAttributionIfNeeded() { guard attributionFetcher.isAuthorizedToPostSearchAds else { diff --git a/PurchasesCoreSwift/Attribution/TrackingManagerProxy.swift b/PurchasesCoreSwift/Attribution/TrackingManagerProxy.swift index fd74dc4287..b4fb3a5aa3 100644 --- a/PurchasesCoreSwift/Attribution/TrackingManagerProxy.swift +++ b/PurchasesCoreSwift/Attribution/TrackingManagerProxy.swift @@ -37,7 +37,6 @@ class FakeTrackingManager: NSObject { } -// TODO: Test if Objc can be removed. @objc class TrackingManagerProxy: NSObject { static let mangledTrackingClassName = "NGGenpxvatZnantre" diff --git a/PurchasesCoreSwift/FoundationExtensions/Data+Extensions.swift b/PurchasesCoreSwift/FoundationExtensions/Data+Extensions.swift index 680845381d..180f377d92 100644 --- a/PurchasesCoreSwift/FoundationExtensions/Data+Extensions.swift +++ b/PurchasesCoreSwift/FoundationExtensions/Data+Extensions.swift @@ -17,7 +17,7 @@ import Foundation extension NSData { - func rc_asString() -> String { + func asString() -> String { var deviceTokenString = "" self.enumerateBytes { bytes, byteRange, _ in for index in stride(from: 0, to: byteRange.length, by: 1) { @@ -32,12 +32,12 @@ extension NSData { extension Data { - var rc_asString: String { - return (self as NSData).rc_asString() + var asString: String { + return (self as NSData).asString() } // Returns a string representing a fetch token. - var rc_asFetchToken: String { + var asFetchToken: String { return self.base64EncodedString() } diff --git a/PurchasesCoreSwift/FoundationExtensions/String+Extensions.swift b/PurchasesCoreSwift/FoundationExtensions/String+Extensions.swift index 5f9a091e2b..fea77de69f 100644 --- a/PurchasesCoreSwift/FoundationExtensions/String+Extensions.swift +++ b/PurchasesCoreSwift/FoundationExtensions/String+Extensions.swift @@ -32,9 +32,9 @@ private struct ROT13 { fileprivate static func string(_ string: String) -> String { if ROT13.key.isEmpty { - for i in 0 ..< 26 { - ROT13.key[ROT13.uppercase[i]] = ROT13.uppercase[(i + 13) % 26] - ROT13.key[ROT13.lowercase[i]] = ROT13.lowercase[(i + 13) % 26] + for number in 0 ..< 26 { + ROT13.key[ROT13.uppercase[number]] = ROT13.uppercase[(number + 13) % 26] + ROT13.key[ROT13.lowercase[number]] = ROT13.lowercase[(number + 13) % 26] } } diff --git a/PurchasesCoreSwift/Networking/Backend.swift b/PurchasesCoreSwift/Networking/Backend.swift index 1844219ddc..f37d6a2d60 100644 --- a/PurchasesCoreSwift/Networking/Backend.swift +++ b/PurchasesCoreSwift/Networking/Backend.swift @@ -83,12 +83,12 @@ class Backend { subscriberAttributes subscriberAttributesByKey: SubscriberAttributeDict?, completion: @escaping BackendPurchaserInfoResponseHandler) { // swiftlint:enable function_parameter_count - let fetchToken = receiptData.rc_asFetchToken + let fetchToken = receiptData.asFetchToken var body: [String: Any] = [ "fetch_token": fetchToken, "app_user_id": appUserID, "is_restore": isRestore, - "observer_mode": observerMode, + "observer_mode": observerMode ] let cacheKey = @@ -153,15 +153,15 @@ class Backend { } } - // swiftlint:disable function_parameter_count + // swiftlint:disable function_parameter_count function_body_length func post(offerIdForSigning offerIdentifier: String, productIdentifier: String, subscriptionGroup: String, receiptData: Data, appUserID: String, completion: @escaping OfferSigningResponseHandler) { - // swiftlint:enable function_parameter_count - let fetchToken = receiptData.rc_asFetchToken + // swiftlint:enable function_parameter_count function_body_length + let fetchToken = receiptData.asFetchToken let requestBody: [String: Any] = ["app_user_id": appUserID, "fetch_token": fetchToken, @@ -365,7 +365,7 @@ class Backend { return } - let fetchToken = receiptData.rc_asFetchToken + let fetchToken = receiptData.asFetchToken let path = "/subscribers/\(appUserID)/intro_eligibility" let body: [String: Any] = ["product_identifiers": productIdentifiers, "fetch_token": fetchToken] @@ -437,7 +437,8 @@ private extension Backend { if statusCode > HTTPStatusCodes.redirect.rawValue { let backendCode = maybeNumberFromError(code: response?["code"]) let backendMessage = response?["message"] as? String - let responsError = ErrorUtils.backendError(withBackendCode: backendCode as NSNumber?, backendMessage: backendMessage) + let responsError = ErrorUtils.backendError(withBackendCode: backendCode as NSNumber?, + backendMessage: backendMessage) completion(nil, false, ErrorUtils.networkError(withUnderlyingError: responsError)) return } diff --git a/PurchasesCoreSwift/Networking/HTTPClient.swift b/PurchasesCoreSwift/Networking/HTTPClient.swift index 7d831505c1..a21fdb87b4 100644 --- a/PurchasesCoreSwift/Networking/HTTPClient.swift +++ b/PurchasesCoreSwift/Networking/HTTPClient.swift @@ -105,7 +105,7 @@ private extension HTTPClient { requestBody: maybeRequestBody, authHeaders: authHeaders, retried: retried, - completionHandler: maybeCompletionHandler) + completion: maybeCompletionHandler) } if serially { @@ -120,9 +120,10 @@ private extension HTTPClient { request: HTTPRequest, data maybeData: Data?, error maybeNetworkError: Error?, - completionHandler maybeCompletionHandler: ((Int, [String: Any]?, Error?) -> Void)?, + completion maybeCompletionHandler: ((Int, [String: Any]?, Error?) -> Void)?, beginNextRequestWhenFinished: Bool, retried: Bool) { + // swiftlint:enable function_parameter_count operationDispatcher.dispatchOnHTTPSerialQueue { [self] in self.threadUnsafeHandleResponse(urlResponse: maybeURLResponse, request: request, @@ -134,13 +135,15 @@ private extension HTTPClient { } } + // swiftlint:disable function_body_length function_parameter_count func threadUnsafePerformRequest(_ httpMethod: String, serially: Bool, path: String, requestBody maybeRequestBody: [String: Any]?, authHeaders: [String: String], retried: Bool, - completionHandler maybeCompletionHandler: ((Int, [String: Any]?, Error?) -> Void)?) { + completion maybeCompletionHandler: ((Int, [String: Any]?, Error?) -> Void)?) { + // swiftlint:enable function_body_length function_parameter_count let requestHeaders = self.defaultHeaders.merging(authHeaders) let maybeURLRequest = self.createRequest(httpMethod: httpMethod, @@ -194,14 +197,14 @@ private extension HTTPClient { request: request, data: data, error: error, - completionHandler: maybeCompletionHandler, + completion: maybeCompletionHandler, beginNextRequestWhenFinished: serially, retried: retried) } task.resume() } - // swiftlint:disable function_parameter_count + // swiftlint:disable function_body_length function_parameter_count func threadUnsafeHandleResponse(urlResponse maybeURLResponse: URLResponse?, request: HTTPRequest, data maybeData: Data?, @@ -209,6 +212,7 @@ private extension HTTPClient { completionHandler maybeCompletionHandler: ((Int, [String: Any]?, Error?) -> Void)?, beginNextRequestWhenFinished: Bool, retried: Bool) { + // swiftlint:enable function_body_length function_parameter_count var shouldBeginNextRequestWhenFinished = beginNextRequestWhenFinished var statusCode = HTTPStatusCodes.networkConnectTimeoutError.rawValue var jsonObject: [String: Any]? @@ -310,7 +314,9 @@ private extension HTTPClient { do { urlRequest.httpBody = try JSONSerialization.data(withJSONObject: requestBody) } catch let error { - Logger.error(String(format: Strings.network.creating_json_error, requestBody, error.localizedDescription)) + Logger.error(String(format: Strings.network.creating_json_error, + requestBody, + error.localizedDescription)) return nil } } else { diff --git a/PurchasesCoreSwift/Public/EntitlementInfo.swift b/PurchasesCoreSwift/Public/EntitlementInfo.swift index ea5fa7e1b1..cbc2e26ba9 100644 --- a/PurchasesCoreSwift/Public/EntitlementInfo.swift +++ b/PurchasesCoreSwift/Public/EntitlementInfo.swift @@ -199,7 +199,9 @@ import Foundation """ } + // swiftlint:disable cyclomatic_complexity public override func isEqual(_ object: Any?) -> Bool { + // swiftlint:enable cyclomatic_complexity guard let info = object as? EntitlementInfo else { return false } diff --git a/PurchasesCoreSwift/Public/Error Handling/BackendErrorCode.swift b/PurchasesCoreSwift/Public/Error Handling/BackendErrorCode.swift index dd9719ed37..7a1f244771 100644 --- a/PurchasesCoreSwift/Public/Error Handling/BackendErrorCode.swift +++ b/PurchasesCoreSwift/Public/Error Handling/BackendErrorCode.swift @@ -46,8 +46,9 @@ import Foundation extension BackendErrorCode { + // swiftlint:disable cyclomatic_complexity func toPurchasesErrorCode() -> ErrorCode { - + // swiftlint:enable cyclomatic_complexity switch self { case .invalidPlatform: return .configurationError @@ -85,7 +86,6 @@ extension BackendErrorCode { @unknown default: return .unknownError } - } } diff --git a/PurchasesCoreSwift/Public/Error Handling/ErrorUtils.swift b/PurchasesCoreSwift/Public/Error Handling/ErrorUtils.swift index b8c97ec262..d593424e7b 100644 --- a/PurchasesCoreSwift/Public/Error Handling/ErrorUtils.swift +++ b/PurchasesCoreSwift/Public/Error Handling/ErrorUtils.swift @@ -22,9 +22,9 @@ import StoreKit * Constructs an NSError with the [ErrorCode.networkError] code and a populated [NSUnderlyingErrorKey] in * the [NSError.userInfo] dictionary. * - * @param underlyingError The value of the [NSUnderlyingErrorKey] key. + * - Parameter: underlyingError The value of the [NSUnderlyingErrorKey] key. * - * @note This error is used when there is an error performing network request returns an error or when there + * - Note: This error is used when there is an error performing network request returns an error or when there * is an [NSJSONSerialization] error. */ @objc public static func networkError(withUnderlyingError underlyingError: Error) -> Error { @@ -36,10 +36,10 @@ import StoreKit * [RCUnderlyingErrorKey] in the [NSError.userInfo] dictionary. The backend error code will be mapped using * [BackendErrorCode.toPurchasesErrorCode()]. * - * @param backendCode The numerical value of the error. - * @param backendMessage The message of the errror contained under the [NSUnderlyingErrorKey] key. + * - Parameter: backendCode The numerical value of the error. + * - Parameter: backendMessage The message of the errror contained under the [NSUnderlyingErrorKey] key. * - * @note This error is used when an network request returns an error. The backend error returned is wrapped in + * - Note: This error is used when an network request returns an error. The backend error returned is wrapped in * this internal error code. */ @objc public static func backendError(withBackendCode backendCode: NSNumber?, @@ -52,12 +52,13 @@ import StoreKit * [RCUnderlyingErrorKey] in the [NSError.userInfo] dictionary. The backend error code will be mapped using * [BackendErrorCode.toPurchasesErrorCode()]. * - * @param backendCode The numerical value of the error. - * @param backendMessage The message of the errror contained under the [NSUnderlyingErrorKey] key in the UserInfo dictionary. - * @param finishable Will be added to the UserInfo dictionary under the [RCFinishableKey] to indicate if the transaction - * should be finished after this error. + * - Parameter: backendCode The numerical value of the error. + * - Parameter: backendMessage The message of the errror contained under the [NSUnderlyingErrorKey] key in the + * UserInfo dictionary. + * - Parameter: finishable Will be added to the UserInfo dictionary under the [RCFinishableKey] to indicate if the + * transaction should be finished after this error. * - * @note This error is used when an network request returns an error. The backend error returned is wrapped in + * - Note: This error is used when an network request returns an error. The backend error returned is wrapped in * this internal error code. */ @objc public static func backendError(withBackendCode backendCode: NSNumber?, @@ -73,7 +74,7 @@ import StoreKit /** * Constructs an Error with the [ErrorCode.unexpectedBackendResponseError] code. * - * @note This error is used when an network request returns an unexpected response. + * - Note: This error is used when an network request returns an unexpected response. */ @objc public static func unexpectedBackendResponseError() -> Error { return error(with: ErrorCode.unexpectedBackendResponseError) @@ -82,8 +83,8 @@ import StoreKit /** * Constructs an Error with the [ErrorCode.missingReceiptFileError] code. * - * @note This error is used when the receipt is missing in the device. This can happen if the user is in sandbox or - * if there are no previous purchases. + * - Note: This error is used when the receipt is missing in the device. This can happen if the user is in + * sandbox or if there are no previous purchases. */ @objc public static func missingReceiptFileError() -> Error { return error(with: ErrorCode.missingReceiptFileError) @@ -92,7 +93,7 @@ import StoreKit /** * Constructs an Error with the [ErrorCode.invalidAppUserIdError] code. * - * @note This error is used when the appUserID can't be found in user defaults. This can happen if user defaults + * - Note: This error is used when the appUserID can't be found in user defaults. This can happen if user defaults * are removed manually or if the OS deletes entries when running out of space. */ @objc public static func missingAppUserIDError() -> Error { @@ -102,7 +103,7 @@ import StoreKit /** * Constructs an Error with the [ErrorCode.productDiscountMissingIdentifierError] code. * - * @note This error code is used when attemping to post data about product discounts but the discount is + * - Note: This error code is used when attemping to post data about product discounts but the discount is * missing an indentifier. */ @objc public static func productDiscountMissingIdentifierError() -> Error { @@ -112,7 +113,7 @@ import StoreKit /** * Constructs an Error with the [ErrorCode.productDiscountMissingSubscriptionGroupIdentifierError] code. * - * @note This error code is used when attemping to post data about product discounts but the discount is + * - Note: This error code is used when attemping to post data about product discounts but the discount is * missing a subscriptionGroupIndentifier. */ @objc public static func productDiscountMissingSubscriptionGroupIdentifierError() -> Error { @@ -122,7 +123,7 @@ import StoreKit /** * Constructs an Error with the [ErrorCode.invalidAppUserIdError] code. * - * @note This error is used when the appUserID can't be found in user defaults. This can happen if user defaults + * - Note: This error is used when the appUserID can't be found in user defaults. This can happen if user defaults * are removed manually or if the OS deletes entries when running out of space. */ @objc public static func missingAppUserIDForAliasCreationError() -> Error { @@ -132,7 +133,7 @@ import StoreKit /** * Constructs an Error with the [ErrorCode.logOutAnonymousUserError] code. * - * @note This error is used when logOut is called but the current user is anonymous, + * - Note: This error is used when logOut is called but the current user is anonymous, * as noted by RCPurchaserInfo's isAnonymous property. */ @objc public static func logOutAnonymousUserError() -> Error { @@ -142,8 +143,8 @@ import StoreKit /** * Constructs an Error with the [ErrorCode.paymentPendingError] code. * - * @note This error is used during an “ask to buy” flow for a payment. The completion block of the purchasing function - * will get this error to indicate the guardian has to complete the purchase. + * - Note: This error is used during an “ask to buy” flow for a payment. The completion block of the purchasing + * function will get this error to indicate the guardian has to complete the purchase. */ @objc public static func paymentDeferredError() -> Error { return error(with: ErrorCode.paymentPendingError, message: "The payment is deferred.") @@ -166,17 +167,19 @@ import StoreKit /** * Constructs an Error with the [ErrorCode.operationAlreadyInProgressError] code. * - * @note This error is used when a purchase is initiated for a product, but there's already a purchase for the same product in progress. + * - Note: This error is used when a purchase is initiated for a product, but there's already a purchase for the + * same product in progress. */ @objc public static func operationAlreadyInProgressError() -> Error { return error(with: ErrorCode.operationAlreadyInProgressForProductError) } /** - * Maps an SKError to a Error with a [ErrorCode]. Adds a underlying error in the NSError.userInfo dictionary. The SKError code will be mapped using + * Maps an SKError to a Error with a [ErrorCode]. Adds a underlying error in the NSError.userInfo dictionary. + * The SKError code will be mapped using * [SKError.toPurchasesErrorCode()]. * - * @param skError The originating [SKError]. + * - Parameter: skError The originating [SKError]. */ @objc public static func purchasesError(withSKError skError: Error) -> Error { let errorCode = (skError as? SKError)?.toPurchasesErrorCode() ?? .unknownError diff --git a/PurchasesCoreSwift/Public/IntroEligibility.swift b/PurchasesCoreSwift/Public/IntroEligibility.swift index a5523ee430..49884b556e 100644 --- a/PurchasesCoreSwift/Public/IntroEligibility.swift +++ b/PurchasesCoreSwift/Public/IntroEligibility.swift @@ -9,11 +9,11 @@ import Foundation /** - @typedef RCIntroEligibilityStatus - @brief Enum of different possible states for intro price eligibility status. - @constant RCIntroEligibilityStatusUnknown RevenueCat doesn't have enough information to determine eligibility. - @constant RCIntroEligibilityStatusIneligible The user is not eligible for a free trial or intro pricing for this product. - @constant RCIntroEligibilityStatusEligible The user is eligible for a free trial or intro pricing for this product. + * - Note: Enum of different possible states for intro price eligibility status. + * RCIntroEligibilityStatusUnknown RevenueCat doesn't have enough information to determine eligibility. + * RCIntroEligibilityStatusIneligible The user is not eligible for a free trial or intro pricing for this + * product. + * @constant RCIntroEligibilityStatusEligible The user is eligible for a free trial or intro pricing for this product. */ @objc(RCIntroEligibilityStatus) public enum IntroEligibilityStatus: Int { /** diff --git a/PurchasesCoreSwift/Public/Package.swift b/PurchasesCoreSwift/Public/Package.swift index 5fe86c93ed..7ea163d957 100644 --- a/PurchasesCoreSwift/Public/Package.swift +++ b/PurchasesCoreSwift/Public/Package.swift @@ -50,7 +50,7 @@ private extension PackageType { "$rc_three_month": .threeMonth, "$rc_two_month": .twoMonth, "$rc_monthly": .monthly, - "$rc_weekly": .weekly, + "$rc_weekly": .weekly ] } } @@ -94,10 +94,19 @@ private extension PackageType { } @objc public extension Package { + + /** + * - Parameter packageType: A PackageType. + * - Returns: an optional description of the packageType. + */ static func string(from packageType: PackageType) -> String? { return packageType.description } + /** + * - Parameter string: A string that maps to a enumeration value of type PackageType + * - Returns: a PackageType for the given string. + */ class func packageType(from string: String) -> PackageType { if let packageType = PackageType.typesByDescription[string] { return packageType @@ -105,4 +114,5 @@ private extension PackageType { return string.hasPrefix("$rc_") ? .unknown : .custom } + } diff --git a/PurchasesCoreSwift/Public/PurchaserInfo.swift b/PurchasesCoreSwift/Public/PurchaserInfo.swift index 1fd60e3559..661a4c4b74 100644 --- a/PurchasesCoreSwift/Public/PurchaserInfo.swift +++ b/PurchasesCoreSwift/Public/PurchaserInfo.swift @@ -71,12 +71,12 @@ import Foundation @objc public let originalPurchaseDate: Date? /** - Returns the build number (in iOS) or the marketing version (in macOS) for the version of the application when the user bought the app. - This corresponds to the value of CFBundleVersion (in iOS) or CFBundleShortVersionString (in macOS) in the Info.plist file when the purchase was originally made. - Use this for grandfathering users when migrating to subscriptions. - - - Note: This can be nil, see -`Purchases.restoreTransactions(completionBlock:)` + * The build number (in iOS) or the marketing version (in macOS) for the version of the application when the user + * bought the app. This corresponds to the value of CFBundleVersion (in iOS) or CFBundleShortVersionString + * (in macOS) in the Info.plist file when the purchase was originally made. Use this for grandfathering users + * when migrating to subscriptions. + * + * - Note: This can be nil, see -`Purchases.restoreTransactions(completionBlock:)` */ @objc public let originalApplicationVersion: String? @@ -254,7 +254,8 @@ import Foundation dateFormatter: dateFormatter) let latestNonSubscriptionTransactionsByProductId = [String: [String: Any]]( - uniqueKeysWithValues: nonSubscriptionsByProductId.map { productId, transactionsArray in (productId, transactionsArray.last ?? [:]) + uniqueKeysWithValues: nonSubscriptionsByProductId.map { productId, transactionsArray in + (productId, transactionsArray.last ?? [:]) }) self.allTransactionsByProductId = latestNonSubscriptionTransactionsByProductId diff --git a/PurchasesCoreSwift/Public/Purchases.swift b/PurchasesCoreSwift/Public/Purchases.swift index 4251bd3b00..94dd25eaa4 100644 --- a/PurchasesCoreSwift/Public/Purchases.swift +++ b/PurchasesCoreSwift/Public/Purchases.swift @@ -42,7 +42,6 @@ public typealias ReceiveProductsBlock = ([SKProduct]) -> Void Completion block for `-[Purchases purchaseProduct:withCompletionBlock:]` */ public typealias PurchaseCompletedBlock = (SKPaymentTransaction?, PurchaserInfo?, Error?, Bool) -> Void -public typealias RCPurchaseCompletedBlock = PurchaseCompletedBlock /** Deferred block for `purchases:shouldPurchasePromoProduct:defermentBlock:` @@ -217,6 +216,7 @@ public typealias PaymentDiscountBlock = (SKPaymentDiscount?, Error?) -> Void platformFlavorVersion: nil) } + // swiftlint:disable function_body_length @objc public convenience init(apiKey: String, appUserID: String?, userDefaults: UserDefaults?, @@ -307,6 +307,7 @@ public typealias PaymentDiscountBlock = (SKPaymentDiscount?, Error?) -> Void offeringsManager: offeringsManager, purchasesOrchestrator: purchasesOrchestrator) } + // swiftlint:enable function_body_length init(appUserID: String?, requestFetcher: StoreKitRequestFetcher, @@ -541,10 +542,10 @@ extension Purchases { /** * Subscriber attribute associated with the install ad for the user * - * - Parameter ad: nil will delete the subscriber attribute. + * - Parameter installAd: nil will delete the subscriber attribute. */ - @objc public func setAd(_ ad: String) { - subscriberAttributesManager.setAd(ad, appUserID: appUserID) + @objc public func setAd(_ installAd: String) { + subscriberAttributesManager.setAd(installAd, appUserID: appUserID) } /** @@ -963,8 +964,10 @@ public extension Purchases { * - Parameter receiveEligibility: A block that receives a dictionary of product_id -> `RCIntroEligibility`. */ @objc + // swiftlint:disable line_length func checkTrialOrIntroductoryPriceEligibility(_ productIdentifiers: [String], completionBlock receiveEligibility: @escaping ReceiveIntroEligibilityBlock) { + // swiftlint:enable line_length receiptFetcher.receiptData(refreshPolicy: .onlyIfEmpty) { maybeData in if #available(iOS 12.0, macOS 10.14, macCatalyst 13.0, tvOS 12.0, watchOS 6.2, *), let data = maybeData { @@ -1031,6 +1034,7 @@ public extension Purchases { private func modernEligibilityHandler(maybeReceiptData data: Data, productIdentifiers: [String], completionBlock receiveEligibility: @escaping ReceiveIntroEligibilityBlock) { + // swiftlint:disable line_length introEligibilityCalculator .checkTrialOrIntroductoryPriceEligibility(with: data, productIdentifiers: Set(productIdentifiers)) { receivedEligibility, maybeError in @@ -1064,6 +1068,7 @@ public extension Purchases { } } } + // swiftlint:enable line_length } private func purchase(product: SKProduct, @@ -1171,6 +1176,28 @@ extension Purchases { platformFlavorVersion: nil) } + /** + * Configures an instance of the Purchases SDK with a custom userDefaults. Use this constructor if you want to + * sync status across a shared container, such as between a host app and an extension. The instance of the + * Purchases SDK will be set as a singleton. + * You should access the singleton instance using Purchases.sharedPurchases + * + * - Parameter apiKey: The API Key generated for your app from https://app.revenuecat.com/ + * + * - Parameter appUserID: The unique app user id for this user. This user id will allow users to share their + * purchases and subscriptions across devices. Pass nil if you want `RCPurchases` to generate this for you. + * + * - Parameter observerMode: Set this to TRUE if you have your own IAP implementation and want to use only + * RevenueCat's backend. Default is FALSE. + * + * - Parameter userDefaults: Custom userDefaults to use + * + * - Parameter platformFlavor: The current platformFlavor you are configuring. + * + * - Parameter platformFlavorVersion: The current version of the platformFlavor you are configuring. + * + * - Returns: An instantiated `Purchases` object that has been set as a singleton. + */ public static func configure(apiKey: String, appUserID: String?, observerMode: Bool, diff --git a/PurchasesCoreSwift/Purchasing/OfferingsManager.swift b/PurchasesCoreSwift/Purchasing/OfferingsManager.swift index 722736cf4c..704ad5c047 100644 --- a/PurchasesCoreSwift/Purchasing/OfferingsManager.swift +++ b/PurchasesCoreSwift/Purchasing/OfferingsManager.swift @@ -107,7 +107,8 @@ private extension OfferingsManager { } func handleOfferingsUpdateError(_ error: Error, completion: ReceiveOfferingsBlock?) { - Logger.appleError(String(format: Strings.offering.fetching_offerings_error, error.localizedDescription as CVarArg)) + Logger.appleError(String(format: Strings.offering.fetching_offerings_error, + error.localizedDescription as CVarArg)) deviceCache.clearOfferingsCacheTimestamp() dispatchCompletionOnMainThreadIfPossible(completion, offerings: nil, @@ -141,7 +142,9 @@ private extension OfferingsManager { } } - func dispatchCompletionOnMainThreadIfPossible(_ completion: ReceiveOfferingsBlock?, offerings: Offerings?, error: Error?) { + func dispatchCompletionOnMainThreadIfPossible(_ completion: ReceiveOfferingsBlock?, + offerings: Offerings?, + error: Error?) { if let completion = completion { operationDispatcher.dispatchOnMainThread { completion(offerings, error) diff --git a/PurchasesCoreSwift/Purchasing/ProductInfo.swift b/PurchasesCoreSwift/Purchasing/ProductInfo.swift index 19fb566de4..baac854d2f 100644 --- a/PurchasesCoreSwift/Purchasing/ProductInfo.swift +++ b/PurchasesCoreSwift/Purchasing/ProductInfo.swift @@ -38,7 +38,8 @@ struct ProductInfo { } @available(iOS 11.2, macOS 10.13.2, tvOS 11.2, watchOS 6.2, *) - static func paymentMode(fromSKProductDiscountPaymentMode paymentMode: SKProductDiscount.PaymentMode) -> ProductInfo.PaymentMode { + static func paymentMode(fromSKProductDiscountPaymentMode paymentMode: SKProductDiscount.PaymentMode) -> + PaymentMode { switch paymentMode { case .payUpFront: return .payUpFront diff --git a/PurchasesCoreSwift/Purchasing/PromotionalOffer.swift b/PurchasesCoreSwift/Purchasing/PromotionalOffer.swift index fbbd252216..c09be2a280 100644 --- a/PurchasesCoreSwift/Purchasing/PromotionalOffer.swift +++ b/PurchasesCoreSwift/Purchasing/PromotionalOffer.swift @@ -30,7 +30,8 @@ class PromotionalOffer { paymentMode: rcPaymentMode) } - required public init(offerIdentifier: String?, price: NSDecimalNumber, paymentMode: ProductInfo.PaymentMode) { + // swiftlint:disable missing_docs + public init(offerIdentifier: String?, price: NSDecimalNumber, paymentMode: ProductInfo.PaymentMode) { self.offerIdentifier = offerIdentifier self.price = price self.paymentMode = paymentMode diff --git a/PurchasesCoreSwift/Purchasing/PurchasesOrchestrator.swift b/PurchasesCoreSwift/Purchasing/PurchasesOrchestrator.swift index 5c0d0c7ead..67eabddfed 100644 --- a/PurchasesCoreSwift/Purchasing/PurchasesOrchestrator.swift +++ b/PurchasesCoreSwift/Purchasing/PurchasesOrchestrator.swift @@ -125,6 +125,7 @@ class PurchasesOrchestrator { return } + // swiftlint:disable line_length self.backend.post(offerIdForSigning: discountIdentifier, productIdentifier: product.productIdentifier, subscriptionGroup: subscriptionGroupIdentifier, @@ -134,7 +135,7 @@ class PurchasesOrchestrator { completion(nil, error) return } - + // swiftlint:enable line_length guard let keyIdentifier = maybeKeyIdentifier, let nonce = maybeNonce, let signature = maybeSignature, @@ -306,7 +307,8 @@ private extension PurchasesOrchestrator { // MARK: Private funcs. private extension PurchasesOrchestrator { - func getAndRemovePurchaseCompletedCallback(forTransaction transaction: SKPaymentTransaction) -> PurchaseCompletedBlock? { + func getAndRemovePurchaseCompletedCallback(forTransaction transaction: SKPaymentTransaction) -> + PurchaseCompletedBlock? { guard let productIdentifier = transaction.productIdentifier else { return nil } @@ -416,7 +418,9 @@ private extension PurchasesOrchestrator { let currentAppUserID = appUserID let unsyncedAttributes = unsyncedAttributes // Refresh the receipt and post to backend, this will allow the transactions to be transferred. + // swiftlint:disable line_length // https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Restoring.html + // swiftlint:enable line_length receiptFetcher.receiptData(refreshPolicy: receiptRefreshPolicy) { maybeReceiptData in guard let receiptData = maybeReceiptData, !receiptData.isEmpty else { diff --git a/PurchasesCoreSwift/Purchasing/ReceiptFetcher.swift b/PurchasesCoreSwift/Purchasing/ReceiptFetcher.swift index d9742a7def..9b595be76f 100644 --- a/PurchasesCoreSwift/Purchasing/ReceiptFetcher.swift +++ b/PurchasesCoreSwift/Purchasing/ReceiptFetcher.swift @@ -63,10 +63,12 @@ private extension ReceiptFetcher { // correct receipt. // This has been filed as radar FB7699277. More info in https://github.com/RevenueCat/purchases-ios/issues/207. - let minimumOSVersionWithoutBug: OperatingSystemVersion = OperatingSystemVersion(majorVersion: 7, minorVersion: 0, patchVersion: 0) - let isBelowMinimumOSVersionWithoutBug: Bool = ProcessInfo.processInfo.isOperatingSystemAtLeast(minimumOSVersionWithoutBug) + let firstOSVersionWithoutBug: OperatingSystemVersion = OperatingSystemVersion(majorVersion: 7, + minorVersion: 0, + patchVersion: 0) + let isBelowFirstOSVersionWithoutBug = ProcessInfo.processInfo.isOperatingSystemAtLeast(firstOSVersionWithoutBug) - if isBelowMinimumOSVersionWithoutBug && SystemInfo.isSandbox { + if isBelowFirstOSVersionWithoutBug && SystemInfo.isSandbox { let receiptURLFolder: URL = receiptURL.deletingLastPathComponent() let productionReceiptURL: URL = receiptURLFolder.appendingPathComponent("receipt") receiptURL = productionReceiptURL diff --git a/PurchasesCoreSwift/SubscriberAttributes/SubscriberAttributesManager.swift b/PurchasesCoreSwift/SubscriberAttributes/SubscriberAttributesManager.swift index 9f361ddecd..df28504d8d 100644 --- a/PurchasesCoreSwift/SubscriberAttributes/SubscriberAttributesManager.swift +++ b/PurchasesCoreSwift/SubscriberAttributes/SubscriberAttributesManager.swift @@ -55,7 +55,7 @@ class SubscriberAttributesManager { func setPushToken(_ maybePushToken: Data?, appUserID: String) { logAttributionMethodCalled(functionName: #function) - let maybePushTokenString = maybePushToken?.rc_asString + let maybePushTokenString = maybePushToken?.asString setPushTokenString(maybePushTokenString, appUserID: appUserID) } diff --git a/PurchasesCoreSwiftTests/FoundationExtensions/NSData+RCExtensionsTests.swift b/PurchasesCoreSwiftTests/FoundationExtensions/NSData+RCExtensionsTests.swift index 34640b5542..8ff1615505 100644 --- a/PurchasesCoreSwiftTests/FoundationExtensions/NSData+RCExtensionsTests.swift +++ b/PurchasesCoreSwiftTests/FoundationExtensions/NSData+RCExtensionsTests.swift @@ -25,17 +25,17 @@ class NSDataExtensionsTests: XCTestCase { let nsData = data as NSData - expect(nsData.rc_asString()) == "e388152d6c67f4d5f7a78edf073946f158357f89a1dc74dff80a796740fd9d91" + expect(nsData.asString()) == "e388152d6c67f4d5f7a78edf073946f158357f89a1dc74dff80a796740fd9d91" } func testAsFetchToken() { let receiptFilename = "base64EncodedReceiptSampleForDataExtension" let storedReceiptText = NSDataExtensionsTests.readFile(named: receiptFilename) let storedReceiptData = NSDataExtensionsTests.sampleReceiptData(receiptName: receiptFilename) - let fetchToken = storedReceiptData.rc_asFetchToken + let fetchToken = storedReceiptData.asFetchToken expect(fetchToken).to(equal(storedReceiptText)) - expect(storedReceiptData.rc_asFetchToken).to(equal(storedReceiptText)) + expect(storedReceiptData.asFetchToken).to(equal(storedReceiptText)) } } diff --git a/PurchasesCoreSwiftTests/Purchasing/PurchaserInfoTests.swift b/PurchasesCoreSwiftTests/Purchasing/PurchaserInfoTests.swift index 014f413fdd..f301cb0266 100644 --- a/PurchasesCoreSwiftTests/Purchasing/PurchaserInfoTests.swift +++ b/PurchasesCoreSwiftTests/Purchasing/PurchaserInfoTests.swift @@ -116,10 +116,18 @@ class BasicPurchaserInfoTests: XCTestCase { } func testParsesOtherPurchases() { + let nonConsumables = purchaserInfo!.nonSubscriptionTransactions + expect(nonConsumables.count).to(equal(1)) + + expect(nonConsumables[0].productId).to(equal("onetime_purchase")) + } + + @available(*, deprecated) // Ignore deprecation warnings + func testDeprecatedParsesOtherPurchases() { let nonConsumables = purchaserInfo!.nonConsumablePurchases expect(nonConsumables.count).to(equal(1)) - expect(nonConsumables as NSSet).to(contain(["onetime_purchase"])) + expect(nonConsumables).to(contain(["onetime_purchase"])) } func testOriginalApplicationVersionNilIfNotPresent() { diff --git a/PurchasesCoreSwiftTests/Purchasing/PurchasesTests.swift b/PurchasesCoreSwiftTests/Purchasing/PurchasesTests.swift index 24d47ad069..8a384916d4 100644 --- a/PurchasesCoreSwiftTests/Purchasing/PurchasesTests.swift +++ b/PurchasesCoreSwiftTests/Purchasing/PurchasesTests.swift @@ -2809,7 +2809,7 @@ class PurchasesTests: XCTestCase { } } - @available(iOS, deprecated: 0.1) // Ignore deprecation warnings + @available(*, deprecated) // Ignore deprecation warnings func testSetDebugLogsEnabledSetsTheCorrectValue() { Logger.logLevel = .warn diff --git a/PurchasesCoreSwiftTests/SubscriberAttributes/PurchasesSubscriberAttributesTests.swift b/PurchasesCoreSwiftTests/SubscriberAttributes/PurchasesSubscriberAttributesTests.swift index 9f2172e61d..1989e8b510 100644 --- a/PurchasesCoreSwiftTests/SubscriberAttributes/PurchasesSubscriberAttributesTests.swift +++ b/PurchasesCoreSwiftTests/SubscriberAttributes/PurchasesSubscriberAttributesTests.swift @@ -257,14 +257,14 @@ class PurchasesSubscriberAttributesTests: XCTestCase { func testSetPushTokenMakesRightCalls() { setupPurchases() let tokenData = Data("ligai32g32ig".data(using: .utf8)!) - let tokenString = (tokenData as NSData).rc_asString() + let tokenString = (tokenData as NSData).asString() Purchases.shared.setPushToken(tokenData) expect(self.mockSubscriberAttributesManager.invokedSetPushTokenCount) == 1 let receivedPushToken = self.mockSubscriberAttributesManager.invokedSetPushTokenParameters!.pushToken! - expect((receivedPushToken as NSData).rc_asString()) == tokenString + expect((receivedPushToken as NSData).asString()) == tokenString expect(self.mockSubscriberAttributesManager.invokedSetPushTokenParameters?.appUserID) == mockIdentityManager .currentAppUserID } diff --git a/PurchasesCoreSwiftTests/SubscriberAttributes/SubscriberAttributesManagerTests.swift b/PurchasesCoreSwiftTests/SubscriberAttributes/SubscriberAttributesManagerTests.swift index 5bcf6057b3..cb24d826c2 100644 --- a/PurchasesCoreSwiftTests/SubscriberAttributes/SubscriberAttributesManagerTests.swift +++ b/PurchasesCoreSwiftTests/SubscriberAttributes/SubscriberAttributesManagerTests.swift @@ -292,7 +292,7 @@ class SubscriberAttributesManagerTests: XCTestCase { let receivedAttribute = invokedParams.attribute expect(receivedAttribute.key) == "$apnsTokens" - let tokenString = (tokenData as NSData).rc_asString() + let tokenString = (tokenData as NSData).asString() expect(receivedAttribute.value) == tokenString expect(receivedAttribute.isSynced) == false } @@ -315,7 +315,7 @@ class SubscriberAttributesManagerTests: XCTestCase { func testSetPushTokenSkipsIfSameValue() { let tokenData = "ligai32g32ig".data(using: .utf8)! - let tokenString = (tokenData as NSData).rc_asString() + let tokenString = (tokenData as NSData).asString() self.mockDeviceCache.stubbedSubscriberAttributeResult = SubscriberAttribute(withKey: "$apnsTokens", value: tokenString) @@ -326,7 +326,7 @@ class SubscriberAttributesManagerTests: XCTestCase { func testSetPushTokenOverwritesIfNewValue() { let tokenData = "ligai32g32ig".data(using: .utf8)! - let tokenString = (tokenData as NSData).rc_asString() + let tokenString = (tokenData as NSData).asString() let oldSyncTime = Date() self.mockDeviceCache.stubbedSubscriberAttributeResult = SubscriberAttribute(withKey: "$apnsTokens", diff --git a/SwiftStyleGuide.swift b/SwiftStyleGuide.swift index 5ef891dcd5..580242b872 100644 --- a/SwiftStyleGuide.swift +++ b/SwiftStyleGuide.swift @@ -136,6 +136,7 @@ private extension String { } +// swiftlint:disable identifier_name // Use one line per let in a guard with multiple lets. let maybe🌮 = restaurant.order("🥗") let maybe🥤 = restaurant.order("☕️")