Deep_Dev

 

๐Ÿ“š 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