Task
Updated:
Task
There is a whole bunch of different types of tasks and using task groups and detach tasks
- Basic Task with async/await
// MARK: - VIEWMODEL
class TaskBootCampViewModel: ObservableObject {
// MARK: - PROPERTY
@Published var image: UIImage? = nil
// MARK: - INIT
// MARK: - FUNCTION
func fetchImage() async {
do {
guard let url = URL(string: "https://picsum.photos/1000") else { return }
let (data, _) = try await URLSession.shared.data(from: url, delegate: nil)
self.image = UIImage(data: data)
} catch {
print(error.localizedDescription)
}
}
}
// MARK: - VIEW
struct TaskBootCamp: View {
// MARK: - PROPERTY
@StateObject private var vm = TaskBootCampViewModel()
// MARK: - BODY
var body: some View {
VStack(spacing: 40) {
if let image = vm.image {
Image(uiImage: image)
.resizable()
.scaledToFit()
.frame(width: 200, height: 200)
}
} //: VSTACK
.onAppear {
Task {
await vm.fetchImage()
}
}
}
}
- Set up task priority
// MARK: - VIEW
struct TaskBootCamp: View {
// MARK: - PROPERTY
@StateObject private var vm = TaskBootCampViewModel()
// MARK: - BODY
var body: some View {
VStack(spacing: 40) {
if let image = vm.image {
Image(uiImage: image)
.resizable()
.scaledToFit()
.frame(width: 200, height: 200)
}
if let image = vm.image2 {
Image(uiImage: image)
.resizable()
.scaledToFit()
.frame(width: 200, height: 200)
}
} //: VSTACK
.onAppear {
// The order of task priorities
Task(priority: .high) {
// Task.yield() just can wait and let other tasks go in front of it if there are other tasks
await Task.yield()
print("High: \(Thread.current) : \(Task.currentPriority)")
}
Task(priority: .userInitiated) {
print("UserInitiated: \(Thread.current) : \(Task.currentPriority)")
}
Task(priority: .medium) {
print("Medium: \(Thread.current) : \(Task.currentPriority)")
}
Task(priority: .low) {
print("Low: \(Thread.current) : \(Task.currentPriority)")
}
Task(priority: .utility) {
print("Utility: \(Thread.current) : \(Task.currentPriority)")
}
Task(priority: .background) {
print("Background: \(Thread.current) : \(Task.currentPriority)")
}
- Add child Tasks
// child tasks will inherit all of the metadata from parent tasks
Task(priority: .userInitiated) {
print("UserInitiated: \(Thread.current) : \(Task.currentPriority)")
// detached has a different priority than the parent priority
Task.detached {
print("detached: \(Thread.current) : \(Task.currentPriority)")
}
}
- Cancel Tasks
There’s one of the most important things about tasks because when you create so many tasks in your app. But, if you move away from the screen or does something else we also want the ability to cancel to save power wise and might want to cancel them if things move off screen
// MARK: - HOMEVIEW
struct TaskBootCampHomeView: View {
var body: some View {
NavigationView {
ZStack {
NavigationLink("Click Me!!") {
TaskBootCamp()
}
} //: ZSTACK
} //: NAVIGATION
}
}
// MARK: - VIEW
struct TaskBootCamp: View {
// MARK: - PROPERTY
@StateObject private var vm = TaskBootCampViewModel()
@State private var fetchImageTask: Task<(), Never>? = nil
// MARK: - BODY
var body: some View {
VStack(spacing: 40) {
if let image = vm.image {
Image(uiImage: image)
.resizable()
.scaledToFit()
.frame(width: 200, height: 200)
}
if let image = vm.image2 {
Image(uiImage: image)
.resizable()
.scaledToFit()
.frame(width: 200, height: 200)
}
} //: VSTACK
.onDisappear(perform: {
// when display off then cancel tasks during process
fetchImageTask?.cancel()
})
.onAppear {
fetchImageTask = Task {
print(Thread.current)
print(Task.currentPriority)
await vm.fetchImage()
}
}
}
}
- Task modifier (iOS 15)
.task { code }
: Adds an asynchronous task to perform when this view appears
// MARK: - HOMEVIEW
struct TaskBootCampHomeView: View {
var body: some View {
NavigationView {
ZStack {
NavigationLink("Click Me!!") {
TaskBootCamp()
}
} //: ZSTACK
} //: NAVIGATION
}
}
// MARK: - VIEW
struct TaskBootCamp: View {
// MARK: - PROPERTY
@StateObject private var vm = TaskBootCampViewModel()
@State private var fetchImageTask: Task<(), Never>? = nil
// MARK: - BODY
var body: some View {
VStack(spacing: 40) {
if let image = vm.image {
Image(uiImage: image)
.resizable()
.scaledToFit()
.frame(width: 200, height: 200)
}
if let image = vm.image2 {
Image(uiImage: image)
.resizable()
.scaledToFit()
.frame(width: 200, height: 200)
}
} //: VSTACK
// SwiftUI automatically cancels the task if the view disappears before the action completes
.task {
await vm.fetchImage()
}
Task.checkCancellation() throws
If the task is cancelled this will throw an error
for x in array {
// working loop right here if it was running this after each piece of work it would check if the task is canceled and then it would stop if the task was canceled and it would throw back an error our of this function. Obviously, not throwing so we have to deal with all of that if you have long running tasks and you are going to cancel them you might want to throw in an occasional check for the task actually being cancelled
try Task.checkCancellation()
}
🗃 Reference
SwiftUI Thinking - https://youtu.be/fTtaEYo14jI
Leave a comment