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
 

Videos

Abrir menú Cerrar menú
  • Colecciones
  • Todos los videos
  • Información

Más videos

  • Información
  • Código
  • Swift concurrency: Update a sample app

    Discover Swift concurrency in action: Follow along as we update an existing sample app. Get real-world experience with async/await, actors, and continuations. We'll also explore techniques for migrating existing code to Swift concurrency over time.

    To get the most out of this code-along, we recommend first watching “Meet async/await in Swift” and “Protect mutable state with Swift actors” from WWDC21.

    Note: To create an async task in Xcode 13 beta 3 and later, use the Task initializer instead.

    Recursos

    • Updating an App to Use Swift Concurrency
    • The Swift Programming Language: Concurrency
      • Video HD
      • Video SD

    Videos relacionados

    WWDC22

    • Create a more responsive media app
    • Eliminate data races using Swift Concurrency
    • Visualize and optimize Swift concurrency

    WWDC21

    • Explore structured concurrency in Swift
    • Meet async/await in Swift
    • Protect mutable state with Swift actors
    • Swift concurrency: Behind the scenes
    • What's new in SwiftUI

    WWDC20

    • Build complications in SwiftUI
  • Buscar este video…
    • 0:07:34 - Call the async version of the HKHealthKitStore save(_:) method

      do {
          try await store.save(caffeineSample)
          self.logger.debug("\(mgCaffeine) mg Drink saved to HealthKit")
      } catch {
          self.logger.error("Unable to save \(caffeineSample) to the HealthKit store: \(error.localizedDescription)")
      }
    • 0:09:38 - Change save(drink:) to be an async function

      public func save(drink: Drink) async {
    • 0:10:15 - Create a new asynchronous task

      Task { await self.healthKitController.save(drink: drink) }
    • 0:12:13 - Add an async alternative for requestAuthorization(completionHandler:)

      @available(*, deprecated, message: "Prefer async alternative instead")
      public func requestAuthorization(completionHandler: @escaping (Bool) -> Void ) {
          Task {
              let result = await requestAuthorization()
              completionHandler(result)
          }
      }
    • 0:14:55 - Update the async version of requestAuthorization()

      public func requestAuthorization() async -> Bool {
          guard isAvailable else { return false }
      
          do {
              try await store.requestAuthorization(toShare: types, read: types)
              self.isAuthorized = true
              return true
          } catch let error {
              self.logger.error("An error occurred while requesting HealthKit Authorization: \(error.localizedDescription)")
              return false
          }
      }
    • 0:15:43 - Add an async alternative for loadNewDataFromHealthKit(completionHandler:)

      @available(*, deprecated, message: "Prefer async alternative instead")
      public func loadNewDataFromHealthKit(completionHandler: @escaping (Bool) -> Void = { _ in }) {
          Task { completionHandler(await self.loadNewDataFromHealthKit()) }
      }
    • 0:17:43 - Create a queryHealthKit() helper function that uses a continuation

      private func queryHealthKit() async throws -> ([HKSample]?, [HKDeletedObject]?, HKQueryAnchor?) {
          return try await withCheckedThrowingContinuation { continuation in
              // Create a predicate that only returns samples created within the last 24 hours.
              let endDate = Date()
              let startDate = endDate.addingTimeInterval(-24.0 * 60.0 * 60.0)
              let datePredicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: [.strictStartDate, .strictEndDate])
      
              // Create the query.
              let query = HKAnchoredObjectQuery(
                  type: caffeineType,
                  predicate: datePredicate,
                  anchor: anchor,
                  limit: HKObjectQueryNoLimit) { (_, samples, deletedSamples, newAnchor, error) in
      
                  // When the query ends, check for errors.
                  if let error = error {
                      continuation.resume(throwing: error)
                  } else {
                      continuation.resume(returning: (samples, deletedSamples, newAnchor))
                  }
              }
              store.execute(query)
          }
      }
    • 0:20:17 - Update the async version of loadNewDataFromHealthKit()

      @discardableResult
      public func loadNewDataFromHealthKit() async -> Bool {
          
          guard isAvailable else {
              logger.debug("HealthKit is not available on this device.")
              return false
          }
          
          logger.debug("Loading data from HealthKit")
      
          do {
              let (samples, deletedSamples, newAnchor) = try await queryHealthKit()
      
              // Update the anchor.
              self.anchor = newAnchor
      
              // Convert new caffeine samples into Drink instances.
              let newDrinks: [Drink]
              if let samples = samples {
                  newDrinks = self.drinksToAdd(from: samples)
              } else {
                  newDrinks = []
              }
      
              // Create a set of UUIDs for any samples deleted from HealthKit.
              let deletedDrinks = self.drinksToDelete(from: deletedSamples ?? [])
      
              // Update the data on the main queue.
              await MainActor.run {
                  // Update the model.
                  self.updateModel(newDrinks: newDrinks, deletedDrinks: deletedDrinks)
              }
              return true
          } catch {
              self.logger.error("An error occurred while querying for samples: \(error.localizedDescription)")
              return false
          }
      }
    • 0:25:09 - Annotate updateModel(newDrinks:deletedDrinks:) with @MainActor

      @MainActor
      private func updateModel(newDrinks: [Drink], deletedDrinks: Set<UUID>) {
    • 0:26:43 - Remove MainActor.run from the call site of updateModel(newDrinks:deletedDrinks:)

      await self.updateModel(newDrinks: newDrinks, deletedDrinks: deletedDrinks)
    • 0:29:24 - Change HealthKitController to be an actor

      actor HealthKitController {
    • 0:32:31 - Move updateModel(newDrinks:deletedDrinks:) to CoffeeData

      @MainActor
      public func updateModel(newDrinks: [Drink], deletedDrinks: Set<UUID>) {
      
          guard !newDrinks.isEmpty && !deletedDrinks.isEmpty else {
              logger.debug("No drinks to add or delete from HealthKit.")
              return
          }
      
          // Remove the deleted drinks.
          var drinks = currentDrinks.filter { deletedDrinks.contains($0.uuid) }
      
          // Add the new drinks.
          drinks += newDrinks
      
          // Sort the array by date.
          drinks.sort { $0.date < $1.date }
      
          currentDrinks = drinks
      }
    • 0:33:18 - Update the call site of updateModel(newDrinks:deletedDrinks:)

      await model?.updateModel(newDrinks: newDrinks, deletedDrinks: deletedDrinks)
    • 0:34:01 - Mark the deprecated completion handler methods as nonisolated

      @available(*, deprecated, message: "Prefer async alternative instead")
      nonisolated public func requestAuthorization(completionHandler: @escaping (Bool) -> Void ) {
          // ...
      }
      
      @available(*, deprecated, message: "Prefer async alternative instead")
      nonisolated public func loadNewDataFromHealthKit(completionHandler: @escaping (Bool) -> Void = { _ in }) {
          // ...
      }
    • 0:36:20 - Create a private CoffeeDataStore actor for loading and saving

      private actor CoffeeDataStore {
      
      }
    • 0:36:43 - Add a dedicated logger for CoffeeDataStore

      let logger = Logger(subsystem: "com.example.apple-samplecode.Coffee-Tracker.watchkitapp.watchkitextension.CoffeeDataStore", category: "ModelIO")
    • 0:37:05 - Add an instance of the actor to CoffeeData

      private let store = CoffeeDataStore()
    • 0:38:37 - Move the savedValue property from CoffeeData to CoffeeDataStore

      private var savedValue: [Drink] = []
    • 0:39:00 - Move the dataURL property from CoffeeData to CoffeeDataStore

      private var dataURL: URL {
          get throws {
              try FileManager
                  .default
                  .url(for: .documentDirectory,
                       in: .userDomainMask,
                       appropriateFor: nil,
                       create: false)
                  // Append the file name to the directory.
                  .appendingPathComponent("CoffeeTracker.plist")
          }
      }
    • 0:42:42 - Move the didSet for currentDrinks to a new async function

      @Published public private(set) var currentDrinks: [Drink] = []
      
      private func drinksUpdated() async {
          logger.debug("A value has been assigned to the current drinks property.")
      
          // Update any complications on active watch faces.
          let server = CLKComplicationServer.sharedInstance()
          for complication in server.activeComplications ?? [] {
              server.reloadTimeline(for: complication)
          }
      
          // Begin saving the data.
          await store.save(currentDrinks)
      }
    • 0:44:00 - Update addDrink(mgCaffeine:onData:) to call drinksUpdated()

      // Save drink information to HealthKit.
      Task {
          await self.healthKitController.save(drink: drink)
          await self.drinksUpdated()
      }
    • 0:44:09 - Update updateModel(newDrinks:deletedDrinks:) to call drinksUpdated()

      await drinksUpdated()
    • 0:44:17 - Mark the updateModel(newDrinks:deletedDrinks:) method as async

      @MainActor
      public func updateModel(newDrinks: [Drink], deletedDrinks: Set<UUID>) async {
    • 0:45:26 - Complete the move of the save() method into CoffeeDataStore

      // Begin saving the drink data to disk.
      func save(_ currentDrinks: [Drink]) {
      
          // Don't save the data if there haven't been any changes.
          if currentDrinks == savedValue {
              logger.debug("The drink list hasn't changed. No need to save.")
              return
          }
      
          // Save as a binary plist file.
          let encoder = PropertyListEncoder()
          encoder.outputFormat = .binary
      
          let data: Data
      
          do {
              // Encode the currentDrinks array.
              data = try encoder.encode(currentDrinks)
          } catch {
              logger.error("An error occurred while encoding the data: \(error.localizedDescription)")
              return
          }
      
          // Save the data to disk as a binary plist file.
          do {
              // Write the data to disk.
              try data.write(to: self.dataURL, options: [.atomic])
      
              // Update the saved value.
              self.savedValue = currentDrinks
      
              self.logger.debug("Saved!")
          } catch {
              self.logger.error("An error occurred while saving the data: \(error.localizedDescription)")
          }
      }
    • 0:46:20 - Move the top part of the load() method into CoffeeDataStore

      func load() -> [Drink] {
          logger.debug("Loading the model.")
      
          var drinks: [Drink]
      
          do {
              // Load the drink data from a binary plist file.
              let data = try Data(contentsOf: self.dataURL)
      
              // Decode the data.
              let decoder = PropertyListDecoder()
              drinks = try decoder.decode([Drink].self, from: data)
              logger.debug("Data loaded from disk")
          } catch CocoaError.fileReadNoSuchFile {
              logger.debug("No file found--creating an empty drink list.")
              drinks = []
          } catch {
              fatalError("*** An unexpected error occurred while loading the drink list: \(error.localizedDescription) ***")
          }
      
          // Update the saved value.
          savedValue = drinks
          return drinks
      }
    • 0:48:01 - Update the load() method in CoffeeData to use the actor

      func load() async {
          var drinks = await store.load()
      
          // Drop old drinks
          drinks.removeOutdatedDrinks()
      
          // Assign loaded drinks to model
          currentDrinks = drinks
      
          // Load new data from HealthKit.
          let success = await self.healthKitController.requestAuthorization()
          guard success else {
              logger.debug("Unable to authorize HealthKit.")
              return
          }
      
          await self.healthKitController.loadNewDataFromHealthKit()
      }
    • 0:49:08 - Update the CoffeeData initializer to use an async task

      Task { await load() }
    • 0:50:03 - Annotate CoffeeData with @MainActor

      @MainActor
      class CoffeeData: ObservableObject {
    • 0:52:18 - Replace the completion handler usage in the handle(_:) method of ExtensionDelegate

      // Check for updates from HealthKit.
      let model = CoffeeData.shared
      
      Task {
          let success = await model.healthKitController.loadNewDataFromHealthKit()
      
          if success {
              // Schedule the next background update.
              scheduleBackgroundRefreshTasks()
              self.logger.debug("Background Task Completed Successfully!")
          }
      
          // Mark the task as ended, and request an updated snapshot, if necessary.
          backgroundTask.setTaskCompletedWithSnapshot(success)
      }

Developer Footer

  • Videos
  • WWDC21
  • Swift concurrency: Update a sample app
  • 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