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
 

Vidéos

Ouvrir le menu Fermer le menu
  • Collections
  • Toutes les vidéos
  • À propos

Plus de vidéos

  • À propos
  • Code
  • Inspectors in SwiftUI: Discover the details

    Meet Inspectors — a structural API that can help bring a new level of detail to your apps. We'll take you through the fundamentals of the API and show you how to adopt it. Learn about the latest updates to sheet presentation customizations and find out how you can combine the two to create perfect presentation experiences.

    Chapitres

    • 0:32 - Inspector
    • 3:27 - 🍎🍐🍋🍒
    • 9:11 - 🍏
    • 9:37 - Presentation customizations

    Ressources

      • Vidéo HD
      • Vidéo SD

    Vidéos connexes

    WWDC23

    • What’s new in SwiftUI
  • Rechercher dans cette vidéo…
    • 3:35 - Sample models and views

      // Copy+Paste the below into an Xcode project to support building and running the session's code snippets
      
      import SwiftUI
      
      @main
      struct SwiftUIInspectors: App {
          var body: some Scene {
              WindowGroup {
                  ContentView()
                      .environmentObject(AnimalStore())
              }
          }
      }
      
      struct AnimalInspectorForm: View {
          var animal: Binding<Animal>?
          @EnvironmentObject private var animalStore: AnimalStore
      
          var body: some View {
              Form {
                  if let animal = animal {
                      SelectedAnimalInspector(animal: animal, animalStore: animalStore)
                  } else {
                      ContentUnavailableView {
                          Image(systemName: "magnifyingglass.circle")
                      } description: {
                          Text("Select a suspect to inspect")
                      } actions: {
                          Text("Fill out details from the interview")
                      }
                  }
              }
              #if os(iOS)
              .navigationBarTitleDisplayMode(.inline)
              #endif
          }
      }
      
      struct SelectedAnimalInspector: View {
          @Binding var animal: Animal
          @ObservedObject var animalStore: AnimalStore
      
          var body: some View {
                  Section("Identity") {
                      TextField("Name", text: $animal.name)
                      Picker("Paw Size", selection: $animal.pawSize) {
                          Text("Small").tag(PawSize.small)
                          Text("Medium").tag(PawSize.medium)
                          Text("Large").tag(PawSize.large)
                      }
                      FruitList(selectedFruits: $animal.favoriteFruits, fruits: allFruits)
                  }
      
                  Section {
                      TextField(text: animalStore(\.alibi, for: animal), prompt: Text("What was \(animal.name) doing at the time of nibbling?"), axis: .vertical) {
                          Text("Alibi")
                      }
                      .lineLimit(4, reservesSpace: true)
                      if let schedule = Binding(animalStore(\.sleepSchedule, for: animal)) {
                          SleepScheduleView(schedule: schedule)
                      } else {
                          Button("Add Sleep Schedule") {
                              animalStore.write(\.sleepSchedule, value: Animal.Storage.newSleepSchedule, for: animal)
                          }
                      }
                      Slider(
                          value: animalStore(\.suspiciousLevel, for: animal), in: 0...1) {
                              Text("Suspicion Level")
                          } minimumValueLabel: {
                              Image(systemName: "questionmark")
                          } maximumValueLabel: {
                              Image(systemName: "exclamationmark.3")
                          }
                  } header: {
                      Text("Interview")
                  }
                  .presentationDetents([.medium, .large])
          }
      }
      
      private struct FruitList: View {
          @Binding var selectedFruits: [Fruit]
          var fruits: [Fruit]
      
          var body: some View {
              Section("Favorite Fruits") {
                      ForEach(allFruits) { fruit in
                          Toggle(isOn: .init(get: {
                              selectedFruits.contains(fruit)
                          }, set: { newValue in
                              if newValue && !selectedFruits.contains(fruit) {
                                  selectedFruits.append(fruit)
                              } else {
                                  _ = selectedFruits.firstIndex(of: fruit).map {
                                      selectedFruits.remove(at: $0)
                                  }
                              }
                          })) {
                              HStack {
                                  FruitImage(fruit: fruit, size: .init(width: 40, height: 40), bordered: true)
                                  Text(fruit.name).font(.body)
                              }
                          }
                      }
              }
          }
      
          @ViewBuilder
          private func selectionBackground(isSelected: Bool) -> some View {
              if isSelected {
                  RoundedRectangle(cornerRadius: 2).inset(by: -2)
                      .fill(.selection)
              }
          }
      }
      
      private struct SleepScheduleView: View {
          @Binding var schedule: Animal.Storage.SleepSchedule
          var body: some View {
              DatePicker(selection: .init(get: {
                  Calendar.current.date(from: schedule.sleepTime) ?? Date()
              }, set: { newDate in
                  schedule.sleepTime = Calendar.current.dateComponents([.hour, .minute], from: newDate)
              }), displayedComponents: [.hourAndMinute]) {
                  Text("Sleep at: ")
              }
      
              DatePicker(selection: .init(get: {
                  Calendar.current.date(from: schedule.wakeTime) ?? Date()
              }, set: { newDate in
                  schedule.wakeTime = Calendar.current.dateComponents([.hour, .minute], from: newDate)
              }), displayedComponents: [.hourAndMinute]) {
                  Text("Awake at: ")
              }
          }
      }
      
      struct AppState {
          var selection: String? = "Snail"
          var animals: [Animal] = allAnimals
          var inspectorPresented: Bool = true
          var inspectorWidth: CGFloat = 270
          var cornerRadius: CGFloat? = nil
      }
      
      extension Binding where Value == AppState {
          func binding() -> Binding<Animal>? {
              self.projectedValue.animals.first {
                  $0.wrappedValue.id == self.selection.wrappedValue
              }
          }
      }
      
      extension Animal {
          struct Storage: Codable {
              var alibi: String = ""
              var sleepSchedule: SleepSchedule? = nil
      
              /// Value between 0 and 1 representing how suspicious the animal is.
              /// 1 is guilty.
              var suspiciousLevel: Double = 0.0
      
              struct SleepSchedule: Codable {
                  var sleepTime: DateComponents
                  var wakeTime: DateComponents
              }
      
              static let newSleepSchedule: SleepSchedule = {
                  // Asleep at 10:30, awake at 6:30
                  .init(
                      sleepTime: DateComponents(hour: 22, minute: 30),
                      wakeTime: DateComponents(hour: 6, minute: 30))
              }()
          }
      
      }
      
      final class AnimalStore: ObservableObject {
      
          var storage: [Animal.ID: Animal.Storage] = [:]
      
          /// Getter for properties of an animal stored in self
          func callAsFunction<Result>(_ keyPath: WritableKeyPath<Animal.Storage, Result>, for animal: Animal) -> Binding<Result> {
              Binding { [self] in
                  storage[animal.id, default: .init()][keyPath: keyPath]
              } set: { [self] newValue in
                  self.objectWillChange.send()
                  var animalStore = storage[animal.id, default: .init()]
                  animalStore[keyPath: keyPath] = newValue
                  storage[animal.id] = animalStore
              }
          }
      
          func write<Value>(_ keyPath: WritableKeyPath<Animal.Storage, Value>, value: Value, for animal: Animal) {
              objectWillChange.send()
              var animalStore = storage[animal.id, default: .init()]
              animalStore[keyPath: keyPath] = value
              storage[animal.id] = animalStore
          }
      
          func read<Value>(_ keyPath: WritableKeyPath<Animal.Storage, Value>, for animal: Animal) -> Value {
              storage[animal.id, default: .init()][keyPath: keyPath]
          }
      }
      
      struct AnimalTable: View {
          @Binding var state: AppState
          @EnvironmentObject private var animalStore: AnimalStore
          @Environment(\.horizontalSizeClass) private var sizeClass: UserInterfaceSizeClass?
      
      
          var fruitWidth: CGFloat {
              #if os(iOS)
              40.0
              #else
              25.0
              #endif
          }
      
          var body: some View {
              Table(state.animals, selection: $state.selection) {
                  TableColumn("Name") { animal in
                      HStack {
                          Text(animal.emoji).font(.title)
                              .padding(2)
                              .background(.thickMaterial, in: RoundedRectangle(cornerRadius: 3))
                          Text(animal.name + " " + animal.species).font(.title3)
                      }
                  }
                  TableColumn("Favorite Fruits") { animal in
                      HStack {
                          ForEach(animal.favoriteFruits.prefix(3)) { fruit in
                              FruitImage(fruit: fruit, size: .init(width: fruitWidth, height: fruitWidth), scale: 2.0, bordered: state.selection == animal.id)
                          }
                      }
                      .padding(3.5)
                  }
                  TableColumn("Suspicion Level") { animal in
                      SuspicionTableCell(animal: animal)
                  }
              }
              #if os(macOS)
              .alternatingRowBackgrounds(.disabled)
              #endif
              .tableStyle(.inset)
          }
      }
      
      private struct SuspicionTableCell: View {
          var animal: Animal
          @Environment(\.backgroundProminence) private var backgroundProminence
          @EnvironmentObject private var animalStore: AnimalStore
      
          var body: some View {
              let color = SuspiciousText.model(for: animalStore.read(\.suspiciousLevel, for: animal)).1
              HStack {
                  Image(
                      systemName: "cellularbars",
                      variableValue: animalStore.read(\.suspiciousLevel, for: animal)
                  )
                  .symbolRenderingMode(.hierarchical)
                  SuspiciousText(
                      suspiciousLevel:
                          animalStore.read(\.suspiciousLevel, for: animal),
                      selected: backgroundProminence == .increased)
              }
              .foregroundStyle(backgroundProminence == .increased ? AnyShapeStyle(.white) : AnyShapeStyle(color))
          }
      }
      
      private struct SuspiciousText: View {
          var suspiciousLevel: Double
          var selected: Bool
      
          static fileprivate func model(for level: Double) -> (String, Color) {
              switch level {
              case 0..<0.2:
                  return ("Unlikely", .green)
              case 0.2..<0.5:
                  return ("Fishy", .mint)
              case 0.5..<0.9:
                  return ("Very suspicious", .orange)
              case 0.9...1:
                  return ("Extremely suspicious!", .red)
              default:
                  return ("Suspiciously Unsuspicious", .blue)
              }
          }
      
          var body: some View {
              let model = Self.model(for: suspiciousLevel)
              Text(model.0)
                  .font(.callout)
          }
      }
      
      struct Animal: Identifiable {
          var name: String
          var species: String
          var pawSize: PawSize
          var favoriteFruits: [Fruit]
          var emoji: String
      
          var id: String { species }
      }
      
      var allAnimals: [Animal] = [
          .init(name: "Fabrizio", species: "Fish", pawSize: .small, favoriteFruits: [.arbutusUnedo, .bigBerry, .elstar], emoji: "🐟"),
          .init(name: "Soloman", species: "Snail", pawSize: .small, favoriteFruits: [.elstar, .flavorKing], emoji: "🐌"),
          .init(name: "Ding", species: "Dove", pawSize: .small, favoriteFruits: [.quercusTomentella, .pinkPearlApple, .lapins], emoji: "🕊️"),
          .init(name: "Catie", species: "Crow", pawSize: .small, favoriteFruits: [.pinkPearlApple, .goldenNectar, .hauerPippin], emoji: "🐦‍⬛"),
          .init(name: "Miko", species: "Cat", pawSize: .small, favoriteFruits: [.belleDeBoskoop, .tompkinsKing, .lapins], emoji: "🐈"),
          .init(name: "Ricardo", species: "Rabbit", pawSize: .small, favoriteFruits: [.mariposa, .elephantHeart], emoji: "🐰"),
          .init(name: "Cornelius", species: "Duck", pawSize: .medium, favoriteFruits: [.greenGage, .goldenNectar], emoji: "🦆"),
          .init(name: "Maria", species: "Mouse", pawSize: .small, favoriteFruits: [.arbutusUnedo, .elephantHeart], emoji: "🐹"),
          .init(name: "Haku", species: "Hedgehog", pawSize: .small, favoriteFruits: [.christmasBerry, .creepingSnowberry, .goldenGem], emoji: "🦔"),
          .init(name: "Rénard", species: "Raccoon", pawSize: .medium, favoriteFruits: [.belleDeBoskoop, .bigBerry, .christmasBerry, .kakiFuyu], emoji: "🦝")
      ]
      
      enum PawSize: Hashable {
          case small
          case medium
          case large
      }
      
      struct Fruit: Identifiable, Hashable {
          var name: String
          var color: Color
          var id: String { name }
      }
      
      struct FruitImage: View {
          var fruit: Fruit
          var size: CGSize? = .init(width: 50, height: 50)
          var scale: CGFloat = 1.0
          var bordered = false
      
          var body: some View {
              fruit.color // Actual assets replaced with Color
                  .scaleEffect(scale)
                  .scaledToFill()
                  .frame(width: size?.width, height: size?.height)
                  .mask { RoundedRectangle(cornerRadius: 4) }
                  .overlay {
                      if bordered {
                          RoundedRectangle(cornerRadius: 4)
                              .stroke(fruit.color, lineWidth: 2)
                      }
                  }
          }
      }
      
      extension Fruit {
          static let goldenGem = Fruit(name: "Golden Gem Apple", color: .yellow)
          static let flavorKing = Fruit(name: "Flavor King Plum", color: .purple)
          static let mariposa = Fruit(name: "Mariposa Plum", color: .red)
          static let tompkinsKing = Fruit(name: "Tompkins King Apple", color: .yellow)
          static let greenGage = Fruit(name: "Green Gage Plum", color: .green)
          static let lapins = Fruit(name: "Lapins Sweet Cherry", color: .purple)
          static let hauerPippin = Fruit(name: "Hauer Pippin Apple", color: .red)
          static let belleDeBoskoop = Fruit(name: "Belle De Boskoop Apple", color: .red)
          static let elstar = Fruit(name: "Elstar Apple", color: .yellow)
          static let goldenDeliciousApple = Fruit(name: "Golden Delicious Apple", color: .yellow)
          static let creepingSnowberry = Fruit(name: "Creeping Snowberry", color: .white)
          static let quercusTomentella = Fruit(name: "Channel Island Oak Acorn", color: .brown)
          static let elephantHeart = Fruit(name: "Elephant Heart Plum", color: .red)
          static let goldenNectar = Fruit(name: "Golden Nectar Plum", color: .yellow)
          static let pinkPearlApple = Fruit(name: "Pink Pearl Apple", color: .pink)
          static let christmasBerry = Fruit(name: "Christmas Berry", color: .red)
          static let kakiFuyu = Fruit(name: "Kaki Fuyu Persimmon", color: .orange)
          static let bigBerry = Fruit(name: "Big Berry Manzanita", color: .red)
          static let arbutusUnedo = Fruit(name: "Strawberry Tree", color: .red)
      }
      
      extension Array where Element == Fruit {
          var groupID: Fruit.ID {
              reduce("") { result, next in
                  result.appending(next.id)
              }
          }
      }
      
      var allFruits: [Fruit] = [
          .goldenGem,
          .flavorKing,
          .mariposa,
          .tompkinsKing,
          .greenGage,
          .lapins,
          .hauerPippin,
          .belleDeBoskoop,
          .elstar,
          .goldenDeliciousApple,
          .creepingSnowberry,
          .quercusTomentella,
          .elephantHeart,
          .goldenNectar,
          .kakiFuyu,
          .bigBerry,
          .arbutusUnedo,
          .pinkPearlApple,
      ]
    • 3:54 - Xcode Previews

      import SwiftUI
      
      #Preview("Meet Inspector", traits:
              .fixedLayout(width: 800, height: 500)
      ) {
          ContentView()
              .navigationTitle("SwiftUI Inspectors")
              .environmentObject(AnimalStore())
      }
      
      public struct ContentView: View {
          @State private var state = AppState()
          @State private var presented = true
      
          public var body: some View {
              AnimalTable(state: $state)
                  .inspector(isPresented: $presented) {
                      AnimalInspectorForm(animal: $state.binding())
                          .inspectorColumnWidth(
                              min: 200, ideal: 300, max: 400)
                          .toolbar {
                              Spacer()
                              Button {
                                  presented.toggle()
                              } label: {
                                  Label("Toggle Inspector", systemImage: "info.circle")
                              }
                          }
                  }
          }
      }
      
      import MapKit
      
      struct FruitNibbleBulletin: View {
          var fruit: Fruit = .pinkPearlApple
          @Environment(\.dismiss) private var dismiss
      
          var body: some View {
              NavigationStack {
                  ScrollView {
                      VStack(alignment: .leading) {
                          Grid(horizontalSpacing: 12, verticalSpacing: 2) {
                              GridRow {
                                  FruitImage(fruit: fruit, size: .init(width: 60, height: 60), bordered: false)
      
                                  Text("""
                                  A \(fruit.name.lowercased()) was nibbled! The bite \
                                  happened at 9:41 AM. The nibbler left behind only \
                                  a few seeds.
                                  """
                                  )
                              }
                              GridRow {
                                  Text("""
                                  The Fruit Inspectors were on \
                                  the scene moments after it happened. \
                                  Unfortunately, their efforts to catch the nibbler \
                                  were fruitless.
                                  """).gridCellColumns(2)
                              }
                          }
                          GroupBox("Clues") {
                              LabeledContent("Paw Size") {
                                  Text("Large")
                              }
                              LabeledContent("Favorite Fruit") {
                                  Text("\(fruit.name.capitalized(with: .current))")
                              }
                              LabeledContent("Alibi") {
                                  Text("None")
                              }
                          }
                          HStack {
                              VStack {
                                  fruit.color
                                      .aspectRatio(contentMode: ContentMode.fit)
                                      .shadow(radius: 2.5)
                                  Text("The pink pearls left behind").font(.caption)
                                      .frame(alignment: .leading)
                              }
                              AppleParkMap()
                                  .mask(RoundedRectangle(cornerSize: CGSize(width: 20, height: 10)))
      
                          }
                          Text("The Fruit Inspection team was on the scene minutes after the incident. However, their attempts to discover any meaningful clues around the identity of the nibbler were fruitless.")
                      }
                      .scenePadding(.horizontal)
                      .toolbar {
                          ToolbarItem {
                              Button(role: .cancel) {
                                  dismiss()
                              } label: {
                                  Label("Close", systemImage: "xmark.circle.fill")
                              }
                              .symbolRenderingMode(.monochrome)
                              .tint(.secondary)
                          }
                      }
                  }
      #if os(iOS)
                  .navigationBarTitleDisplayMode(.inline)
      #endif
                  .navigationTitle("Fruit Nibble Bulletin")
              }
          }
      }
      
      struct AppleParkMap: View {
          @State private var region = MKCoordinateRegion(
              center: CLLocationCoordinate2D(latitude: 37.334_371,
                                             longitude: -122.009_558),
              latitudinalMeters: 100,
              longitudinalMeters: 100
          )
      
          var body: some View {
              GeometryReader { geometry in
                  Map(position: .constant(.automatic), bounds: .init(centerCoordinateBounds: region, minimumDistance: 100, maximumDistance: 100), interactionModes: [], scope: .none) { }
              }
              .frame(height: 180, alignment: .center)
          }
      }
    • 7:14 - Inspector content inside a navigation structure

      struct Example1: View {
          @State private var state = AppState()
      
          var body: some View {
              NavigationStack {
                  AnimalTable(state: $state)
                      .inspector(isPresented: $state.inspectorPresented) {
                          AnimalInspectorForm(animal: $state.binding())
                      }
                      .toolbar {
                          Button {
                              state.inspectorPresented.toggle()
                          } label: {
                              Label("Toggle Inspector", systemImage: "info.circle")
                          }
                      }
              }
          }
      }
    • 7:55 - Inspector content outside a navigation structure

      struct Example2: View {
          @State private var state = AppState()
          
          var body: some View {
              NavigationStack {
                  AnimalTable(state: $state)
              }
              .inspector(isPresented: $state.inspectorPresented) {
                  AnimalInspectorForm(animal: $state.binding())
                      .toolbar {
                          ToolbarItem(placement: .principal) {
                              HStack {
                                  Button {
                                  } label: {
                                      Image(systemName: "rectangle.and.pencil.and.ellipsis")
                                  }
                                  Button {
                                  } label: {
                                      Image(systemName: "pawprint.circle")
                                  }
                              }
                          }
                      }
              }
          }
      }
    • 8:56 - Inspector with NavigationSplitView: detail column

      NavigationSplitView {
        Sidebar()
      } detail: {
        AnimalTable()
          .inspector(presented: $isPresented) {
            AnimalInspectorForm()
          }
      }
    • 9:06 - Inspector with NavigationSplitView: Outside

      NavigationSplitView {
        Sidebar()
      } detail: {
        AnimalTable()
      }
      .inspector(presented: $isPresented) {
        AnimalInspectorForm()
      }
    • 9:49 - Presentation customizations

      .sheet(item: $nibbledFruit) { fruit in
        FruitNibbleBulletin(fruit: fruit)
          .presentationBackground(.thinMaterial)
          .presentationDetents([.height(200), .medium, .large])
          .presentationBackgroundInteraction(.enabled(upThrough: .height(200)))
      }
    • 11:58 - Presentation customizations on Inspector

      .inspector(presented: $state.inspectorPresented) {
        AnimalInspectorForm(animal: $state.binding())
          .presentationDetents([.height(200), .medium, .large])
          .presentationBackgroundInteraction(.enabled(upThrough: .height(200)))
      }

Developer Footer

  • Vidéos
  • WWDC23
  • Inspectors in SwiftUI: Discover the details
  • 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