포스트

Async/Await (10)

Async Sequence를 지원하는 API

  1. URL
  2. FileHandle
  3. URLSession
  4. NotificationCenter

또한 우리가 알고있는 고차함수

map, compactMap, filter, first, prefix, zip 등을 활용할 수 있다.

WWDC

1. FileHandle

URL은 직전에 했기에 FileHandle을 사용해본다.

우선 File을 사용하기 위해 경로 설정을 해줘야한다.

1
2
let paths = Bundle.main.paths(forResourcesOfType: "txt", inDirectory: nil)
let fileHandle = FileHandle(forReadingAtPath: paths[0])

그리고 우린 파일 경로가 하나이기에 index를 0으로 해준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Task {
    for try await line in fileHandle!.bytes {
        print(line)
    }
}
/*
74
111
104
110
10
77
97
114
121
10
10
*/

각 라인별 바이트가 나온다.

2. URL

물론 파일도 URL을 통해서 읽을 수가 있다.

1
2
3
4
5
6
7
8
9
10
11
Task {
    let url = URL(fileURLWithPath: paths[0])
    
    for try await line in url.lines {
        print(line)
    }
}
/*
John
Mary
*/

한줄씩 읽는다.

3. URLSession

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let url = URL(string: "https://www.google.com")!
Task {
    let (bytes, _) = try await URLSession.shared.bytes(from: url)
    for try await byte in bytes {
        print(byte)
    }
}
/*
102
117
110
99
116
105
111
너무 길어서 후략
*/

4, Notification Center

1
2
3
4
5
6
7
Task {
    let center = NotificationCenter.default
    let _ = await center.notifications(named: UIApplication.didEnterBackgroundNotification).first {
        guard let key = ($0.userInfo?["Key"]) as? String else { return false }
        return key == "SomeValue"
    }
}

위의 코드는 앱이 Background드로 들어가는 상태일때

알림 중에서 userInfo에 “Key”라는 값이 있고, 그 값이 “SomeValue”와 일치하는 첫 번째 알림을 찾는다. 조건이 만족되면 해당 알림을 반환하고, 그렇지 않으면 다음 알림을 기다린다.

기존 Callback 또는 Handler를 AsyncSequence로 변환하기 위한 AsyncStream 활용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class BitcoinPriceMonitor {
    
    var price: Double = 0.0
    var timer: Timer?
    var priceHandler: (Double) -> Void = { _ in }
    
    @objc func getPrice() {
        priceHandler(Double.random(in: 20000...40000))
    }
    
    func startUpdating() {
        timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(getPrice), userInfo: nil, repeats: true)
    }
    
    func stopUpdating() {
        timer?.invalidate()
    }
    
}

Class를 하나 만들어 준다.

1
var priceHandler: (Double) -> Void = { _ in }

initializing을 하기 위해 위와 같이 만들어 준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let bitcoinPriceMonitor = BitcoinPriceMonitor()
bitcoinPriceMonitor.priceHandler = {
    print($0)
}

bitcoinPriceMonitor.startUpdating()

/*
21045.523121187536
20192.72163692504
34665.56744521043
29779.326709794215
35158.70015038814
25228.016388562803
33050.03001821222
23941.779428903712
29135.677928071524
22594.94240977771
*/

1초단위로 이렇게 출력이 된다.

이제 AsyncStream을 사용해서 만들어본다.

1
AsyncStream(Double.self) { continuation in

이렇게 closure형태로 쓰는데 괄호안에는 Return Type을 작성해준다.

그리고 하나 더 특이점이라면 continuation뒤에 yield를 사용해준다.

CleanShot 2024-12-02 at 01 10 54

대기 중인 작업을 중단 지점에서 주어진 결과의 성공 값을 반환하며 정상적으로 재개시킨다.

즉, 이전 작업에서 성공했던 값을 반환하면서 다음 값을 기다리는 상태로 전환한다. 라고 생각하면 될듯.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let bitcoinPriceStream = AsyncStream(Double.self) { continuation in
    let bitcoinPriceMonitor = BitcoinPriceMonitor()
    bitcoinPriceMonitor.priceHandler = { result in
        print("sending: \(result)")
        continuation.yield(result)
        print("sent: \(result)")
    }
    
    //continuation.onTermination = { _ in }
    
    bitcoinPriceMonitor.startUpdating()
}

Task {
    for await bitcoinPrice in bitcoinPriceStream {
        print("received: \(bitcoinPrice)")
    }
}

이해를 돕고자 print를 달아주었다.

1
2
3
4
5
6
7
8
9
10
11
12
sending: 38041.31187779264
received: 38041.31187779264
sent: 38041.31187779264
sending: 25532.912948323756
received: 25532.912948323756
sent: 25532.912948323756
sending: 32142.705564361964
received: 32142.705564361964
sent: 32142.705564361964
sending: 36629.111843853
sent: 36629.111843853
received: 36629.111843853

이런식으로 결과가 나온다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.