์ด๋ชจ์ €๋ชจ/Swift

TaskCounter๋กœ ๋ณ‘๋ ฌ ์ž‘์—… ์™„๋ฃŒ ๊ณ„์ˆ˜ ํŒŒ์•…ํ•˜๊ธฐ

ARpple 2023. 12. 13. 18:10

 

๐Ÿ’ก ์—ฌ๋Ÿฌ ์ด๋ฏธ์ง€๋ฅผ ํ•œ๋ฒˆ์— ๊ฐ€์ ธ์™€์•ผํ•˜๋Š” ์ž‘์—…์ด ์žˆ์—ˆ๋‹ค. ์ด๋ฏธ์ง€๋“ค์„ ๊ฐ€์ ธ์˜ค๋Š” Task๋ฅผ ๋น„๋™๊ธฐ๋กœ ๊ฐ€์ ธ์˜ค๋Š” ์ž‘์—…์„ TaskGroup์œผ๋กœ ์ง„ํ–‰ํ–ˆ๋‹ค. ์ด๋ฏธ์ง€๋“ค์„ ๊ฐ€์ ธ์˜ค๋Š” ๋ชจ๋“  ์ž‘์—… ์™„๋ฃŒ ํ›„, ๋ทฐ์— ๋ณด์—ฌ์ฃผ๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ–ˆ๊ณ  ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ณผ์ •์—์„œ Progress๋ฅผ ๋ณด์—ฌ์ฃผ๋ ค ํ–ˆ๋‹ค. ๊ฐ๊ฐ์˜ ์ž‘์—…์ด ์™„๋ฃŒ๋œ ๊ณ„์ˆ˜๋ฅผ ์ถ”์ ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ฐพ์ง€ ๋ชปํ–ˆ๊ณ  ์นด์šดํ„ฐ๋ฅผ ์ง์ ‘ ๋งŒ๋“ค์–ด ์ด๋ฏธ์ง€๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ž‘์—… ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋‹ค๋ฅธ ์ž‘์—…๋„ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” Task(Group)Counter๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.

์‚ฌ์šฉ ๊ธฐ์ˆ 

  • Actor → ํ•„์ˆ˜, TaskCounter๋Š” actor ์ธ์Šคํ„ด์Šค
  • Combine → RxSwift๋กœ ๊ตฌ์„ฑํ•ด๋„ ์ƒ๊ด€ ์—†์„ ๊ฒƒ ๊ฐ™๋‹ค.
  • Concurrency

๊ตฌํ˜„ ๋งค์ปค๋‹ˆ์ฆ˜

์ €์žฅ ํ”„๋กœํผํ‹ฐ

  • count: ์ž‘์—… ์™„๋ฃŒํ•œ ๊ณ„์ˆ˜๋ฅผ ์นด์šดํŠธ
  • maxCount: ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌํ•  ์ž‘์—…์˜ ๊ณ„์ˆ˜
  • completed: ๋ชจ๋“  ์ž‘์—…์ด ์™„๋ฃŒ ๋˜์—ˆ๋Š”์ง€ ์•Œ๋ ค์ฃผ๋Š” PassthroughSubjectrxSwift์˜ PublishSubject

์นด์šดํŒ… ๊ด€๋ จ ๋ฉ”์„œ๋“œ) private level

  • increment
  • reset
  • changeMax
  • failed()

Task ์ฒ˜๋ฆฌ ๊ด€๋ จ ๋ฉ”์„œ๋“œ

  • progress: ์ž‘์—… ์™„๋ฃŒ ๊ณ„์ˆ˜๊ฐ€ ์˜ฌ๋ผ๊ฐ€๋ฉด ๋ณด๋‚ด๋Š” Publisher → rxSwift์˜ Observer
  • run: ์‹ค์ œ ์ž‘์—…ํ•  ๋ฐ์ดํ„ฐ ๊ทธ๋ฃน๊ณผ ๊ฐ๊ฐ์˜ ๊ทธ๋ฃน์— ์‹คํ–‰ํ•  ์ž‘์—…์„ ์ž…๋ ฅํ•˜๋Š” async ๋ฉ”์„œ๋“œ

์ „์ฒด ์ฝ”๋“œ

import UIKit
import Combine
actor TaskCounter{
    @Published private(set) var count = 0
    @Published private(set) var maxCount: Int = 0
    private(set) var completed = PassthroughSubject<Bool,Never>()
    private var subscription = Set<AnyCancellable>()
    func changeMax(_ max:Int){ self.maxCount = max }
    private func increment(){
        count += 1
        if maxCount == count{
            count = 0
            completed.send(true)
        }
    }
    private func reset(){ count = 0 }
    private func failed(){ completed.send(false) }
    var progress: AnyPublisher<(Int,Int),Never>{
        $count.combineLatest($maxCount).eraseToAnyPublisher()
    }
}
extension TaskCounter{
    func run<T>(_ results: [T],action:@escaping ((T) async throws ->Void)) async throws{
        subscription.removeAll()
        self.changeMax(results.count)
        self.reset()
        try await withThrowingTaskGroup(of: Void.self) { group in
            for result in results{
                group.addTask {
                    do{
                        try await action(result)
                        await self.increment()
                    }catch{
                        await self.failed()
                    }
                }
            }
            self.completed.sink(receiveValue: {[group] val in
                if !val{ group.cancelAll() }
            }).store(in: &subscription)
            try await group.waitForAll()
        }
    }
}
โœ… ๋ณ‘๋ ฌ๋กœ ์ง„ํ–‰ํ•˜๋Š” ์ž‘์—…์—์„œ Return ํ•˜๋Š” ๊ฐ’์ด ์กด์žฌํ•œ๋‹ค๋ฉด return ๊ฐ’์˜ ํƒ€์ž… ๊ณ„์ˆ˜์— ๋งž๊ฒŒ ์ œ๋„ค๋ฆญ ํƒ€์ž…์„ ์ถ”๊ฐ€ํ•œ run ๋ฉ”์„œ๋“œ๋ฅผ ์ž‘์„ฑํ•ด์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.