Modern Concurrency: Beyond the Basics

Oct 20 2022 Swift 5.5, iOS 15, Xcode 13.4

Part 2: Concurrent Code

18. Using a GlobalActor

Episode complete

Play next episode

Next
Save for later
About this episode
See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 17. Creating a GlobalActor Next episode: 19. Challenge: Using a GlobalActor

This video Using a GlobalActor was last updated on Oct 20 2022

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

You can unlock the rest of this video course, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.

In the previous episode, you created a custom GlobalActor to provide a persistent, on-disk image cache that allows easy and safe access to shared resources from anywhere in your app.

Wiring up the persistence layer

Before you do anything with ImageDatabase, you need to set it up safely by calling its setUp method. You can do that anywhere in your code but, for this example, you’ll do it along with the rest of your app setup.

try await ImageDatabase.shared.setUp()
ImageDatabase.shared.image(file.url)
ImageDatabase.shared.image(file.url)
Download: http://localhost:8080/gallery/image?26
In memory cache.
In memory cache.
Download: http://localhost:8080/gallery/image?2
In memory cache.
Download: http://localhost:8080/gallery/image?9
Download: http://localhost:8080/gallery/image?22
...
In memory cache.
In memory cache.
In memory cache.
In disk cache.
In disk cache.
Download: http://localhost:8080/gallery/image?10
In disk cache.

Adding a cache hit counter

The bottom bar has placeholders for information that helps you debug your caching mechanism.

@MainActor private(set) var inMemoryAccess: AsyncStream<Int>?
private var inMemoryAcccessContinuation: AsyncStream<Int>.Continuation?
private var inMemoryAccessCounter = 0
private var inMemoryAccessCounter = 0🟩 {
  didSet { inMemoryAcccessContinuation?.yield(inMemoryAccessCounter) }
}🟥
func setUp() async {
  let accessStream = AsyncStream<Int> { continuation in
    inMemoryAcccessContinuation = continuation
  }
}
func setUp() async {
  let accessStream = AsyncStream<Int> { continuation in
    inMemoryAcccessContinuation = continuation
  }
  🟩
  await MainActor.run { inMemoryAccess = accessStream }
  🟥
}
inMemoryAccessCounter += 1
deinit {
  inMemoryAcccessContinuation?.finish()
}

Displaying the counter

Now, you need to setup the image loader. A safe place to call ImageLoader.setUp() is your database’s own setUp().

await imageLoader.setUp()
.task {
  guard let memoryAccessSequence = 
    ImageDatabase.shared.imageLoader.inMemoryAccess else {
    return
  }
}
.task {
  guard let memoryAccessSequence = 
    ImageDatabase.shared.imageLoader.inMemoryAccess else {
    return
  }
  🟩
  for await count in memoryAccessSequence {
    inMemoryAccessCount = count
  }
  🟥
}

Purging the in-memory cache

You’ll soon wire up the button that clears the memory cache.

func clearInMemoryAssets() async {
  await imageLoader.clear()
  print("Cleared in-memory cache.")
}
Task {
  await ImageDatabase.shared.clearInMemoryAssets()
}
Task {
  await ImageDatabase.shared.clearInMemoryAssets()
  🟩
  try await model.loadImages()
  🟥
}