ํฐ์คํ ๋ฆฌ ๋ทฐ
ํด๋น ๊ธ์, ํ๋ฉด๋ฐฉ์ก์ ๋ํ ๊ฐ์ธ ๊ณต๋ถ๋ฅผ ์งํํ๋ฉฐ ์์ฒญํ WWDC 2018 - Live Screen Broadcast with ReplayKit์ ์ ๋ฆฌํ ๊ธ์ ๋๋ค.
ํด์๊ณผ ์ ๋ฆฌ๋ ๋ด์ฉ์ด ์๋ฒฝํ์ง ์์ผ๋ฏ๋ก, ์๋ชป๋ ๋ถ๋ถ์ ๋ํด์ ์ง์ ๋ถํ๋๋ฆฝ๋๋ค. ๐
1. WWDC 2016 - Go Live with ReplayKit
2. WWDC 2018 - Live Screen Broadcast with ReplayKit
- ReplayKit์ ์ฌ์ฉ์์ App์ ํ๋ฉด, ์ค๋์ค ๋ฐ ๋ง์ดํฌ ์ฌ์ด๋๋ฅผ ์ค์๊ฐ์ผ๋ก ๊ธฐ๋กํ๊ณ ์์ ํ๊ณ ๊ณต์ ํ ์ ์๊ฒ ํด์ฃผ๋ ํ๋ ์์ํฌ์ด๋ค.
- ReplayKit์ Live Broadcast๋ฅผ ์ง์ํ๋ค.
ReplayKit
- ReplayKit์ ํ๋ฉด๊ณผ ์ค๋์ค ๊ทธ๋ฆฌ๊ณ 3rd-party ๋ผ์ด๋ธ์คํธ๋ฆฌ๋ฐ ์๋น์ค๋ฅผ ์ ๊ณตํ๋ค.
- ReplayKit์ ์ค์๊ฐ๋ฐฉ์ก ์ฑ์ ์ํด ๋ค๋ฅธ Application์์ ์บก์ฒํ ์ฝํ ์ธ ๋ฅผ ์์ ๋ฐ๊ณ , ๋์ผํ ๋๋ฐ์ด์ค์์ ๋ฐ๋ก ์๋ฒ๋ก ์ธ์ฝ๋ฉ ๋ฐ ์คํธ๋ฆฌ๋ฐ ํ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
ReplayKit์ ์๋์ ๊ฐ์ ๋ค์ํ ํน์ง์ ๊ฐ์ง๋ค
- HD ํ์ง์ ์ ๊ณต
- ๋ฎ์ ์ง์ฐ ์๊ฐ
- ์ ์ ์ฑ๋ฅ ์ํฅ
- ์ ์ ๋ฐฐํฐ๋ฆฌ ์ฌ์ฉ๋
- ๊ฐ์ธ ์ ๋ณด ๋ณดํธ ๋ฐฉ์ ๋ง๋ จ
Live Broadcast
- 3rd-party ์๋น์ค๋ฅผ ํตํ ๋ผ์ด๋ธ ๋ฐฉ์ก ์๋น์ค๋ฅผ ์ ๊ณตํ๋ค.
- ์ค๋์ค ๋ฐ ๋น๋์ค๋ฅผ ์ฌ์ฉ์์ ๋๋ฐ์ด์ค์์ ์ง์ stream ๊ฐ๋ฅํ๋ค.
- ํ๋ฉด ๋ นํ๋ฅผ ํ๋ ๋์์ ๋ง์ดํฌ๋ฅผ ํตํ ์ฌ์ฉ์์ ์์ฑ๋ ๋ น์ ๋ฐ FaceTime์ ํ์ฉํ ์นด๋ฉ๋ผ ๊ธฐ๋ฅ๋ ์ ๊ณตํ๋ค (์นด๋ฉ๋ผ๋ iOS ํ์ )
- ๋ชจ๋ ์ฝํ ์ธ ๋ค์ ์์ ํ๊ณ , ์ค์ง broadcast service๋ฅผ ํตํด์๋ง ์ ๊ทผ ๊ฐ๋ฅํ๋ค.
Usage example
- Youtube๋ฅผ ํตํ ๊ฒ์ ํ๋ ์ด ์์
- WebEx ์ ํ๋ฅผ ํตํ ํ๋ฉด ๊ณต์
- TeamViewerQS์ ๊ฐ์ ์๋น์ ์ง์ ์์
- ๊ทธ๋ฆผ ๊ทธ๋ฆฌ๋ ์ฑ์ Streaming ํ๋ Facebook
ReplayKit VS ReplayKit 2
- ๊ธฐ์กด์ ReplayKit์ ํ์ฉํ broadcast์์๋, ์ฌ์ฉ์๊ฐ App์ ์ฌ์ฉํ์ฌ ๋ฐฉ์ก์ ์์ํ๊ณ ์ค์งํฉ๋๋ค.
- ๋ถ๋ชจ App์ ReplayKit API์ ์ง์ ํต์ ํ์ฌ Extension์ฑ์ ํตํด ๋ถ๋ชจ App์ ๋ฏธ๋์ด ๋ฐ ์ค๋์ค ์ฝํ ์ธ ๋ฅผ ์ ๊ณตํฉ๋๋ค.
- Extension์ฑ์ ํด๋น ์ฝํ ์ธ ๋ค์ ์ธ์ฝ๋ฉํ์ฌ ์๋ฒ๋ก ์คํธ๋ฆฌ๋ฐ ํฉ๋๋ค.
- ์ด ์ ์ฒด์ ๊ณผ์ ์ ์ฐ๋ฆฌ๋ In-App Broadcast๋ผ๊ณ ๋ถ๋ฆ ๋๋ค.
์๋ก์์ง ReplayKit2์์๋ ํน์ ์ฑ์ ์ข ์๋๋ ํ๋ฉด์ด๋ ์ค๋์ค๋ฟ ์๋๋ผ, ์์คํ ์ ์ฒด์ ์ค๋์ค๋ฅผ ๋ น์ํ๊ณ ํ๋ฉด์ ๋ฐฉ์กํ ์ ์์ต๋๋ค.
- ์ฌ์ฉ์๋ Contol Center๋ฅผ ํตํด ๋ฐฉ์ก์ ์์ํ ์ ์์ต๋๋ค.
- Control Center๋, ๋๋ฐ์ด์ค ์๋จ์ ํ๋จ์ผ๋ก swipe ํ๋ฉด ๋์ค๋ ๋ค์ํ ์ ์ด์ผํฐ ํญ๋ชฉ๋ค.. ๊ทธ์ค์์, ํ๋ฉด ๋ นํ ๋ฒํผ์ ์ฌ์ฉ - ๊ทธ๋ฆฌ๊ณ ๋ง์ฐฌ๊ฐ์ง๋ก, ๋ นํ๋๋ ํ๋ฉด๊ณผ ๋ น์๋๋ ์์ฑ๋ค์ Extension์ผ๋ก ๊ตฌํ๋ ์ฑ์์ Server์ ํต์ ์ ํตํด ๋ฐฉ์ก์ด ๋ฉ๋๋ค.
์ฝ์ ํธ์ ์ผ๋ก, ์ด ๋ ๊ฐ์ ๋ฐฉ์์ ์์ ํ ๋ค๋ฅด๋ค.
1. In-App Broadcast
App or Game (์ฑ ๋๋ ๊ฒ์)
- ๋น๋์ค ๋๋ ์ค๋์ค ์ฝํ ์ธ ๋ฅผ ์ ๊ณต: ์ฑ์ด๋ ๊ฒ์์์ ์ฌ์ฉ์์๊ฒ ํ๋ฉด์ ๋ นํํ๊ฑฐ๋ ๋ฐฉ์กํ ์ ์๋ ์ฝํ ์ธ ๋ฅผ ์ ๊ณตํฉ๋๋ค.
- App ๋ด์์ ์ง์ ๋ฐฉ์ก ์์๊ณผ ์ค์ง: ์ฑ์ ์ฌ์ฉ์์๊ฒ ๋ฐฉ์ก์ ์์ํ๊ณ ์ค์งํ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
Broadcaster App (Extension App) (๋ฐฉ์ก ํ์ฅ ์ฑ)
- 3rd-party ์คํธ๋ฆฌ๋ฐ ์๋น์ค์ ๋ก๊ทธ์ธ: ๋ฐฉ์ก ํ์ฅ ์ฑ์ ์ฌ์ฉ์๊ฐ YouTube, Facebook Live ๋ฑ์ ์ธ๋ถ ์คํธ๋ฆฌ๋ฐ ์๋น์ค์ ๋ก๊ทธ์ธํ ์ ์๋๋ก ๋์์ค๋๋ค.
- ๋ฐฉ์ก ์ฝํ ์ธ ๋ฅผ ์๋ฒ์ ์ ๋ก๋: ์ฌ์ฉ์๊ฐ ๋ฐฉ์ก ์ฝํ ์ธ ๋ฅผ ์ค์๊ฐ์ผ๋ก ์คํธ๋ฆฌ๋ฐ ์๋น์ค์ ์๋ฒ์ ์ ์กํ๋ ์์ ์ ์ฒ๋ฆฌํฉ๋๋ค.
2. iOS System Broadcast
- ํ๋ฉด์ ๋ ธ์ถ๋๋ ๋ชจ๋ ํ๋๊ณผ ์ฌ์ด๋๋ฅผ ๋ฐฉ์กํ ์ ์๋ค.
- Control Center๋ฅผ ํตํด์ ๋ฐฉ์ก์ ์์ ๋ฐ ์ข ๋ฃ๋ฅผ ํ ์ ์๋ค.
- ์์คํ
์ ๋ฐ์ ๊ฑธ์ณ์ ์คํ๋๋ฏ๋ก, ์ธ์
์ด ์๋์ ์ํฉ์์๋ ๊ณ์ ์ด์ด์ง๋ค.
- ํ ํ๋ฉด์์ ์ฑ์ผ๋ก ์ด๋ํ๊ฑฐ๋
- ํ ์ฑ์์ ๋ค๋ฅธ ์ฑ์ผ๋ก ์ด๋ํ ๋ - iOS11+ ์์ ์ฌ์ฉ ๊ฐ๋ฅํ๋ค.
System Broadcast Picker
- ์ฌ์ฉ์๋ค์ iOS syetem broadcast๋ฅผ App์ ๋ฒ์ด๋์ง ์๊ณ ์์ํ ์ ์๊ณ , ๋งค์ฐ ๋จ์ํ ๋ฒํผ ํ๋๋ก ๋์์ด ๊ฐ๋ฅํ๋ค.
- iOS 12๋ถํฐ ์ฌ์ฉ ๊ฐ๋ฅํ๋ค.
// RPSystemBroadcastPickerView
class RPSyetemBroadcastPickerView: UIView {
open var preferredExtension: String
}
class ViewController: UIViewController {
var broadcastPicker: RPSystemBroadcastPickerView?
override func viewDidLoad() {
super.viewDidLoad()
broadcastPicker = RPSystemBroadcastPickerView(frame: ์ํ๋_ํ๋ ์)
view.addSubview(broadcastPicker!)
}
}
- UIView๋ฅผ ์๋ธํด๋์ฑํ RPSystemBroadcastPickerView๋ฅผ ์ฌ์ฉํ๋ค.
- Storyboard์ UI-Component๋ก ์ฌ์ฉํ ์ ์๊ณ , ์ฝ๋ ๋ฒ ์ด์ค๋ก๋ ์ฌ์ฉ ๊ฐ๋ฅํ๋ค.
- ์ด ์๋ก์ด API๋ฅผ ํตํด, ์ฌ์ฉ์๋ Application์์ ์ง์ ๋ฐฉ์ก์ ์์ํ ์ ์๊ณ , ํ๋ฉด๋
นํ ๋ฐ ์ค์ ์ ํ์ฑํํ๋ ๋ฐฉ๋ฒ ๋ฑ์
Control Center์์ ์ฝ๊ฒ ์ฐพ์ ์ ์๋ค.
์ฌ์ฉ์๋ ์์ ์ ์๋น์ค๋ง์ด PickerView์ ๋
ธ์ถ๋๊ธฐ๋ฅผ ์ํ ๊ฒ์ด๊ณ ,
ReplayKit์ ์ด๊ฒ์ ์ ๊ณตํ๊ธฐ ์ํด preferredExtension์ด๋ผ๋ API๋ฅผ ์ ๊ณตํ๋ค.
preferredExtension
๊ธฐ๋ณธ์ ์ผ๋ก RPBroadcastPickerView๋ Syetem์์ ์ ๊ณตํ๋ ๋ค์ํ `Broadcast Extension`์ ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ์ฃผ์ง๋ง, preferredExtension`์ ์ค์ ํ์ฌ ํน์ Broadcast Extension๋ง ์ ํ์ ์ผ๋ก ํ์ํ ์ ์๋ค.
class ViewController: UIViewController {
var broadcastPicker: RPSystemBroadcastPickerView?
override func viewDidLoad() {
super.viewDidLoad()
broadcastPicker = RPSystemBroadcastPickerView(frame: ์ํ๋_ํ๋ ์)
broadcastPicker?.preferredExtension = "com.vaultmicro.camerafi.ios.extension" // ๋์ Extension์ ๋ํ bundle identifier
view.addSubview(broadcastPicker!)
}
}
- preferredExtension์์ฑ์ broadcast picker๋ฅผ ํน์ Broadcast Extension์ ์ฐ๊ฒฐํ๋ ์ญํ ์ ํ๋ค.
- preferredExtension์๋ ์ฌ์ฉ์์ Broadcast Extension์ ๋ํ Bundle Identifier๋ฅผ ๋ฑ๋กํ ์ ์๋ค.
- ์ด๋ ๊ฒ ๋๋ฉด, PickerView์๋ ์ฌ์ฉ์์ ์ฑ๋ง ๋ชฉ๋ก์ ๋์ด. - ๊ทธ๋ฆฌ๊ณ ํด๋น property๋ `RPBroadcastPickerView`๊ฐ present ๋๊ธฐ ์ ์ ๋ฏธ๋ฆฌ setting ํด์ผ ํ๋ค.
RPSystemBroadcastPickerView
- RPSystemBroadcastPickerView๋ ๋ฐฉ์ก์ ์์ํ๊ฑฐ๋ ์ ํํ ์ ์๋ ์์คํ ์ UI๋ฅผ ํ์ํ๋ค.
- RPSyetemBroadcastPickerView๋ ์ฌ์ฉ์๊ฐ ๋ฐฉ์ก์ ์์ํ ๋, ์ฌ๋ฌ ๋ฐฉ์ก ์ต์ ์ ์ ํํ ์ ์๊ฒ ํด ์ค๋ค.
- RPSyetemBroadcastPickerView๋ ๋ฐฉ์ก ์ธ์
์ ์ํ๋ฅผ ์ง์ ์ ์ผ๋ก ๊ด๋ฆฌํ์ง ์๋๋ค.
- ํด๋น ๋ทฐ๋ broadcast session์ ์์ํ๊ณ , ํ๋ฉด์ ๋ฐฉ์ก์ ์ ํํ๋ UI๋ฅผ ํ์ํ์ง๋ง, ๋ฐฉ์ก ์ธ์ ์์ฒด๋ ๋ค๋ฅธ ํด๋์ค์์ ๊ด๋ฆฌํ๋ค.
Session๊ณผ์ ์ฐ๊ฒฐ ์์
1. Screen recording control: ํ๋ฉด ๋
นํ ์ ์ด๋ฅผ ๋ด๋นํ๋ค.
2. Broadcast picker: ๋ฐฉ์ก ์๋น์ค๋ฅผ ์ ํํ๊ณ , RPSystemBroadcastPickerView๊ฐ ๋ด๋น
3. Status bar delegate: ์ํ ํ์์ค์ ๊ด๋ฆฌํ๋ ๋ถ๋ถ
Developing Broadcast Extensions (For ReplayKit 2)
Broadcast App and Extension
- Broadcast App๊ณผ Extension์ ๋ถ๋ฆฌ๋ ๋ฐ์ด๋๋ฆฌ ํ์ผ์ ๊ฐ์ง๊ณ ์๋ค.
- ๊ทธ๋ฆฌ๊ณ ๊ฐ๊ฐ์ ๋ ๋ฆฝ๋ ํ๋ก์ธ์ค์์ ๋์ํ๋ค.
Broadcast Application: ์คํธ๋ฆฌ๋ฐ ์ฌ์ดํธ ๋ก๊ทธ์ธ, ๋ฐฉ์ก ์ ๋ชฉ ์ค์ ๋ฑ๋ฑ
Broadcast Upload Extension: sample์ ์ธ์ฝ๋ฉ, ์ค์ ์๋ฒ์ upload!
Broadcast Upload Extension
- App์ผ๋ก๋ถํฐ ์ค๋์ค์ ๋น๋์ค sample์ ๋ฐ์์จ๋ค.
- ํด๋น sample์ ์ธ์ฝ๋ฉํด์, Video Stream์ ์ ๋ก๋ํ๋ค.
- ๋๋ฐ์ด์ค์ ํ์ ์ ๊ฐ์งํ๋ค.
// SampleHandler created by Xcode templates for Upload Extension
class SampleHandler: RPBroadcastSampleHandler {
// User has requested to start the broadcast
override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?)
// User has requested to finish the broadcast
override func broadcastFinished()
// Handle the sample buffer here
override func processSampleBuffer(
_ sampleBuffer: CMSampleBuffer,
with sampleBufferType: RPSampleBufferType
)
// Use details of application to annotate the broadcast
override func broadcastAnnotated(withApplicationInfo info: [String: NSObject])
}
- Broadcast Upload Extension์ ์์ฑํ๊ฒ ๋๋ฉด, SampleHandler๋ผ๋ Class๊ฐ ์์ฑ๋๋ค.
- ํด๋น Class๋ฅผ ํ์ฉํด์, Broadcast๋ด์ ๊ฐ์ข ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๊ฒ ๋๋ค.
- ๋ํ, App์ผ๋ก๋ถํฐ ๋ค์ด์ค๋ ์ค๋์ค์ ๋น๋์ค sample์ ์ฒ๋ฆฌํ๋ ๊ธฐ๋ฅ์ ๊ตฌํ ๊ฐ๋ฅํ๋ค
Broadcast Lifecycle
1. Handling Sign-In and Broadcast Setup
- ์ด ๋จ๊ณ์์๋ ์์ง, Extension๊ณผ์ ์ฐ๊ฒฐ์ด ๋์ง ์์ ์ํ์ด๋ค.
- ๋ํ, Extension๊ณผ ์ฐ๊ฒฐ๋๊ธฐ ์ Broadcast Service์ ๋ก๊ทธ์ธ์ด๋, ๋ฐฉ์ก ์ ๋ชฉ ์ค์ ๋ฑ๊ณผ ๊ฐ์ ๋ฐฉ์ก ์ค๋น ๊ณผ์ ์ ์ธํ ํ๊ฒ ๋๋ค.
2. Initialization
- Control Center๋ฅผ ํตํด์ ํน์ Broadcast Picker๊ฐ ๊ตฌํ๋ ์ฌ์ฉ์์ App์ ํตํด
- ReplayKit์ด Extension์ ํ๋ก์ธ์ค๋ฅผ ์คํ์ํค๋ฉด, SampleHandler ํด๋์ค์ ์ธ์คํด์ค๊ฐ ์์ฑ๋๋ค.
class SampleHandler: RPBroadcastSampleHandler {
// override init to read login credentials from shared keychain
override func init() {
super.init()
session = BroadcastSession.instance
var credentials = KeychainAccess.getLoginCredentials()
session.authentificate(credentials)
}
}
3. Handling broadcastStarted
- Extension์ด ์คํ๋๊ณ SamplerHandler ๊ฐ์ฒด๊ฐ ์์ฑ๋๋ฉด, Extension์ ReplayKit์ด broadcastStarted(withSetupInfo:)๋ผ๋ ํจ์๋ฅผ ํตํด ๋น๋์ค์ ์ค๋์ค sample์ ์ ๊ณตํ๊ธฐ ์์ํจ์ ์๋ฆฐ๋ค.
- ์ด broadcastStarted(withSetupInfo:)๋ฅผ ํตํด, ์์ ์์ง์ ๋ง๋ค๊ฑฐ๋ sample์ ์ค์๊ฐ์ผ๋ก ๋ฐ์์ ์ธ์ฝ๋ฉํ์ฌ ์๋ฒ์ ์ ๋ก๋ํ๋ ๋ฐ ํ์ํ ์์ ๋ค์ ์ํํ๊ณ ์ถ์ ๊ฒ์ด๋ค.
4. Processing Media Samples
- ReplayKit์ ์์ ๋ฐ ์ฌ์ด๋ sample์ raw data์ ์ ๊ณตํด ์ฃผ๊ณ , Extension์ ์ด ๋ฐ์ดํฐ๋ฅผ ์ธ์ฝ๋ฉํ๋ ์์ ๊ณผ ์ด๊ฒ์ ์ค์ ์๋น์ค์ ์ ์กํ๋ ๊ณผ์ ์ด ํ์ํ๋ค.
processSampleBuffer
ReplayKit์ Extension์ 3๊ฐ์ง ํ์ ์ sample๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ค.
- Video (๋ นํํ๋ ํ๋ฉด)
- Audio[App] (์คํ์ค์ธ Application์ ์ฌ์ด๋)
- Audio[Microphone] (ํ์ฌ ๋ง์ดํฌ์์ ์์๋๋ ์ฌ์ด๋)
Extension์ ์ด 3๊ฐ์ง ํ์ ์ ๋ชจ๋ ์ธ์ฝ๋ฉํ์ฌ, ์ํ๋ ์คํธ๋ฆฌ๋ฐ ์๋น์ค์ upload ํ๋ค.
override func processSampleBuffer(
_ sampleBuffer: CMSampleBuffer,
with sampleBufferType: RPSampleBufferType
) {
switch sampleBufferType {
case .video:
var imageBuffer: CVImageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
var pts = CMSampleBufferGetPresentationTimeStamp(sampleBuffer) as CMTime
VTCompressionSessionEncoderFrame(session, imageBuffer, pts, kCMTimeInvalid, nil, nil, nil)
break
case .audioApp:
// Handle audio sample buffer for app audio
break
case .audioMic:
// Handle audio sample buffer for mic audio
break
}
}
- ์ด ํจ์๋ buffer์ ํ์ ์ค ํ๋์ธ CMSampleBuffer๋ฅผ ์ธ์๋ก ๋ฐ์์จ๋ค.
- ๋ํ, ์ด ์ฝ๋ ์์์์ CMSampleBuffer๋ฅผ Encode ํ๋ ๊ณผ์ ์ ๋ณด์ฌ์ค๋ค.
5. Handling Application Information
- ์ด๋ ๊ฒ sample์ ์ ์ ํ๊ฒ encode ํด์ ์๋ฒ์ upload ํ๋ฉด, ์ ์ธ๊ณ์ ์ฌ๋๋ค์ ํด๋น ๋ฐฉ์ก์ ๋ณผ ์ ์์ต๋๋ค.
- ํ์ง๋ง, ์ด ์ ์ธ๊ณ์ ์์ฒญ์๋ค์๊ฒ '๋์ ๋ฐฉ์ก'์ ๋ํ ์ ๋ณด๋ฅผ ์๋ ค์ผ ํ ํ์๊ฐ ์์ต๋๋ค.
๋ฐ๋ผ์, ReplayKit์ broadcastAnnotatedWithApplicationInfo๋ผ๋ API๋ฅผ ์ ๊ณตํฉ๋๋ค.
broadcastAnnotatedWithApplicationInfo
- ์ด API๋ ๋ฐฉ์ก์ ์ ๋ก๋ ํ ๋, ํด๋น ๋ฐฉ์ก์ ๋ํ ์ ๋ณด (๋ฐฉ์ก ์ ๋ชฉ, ์ค๋ช , ๋ฐฉ์กํ๋ ๊ฒ์ ์ด๋ฆ ๋ฑ)์ ์๋ฒ์ ์ถ๊ฐ๋ก ํฌํจ์ํฌ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณต
- ๋น๋์ค ๋ฐ ์ค๋์ค sample์ Extension์ ํตํด ์๋ฒ์ uploadํ ๋, ์ด ๋ฐฉ์ก์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ํจ๊ป ํฌํจ์ํฌ ์ ์์ต๋๋ค.
override func broadcastAnnotated(withApplicationInfo applicationInfo: [AnyHashable : Any]) {
var bundleIdentifier = applicationInfo[RPApplicationInfoBundleIdentifierKey]
if bundleIdentifier != nil {
session.addMetadataWithApplicationInfo(bundleIdentifier)
}
}
6. Handling broadcastFinished
- ์ด๋ ํ ์ด์ ๋ก ๋ฐฉ์ก์ด ์ข ๋ฃ๋๋ฉด Extension์์ broadcastFinished ํจ์๊ฐ ํธ์ถ๋๋ค.
7. Handling Error(ex. Sign-In)
- ๋ฐฉ์ก์ ์คํํ๋ ๊ณผ์ ์์, ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด (์๋ฅผ ๋ค๋ฉด ๊ณ์ ๊ด๋ จ) ์ด๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด์ ReplayKit์ ํน์ API๋ฅผ ์ ๊ณตํด ์ค๋ค.
- ๊ทธ๋ฆฌ๊ณ Extension์ ํน์ API๋ฅผ ํธ์ถํ๊ณ , ReplayKit์ Session์ ์ข ๋ฃํ๊ณ Extension์ด ์ ๊ณตํ Error Message๋ฅผ ํฌํจํ Alert์ ์ฌ์ฉ์์๊ฒ ์ ๊ณตํ๋ค.
- ๊ทธ๋ฆฌ๊ณ , Alert ํ๋จ์ [Go To Application]์ ์ ํํ๋ฉด, ReplayKit์ ๊ธฐ์กด์ Application์ผ๋ก ์ฌ์ฉ์๋ฅผ ๋๋ ค๋ณด๋ธ๋ค.
override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) {
// Verify user is logged in and there's network connectivity
if session.userLoggedin() {
session.createMediaEngine()
} else {
let userInfo = [NSLocalizedFailureReasonErrorKey: "Not Logged In"]
let error = NSError(domain: "RPBroadcastErrorDomain", code: 401, userInfo: userInfo)
finishBroadcastWithError(error)
}
}