class TaskBootcampViewModel: ObservableObject {
@Published var image: UIImage? = nil
@Published var image2: UIImage? = nil
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)
}
}
func fetchImage2() 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.image2 = UIImage(data: data)
} catch {
print(error.localizedDescription)
}
}
}
요런 뷰모델이 있다고 해봅시다
struct TaskBootcamp: View {
@StateObject private var viewModel = TaskBootcampViewModel()
var body: some View {
VStack(spacing: 40) {
if let image = viewModel.image {
Image(uiImage: image)
.resizable()
.scaledToFit()
.frame(width: 200, height: 200)
}
if let image = viewModel.image2 {
Image(uiImage: image)
.resizable()
.scaledToFit()
.frame(width: 200, height: 200)
}
}
.onAppear {
Task {
await viewModel.fetchImage()
await viewModel.fetchImage2()
}
}
}
}
그리고 이런 뷰가 있다고해봅시다!
onAppear일 때 fetch 하고 있는데
await로 처리하고 있어서 첫번째 이미지가 다 불러와진 후에
image2가 화면에 뜹니다
Task 안에서 await가 위아래로 있으면 위에 로직이 끝나고 다음 로직이 실행된다!
(한 Task 안에서 같이 로딩할 방법도 있긴함! 이건 담시간에)
이런 상황일 때 간단히 처리하는 방법은
Task 블록은 두개 따로따로 작성해주는 거!
오케이?
그럼 만약에 2개 이상의 Task가 있을 경우에 처리되는 순서는 어떻게 될까?
똑같다?!
사실 Task 만들 때 priority 설정해줄 수 있음
high : <_NSMainThread: 0x600003bcc100>{number = 1, name = main} : TaskPriority(rawValue: 25)
userInitiated : <_NSMainThread: 0x600003bcc100>{number = 1, name = main} : TaskPriority(rawValue: 25)
medium : <_NSMainThread: 0x600003bcc100>{number = 1, name = main} : TaskPriority(rawValue: 21)
background : <_NSMainThread: 0x600003bcc100>{number = 1, name = main} : TaskPriority(rawValue: 9)
LOW : <_NSMainThread: 0x600003bcc100>{number = 1, name = main} : TaskPriority(rawValue: 17)
utility : <_NSMainThread: 0x600003bcc100>{number = 1, name = main} : TaskPriority(rawValue: 17)
콘솔에 이렇게 찍히는 걸 볼 수 있었다
Task sleep 가능했었죠
만약에 특정한 시점에서 나 말고 다른 애들 priority를 먼저 높여주고 싶다!
Task.yield()
라는 메소드 사용하면 됨
Task 안에 Task도 넣을 수는 있지만 지금처럼 작성할 경우엔 똑같은 priority다
child 태스크는 parent 태스크의 메타데이터를 다 물려받아서 그럼
detach로 떼버리면 priority도 달라짐! (근데 문서에서는 이거 웬만해선 사용하지 말라고함)
fetchImage 메소드 5초 동안 Task 잠들게 해줌
그리고 MainActor로 메인에서 이미지가 바뀔 수 있게 해주고!
이상태에서 NavigationLink로 새로운 뷰로 진입시에 이 비동기 메소드가 호출되게 해주면 5초 전에 뷰를 빠져나와도 백에서 작업이 취소가 안되서 프린트가 찍혀버림!
어떻게 해줘야할까?
Task 타입의 변수에 넣고 이걸
.onDisappear일 때 cancel 해주는겨
ios 15에서 새로 생긴 modifier는
.task { } modifier다!!
따로 Task 안만들고도 .task로 처리가 가능함
이러면 .onAppear일 때 , .onDisAppear일 때 따로 처리 안해줘도
알아서 뷰가 사라지면 task가 cancel 됩니다!!
근데 cancell이 된다고 해서 바로 즉시 cancel이 되는 것은 아니다
try Task.checkCancellation()
로 체크해보기