Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Looking forward to the first stable version #1

Open
Volodymyr-13 opened this issue Sep 2, 2024 · 20 comments
Open

Looking forward to the first stable version #1

Volodymyr-13 opened this issue Sep 2, 2024 · 20 comments

Comments

@Volodymyr-13
Copy link

Hey Russell!

I’m really excited about this—it’s just the lightweight and modern in-app package we’ve been looking for!

Currently, we’re using StoreHelper for subscriptions and non-consumable in-app purchases, and it’s been working well. However, your package aligns perfectly with our goals of adopting the latest technologies. We’re targeting iOS 16 (16.4 could be) as our minimum and always aim to use the latest Xcode.

This is exactly what we need, and we can’t wait to integrate it into our app. We’re eager to test it out, report any bugs, and provide feedback. Once it’s ready for a first release candidate, like version 0.1, we’ll definitely give it a try and contribute to its improvement if needed.

@russell-archer
Copy link
Owner

Hi Volodymyr!

So glad StoreHelper's been useful for you!

I'm getting towards "finishing" SKHelper - it's currently in a beta state.
Everything seems to work but no doubt there are some issues :-)

The main driver for doing this new package was to move away from creating my own views and reply on the new StoreKit Views. In particular, using the SubscriptionStoreView() view allowed me to get rid of quite a bit of code!

Also, I wanted to remove all dependencies on the old StoreKit1. This was required to support App Store promotions of in-app purchases. StoreKit2 now support using App Intents for promotions - much simpler. However, it looks like to use this you need to support iOS17 as a minimum. Not 100% sure at this point.

Thanks again for the kind words!

Russell

@Volodymyr-13
Copy link
Author

Volodymyr-13 commented Sep 5, 2024

Sounds great!
SubscriptionStoreView might work for some, but we use a lot of custom UI and A/B testing.

As for iOS 16, we’re considering dropping support, but it feels a bit early—maybe next year?

I’m keeping an eye on my app’s analytics. Once iOS 16 usage drops to 5% or less, we’ll likely drop it. Though, I already feel like dropping it now! :)

Screenshot Medium
Screenshot 1 Medium
Screenshot 2 Medium

@Volodymyr-13
Copy link
Author

Volodymyr-13 commented Sep 27, 2024

Hey Russell,

Support for consumable transactions requires iOS 18+

I have a question about this. Even though we're only using subscriptions and a lifetime (non-consumable) purchase, why is the minimum version set to iOS 18?

With this new release, a lot of users are still on iOS 17, and many will continue using it. Setting iOS 18 as the minimum seems too early, as it could exclude a significant number of devices that haven’t upgraded yet. I also know plenty of users who will never upgrade! :D

Screenshot 1

@russell-archer
Copy link
Owner

Hi Volodymyr, everything absolutely should build correctly for iOS 17.

iOS 18 is only required if you want to support consumables. The reason is that prior to iOS 18 StoreKit did not store transaction details for consumables and you had to track them yourself.

With iOS 18 consumable transactions can now be accessed via:

  • all sequence in Transaction
  • latest(for:) in Transaction
  • latestTransaction in Product

You also have to set SKIncludeConsumableInAppPurchaseHistory = YES in your app's info.plist file.

So, if you were to use SKHelper to manage consumables with iOS 17 I would expect everything to build and run OK. But SKHelper would not find any details for consumable transactions.

@Volodymyr-13
Copy link
Author

Volodymyr-13 commented Sep 29, 2024

I see, that’s good. One more question: we’re looking for the ability to track subscription statuses, such as when a user starts a trial and then the next states, like unsubscribed or converted to paid, and so on. Would this be easily possible with iOS 17 as the minimum target(mean any new API's now available)?

@russell-archer
Copy link
Owner

Yes, SKHelper tracks subscription status changes. Also, it will "catch up" with any changes that happen while the app's in the background when it's activated again. At the moment I'm not re-broadcasting those changes, but it would be a trvial change to make if that would be useful for you.

@russell-archer
Copy link
Owner

I've just implemented an optional closure that can be passed to the SKHelper initializer so that you can get "live" updates on subscription changes:

@main
struct SKHelperDemoApp: App {
    @State private var store = SKHelper() { productId, transactionId, renewalState, hasExpired in
        
        let newState = renewalState.localizedDescription.lowercased(with: Locale.current)
        print("Subscription \(productId) now \"\(newState)\" with transaction id \(transactionId). The subscription has \(hasExpired ? "expired" : "not expired").")
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(store)
        }
    }
}

Let me know if this is the sort of thing you were looking for or not!

@Volodymyr-13
Copy link
Author

I'm not sure if I should post bugs here, but I just created a simple project, added SKHelper and tried to build it in release mode and got a lot of error like:

SKHelperLog.swift:72:58 Cannot find 'storeLog' in scope

@russell-archer
Copy link
Owner

russell-archer commented Oct 5, 2024 via email

@russell-archer
Copy link
Owner

russell-archer commented Oct 6, 2024

Fixed the logging issue. Also added a new onSubscriptionChange(onChange:) view modifier so do you can things like this:

// Example usage:
SKHelperSubscriptionStoreView()
    .onSubscriptionChange() { productId, transactionId, renewalState, hasExpired  in
        print("The status of subscription \(productId) changed to \(renewalState.localizedDescription)")
    }

@Volodymyr-13
Copy link
Author

Volodymyr-13 commented Oct 6, 2024

Thanks for the quick fix and the updates! Now it works(probably a new minor release would be pushed soon? Since, I'm using version currently 1.1.2 for SPM)

I’m looking forward to easily tracking users who have subscribed, canceled, or converted to a paid subscription state. That would be great for testing! We’re preparing a new class for this to try on our RC app version for future tests. I'll keep you posted as soon as we get some results or bugs/problems :D

@Volodymyr-13
Copy link
Author

@russell-archer

Hey Russell,

We conducted a partial release with version 1.1.4 and encountered issues where the Premium status unexpectedly reverted to Free for some users.

It seems this issue may be related to the cache, which we had disabled—perhaps I should have clarified this earlier...

That said, isn’t cache generally necessary? Why is there an option to disable it? Is it intended for situations where the Premium state is managed externally, rather than by the app itself?
In our case, we can’t think of any scenario like that; we just want a library that handles everything autonomously without us thinking about that :D

@russell-archer
Copy link
Owner

russell-archer commented Nov 3, 2024 via email

@russell-archer
Copy link
Owner

russell-archer commented Nov 3, 2024 via email

@Volodymyr-13
Copy link
Author

Volodymyr-13 commented Nov 3, 2024

I meant to say that I will have a look for any issues I can see with the cache when it’s disabled!

@russell-archer Thanks!
All in all, I get it and will definitely enable the cache for now!

This is what the cache is for.

Users who reported this issue were able to restore their premium version using Restore Purchases, so I think it's simply due to a lack of internet connection when running the app.

@Volodymyr-13
Copy link
Author

Here’s an edited version of your message:

@russell-archer, with cache enabled and the updated release, I haven’t encountered any issues so far, but I’m still in the testing phase.

I do have a question: after initializing SKHelper, I noticed the following log output:

Starting request for localized product information from the App Store
Configuration success
Starting request for localized product information from the App Store
Configuration success
Localized product information successfully retrieved from the App Store
Localized product information successfully retrieved from the App Store

Our main goal is to use a public library for handling all in-app purchases within our app. However, we are not using the library exactly as intended; while we leverage its core functionality, we use custom product models, so we don’t need SKHelper as an Observable.

Given this, we have a couple of questions:

• Is it possible to make the requestProducts method optional?
• Could SKHelper be made say - a bit more modular, allowing us to use it for in-app purchases without SwiftUI and with our own implementation?

@russell-archer
Copy link
Owner

russell-archer commented Nov 5, 2024

Re your log output. That's not what I see. The call to requestProducts() is made in the SKHelper initializer, so somehow you're creating SKHelper twice. or maybe you're calling requestProducts() separately as well?

I could certainly create a convenience initializer and not call requestProducts() in that case. However, requestProducts() also builds the cache of entitlements.

Re making SKHelper more modular. I know what you mean, but it kind of goes the opposite way in which I'd intended. With StoreHelper (which was more modular I think) it had become overly complex. With SKHelper my intention was to have the minimum of custom StoreKit code and rely as much as possible on SwiftUI StoreKit Views. So I was really not intending to support UIKit. This is, of course, because I created SKHelper to fit my needs :-)

@Volodymyr-13
Copy link
Author

or maybe you're calling requestProducts() separately as well?

Yes. So if it used as part of the cache this might not work well.. all right, so perhaps this might not be a good idea if disabling it..

SKHelper more modular

Well, our goal is to always use new things and use the latest technologies. Our application is made entirely on SwiftUI, the minimum version is iOS17.
However, we use our own views, but with SKHelper it's easier for us to get this working right away.

@Volodymyr-13
Copy link
Author

Volodymyr-13 commented Nov 7, 2024

@russell-archer
Hey Russell, the cache seems to work well for now, though it feels a bit too "aggressive".

I tested with the debug build using a sandbox account with purchased a non-consumable in-app purchase for premium access. So the app was Premium state.
Next, I installed the App Store build over it, which doesn’t have any active subscriptions or non-consumable purchases, the app stayed in the Premium state(too bad I can't check any logs about this in this release build from App Store). We’re not caching user states ourselves; we’re fully relying on SKHelper for this. So this is like a bit wrong.. that some user was in premium, then using another user without premium.. still keeps app premium.
Final test here, just to confirm that my App Store acc is clear of Premium - I deleted app and reinstalled it again from the App Store, as result it starts in the Free state, as expected.

Another test, when cached Premium state, then even after logging out of the actual App Store account - premium access remains... this I call like too aggressive) or maybe I'm wrong, don't have that experience.. perhaps its ok to keep this state for a several days..

So I’m wondering, how long this state persists? What would happen in cases of billing issues, subscription cancellations, or account logouts in terms of days or how this is managed..?

UPD:
Some other users are now telling me the opposite: when the app has been offline for too long, now their Premium is gone...

@Volodymyr-13
Copy link
Author

@russell-archer And what about logs, can I disable them? So far I can see a lot of:
"Starting request for localized product information from the App Store"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants