๐ Semaphore (์ธ๋งํฌ์ด )
์ผ๋จ ์ธ๋งํฌ์ด์ ๋ํด์ ํฌ์คํ ํ๊ธฐ์ ์ ๊ด๋ จ๋ ์ด์์ธ, ์ด์ ํฌ์คํ 'LiveActivity' ํฌ์คํ ์ ๋ณด๊ณ ์ค๋ฉด ์ดํดํ๋๋ฐ ๋์์ด ๋ ๊ฒ์ด๋ค.
https://leesangdo.tistory.com/310
[SwiftUI] LiveActivity ๊ตฌํํ ๋, ์ฑ ๊ฐ์ ์ข ๋ฃ์ LiveAcivity ์ข ๋ฃ์ ๋ํด
๐ Live Activity ์ข ๋ฃ '์ฌ๋ผ์' ์ฑ ๊ฐ๋ฐ์ ํ ๋, LiveActivity ๊ตฌํ์ ๋ด๋นํ์๋๋ฐ ๋น์์ ๊ฒช์๋ ์ด์์ ๊ด๋ จํ์ฌ ๋ ธ์ ์ ๊ธฐ๋กํด๋์๋๊ฒ์ ํฐ์คํ ๋ฆฌ์๋ ์ฎ๊ฒจ์ ์ด๋ณด๊ณ ์ ํ๋ค. ์ดํ ์ธ๋งํฌ์ด
leesangdo.tistory.com
์ ํฌ์คํ ์ ๋ณด๋ฉด ์๋ค์ํผ, LiveActivity ์ข ๋ฃ๋ฅผ Dynamic Island Expanded ์์ญ์์ X ๋ฒํผ์ ํตํด ์ข ๋ฃ์์ผ์ผํ๋ ์ฐํ์ ์ธ ๋ฐฉ๋ฒ์ผ๋ก ๊ตฌํ์ ๋ง์ณ๋์์๋ค.
์ข ๋ฃ๋ฒํผ์ ํตํด ์ข ๋ฃ๊ฐ ๋๋ ์ฐํ์ ์ธ๊ฒ์ด ์๋์ง๋ ๋ชฐ๋ผ๋, ๊น๋ํ์ง ์์ ๋ฐฉ๋ฒ์ด๋ผ๋ ๊ฒ์ ํ์คํ๋ค. ๐ญ๐ญ
๋ฐฑ๊ทธ๋ผ์ด๋์์ญ์์ ์ฑ์ ๊ฐ์ ์ข ๋ฃํ๋ฉด LiveActivity๊ฐ ์ฌ๋ผ์ง์ง ์๋ ๊ฒ์ ์ฌ์ ํ๊ธฐ ๋๋ฌธ์ด๋ค.
์ค์ ์ฐ๋ฆฌ์ ํ๊ฒ ์ ์ ๊ฐ, LiveActivity๊ฐ ๊ณ์ ๋จ์์๋๊ฑธ๋ก ๋ฌธ์๋ฅผ ํ์ผ๋
์ฌ์ฉ์ ์ ์ฅ์์๋ ์ ๋ง ์ข์ง ์์์ฑ๋ก ๋จ์์์๋ค.
์ด ์ด์๋ง์ด ๊ณ์ ๋ง์์ ๊ฑธ๋ ธ๊ณ , ์ง๊ธ์ ํด๊ฒฐ์ ํ๊ธฐ์ ํฌ์คํ ์ ํ๋ค.
๋น์ ๊ฐ๋ฐ์ ํ ๋์๋, ๋ก์ปฌ๋ก๋ง ๋์ํ๋ ์ฑ์ด๊ธฐ์ ์ธ๋ถ(์๋ฒ)์ ๋์์์ด๋ ์ฑ์ ๊ฐ์ ์ข ๋ฃํ์๋ ์ถ๊ฐ์ ์ธ ํจ์ ์คํ์ด ๋ถ๊ฐ๋ฅํ์ค ์์๋๋ฐ, ์ต๊ทผ์ ๋ค์ ๊ตฌ๊ธ๋ง์ ํด๋ณด๋ ์ฑ์ด ์ข ๋ฃ๋์์ ๋ ์คํ๋๋ ๋ฉ์๋๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณต๋๊ณ ์์๋ค.
์ฐ์ SwiftUI์๊ธฐ๋๋ฌธ์, AppDelegate ํ์ผ์ ์์ฑํด์ฃผ์ด์ผํ๋ค.
class AppDelegate: UIResponder, UIApplicationDelegate {
// ์ฑ์ด ์ข
๋ฃ๋ ๋ ์คํ
func applicationWillTerminate(_ application: UIApplication) {
print(#function)
}
}
์์ ์๋ AppDelegate ํ์ผ๋ด์ applicationWillTerminate๊ฐ ์ฑ์ด ์ข ๋ฃ๋์์๋ ์๋์ผ๋ก ์คํ๋๋ ํจ์์ด๋ค.
์ดํ, @main ํ์ผ์์ AppDelegate๋ฅผ ์ ๊ทผํ ์ ์๊ฒ ์ฐ๊ฒฐ์ ํ๋ค.
import SwiftUI
@main
struct ContentApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
์ด๋ ๊ฒ ๋ง์ด๋ค.
์ด๋ ๊ฒํ๊ณ ํ ์คํธ๋ฅผ ํด๋ณด๋ฉด, ์ฑ์ ์ค์์ดํ๋ก ๊ฐ์ ์ข ๋ฃํ์๋ AppDelegate์ ํจ์๊ฐ print๋๋๊ฒ์ ํ์ธํ ์ ์์๋ค.
์ดํ๋ถํฐ๊ฐ ์ฝ๊ฐ์ ๊ณ ๋ฏผ์ด์๋ค.
class AppDelegate: UIResponder, UIApplicationDelegate {
// ์ฑ์ด ์ข
๋ฃ๋ ๋ ์คํ
func applicationWillTerminate(_ application: UIApplication) {
Task {
print(#function)
await NoiseMeter.shared.endLiveActivity()
}
}
}
NoiseMeter.shared.endLiveActivity()๊ฐ LiveActivity๋ฅผ ์ข ๋ฃ์ํค๋ ํจ์์ธ๋ฐ, ์ด๋ ๊ฒ ์คํ์ ํ๋๋ applicationWillTerminate()๋ ์คํ์ด ๋๋๋ฐ, endLiveActivity()๋ ์คํ์ด ๋์ง ์์๋ค.
๐ค ์ ? ์ด์งธ์ ? ๐ค
์ ์ ์๊ฐ์ ํด๋ณด์๋๋ฐ, ๋์ ์๊ฐ์ผ๋ก๋ applicationWillTerminate๊ฐ ํธ์ถ๋ ์์ ์์, ๋น๋๊ธฐํจ์์ธ endLiveActivity๊ฐ ๋น๋๊ธฐ๋ก ์์ ์ ์์ํ๋ฉด์ ํ๋ก์ธ์ค๋ฅผ ๋๋ฌด ๋นจ๋ฆฌ ์ข ๋ฃํด๋ฒ๋ ค ๋ก์ง์ด ๋๊น์ง ์คํ๋์ง ๋ชปํ๋ ๊ฑธ๋ก ํ๋จํ๋ค.
๊ทธ๋์ ๊ธฐ์กด์ ๋น๋๊ธฐ ํจ์์ธ endLiveActivity()ํจ์๋ฅผ
func endLiveActivity() async {
print(#function)
let semaphore = DispatchSemaphore(value: 0)
Task {
if let currentActivity = activity {
await currentActivity.end(nil, dismissalPolicy: .immediate)
print("Ending the Live Activity: \(currentActivity.id)")
self.activity = nil
}
semaphore.signal()
}
cancellation?.cancel()
updateTimer?.invalidate()
updateTimer = nil
Task {
await self.stopMetering()
}
semaphore.wait()
}
์ด๋ ๊ฒ ์ฐธ๊ณ ํ ๋ ํผ๋ฐ์ค์ฒ๋ผ, ์ธ๋งํฌ์ด๋ฅผ ์ด์ฉํด์ ๋น๋๊ธฐ ํจ์๊ฐ ๋ชจ๋ ๋๋๋๊ฑธ ๊ธฐ๋ค๋ฆฌ๋ฉด์ ๋ชจ๋ ์คํ๋๊ณ ๋๋ ์ ์๊ฒ ์๋ํ๋๋ฐ
semaphore.wait()
์ ์ฝ๋๊ฐ, Swift6์์๋ ๋น๋๊ธฐํจ์ ๋ด๋ถ์์ ์ธ๋งํฌ์ด๋ก ํ์ฌ๊ธ ์ค๋ ๋๋ฅผ ์ง์ ๋ธ๋กํน ํ๋๊ฒ์ ๋ง๋๋ค๊ณ ํ๋ค. ๊ทธ๋์ ๋น๋๊ธฐ ํจ์ ๋ด์์ ๋๊ธฐ์ ์ผ๋ก ๋๊ธฐํ ์ ์๋ค๊ณ ํ๊ธฐ์, wait()์ ๋นผ๊ณ endLiveActivity๋ ๋๊ธฐํจ์๋ก ์์ ํ์๋ค.
๊ทธ ๋ค์์ ๋ด๋ถ์์ ์ถ๊ฐ์ ์ผ๋ก ํธ์ถํ๋ stopMetering()๋ ๋น๋๊ธฐ์์ด์ ์ญ์๋ ๋ผ์ด๋ธ์กํฐ๋นํฐ๊ฐ ์ข ๋ฃ๋์ง ์์๋ค.
์ต์ข ์ ์ผ๋ก endLiveActivity()์ stopMetering()๋ ๋๊ธฐํจ์๋ก ๋ณ๊ฒฝ์ ํ์๋ค. ๊ทผ๋ฐ ์ด๋ ๊ฒ ๋ชจ๋ ๋ชจ๋ ๋๊ธฐ๋ก ์ฒ๋ฆฌํด๋ฒ๋ฆฌ๋๊น, ๊ตณ์ด ์ธ๋งํฌ์ด๊ฐ ํ์ํ์ง ์๊ฒ ๋๋ฉด์ ์ธ๋งํฌ์ด ์ฝ๋๊ฐ ๋ชจ๋ ํ์ํ์ง ์๊ฒ ๋์๋ค.
๊ธฐ์กด์ ํ์์ด ๋น๋๊ธฐํจ์๋ก ๊ตฌํ์ ํ์๋๋ฐ, ํด๋น ํจ์๋ค์ด ์๊ฐ์ด๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ง์ด ์ก์๋จน์์ ๋๋ก ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํด์ผํ๋ ์์ ์ ์๋๊ธฐ์ ๋๊ธฐ๋ก ๋ณ๊ฒฝํ๋ฉด์
์ฑ ๊ฐ์ ์ข ๋ฃ -> applicationWillTerminate ์คํ -> endLiveActivity()๋ฅผ ์คํํ๋ฉด์
์ฑ ๊ฐ์ ์ข ๋ฃํ์๋์๋ ๋ผ์ด๋ธ ์กํฐ๋นํฐ๊ฐ ๊น๋ํ๊ฒ ๊ฐ์ด ์ข ๋ฃ๋๊ฒ ๊ตฌํ ๋ฐ ์์ ์ ํด๋์๋ค !
์ต์ข ์ฝ๋
func applicationWillTerminate(_ application: UIApplication) {
print(#function)
NoiseMeter.shared.endLiveActivity()
print("[์ฑ ๊ฐ์ ์ข
๋ฃ ๋จ : endLiveActivity Done]")
}
func endLiveActivity() {
print(#function)
Task {
if let currentActivity = activity {
await currentActivity.end(nil, dismissalPolicy: .immediate)
print("Ending the Live Activity: \(currentActivity.id)")
self.activity = nil
}
}
cancellation?.cancel()
updateTimer?.invalidate()
updateTimer = nil
self.stopMetering()
}
๊ทธ๋ฐ๋ฐ, ์์ง ์กฐ๊ธ ๋ ํ ์คํธํด๋ณด๊ณ ์ถ์ ๋ถ๋ถ์ด์๋ค.
๋ฐ์ ์๋ ๋ด๊ฐ ์ฐธ๊ณ ํ ๋ ํผ๋ฐ์ค๋ฅผ ๋ณด๋ฉด
"๋ฉ์๋๊ฐ ๋ฐํ๋์ด์ ์ค์ ๋ก ์ข ๋ฃ๋ ๊ธฐํ๋ฅผ ๊ฐ๊ธฐ์ ์ ์ฑ์ด ์ข ๋ฃ๋ ์ ์๋ค."
"๋ฉ์๋์์ ๋ฐํํ๊ธฐ ์ ์ ํ๋์ด ๋๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๋๋ก ๊ฐ์ ํ๋ ์ธ๋งํฌ์ด๋ฅผ ์ถ๊ฐํ๋ค."
๋ผ๊ณ ์ค๋ช ์ ํ๋ค.
๊ทธ๋ฆฌ๊ณ ์์๋ก ์ฒจ๋ถํ ์ฝ๋๋ ํจ์๋ช ์์ฒด๋ ๋น๋๊ธฐ๋ก ๋์ด์๋ค.
์ฆ, ๋๋ ๊ธฐ์กด์ ๋น๋๊ธฐ ํจ์๋ฅผ ๋๊ธฐํจ์๋ก ์์ ํ๋ฉด์ ์ธ๋งํฌ์ด๋ฅผ ์ฌ์ฉํ์ง ์์๋ ์ด์๋ฅผ ํด๊ฒฐํ๋๋ฐ
๊ธฐ์กด์ฒ๋ผ ๋น๋๊ธฐํจ์๋ฅผ ์ ์งํ๋ฉด์ ์ธ๋งํฌ์ด๋ฅผ ์ด์ฉํด ๋ผ์ด๋ธ ์กํฐ๋นํฐ๋ฅผ ์ข ๋ฃํ๋๊ฒ์ผ๋ก ์ฝ๋๋ฅผ ๋ค์ ํ ์คํธ ํด๋ด์ผ๊ฒ ๋ค.
์ด๊ฒ์ ์ด์ด์ ์ธ๋งํฌ์ด ์ ๋ฆฌ์ 3ํธ์ผ๋ก ํฌ์คํ ์ ํด๋ณผ ๊ฒ์ด๋ค. ๐๐ฅ๐ฅ
์ฐธ๊ณ ๋ ํผ๋ฐ์ค
https://developer.apple.com/forums/thread/732418
Dismiss Live Activities on App Ter… | Apple Developer Forums
I'm stuck in the same situation, I was working with a live activity that creates a timer but I closed the app and couldn't get the timer to stop until I deleted the app, I even tried restarting my phone which did not help at all.
developer.apple.com
'๐ iOS > Swift' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Swift][iOS] GCD API ๋์ ๋ฐฉ์๊ณผ ํ์์ฑ (1) (0) | 2023.06.13 |
---|---|
[Swift][iOS] QOS (0) | 2023.06.13 |
[Swift] Struct์ Class์ ์ฐจ์ด์ (1) | 2023.05.15 |
[Swift] Dispatch Queue (0) | 2023.01.27 |