2024 App ServicesApp Store, Distribution & Marketing
WWDC24 · 33 min · App Services / App Store, Distribution & Marketing
Implement App Store Offers
Learn how to engage customers with App Store Offers using App Store Connect, as well as the latest StoreKit features and APIs. Discover how you can set up win-back offers (a new way to re-engage previous subscribers) and generate offer codes for Mac apps. And find out how to test offers in sandbox and Xcode to make sure they work smoothly.
Watch at developer.apple.com ↗Chapters
Code shown on screen · 4 snippets
Present offer code redemption sheet on macOS - SwiftUI API
// Present offer code redemption sheet on macOS - SwiftUI API
import SwiftUI
import StoreKit
struct MyView: View {
var showOfferCodeRedemption: Bool = false
var body: some View {
Button("Redeem Code") {
showOfferCodeRedemption = true
}
.offerCodeRedemption(isPresented: $showOfferCodeRedemption) { result in
// Handle result
}
}
} Choose preferred offer in a SubscriptionStoreView
// Choose preferred offer in a SubscriptionStoreView
import SwiftUI
import StoreKit
struct MyView: View {
let groupID: String
var body: some View {
SubscriptionStoreView(groupID: groupID)
.preferredSubscriptionOffer { product, subscription, eligibleOffers in
let freeTrialOffer = eligibleOffers
.filter { $0.paymentMode == .freeTrial }
.max { lhs, rhs in
lhs.period.value < rhs.period.value
}
return freeTrialOffer ?? eligibleOffers.first
}
}
} Check subscription entitlement and offer eligibility
// Check subscription entitlement and offer eligibility
import StoreKit
func shouldShowMerchandising(
for groupID: String,
productIDs: [Product.ID]
) async throws -> MerchandisingVisibility {
// Get subscription status
let statuses = try await Product.SubscriptionInfo.status(for: groupID)
// Check if the customer is already entitled to the subscription
let entitlement = SubscriptionEntitlement(for: statuses)
if entitlement.autoRenewalEnabled {
return .hidden
}
// Check for offers to show in merchandising UI
let products = try await Product.products(for: productIDs)
let isEligibleForIntroOffer = await Product.SubscriptionInfo.isEligibleForIntroOffer(for: groupID)
if isEligibleForIntroOffer {
let subscriptions = products.map {
($0, $0.subscription?.introductoryOffer)
}
return .visible(subscriptions)
}
// Check for eligible win-back offers
let purchasedStatus = statuses.first {
$0.transaction.unsafePayloadValue.ownershipType == .purchased
}
let renewalInfo = try purchasedStatus?.renewalInfo.payloadValue
let bestWinBackOfferID = renewalInfo?.eligibleWinBackOfferIDs.first
// Return the product with the offer if there is one
if let bestWinBackOfferID {
let subscriptions: [(Product, Product.SubscriptionOffer?)] = products.map {
let winBackOffer = $0.subscription?.winBackOffers.first {
$0.id == bestWinBackOfferID
}
return ($0, winBackOffer)
}
return .visible(subscriptions)
}
// Only return the product if there is no offer
return .visible(products.map { ($0, nil) })
}
struct SubscriptionEntitlement {
let isEntitled: Bool
let autoRenewalEnabled: Bool
init(for statuses: [Product.SubscriptionInfo.Status]) {
let entitledStatuses = statuses.filter {
$0.state == .subscribed || $0.state == .inBillingRetryPeriod || $0.state == .inGracePeriod
}
isEntitled = !entitledStatuses.isEmpty
autoRenewalEnabled = entitledStatuses.contains {
$0.renewalInfo.unsafePayloadValue.willAutoRenew
}
}
}
enum MerchandisingVisibility {
case hidden
case visible([(Product, Product.SubscriptionOffer?)])
} Add a win-back offer to a purchase
// Add a win-back offer to a purchase
import StoreKit
func purchase(
_ product: Product,
with offer: Product.SubscriptionOffer?
) async throws {
// Prepare the purchase options
var purchaseOptions: Set<Product.PurchaseOption> = []
// Add win-back offer to the purchase
if let offer, offer.type == .winBack {
purchaseOptions.insert(.winBackOffer(offer))
}
// Make the purchase
try await product.purchase(options: purchaseOptions)
} Resources
- Testing win-back offers in the sandbox environment
- Merchandising win-back offers in your app
- Supporting win-back offers in your app
- Testing win-back offers in Xcode
- PurchaseIntent
- Supporting offer codes in your app
- offer
- Forum: App Store Distribution & Marketing
- Message
- StoreKit views
- Setting up StoreKit Testing in Xcode
- Submit feedback
- Generating a signature for promotional offers
Related sessions
-
24 min -
23 min -
32 min -
19 min -
37 min