View in English

  • Apple Developer
    • Get Started

    Explore Get Started

    • Overview
    • Learn
    • Apple Developer Program

    Stay Updated

    • Latest News
    • Hello Developer
    • Platforms

    Explore Platforms

    • Apple Platforms
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    • App Store

    Featured

    • Design
    • Distribution
    • Games
    • Accessories
    • Web
    • Home
    • CarPlay
    • Technologies

    Explore Technologies

    • Overview
    • Xcode
    • Swift
    • SwiftUI

    Featured

    • Accessibility
    • App Intents
    • Apple Intelligence
    • Games
    • Machine Learning & AI
    • Security
    • Xcode Cloud
    • Community

    Explore Community

    • Overview
    • Meet with Apple events
    • Community-driven events
    • Developer Forums
    • Open Source

    Featured

    • WWDC
    • Swift Student Challenge
    • Developer Stories
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Centers
    • Documentation

    Explore Documentation

    • Documentation Library
    • Technology Overviews
    • Sample Code
    • Human Interface Guidelines
    • Videos

    Release Notes

    • Featured Updates
    • iOS
    • iPadOS
    • macOS
    • watchOS
    • visionOS
    • tvOS
    • Xcode
    • Downloads

    Explore Downloads

    • All Downloads
    • Operating Systems
    • Applications
    • Design Resources

    Featured

    • Xcode
    • TestFlight
    • Fonts
    • SF Symbols
    • Icon Composer
    • Support

    Explore Support

    • Overview
    • Help Guides
    • Developer Forums
    • Feedback Assistant
    • Contact Us

    Featured

    • Account Help
    • App Review Guidelines
    • App Store Connect Help
    • Upcoming Requirements
    • Agreements and Guidelines
    • System Status
  • Quick Links

    • Events
    • News
    • Forums
    • Sample Code
    • Videos
 

Vídeos

Abrir menu Fechar menu
  • Coleções
  • Todos os vídeos
  • Sobre

Mais vídeos

  • Sobre
  • Código
  • Meet StoreKit for SwiftUI

    Discover how you can use App Store product metadata and Xcode Previews to add in-app purchases to your app with just a few lines of code. Explore a new collection of UI components in StoreKit and learn how you can easily merchandise your products, present subscriptions in a way that helps users make informed decisions, and more.

    Recursos

    • Backyard Birds: Building an app with SwiftData and widgets
    • StoreKit views
    • Submit feedback
      • Vídeo HD
      • Vídeo SD

    Vídeos relacionados

    WWDC23

    • What’s new in App Store Connect
    • What’s new in App Store server APIs
    • What’s new in StoreKit 2 and StoreKit Testing in Xcode
    • What’s new in SwiftUI

    WWDC22

    • What's new in StoreKit testing

    WWDC21

    • Meet StoreKit 2

    WWDC20

    • Introducing StoreKit Testing in Xcode
  • Buscar neste vídeo...
    • 3:35 - Setting up the bird food shop view

      import SwiftUI
      
      struct BirdFoodShop: View {
       
        var body: some View {
          Text("Hello, world!") 
        }
        
      }
    • 3:42 - Import StoreKit to use the new merchandising views with SwiftUI

      import SwiftUI
      import StoreKit
      
      struct BirdFoodShop: View {
       
        var body: some View {
          Text("Hello, world!") 
        }
        
      }
    • 3:51 - Declaring a query to access the bird food data model

      import SwiftUI
      import StoreKit
      
      struct BirdFoodShop: View {
        @Query var birdFood: [BirdFood]
       
        var body: some View {
          Text("Hello, world!") 
        }
        
      }
    • 4:18 - Meet store view

      import SwiftUI
      import StoreKit
      
      struct BirdFoodShop: View {
        @Query var birdFood: [BirdFood]
       
        var body: some View {
          StoreView(ids: birdFood.productIDs) 
        }
        
      }
    • 4:51 - Adding decorative icons to the store view

      import SwiftUI
      import StoreKit
      
      struct BirdFoodShop: View {
        @Query var birdFood: [BirdFood]
      	
        var body: some View {
          StoreView(ids: birdFood.productIDs) { product in 
            BirdFoodProductIcon(productID: product.id)
          }
        }
       
      }
    • 6:38 - Creating a container for a custom store layout

      import SwiftUI
      import StoreKit
      
      struct BirdFoodShop: View {
        @Query var birdFood: [BirdFood]
       
        var body: some View {
          ScrollView {
            VStack(spacing: 10) {
              if let (birdFood, product) = birdFood.bestValue {
                
              }
            }
            .scrollClipDisabled()
          }
          .contentMargins(.horizontal, 20, for: .scrollContent)
          .scrollIndicators(.hidden)
          .frame(maxWidth: .infinity)
          .background(.background.secondary)  
        }
        
      }
    • 6:47 - Meet product view

      import SwiftUI
      import StoreKit
      
      struct BirdFoodShop: View {
        @Query var birdFood: [BirdFood]
       
        var body: some View {
          ScrollView {
            VStack(spacing: 10) {
              if let (birdFood, product) = birdFood.bestValue {
                ProductView(id: product.id)
              }
            }
            .scrollClipDisabled()
          }
          .contentMargins(.horizontal, 20, for: .scrollContent)
          .scrollIndicators(.hidden)
          .frame(maxWidth: .infinity)
          .background(.background.secondary)  
        }
        
      }
    • 7:03 - Adding a decorative icon to the product view

      import SwiftUI
      import StoreKit
      
      struct BirdFoodShop: View {
        @Query var birdFood: [BirdFood]
       
        var body: some View {
          ScrollView {
            VStack(spacing: 10) {
              if let (birdFood, product) = birdFood.bestValue {
                ProductView(id: product.id) {
                  BirdFoodProductIcon(
                    birdFood: birdFood,
                    quantity: product.quantity
                  )
                }
              }
            }
            .scrollClipDisabled()
          }
          .contentMargins(.horizontal, 20, for: .scrollContent)
          .scrollIndicators(.hidden)
          .frame(maxWidth: .infinity)
          .background(.background.secondary)  
        }
        
      }
    • 7:17 - Adding more containers to layout product views

      import SwiftUI
      import StoreKit
      
      struct BirdFoodShop: View {
        @Query var birdFood: [BirdFood]
       
        var body: some View {
          ScrollView {
            VStack(spacing: 10) {
              if let (birdFood, product) = birdFood.bestValue {
                ProductView(id: product.id) {
                  BirdFoodProductIcon(
                    birdFood: birdFood,
                    quantity: product.quantity
                  )
                }
                .padding()
                .background(.background.secondary, in: .rect(cornerRadius: 20))
              }
            }
            .scrollClipDisabled()
            Text("Other Bird Food")
              .font(.title3.weight(.medium))
              .frame(maxWidth: .infinity, alignment: .leading)
            ForEach(birdFood.premiumBirdFood) { birdFood in
              BirdFoodShopShelf(title: birdFood.name) {
                
              }
            }
          }
          .contentMargins(.horizontal, 20, for: .scrollContent)
          .scrollIndicators(.hidden)
          .frame(maxWidth: .infinity)
          .background(.background.secondary)  
        }
        
      }
    • 7:36 - Declaring product views for the remaining products

      import SwiftUI
      import StoreKit
      
      struct BirdFoodShop: View {
        @Query var birdFood: [BirdFood]
       
        var body: some View {
          ScrollView {
            VStack(spacing: 10) {
              if let (birdFood, product) = birdFood.bestValue {
                ProductView(id: product.id) {
                  BirdFoodProductIcon(
                    birdFood: birdFood,
                    quantity: product.quantity
                  )
                }
                .padding()
                .background(.background.secondary, in: .rect(cornerRadius: 20))
              }
            }
            .scrollClipDisabled()
            Text("Other Bird Food")
              .font(.title3.weight(.medium))
              .frame(maxWidth: .infinity, alignment: .leading)
            ForEach(birdFood.premiumBirdFood) { birdFood in
              BirdFoodShopShelf(title: birdFood.name) {
                ForEach(birdFood.orderedProducts) { product in
                  ProductView(id: product.id) {
                    BirdFoodProductIcon(
                      birdFood: birdFood,
                      quantity: product.quantity
                    )
                  }
                }
              }
            }
          }
          .contentMargins(.horizontal, 20, for: .scrollContent)
          .scrollIndicators(.hidden)
          .frame(maxWidth: .infinity)
          .background(.background.secondary)  
        }
        
      }
    • 7:50 - Choosing a product view style

      import SwiftUI
      import StoreKit
      
      struct BirdFoodShop: View {
        @Query var birdFood: [BirdFood]
       
        var body: some View {
          ScrollView {
            VStack(spacing: 10) {
              if let (birdFood, product) = birdFood.bestValue {
                ProductView(id: product.id) {
                  BirdFoodProductIcon(
                    birdFood: birdFood,
                    quantity: product.quantity
                  )
                }
                .padding()
                .background(.background.secondary, in: .rect(cornerRadius: 20))
                .padding()
                .productViewStyle(.large)
              }
            }
            .scrollClipDisabled()
            Text("Other Bird Food")
              .font(.title3.weight(.medium))
              .frame(maxWidth: .infinity, alignment: .leading)
            ForEach(birdFood.premiumBirdFood) { birdFood in
              BirdFoodShopShelf(title: birdFood.name) {
                ForEach(birdFood.orderedProducts) { product in
                  ProductView(id: product.id) {
                    BirdFoodProductIcon(
                      birdFood: birdFood,
                      quantity: product.quantity
                    )
                  }
                }
              }
            }
          }
          .contentMargins(.horizontal, 20, for: .scrollContent)
          .scrollIndicators(.hidden)
          .frame(maxWidth: .infinity)
          .background(.background.secondary)  
        }
        
      }
    • 8:25 - Styling the store view

      StoreView(ids: birdFood.productIDs) { product in 
          BirdFoodShopIcon(productID: product.id)
      }
      .productViewStyle(.compact)
    • 9:53 - Setting up the Backyard Birds pass shop

      import SwiftUI
      import StoreKit
      
      struct BackyardBirdsPassShop: View {
       
          var body: some View {
              Text("Hello, world!") 
          }
        
      }
    • 9:57 - Meet subscription store view

      import SwiftUI
      import StoreKit
      
      struct BackyardBirdsPassShop: View {
          @Environment(\.shopIDs.pass) var passGroupID
       
          var body: some View {
              SubscriptionStoreView(groupID: passGroupID)
          }
        
      }
    • 10:38 - Customizing the subscription store view's marketing content

      import SwiftUI
      import StoreKit
      
      struct BackyardBirdsPassShop: View {
          @Environment(\.shopIDs.pass) var passGroupID
       
          var body: some View {
              SubscriptionStoreView(groupID: passGroupID) {
                  PassMarketingContent() 
              }
          }
        
      }
    • 10:57 - Declaring a full height container background

      import SwiftUI
      import StoreKit
      
      struct BackyardBirdsPassShop: View {
          @Environment(\.shopIDs.pass) var passGroupID
       
          var body: some View {
              SubscriptionStoreView(groupID: passGroupID) {
                  PassMarketingContent()
                      .lightMarketingContentStyle()
                      .containerBackground(for: .subscriptionStoreFullHeight) {
                          SkyBackground()
                      }
              }
          }
        
      }
    • 11:21 - Configuring the control background style

      import SwiftUI
      import StoreKit
      
      struct BackyardBirdsPassShop: View {
          @Environment(\.shopIDs.pass) var passGroupID
       
          var body: some View {
              SubscriptionStoreView(groupID: passGroupID) {
                  PassMarketingContent()
                      .lightMarketingContentStyle()
                      .containerBackground(for: .subscriptionStoreFullHeight) {
                          SkyBackground()
                      }
              }
              .backgroundStyle(.clear)
          }
        
      }
    • 11:44 - Choosing a subscribe button label layout

      import SwiftUI
      import StoreKit
      
      struct BackyardBirdsPassShop: View {
          @Environment(\.shopIDs.pass) var passGroupID
       
          var body: some View {
              SubscriptionStoreView(groupID: passGroupID) {
                  PassMarketingContent()
                      .lightMarketingContentStyle()
                      .containerBackground(for: .subscriptionStoreFullHeight) {
                          SkyBackground()
                      }
              }
              .backgroundStyle(.clear)
              .subscriptionStoreButtonLabel(.multiline)
          }
        
      }
    • 12:01 - Choosing a subscription store picker item background

      import SwiftUI
      import StoreKit
      
      struct BackyardBirdsPassShop: View {
          @Environment(\.shopIDs.pass) var passGroupID
       
          var body: some View {
              SubscriptionStoreView(groupID: passGroupID) {
                  PassMarketingContent()
                      .lightMarketingContentStyle()
                      .containerBackground(for: .subscriptionStoreFullHeight) {
                          SkyBackground()
                      }
              }
              .backgroundStyle(.clear)
              .subscriptionStoreButtonLabel(.multiline)
              .subscriptionStorePicketItemBackground(.thinMaterial)
          }
        
      }
    • 12:20 - Declaring a redeem code button

      import SwiftUI
      import StoreKit
      
      struct BackyardBirdsPassShop: View {
          @Environment(\.shopIDs.pass) var passGroupID
       
          var body: some View {
              SubscriptionStoreView(groupID: passGroupID) {
                  PassMarketingContent()
                      .lightMarketingContentStyle()
                      .containerBackground(for: .subscriptionStoreFullHeight) {
                          SkyBackground()
                      }
              }
              .backgroundStyle(.clear)
              .subscriptionStoreButtonLabel(.multiline)
              .subscriptionStorePicketItemBackground(.thinMaterial)
              .storeButton(.visible, for: .redeemCode)
          }
        
      }
    • 14:10 - Reacting to completed purchases from descendant views

      BirdFoodShop()
          .onInAppPurchaseCompletion { (product: Product, result: Result<Product.PurchaseResult, Error>) in
              if case .success(.success(let transaction)) = result {
                  await BirdBrain.shared.process(transaction: transaction)
                  dismiss()
              }
          }
    • 15:43 - Reacting to in-app purchases starting

      BirdFoodShop()
          .onInAppPurchaseStart { (product: Product) in
              self.isPurchasing = true
          }
    • 16:57 - Declaring a subscription status dependency

      subscriptionStatusTask(for: passGroupID) { taskState in
          if let statuses = taskState.value {
              passStatus = await BirdBrain.shared.status(for: statuses)
          }            
      }
    • 19:37 - Unlocking non-consumables

      currentEntitlementTask(for: "com.example.id") { state in
          self.isPurchased = BirdBrain.shared.isPurchased(
              for: state.transaction
          )
      }
    • 20:52 - Declaring placeholder icons

      ProductView(id: ids.nutritionPelletBox) {
          BoxOfNutritionPelletsIcon()
      } placeholderIcon: {
          Circle()
      }
    • 21:25 - Using the promotional icon

      ProductView(
          id: ids.nutritionPelletBox,
          prefersPromotionalIcon: true
      ) {
          BoxOfNutritionPelletsIcon()
      }
    • 21:56 - Using the promotional icon border

      ProductView(id: ids.nutritionPelletBox) {
          BoxOfNutritionPelletsIcon()
              .productIconBorder()
      }
    • 23:02 - Composing standard styles to create custom styles

      struct SpinnerWhenLoadingStyle: ProductViewStyle {
      
          func makeBody(configuration: Configuration) -> some View {
              switch configuration.state {
              case .loading:
                  ProgressView()
                      .progressViewStyle(.circular)
              default:
                  ProductView(configuration)
              }
          }
      
      }
    • 23:44 - Applying custom styles to the product view

      ProductView(id: ids.nutritionPelletBox) {
          BoxOfNutritionPelletsIcon()
      }
      .productViewStyle(SpinnerWhenLoadingStyle())
    • 23:58 - Declaring custom styles

      struct BackyardBirdsStyle: ProductViewStyle {
      
        func makeBody(configuration: Configuration) -> some View {
          switch configuration.state {
            case .loading: // Handle loading state here
            case .failure(let error): // Handle failure state here
            case .unavailable: // Handle unavailabiltity here
            case .success(let product):
              HStack(spacing: 12) {
                configuration.icon
                VStack(alignment: .leading, spacing: 10) {
                  Text(product.displayName)
                  Button(product.displayPrice) {
                    configuration.purchase()
                  }
                  .bold()
                }
              }
              .backyardBirdsProductBackground()
          }
        }
      
      }
    • 26:44 - Declaring a dependency on products

      @State var productsState: Product.CollectionTaskState = .loading
      
      var body: some View {
          ZStack {
              switch productsState {
              case .loading:
                  BirdFoodShopLoadingView()
              case .failed(let error):
                  ContentUnavailableView(/* ... */)
              case .success(let products, let unavailableIDs):
                  if products.isEmpty {
                      ContentUnavailableView(/* ... */)
                  }
                  else {
                      BirdFoodShop(products: products)
                  }
              }
          }
          .storeProductsTask(for: productIDs) { state in
              self.productsState = state
          }
      }
    • 27:54 - Configuring the visibility of auxiliary buttons

      SubscriptionStoreView(groupID: passGroupID) {
         // ...
      }
      .storeButton(.visible, for: .redeemCode)
    • 29:56 - Adding a sign in action

      @State var presentingSignInSheet = false
      
      var body: some View {
          SubscriptionStoreView(groupID: passGroupID) {
              PassMarketingContent()
                  .containerBackground(for: .subscriptionStoreFullHeight) {
                      SkyBackground()
                  }
          }
          .subscriptionStoreSignInAction {
              presentingSignInSheet = true
          }
          .sheet(isPresented: $presentingSignInSheet) {
              SignInToBirdAccountView()
          }
      }
    • 30:32 - Displaying policies from the App Store metadata

      SubscriptionStoreView(groupID: passGroupID) {
          PassMarketingContent()
              .containerBackground(for: .subscriptionStoreFullHeight) {
                  SkyBackground()
              }
      }
      .subscriptionStorePolicyForegroundStyle(.white)
      .storeButton(.visible, for: .policies)
    • 31:22 - Choosing a control style

      SubscriptionStoreView(groupID: passGroupID) {
          PassMarketingContent()
              .containerBackground(for: .subscriptionStoreFullHeight) {
                  SkyBackground()
              }
      }
      .subscriptionStoreControlStyle(.buttons)
    • 32:28 - Declaring the layout of the subscribe button label

      SubscriptionStoreView(groupID: passGroupID) {
          PassMarketingContent()
              .containerBackground(for: .subscriptionStoreFullHeight) {
                  SkyBackground()
              }
      }
      .subscriptionStoreButtonLabel(.multiline)
    • 32:51 - Declaring the content of the subscribe button label

      SubscriptionStoreView(groupID: passGroupID) {
          PassMarketingContent()
              .containerBackground(for: .subscriptionStoreFullHeight) {
                  SkyBackground()
              }
      }
      .subscriptionStoreButtonLabel(.displayName)
    • 33:04 - Declaring the layout and content of the subscribe button label

      SubscriptionStoreView(groupID: passGroupID) {
          PassMarketingContent()
              .containerBackground(for: .subscriptionStoreFullHeight) {
                  SkyBackground()
              }
      }
      .subscriptionStoreButtonLabel(.multiline.displayName)
    • 33:44 - Decorating subscription plans

      SubscriptionStoreView(groupID: passGroupID) {
          PassMarketingContent()
              .containerBackground(for: .subscriptionStoreFullHeight) {
                  SkyBackground()
              }
      }
      .subscriptionStoreControlIcon { subscription, info in
          Group {
              let status = PassStatus(
                  levelOfService: info.groupLevel
              )
              switch status {
              case .premium:
                  Image(systemName: "bird")
              case .family:
                  Image(systemName: "person.3.sequence")
              default:
                  Image(systemName: "wallet.pass")
              }
          }
          .foregroundStyle(.tint)
          .symbolVariant(.fill)
      }
    • 34:07 - Decorating subscription plans with the button control style

      SubscriptionStoreView(groupID: passGroupID) {
          PassMarketingContent()
              .containerBackground(for: .subscriptionStoreFullHeight) {
                  SkyBackground()
              }
      }
      .subscriptionStoreControlIcon { subscription, info in
          Group {
              let status = PassStatus(
                  levelOfService: info.groupLevel
              )
              switch status {
              case .premium:
                  Image(systemName: "bird")
              case .family:
                  Image(systemName: "person.3.sequence")
              default:
                  Image(systemName: "wallet.pass")
              }
          }
         .symbolVariant(.fill)
      }
      .foregroundStyle(.white)
      .subscriptionStoreControlStyle(.buttons)
    • 34:14 - Adding a container background

      SubscriptionStoreView(groupID: passGroupID) {
          PassMarketingContent()
              .containerBackground(
                  .accent.gradient,
                  for: .subscriptionStore
              )
      }
    • 35:30 - Presenting upgrade offers

      SubscriptionStoreView(
          groupID: passGroupID,
          visibleRelationships: .upgrade
      ) {
          PremiumMarketingContent()
              .containerBackground(for: .subscriptionStoreFullHeight) {
                  SkyBackground()
              }
      }

Developer Footer

  • Vídeos
  • WWDC23
  • Meet StoreKit for SwiftUI
  • Open Menu Close Menu
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    • App Store
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • Icon Composer
    • SF Symbols
    Open Menu Close Menu
    • Accessibility
    • Accessories
    • Apple Intelligence
    • Audio & Video
    • Augmented Reality
    • Business
    • Design
    • Distribution
    • Education
    • Games
    • Health & Fitness
    • In-App Purchase
    • Localization
    • Maps & Location
    • Machine Learning & AI
    • Security
    • Safari & Web
    Open Menu Close Menu
    • Documentation
    • Downloads
    • Sample Code
    • Videos
    Open Menu Close Menu
    • Help Guides & Articles
    • Contact Us
    • Forums
    • Feedback & Bug Reporting
    • System Status
    Open Menu Close Menu
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles
    • Feedback Assistant
    Open Menu Close Menu
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program
    • Mini Apps Partner Program
    • News Partner Program
    • Video Partner Program
    • Security Bounty Program
    • Security Research Device Program
    Open Menu Close Menu
    • Meet with Apple
    • Apple Developer Centers
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Academies
    • WWDC
    Read the latest news.
    Get the Apple Developer app.
    Copyright © 2026 Apple Inc. All rights reserved.
    Terms of Use Privacy Policy Agreements and Guidelines