Modern Concurrency: Getting Started

Oct 18 2022 · Swift 5.5, iOS 15, Xcode 13.4

Part 1: Asynchronous Code

02. Getting Started

Episode complete

Play next episode

Next
About this episode

Leave a rating/review

See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 01. Introduction Next episode: 03. Your First Asynchronous App

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Heads up... You've reached locked video content where the transcript will be shown as obfuscated text.

Part 1: Tasks

In the course materials, locate the starter playground and open it.

print("Doing some work on a task")
let sum = (1...100000).reduce(0, +)
print("1 + 2 + 3 ... 100000 = \(sum)")
print("Doing some work on the main actor")
Doing some work on a task
1 + 2 + 3 ... 100000 = 5000050000
Doing some work on the main actor
🟩Task {🟥
  print("Doing some work on a task")
  let sum = (1...100000).reduce(0, +)
  print("1 + 2 + 3 ... 100000 = \(sum)")
🟩}🟥   // typing this re-indents the 3 lines
print("Doing some work on the main actor")
Doing some work on a task
Doing some work on the main actor
1 + 2 + 3 ... 100000 = 5000050000

Check if running on main actor

Comment out all this code, then scroll down to see some code that helps you check if code is running on the main actor:

let specificKey = DispatchSpecificKey<String>()
DispatchQueue.main.setSpecific(key: specificKey, value: "main")
if DispatchQueue.getSpecific(key: specificKey) == "main" {
  print("\nPlayground runs on main actor")
}

Task {
  print("\nDoing some work on a task")
  if DispatchQueue.getSpecific(key: specificKey) == "main" {
    print("Task runs on main actor")
  } else {
    print("Task doesn't run on main actor")
  }
}
print("Doing some work on the main actor")
Playground runs on main actor

Doing some work on a task
Task doesn't run on main actor
Task {
  print("\nDoing some work on an 🟩unnamed🟥 task")
  let sum = (1...100000).reduce(0, +)
  print("🟩Unnamed task done: 🟥1 + 2 + 3 ... 100000 = \(sum)")
}
print("Doing some work on the main actor")
🟩print("Doing more work on the main actor")🟥

// This task runs after previous task finishes
🟩let task = Task {
  print("\nDoing some work on a named task")
  let sum = (1...100000).reduce(0, +)
  print("🟩Named🟥 task done: 1 + 2 + 3 ... 100000 = \(sum)")
}
print("Doing yet 🟩more🟥 work on the main actor")
🟥
Doing some work on an unnamed task
Doing some work on the main actor
Unnamed task done: 1 + 2 + 3 ... 100000 = 5000050000
Doing more work on the main actor

Doing some work on a named task
Doing yet more work on the main actor
Named task done: 1 + 2 + 3 ... 100000 = 5000050000

Part 2: async/await

Scroll down to the section titled Start here. You might’ve seen the sleep(_:) function before.

sleep(1)
print("wake up")
print("Hello")
Task.sleep(until: .now + .seconds(1), clock: .continuous)  
// I'm using Xcode 14, which has this new Task.sleep method, so I can use seconds instead of nanoseconds.
print("Goodbye")
 🟩Task {🟥
  print("\nHello")
  Task.sleep(until: .now + .seconds(1), clock: .continuous)
  print("Goodbye")
 🟩}🟥
 Task {
  print("\nHello")
  🟩try🟥 Task.sleep(until: .now + .seconds(1), clock: .continuous)
  print("Goodbye")
 }
 Task {
  print("\nHello")
  try 🟩await🟥 Task.sleep(until: .now + .seconds(1), clock: .continuous)
  print("Goodbye")
 }

Asynchronous functions

When you define an asynchronous function that can throw errors, you reverse the order of the keywords.

func helloPauseGoodbye() async throws {
}
func helloPauseGoodbye() async throws {
  🟩
  print("Hello function")  // add " function"
  try await Task.sleep(until: .now + .seconds(1), clock: .continuous)
  print("Goodbye function")  // add " function"
  🟥
}
Task {
  try await helloPauseGoodbye()
}

URLSession

Scroll up to the section titled URLSession. You’ll download and decode a list of learning domains from raywenderlich.com.

// all annotated public
struct Domains: Decodable {
  let data: [Domain]
}
struct Domain: Decodable {
  let attributes: Attributes
}
struct Attributes: Decodable {
  let name: String
  let description: String
  let level: String
}
func fetchDomains() -> [Domain] {
  let url = URL(string: "https://api.raywenderlich.com/api/domains")!
  // and add a dummy return so the compiler doesn't complain
  return []
}

Add async call: async throws / try await

Now, before the return statement, fetch the data:

func fetchDomains() -> [Domain] {
  let url = URL(string: "https://api.raywenderlich.com/api/domains")!
  🟩let (data, _) = try await URLSession.shared.data(from: url)🟥
  // data(from:) is grayed out because the compiler doesn't think it should be used here
  // select it anyway
  return []
}
func fetchDomains() 🟩async 🟥 -> [Domain] {
func fetchDomains() async 🟩throws 🟥 -> [Domain] {

Handle data, response

Normally, you would first verify the server response, but skip that and just decode the data. Replace the dummy return [] line:

return try JSONDecoder().decode(Domains.self, from: data).data

Call async method from Task

fetchDomains() is an async function, so you need to call it in a Task:

Task {
  let domains = try await fetchDomains()
}
Task {
  🟩
  do {
    let domains = try await fetchDomains()
  } catch {
    print(error)
  }
  🟥
}
Task {
  do {
    let domains = try await fetchDomains()
    🟩
    for domain in domains {
      let attr = domain.attributes
      print("\(attr.name): \(attr.description) - \(attr.level)")
    }
    🟥
  } catch {
    print(error)
  }
}
iOS & Swift: Learn iOS development with SwiftUI and UIKit - production
Android & Kotlin: Learn Android development in Kotlin - production
Flutter: Learn multi-platform development with Flutter - production
Server-Side Swift: Learn web development with Swift - beta
Game Technologies: Learn how to create games using popular game technologies. - beta
macOS: Learn macOS development - archived
Archive:  - archived