Commit Graph
10 Commits
Author SHA1 Message Date
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 4ab9d7334f xrOS: added support for new purchase(confirmIn:options:) method (#2683) 2023-06-22 07:31:03 -07:00
NachoSoto b93b082690 Fixed Xcode 13.2.x / Swift 5.5 compatibility (#1994)
Fixes [SDKONCALL-156].

This will be covered by #1990.

[SDKONCALL-156]:
https://revenuecats.atlassian.net/browse/SDKONCALL-156?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2022-10-24 14:36:05 -07:00
NachoSoto 46ac7598fc Abstracted conversion from async to completion-block APIs (#1943)
Created `Async` with some shared methods that make this conversion
simpler in a type-safe way.

Helps turn this:
```swift
_ = Task<Void, Never> {
    do {
        completion(.success(try await f()))
    } catch {
        completion(.failure(ErrorUtils.error())
    }
}
```

Into this:
```swift
Async.call(with: completion) {
    try await f()
}
```
2022-09-28 08:40:59 -07:00
NachoSoto d4ea390970 Introduced type-safe PurchasesError errors and fixed some incorrect returned error types (#1879)
This is the last change from what was started with #1437. That PR introduced a few private error types, but for the errors returned in the public `APIs` we were still passing around `Error`s everywhere, which didn't provide any compile-time guarantees.

### Goals:
- Ensure that public APIs *only* return `ErrorCode`s wrapped with the additional `userInfo`, and not accidentally return other types like `SKError`, `BackendError`, `ErrorCode` directly, etc.
- Guarantee type-safety when passing errors around within our internal implementations instead of assuming certain thrown errors are of a particular type.

### Changes:
- Created `PurchasesError` for wrapping an `ErrorCode` with the additional `userInfo`.
- Changed returned errors from `Error` to `NSError`. This is a change in the API, but the new type is a more specific `Error` so it shouldn't lead to any changes in client apps.
- Because `PurchasesError` isn't implicitly convertible to `NSError`, this ensures that we don't return those values directly, and instead use `PurchasesError.asPublicError`.
- Changed `ErrorCodeConvertible` to a now more precise `PurchasesErrorConvertible`.

Thanks to this new type-safety this PR fixes at least 3 bugs where we were returning private errors instead of `ErrorCode`s.

### Testing:

Thanks to #1871 we know that the returned errors are still convertible to `ErrorCode` so users can `switch` over them.
2022-09-05 13:46:07 -07:00
NachoSoto 0bff3f65fe Sendable support (#1795)
Fixes [CSDK-379] and https://github.com/RevenueCat/purchases-ios/issues/1041#issuecomment-1206445939

[CSDK-379]: https://revenuecats.atlassian.net/browse/CSDK-379?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

### Main changes:
- `Sendable` conformances to all types that are thread-safe
- `@unchecked Sendable` for types that the compiler can't enforce, but with documentation as to why
- Made classes that aren't mocked `final` (note that this doesn't change the API, because none of these were `open` to begin with)
- Made some classes thread-safe that weren't (like `DeviceCache`)

For the non-`final` `class`es, I've actually managed to compile the SDK with all of them as `final` and `Sendable`, to make sure that making them `@unchecked Sendable` didn't hide any other issues.

### Future improvements:
- https://twitter.com/nachosoto/status/1557139992056500224?s=21&t=arWdvEzTIFANBvwqQ0vPiA
- https://twitter.com/nachosoto/status/1557141944777592832?s=21&t=arWdvEzTIFANBvwqQ0vPiA
- https://twitter.com/nachosoto/status/1557143374922059776?s=21&t=arWdvEzTIFANBvwqQ0vPiA

### Depends on:
- #1794
- #1804
- #1806
- #1807
- #1808
- #1813
- #1822
- #1823
- #1824
- #1825
- #1826
2022-08-31 13:30:38 -07:00
NachoSoto c531814dc6 Purchases.customerInfo(): added overload with a new CacheFetchPolicy (#1608)
Fixes [CF-19].
This allows specifying `CustomerInfoManager` fetching behavior, both as a public API for users, and also internally.
Some future uses of `CustomerInfoManager` might want to ensure that we don't rely on cached data, for example.

Thanks to the `APITester` we know that the existing methods still work, this is purely an additive change.

This is the core of the change:
```swift
/// Specifies the behavior for a caching API.
@objc(RCCacheFetchPolicy)
public enum CacheFetchPolicy: Int {
    /// Returns values from the cache, or throws an error if not available.
    case fromCacheOnly

    /// Always fetch the most up-to-date data.
    case fetchCurrent

    /// Returns the cached data if available and not stale, or fetches up-to-date data.
    case notStaleCachedOrFetched

    /// Default behavior: returns the cached data if available (even if stale), or fetches up-to-date data.
    case cachedOrFetched

    /// Default ``CacheFetchPolicy`` behavior.
    public static let `default`: Self = .cachedOrFetched
}
```

The new `CustomerInfoManagerGetCustomerInfoTests` check all the different combinations of fetching behavior.
I've taken some of the existing tests and refactored them to be `async`, as well as added a bunch more tests.

> **Note**: 
There is also one big hidden bug fix:

`Purchases.getCustomerInfo` now calls `BackendError.asPurchasesError`, so we don't end up returning an internal error type.
This was missed because we always return generic `Error` types, so we have no way to know at compile time that we only return `ErrorCode`s.

[CF-19]: https://revenuecats.atlassian.net/browse/CF-19?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2022-05-31 13:04:16 -07:00
NachoSoto 67f7575701 ManageSubscriptionsHelper: fixed discrepancy between SystemInfo.isAppleSubscription(managementURL:) and SystemInfo.appleSubscriptionsURL (#1607)
Fixes [CF-684] and #1594.

`SystemInfo.appleSubscriptionsURL` was a short URL that didn't pass the `SystemInfo.isAppleSubscription(managementURL:)` test. This is fixed and covered by a test now.

### Other changes:
- Made `SystemInfo.isAppleSubscription(managementURL:)` and `SystemInfo.appleSubscriptionsURL` static since they don't need any properties in `SystemInfo`.
- Forced-unwrapped `SystemInfo.appleSubscriptionsURL` to remove unnecessary error handling. This is safe because the URL is provided statically at compile time. Per the docs "This initializer returns nil if the string doesn’t represent a valid URL. For example, an empty string or one containing characters that are illegal in a URL produces nil.". This is known at compile time, and covered by tests, so it could never return `nil`.
- Combined redundant tests in `ManageSubscriptionsHelper`. They were all doing exactly the same for both platforms.

[CF-684]: https://revenuecats.atlassian.net/browse/CF-684?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2022-05-26 11:20:46 -07:00
NachoSoto 34cd8b4a03 Extracted CurrentUserProvider protocol to simplify dependency injection (#1462)
A lot of our classes depended on the mutable `IdentityManager`, when in reality they only needed a read-only `currentAppUserID`.
This PR changes the requirement, with the following advantages:

- Ensure all these classes (`AttributionPoster`, `PurchasesOrchestrator`, `TrialOrIntroPriceEligibiltyChecker`, `BeginRefundRequestHelper`, `ManageSubscriptionsHelper`) cannot logIn / logOut, only read the current user
- Simplify tests by only requiring a smaller type
- Simplifying instantiation of these types outside the normal `Purchases` initialization.

I've also created `MockCurrentUserProvider`, but changed the existing `MockIdentityManager` so that calls to `logIn`/`logOut` just fail. These aren't mocked, and they wouldn't have an impact on the mocked readable values, so calling them is most likely unexpected and should make the test fail.
2022-04-08 15:39:34 -07:00
aboedo f5697de730 renamed Purchases folder to Sources (#1416)
* renamed `Purchases` folder to `Sources`

* temporarily enabling installation test jobs

* re-disabling installation test jobs
2022-03-28 11:53:24 -03:00