Hi,
I am trying to implement the new view modifier to show a subscription view for AppleMusic. In the #wwdc-10294 (Meet MusicKit for Swift) talk, the use case is very clear and it makes sense. We have an album and if the user wants to play it without an active subscription, we show toggle the binding that makes the view appear.
However, using a single boolean removes flexibility so I am trying to figure out the following:
I have two buttons: "Play" and "Add", which will either play the album as in the talk or add it to the user library using the Apple MusicAPI. In both cases if there is no subscription, I'd like to show the offer.
This means I will have two different MusicSubscriptionOffer.Options
configuration, one where the messageIdentifier is .playMusic
and the other one .addMusic.
This also means that I need to use two viewModifiers .musicSubscriptionOffer
resulting in two different bindings (otherwise I assume both view controllers will show when the binding changes?).
Worst case is if I show a list of songs and want to have options for .playMusic
and .addMusic
for each song, then I need a dynamic amount of bindings for the view modifier. Would I need to handle this like list bindings?
Ideally I'd like an API that allows me to pass in an enum and provide options based on the enum cases like other SwiftUI API (like .sheet).
This would allow for dynamic options and only requiring one single .musicSubscriptionOffer.
Is there any solid solution for this at the moment?
Any insights would be helpful, thanks!
Hello @sharedRoutine,
Thank you very much for your question about the musicSubscriptionOffer() modifier.
You can definitely achieve what you want with a single musicSubscriptionOffer() modifier.
Here's how it might look like:
struct AlbumActionsRow: View {
// MARK: - State
let album: Album
/// The Apple Music subscription of the current user.
@State private var musicSubscription: MusicSubscription?
/// The state controlling whether a subscription offer is displayed.
@State private var isShowingSubscriptionOffer = false
/// The options for the Apple Music subscription offer.
@State private var subscriptionOfferOptions = MusicSubscriptionOffer.Options(
affiliateToken: "<affiliate_token>",
campaignToken: "<campaign_token>"
)
// MARK: - View
var body: some View {
HStack {
Button(action: handlePlayButtonSelected) {
Text("Play")
}
Button(action: handleAddButtonSelected) {
Text("Add")
}
}
.task {
for await subscription in MusicSubscription.subscriptionUpdates {
musicSubscription = subscription
}
}
.musicSubscriptionOffer(isPresented: $isShowingSubscriptionOffer, options: subscriptionOfferOptions)
}
// MARK: - Button handlers
private func handlePlayButtonSelected() {
if musicSubscription?.canPlayCatalogContent == true {
play()
}
else if musicSubscription?.canBecomeSubscriber == true {
subscriptionOfferOptions.messageIdentifier = .playMusic
subscriptionOfferOptions.itemID = album.id
print("Presenting music subscription offer with options: \(subscriptionOfferOptions).")
isShowingSubscriptionOffer = true
}
}
private func handleAddButtonSelected() {
if musicSubscription?.hasCloudLibraryEnabled == true {
addToLibrary()
}
else if musicSubscription?.canPlayCatalogContent == false && musicSubscription?.canBecomeSubscriber == true {
subscriptionOfferOptions.messageIdentifier = .addMusic
subscriptionOfferOptions.itemID = nil
print("Presenting music subscription offer with options: \(subscriptionOfferOptions).")
isShowingSubscriptionOffer = true
}
}
private func play() {
…
}
private func addToLibrary() {
…
}
}
When I run this code and tap the Play and Add buttons, I see logs like these in the console:
Presenting music subscription offer with options: MusicSubscriptionOffer.Options(messageIdentifier: .playMusic, itemID: 617154241, affiliateToken: <affiliate_token>, campaignToken: <campaign_token>).
[...]
Presenting music subscription offer with options: MusicSubscriptionOffer.Options(messageIdentifier: .addMusic, affiliateToken: <affiliate_token>, campaignToken: <campaign_token>).
I hope this helps.
Best regards,