Commit Graph
101 Commits
Author SHA1 Message Date
Facundo Menzella 6b5c430082 chore: Enable force_unwrapping for SwiftLint (#4820)
* enable force_unwrapping

* avoid force unwrap

* fixed all errors

* remove extra disable
2025-02-26 12:40:49 +01:00
Mark Villacampa 6f8e53ac40 Fix Xcode 16 warnings (#4334)
When compiling with Xcode 16, we started throwing a bunch of new warnings. Here's a list and explanation of each:

- The most common one was needing to explicitly restate the @unchecked Sendable conformance from a parent class.
- There were a couple switches where a new entry was added in tvOS but only for iOS 18, and needed to add it to make it exhaustive.
- There we a couple of places where we were adding a protocol conformance to a class from a different module e.g. extension DispatchTimeInterval: Comparable. This is considered unsafe now because the owners of the module could add the conformance later. Solved by removing the conformance which wasn't being used widely in the codebase, only in tests.
- Finally there was a class where we were capturing SKRequest and SKProductsRequest instances in a sendable block, but only to either log something, or use them as dictionary key, so I just made the classes Sendable (with the new @retroactice attribute because they belong to a different module)
2024-10-02 11:53:45 +02:00
Will Taylorandandy boedo 41246f18de Replace withCheckedThrowingContinuation Calls With withUnsafeThrowingContinuation (#4286)
* replace withCheckedThrowingContinuation calls with withUnsafeThrowingContinuation

* replaced instances of withCheckedContinuation -> withUnsafeContinuation

* update the links in comments to point to the issue not the PR

* linting

---------

Co-authored-by: andy boedo <andresboedo@gmail.com>
2024-09-18 11:54:00 -03:00
Will Taylor a4055375f6 retry requests with HTTP status 429 2024-07-23 12:41:16 -05:00
NachoSoto 2bcd0ce328 5.0.0 changes: support SK2 by default 2024-07-01 14:37:21 -05:00
NachoSoto 6e9840062f Paywalls: fix finding locales with different regions (#3633)
This was a regression introduced by #3605.

### Example:

Consider a paywall configuration with these locales: `en_US`, `de_DE`.
Running on a device with locale `en_IN` for example.

Before #3605, `PaywallData.localizedConfiguration` would have returned
the `en_US` localization. However, that change made it so that it could
no longer find a matching localization with a different region.

This fix improves the locale lookup so that if it doesn't find a
matching localization with both language AND region, it then tries to
look it up with only the language, before falling back to the default
localization.
2024-02-05 15:26:03 -08:00
NachoSoto 2f1a4e0be8 PaywallViewController: new initializer with Offering identifier (#3587)
Depends on #3586.

This will be used by the hybrid SDKs to be able to load paywalls with an
offering identifier.
2024-01-22 09:00:36 -08:00
32c63cdb46 [SK2] Add support for StoreKit Config files in SK2 (#3365)
When using StoreKit Config file, we do not have access to the
transaction history or subscription status server-side.

To work around this, we introduce a new `StoreKit2Receipt` type that
closely resembles the structure of an SK1 receipt and contains all the
transaction history and subscription status for the customer. It can be
sent in a single request to `/receipt`

---------

Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com>
Co-authored-by: Distiller <distiller@static.38.39.183.137.cyberlynk.net>
Co-authored-by: Distiller <distiller@static.38.23.40.89.cyberlynk.net>
Co-authored-by: Distiller <distiller@static.38.39.185.65.cyberlynk.net>
Co-authored-by: NachoSoto <NachoSoto@users.noreply.github.com>
2023-12-13 17:00:37 +01:00
NachoSoto d3866a28e4 visionOS: fix support for Xcode 15.1 beta 3 (#3409)
Apple changed `xrOS` to `visionOS` on the latest beta. Looks like this
was already supported in prior `Xcode 15.1` betas, so this doesn't break
compatibility with them.
2023-11-14 12:51:39 -08:00
NachoSoto ed80980d08 HTTPClient: verbose logs for request IDs (#3320)
See https://github.com/RevenueCat/khepri/pull/6798.
This can help debug issues as it helps us cross-reference requests on
our end.

### Changes:
- Logging `X-Request-ID` and `X-Amzn-Trace-ID` headers if present (only
in verbose mode)
- Logging these even for failed requests
- Also logging `request_handled_by_load_shedder` for failed requests
- To accomplish that. `HTTPURLResponse` now conforms to our
`HTTPResponseType`, this allows us to extract headers in an uniform way

### Example:
```
[network] DEBUG: ℹ️ API request completed: GET '/v1/subscribers/$RCAnonymousID%3Ad9dc4f3b33054cf28c0f2ec9da0f48d5' (304)
Request-ID: 'be534c50-5231-403b-be2f-7c077861751d'; Amzn-Trace-ID: 'Root=1-6532c6ca-7ef80f961ec9c4145484df13'
```
2023-10-25 20:24:23 -07:00
NachoSoto aba67120ce Catalyst: fixed a couple of Catalyst build warnings (#3120) 2023-09-14 18:28:11 -07:00
NachoSoto 5527f884af Codable: improved decoding errors (#3153) 2023-09-07 07:19:07 -07:00
NachoSoto c249244a13 ReceiptParser: fixed SPM build (#3144)
This was wrong in #3108. `ProcessInfo+Extensions` was added to the
`ReceiptParser` target, but not when building through SPM. This fixes
that.

We didn't catch this with the `spm-receipt-parser` CI job because that
does a release build.
2023-09-05 11:34:43 -07:00
NachoSoto b59582eed6 Integration Tests: workaround to not lose debug logs (#3108) 2023-08-30 06:10:33 -07:00
NachoSoto 581a048308 CustomerInfoManager: post transactions in parallel to POST receipts only once (#2954)
The existing implementation was posting them in sequence, which meant
that it was always going to make N requests.
With this new implementation we can ensure that only one request is made
for all transactions.
2023-08-11 15:39:44 -07:00
NachoSoto dab5035a19 visionOS: support for Xcode 15 beta 6 (#2989)
### Context:

`#if os(iOS)` no longer evaluates to `true` when compiling for
`visionOS`
Which means we need `|| os(xrOS)`.
But to support older Xcode we need `#if swift(>=5.9) && os(xrOS)`.
But, fun fact, short-circuiting doesn’t work in `Xcode 13`.
And we still need that to run iOS <15 tests (`FB12223404` /
`FB12805891`)

💩 

This works around that by defining our own `VISION_OS` flag.

### Changes:
- Added `VISION_OS` compile time flag to determine SDK
- Replaced `#if os(xrOS)` with `#if VISION_OS`
- Added flag to `Package.swift` (thanks @neonichu for helping with this)
2023-08-09 11:19:40 -07:00
NachoSoto ac185188ae StoreKit2TransactionListener: handle transactions asynchronously (#2910)
Currently when launching an app in SK1 mode, if there are multiple
transactions in the queue, we handle them all and begin operations in
parallel. If the cache keys are the same, those end up being a combined
POST receipt request.

However with `StoreKit 2`, because we were waiting to handle each one of
the `StoreKit.Transaction.updates` using `await`, we ended up posting
them all sequentially, instead of relying on `CallbackCache`.

This also adds a lot more coverage for `StoreKit2TransactionListener` to
verify this behavior.
2023-08-07 16:43:50 -07:00
NachoSoto 1de817f913 Logging: log entire cache key with verbose logs (#2888) 2023-07-27 09:16:49 +02:00
NachoSoto bb3a974eb8 Codable: debug log entire JSON when decoding fails (#2864)
This is a simple example, but on a larger JSON it becomes easier for us
to debug decoding the entire JSON rather than seeing only the part that
failed:
```
[codable] DEBUG: ℹ️ Encountered error when decoding JSON: {"e": "e3"}
[codable] ERROR: 😿‼️ Couldn't decode data from json, it was corrupted: Context(codingPath: [CodingKeys(stringValue: "e", intValue: nil)], debugDescription: "Cannot initialize E from invalid String value e3", underlyingError: nil)
```
2023-07-24 13:39:08 +02:00
NachoSoto f7e36fb993 Trusted Entitlements: add support for signing POST body (#2753)
### Changes:
- `HTTPClient` has a new `X-Post-Params-Hash` request header
- Replaced `HTTPRequest.Method.post`'s `Encodable` value with a more
specific `HTTPRequestBody` (like `HTTPResponseBody`)
- `HTTPRequestBody` allows overriding `contentForSignature` (it defaults
to `[]`) which then `Signing` uses for the hash
- Fixed `LogInOperation` not passing the `VerificationResult` to the
result `CustomerInfo`. This is captured in unit and integration tests
now.
- Add support for signing `POST` requests in `Path.logIn` and
`Path.postReceiptData`

Depends on https://github.com/RevenueCat/khepri/pull/6246
2023-07-13 07:45:16 -07:00
NachoSoto c5552fa453 IgnoreDecodeErrors: log decoding error (#2778) 2023-07-12 06:35:54 -07:00
NachoSoto b798114567 Refactor: extracted Collection.subscript(safe:) (#2779)
This is useful to have in extensions and not just in this test.
2023-07-11 10:22:19 -07:00
NachoSoto 3da1c50925 HTTPRequest.Path: escape appUserID (#2747)
We were escaping users for every (hopefully) request. This refactor
moves that responsibility to the path creation in `HTTPRequest.Path` to
ensure it's done consistently.

Additionally, this is required so that `SigningTests` produce a
consistent path (see #2746).
2023-07-05 15:41:51 -07:00
NachoSoto 26a33fc44c Add UInt32(littleEndian32Bits:) and UInt32.littleEndianData (#2713) 2023-06-27 04:54:54 -07:00
NachoSoto ed8d79dfdd Add Data.sha1 (#2696)
This also refactors the implementation to be able to share the same
code.

This algorithm will be used for some of the components in the new
signatures.
2023-06-23 10:45:57 -07:00
NachoSoto 0cadce9fb3 Add Sequence<AdditiveArithmetic>.sum() (#2694) 2023-06-23 07:35:03 -07:00
NachoSoto 1dc19f6750 Refactored Data.asString implementation (#2695) 2023-06-23 07:31:55 -07:00
NachoSoto f05fd88214 Diagnostics: new FileHandler for abstracting file operations (#2673)
Implemented as an `actor` to easily synchronize operations on a file.

Simple interface:
![Screenshot 2023-06-16 at 16 45
14](https://github.com/RevenueCat/purchases-ios/assets/685609/2f4850a3-f915-4330-a5e2-3abf9b37fb54)
2023-06-22 13:02:34 -07:00
NachoSoto a8a0a51fa7 xrOS: fixed runtime warning (#2691)
This was missing in #2683, these methods need to be invoked from the
`@MainActor`.
2023-06-22 08:17:52 -07:00
NachoSoto 4ab9d7334f xrOS: added support for new purchase(confirmIn:options:) method (#2683) 2023-06-22 07:31:03 -07:00
NachoSoto 6d60af2c42 Xcode 15: handle Locale.currencyCode deprecation (#2680) 2023-06-22 06:23:32 -07:00
NachoSoto 25c78f862a Removed unnecessary Strings.trimmedOrError (#2601) 2023-06-09 07:43:41 -07:00
NachoSoto 84102bc9af Dictionary.MergeStrategy: simplify implementation (#2587) 2023-06-06 08:07:08 -07:00
NachoSoto f7d6db204f Fixed Catalyst build with Xcode 15 beta 1 (#2586)
Fixes this error:
> Property cannot be more available than enclosing scope
> Enclosing scope requires availability of application extensions for
Mac Catalyst 13.1 or newer
2023-06-06 07:41:12 -07:00
NachoSoto 0044474911 Dictionary.MergeStrategy: fixed Xcode 15 compilation (#2582)
> Generic parameter 'Value' shadows generic parameter from outer scope
with the same name; this is an error in Swift 6
2023-06-05 16:45:43 -07:00
NachoSoto 599cfda5fb enableAdServicesAttributionTokenCollection: added integration test (#2551)
Because `AAAttribution.attributionToken()` isn't available on
simulators, we currently had no coverage for this entire part of the
codebase.
By mocking the token (on simulator + debug + integration tests only), we
can verify posting to the server works correctly.

As a follow-up, we can also verify that posting it as part of the
receipt with #2549 works correctly as well.
2023-05-26 07:18:51 -07:00
NachoSoto 33a11cb736 DefaultDecodable: moved to Misc/Codable/DefaultDecodable.swift (#2528) 2023-05-26 07:03:41 -07:00
NachoSoto 094e8f2aad Extracted and tested AsyncSequence.extractValues (#2538) 2023-05-25 07:14:58 -07:00
NachoSoto 789a0e5e4d PurchasedProductsFetcher: cache current entitlements (#2507)
To optimize potentially computing offline `CustomerInfo`, this caches
what's likely the most expensive part.
2023-05-19 18:08:03 -07:00
NachoSoto 22f85b3d74 DispatchTimeInterval: fixed Xcode 14.3 compilation (#2397) 2023-04-11 01:22:22 -07:00
339e13d5f1 Offline Entitlements: create CustomerInfo from offline entitlements (#2358)
Based on #2352.
Depends on #2351 and #2365.

---------

Co-authored-by: Andy Boedo <andresboedo@gmail.com>
Co-authored-by: Toni Rico <antonio.rico.diez@revenuecat.com>
2023-03-24 10:41:10 -07:00
NachoSoto 012a7a0b9d Added @DefaultDecodable.Now (#2372) 2023-03-24 07:41:43 -07:00
NachoSoto b495540308 Offline Entitlements: store ProductEntitlementMapping in cache (#2355)
## Changes:
- `DeviceCache` support for caching `ProductsEntitlementsResponse` as
well as "last updated" date
- Added `JSONEncoder.encode` helper matching `JSONDecoder.decode`
version, with an optional `logErrors` parameter
- `OfferingsManager. updateProductsEntitlementsCacheIfStale`
- `Purchases.updateAllCaches` also updates this new cache
- Added `OfferingsManager` / `DeviceCache` / `Purchases` tests
- Integration test for fetching mapping
2023-03-23 13:30:19 -07:00
NachoSoto d4ecc7b298 DispatchTimeInterval & Date: avoid 32-bit overflows, fix watchOS crashes (#2342)
Fixes #2338, [SDKONCALL-237], [SDK-2974].

`Int` is actually 32 bits in 32-bit platforms, like `watchOS` still is
(sort of).
This fixes 2 potential `Int` overflows: `Date.millisecondsSince1970` and
`DispatchTimeInterval` arithmetic using `nanoseconds`.

See also #2340.

[SDKONCALL-237]:
https://revenuecats.atlassian.net/browse/SDKONCALL-237?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2023-03-16 13:24:16 -07:00
Andy Boedo bd7f819af6 replace iff with if and only if (#2323)
Follow up to https://github.com/RevenueCat/purchases-ios/pull/2300,
replace `iff` with the more explicit `if and only if`
2023-03-03 17:06:04 -03:00
NachoSoto a157adfdee EntitlementInfo: add a grace period limit to outdated entitlements (#2288)
This prevents users from keeping an outdated active entitlement forever.
One could start a free trial then block all further requests from the
backend in order to keep that active.

When this check fails, a new warning log is generated:
> WARN: ⚠️ Entitlement is no longer active (expired 2023-02-12 18:15:11
+0000) and it's outside grace period window (last updated 2023-02-12
18:13:31 +0000)
2023-03-01 09:45:29 -08:00
NachoSoto da9ba397a3 Update CustomerInfo.requestDate from 304 responses (#2310)
### Purpose:

This is a prerequisite for #2288. The new grace period means that if we
didn't update the date from the cached responses, entitlements would
become stale and expired after 3 days.

### Changes:
- Added `HTTPResponseBody.copy(with newRequestDate:)` to be able to
modify responses from the header request date
- `CustomerInfo` implements this method to modify its request date
- Added `HTTPResponse.requestDate` to be able to keep track of the
request date from the server
- Added `VerificationResult.from(cache:response:)` to determine the most
restrictive verification result based on what's cached or checked from a
response
- Added `HTTPResponse.copy(with:)` to modify the verification result of
a response using the previous method
- `HTTPClient` now uses the most restrictive verification result
- `HTTPClient` updates request date from server responses or cached
responses (unless verification failed). This was the missing piece for
#2288.

### Tests:
- Verify that `ETagManager` does not use an ETag if verification was
previously not enabled
- Verify that `ETagManager` returns the request date from the server
when returning a cached response
- Verify that `HTTPClient` updates the request date from the server or
from a cached response
- Verify that `HTTPClient` does not update request date if verification
failed
- Tests for `HTTPResponse` request date parsing
- Test for `CustomerInfoResponseHandler` updating request date
- Tests for `CustomerInfo.copy(with newRequestDate:)`


### Other smaller changes:
- Moved `CustomerInfo.asData()` into `Encodable.asJSONEncodedData()`
- Renamed `requestTime` to `requestDate` everywhere for consistency
2023-03-01 09:24:32 -08:00
NachoSoto b707f8abc8 Signing: added request time & eTag to signature verification (#2309)
See https://github.com/RevenueCat/khepri/pull/5300
2023-02-27 09:07:23 -08:00
NachoSoto 88a78411f6 HTTPClient: added support for sending X-Nonce (#2214)
Fixes [CSDK-631].


[CSDK-631]:
https://revenuecats.atlassian.net/browse/CSDK-631?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2023-02-08 09:05:20 -08:00
NachoSoto b87dfe5b51 Added new ReceiptParser.fetchAndParseLocalReceipt (#2204)
Fixes [CSDK-622].

### Changes:
- Extracted `FileReader` to be able to use it in `ReceiptParser`
- Extracted `MockBundle` to be able to use it in `ReceiptParser`
- Added new `public` `ReceiptParser.fetchAndParseLocalReceipt()`, **only
to `ReceiptParser`** (this is not available through `RevenueCat`).
- Added tests for the new API
- Added new API to `ReceiptParserAPITester`

Initially I thought about moving `ReceiptFetcher` to the
`Receipt|Parser` target. Unfortunately that would require moving a whole
bunch of other dependencies (`OperationDispatcher`, etc, etc).
We have no easy way to create a third "Core" framework in which to place
these common dependencies, so that wouldn't be trivial.

This solution adds a much simpler version of `ReceiptFetcher` that
simply reads from `Bundle.appStoreReceiptURL`. As it's documented there,
under normal circumstances `SKReceiptRefreshRequest` is not required, so
this implementation should work for 99% of use cases, and so this way we
avoid increasing the.codebase complexity unnecessarily.

[CSDK-622]:
https://revenuecats.atlassian.net/browse/CSDK-622?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2023-01-17 12:35:56 -08:00