ํฐ์คํ ๋ฆฌ ๋ทฐ
ํด๋น ๊ธ์, ํ๋ฉด๋ฐฉ์ก์ ๋ํ ๊ฐ์ธ ๊ณต๋ถ๋ฅผ ์งํํ๋ฉฐ ์์ฒญํ WWDC 2016 - Go Live with ReplayKit์ ์ ๋ฆฌํ ๊ธ ์ ๋๋ค.
ํด์๊ณผ ์ ๋ฆฌ๋ ๋ด์ฉ์ด ์๋ฒฝํ์ง ์์ผ๋ฏ๋ก, ์๋ชป๋ ๋ถ๋ถ์ ๋ํด์ ์ง์ ๋ถํ๋๋ฆฝ๋๋ค. ๐
ReplayKit์ ์ฌ์ฉ์์ ์ฑ ๋ด ํ๋ฉด๊ณผ, ์ฌ์ด๋ ๊ทธ๋ฆฌ๊ณ ๋ง์ดํฌ์ ์ค๋์ค๋ฅผ ๋
นํํ ์ ์๋ค.
์ด๋ฅผ ํตํด, ๊ฒ์์ ํ๋ ๋์ ์์ฑ ๋ด๋ ์ด์
์ด ๊ฐ๋ฅํ๊ณ , ์ด๋ฅผ ์์คํ
๊ณต์ ์ํธ๋ฅผ ์ฌ์ฉํด์ ๊ณต์ ๊ฐ ๊ฐ๋ฅํ๋ค.
ReplayKit
1. Replaykit์ HD ํ์ง์ ๋น๋์ค๋ฅผ ์ ๊ณตํ๋ค.
- ๊ฒ์ ์ฑ๋ฅ์ ๊ฑฐ์ ์ํฅ์ ์ฃผ์ง ์๊ณ , ๋ฐฐํฐ๋ฆฌ์ ์๋ชจ๋ฅผ ์ต์ํ์ผ๋ก ์ค์
2. ๊ฐ์ธ์ ๋ณด๋ฅผ ๋ณดํธํ๋ค
- ์ ์ ์ ๊ฐ์ธ์ ๋ณด๊ฐ ๋งค์ฐ ์ค์ํ๊ธฐ ๋๋ฌธ์, ๋ นํ ์์ ์ ์ ์ฌ์ฉ์ ํ๋กฌํํธ๋ฅผ ํ์ํด์ ์ ์ ๊ฐ ์ง์ ๊ถํ์ ์ ๊ณตํ ์ ์๋๋ก ํ๋ค.
- ๋ํ, ์๋ฆผ๊ณผ ๋ฉ์์ง ๊ฐ์ System UI๋ฅผ ์ ์ธํ๊ณ ๊ธฐ๋ก๋๋ค.
3. iOS 9 ์ด์๋ถํฐ ๊ฐ๋ฅํ๋ค.
์๋ก์ด ๊ธฐ๋ฅ ์๊ฐ
1. Apple TV OS์์ ์ง์ํ๋ค.
- ๋ฐ๋ผ์, TVOS์ ๊ฒ์ํ๋ ์ด๋ ๋ นํ๊ฐ ๊ฐ๋ฅํ๋ค.
2. Live BroadCasting์ด ๊ฐ๋ฅํ๋ค.
- Third-Party ์๋น์ค๋ฅผ ํตํด์ ์ฌ์ฉ ๊ฐ๋ฅํจ.
3. FaceTime ์นด๋ฉ๋ผ๋ฅผ ๋ นํํ ์ ์๋ค.
- ๋ง์ดํฌ์ ๋ น์ ๊ด๋ จ API๋ฅผ ํฅ์์์ผฐ๋ค.
ReplayKit Architecture
- ์ฌ์ฉ์์ ์ฑ์, OS์๊ฒ '์ง๊ธ ๋
นํ๋ฅผ ์์ํ์ด!' ๋ผ๊ณ ์๋ ค์ผ ํ ํ์๊ฐ ์๋ค
-> ์ด๋ฅผ ์ํดReplayKit
์RPScreenRecorder
ํด๋์ค๋ฅผ ์ ๊ณตํ๋ค. RPScreenRecorder
๋ฅผ ํตํด์, ๋ นํ๋ฅผ ์์ํ ์ ์๋ค.- ๋
นํ๊ฐ ์์ ๋ ์๊ฐ,
Replay Daemon
์ผ๋ก ๋ฉ์ธ์ง๊ฐ ์ ๋ฌ๋๊ณ ,Replay Daemon
์ด ์ฑ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด์Movie
ํ์ผ์ ์์ฑํ๊ธฐ ์์ํ๋ค.
RPScreenRecorder
๋ฅผ ํตํด ๋ นํ๋ฅผ ์ค์งํ๊ฒ ๋๋ฉด,Replay Daemon
์ดMovie
ํ์ผ ์์ฑ์ ์ค๋จํ๊ฒ ๋๋ค.
-> ์ด ๋,Movie
ํ์ผ์ ์์งSyetem
์์ ์กด์ฌํ๋ค.- ์ด
Movie
ํ์ผ์ Application๋จ์ผ๋ก ๋๋ ค์ฃผ๊ธฐ ์ํดRPPreviewViewController
๋ฅผ ์ ๊ณตํ๋ค.
->RPPreviewViewController
๋ฅผ ํตํด, ์ฌ์ฉ์๋ ์์์ ๋ฏธ๋ฆฌ๋ณด๊ณ , ํธ์งํ๊ณ , ๊ณต์ ํ ์ ์๋ค.
Classes and Protocols
RPScreenRecorder
- ๋ นํ๋ฅผ ์์, ์ค์ง, ์ทจ์ ํ ์ ์๋ค.
- ํ์ฌ ๋๋ฐ์ด์ค๊ฐ ๋ นํ๋ฅผ ํ ์ ์๋์ง๋ฅผ ์ฒดํฌํ ์ ์๋ค.
- RPScreenRecorderDelegate
- ๋ นํ ๊ฐ๋ฅ ์ฌ๋ถ๊ฐ ๋ณ๊ฒฝํ๋ ์์ ์ ์ ๋ฌํด์ค๋ค.
- ์๋ฌ๋ก ์ธํด, ๋ นํ๊ฐ ์ค๋จ๋ ์์ ์ ์ ๋ฌํด์ค๋ค.
RPPreviewViewController
- ๋ นํ๋ ์์์ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ์ ๊ณตํด์ค๋ค.
- ์์์ ์์ ๊ณผ ์๋ผ๋ด๊ธฐ๋ฅผ ์ ๊ณตํ๋ค.
- ๊ณต์ ํ๊ธฐ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
- RPPreviewViewControllerDelegate
- ํ๋ฆฌ๋ทฐ๊ฐ ์ข ๋ฃ๋ ์์ ์ ์ ๋ฌํด์ค๋ค.
ReplayKit on Apple TV
Start Recording
func didPressRecordButton() {
let sharedRecorder = RPScreenRecorder.shared()
sharedRecorder.startRecording { error in
if error == nil {
self.showIndicatorView(text: "Recording")
}
}
}
RPScreenRecorder
์ ์ธ์คํด์ค๋ฅผ ํธ์ถstartRecording()
๋ฅผ ํธ์ถํด์, ๋ นํ ์์!
func showIndicatorView(text: String) {
self.recordingIndicatorWindow = UIWindow(frame: UIScreen.main().bounds)
self.recordingIndicatorWindow?.isHidden = false
self.recordingIndicatorWindow?.backgroundColor = UIColor.clear
self.recordingIndicatorWindow?.isUserInteractionEnabled = false
let indicatorView = IndicatorView(text: text)
self.recordingIndicatorWindow?.addSubview(indicatorView)
}
ReplayKit
์Application
์Main-Window
๋ง ๋ นํํ๊ธฐ ๋๋ฌธ์, ์ธ๋์ผ์ดํฐ๊ฐ ํฌํจ๋์ง ์๋๋ก ํ๊ธฐ ์ํด, ๋ณ๋์Window
๋ฅผ ์์ฑํ๋ค.
func didPressStopButton() {
sharedRecorder.stopRecording { previewViewController, error in
self.hideIndicatorView()
if error == nil {
self.previewViewController = previewViewController
self.previewViewController?.previewControllerDelegate = self
}
}
}
- Stop ๋ฒํผ์ ๋๋ฌ์,
didPressStopButton()
์ ํธ์ถํ๋ค๊ณ ๊ฐ์ ํ์. RPScreenRecorder
์ ๊ฐ์ฒด๋ฅผ ํตํดstopRecording()
์ ํธ์ถํ๋ฉด,previewViewController
๊ฐ์ฒด๊ฐ ํจ๊ป ๋์ด์จ๋ค!- ๊ทธ๋ฆฌ๊ณ , ๋์ด์จ
previewViewController
๋ฅผself.previewViewController
์ ํ ๋นํจ์ผ๋ก์จ ์ฌ๋ผ์ง์ง ์๋๋ก ํ๋ค. - previewControllerDelegate ๋ํ, self๋ก ์์.
func didPressPreviewButton() {
if let preview = previewViewController {
preview.mode = .preview /// .share ์ต์
๋ ์์.
self.present(preview, animated: true)
}
}
- preview๋ฅผ ์ค์ ํ๋ฉด์ผ๋ก ๋ณด์ฌ์ค ๋,
mode
๋ฅผ.preview
,.share
์ค ํ๋๋ฅผ ์ ํํ ์ ์๋ค.
func previewControllerDidFinish(_ previewController: RPPreviewViewController) {
previewController.dismiss(animated: true)
}
- ๋ชจ๋์ ์๊ด์์ด previewํ๋ฉด์์ ๋์์ค๋ ค๊ณ ํ ๋
RPPreviewViewControllerDelegate
์previewControllerDidFinish(_ previewController: RPPreviewViewController)
๊ฐ ํธ์ถ๋๋ค. - ์ด ํจ์๋
previewControoler
๋ฅผ ์ ๋ฌํด์ฃผ๊ธฐ ๋๋ฌธ์,ํ์ฌViewController
์์dismiss
ํด์ค๋ค.
Discarding the Recording
์๋ก์ด ๋ นํ๊ฐ ์์์ด ๋๋ฉด, ์ด์ ์ ๋ นํ๋ ์๋์ผ๋ก ์ ๊ฑฐ๋๋ค
- ํ๋์ ์ฑ์๋ ํ๋์ Record๋ง ํ์ฉ๋๊ธฐ ๋๋ฌธ์ด๋ค.
- ๋ํ,
RPScreenRecorder.discardRecording()
๋ฅผ ํธ์ถํ๋ฉด ๋ช ์์ ์ผ๋ก ์ ๊ฑฐ๋ ๊ฐ๋ฅํ๋ค.
ReplayKit on Apple TV
- ์ฑ์ ๋น๋์ค์ ์ค๋์ค ์ปจํ
์ธ ๋ฅผ ๋
นํํ ์ ์๋ค.
-> ๋ง์ดํฌ๋ ์์คํ ์์ ์ง์ ๋ ๋ง์ดํฌ๋ฅผ ์์ํ๋ค. - ํ๋ฆฌ๋ทฐ์ ๊ณต์ ํ๊ธฐ ๊ธฐ๋ฅ์ด ๊ฐ๋ฅํ๋ค.
- ๋จ์ํ API ํญ๋ชฉ๋ค์ iOS์ ๊ฐ์ด ์ฌ์ฉ ๊ฐ๋ฅํจ
- tvOS 10 ๋ถํฐ ์ฌ์ฉ ๊ฐ๋ฅํ๋ค.
Live Broadcast
- ์ ์ ๋ ๋ณธ์ธ์ ๊ฒ์ ํ๋ ์ด๋ฅผ ์ธ๋ถ ์คํธ๋ฆฌ๋ฐ ์๋น์ค๋ฅผ ์ด์ฉํด์ iOS ํน์ tvOS ๋๋ฐ์ด์ค๋ฅผ ํตํด ์ง์ ์๋ฐฉ์ก ํ ์ ์๋ค.
- iOS๋ ์ค์๊ฐ์ผ๋ก FaceTime ์นด๋ฉ๋ผ๋ฅผ ํตํด ๊ฒ์ ํ๋ ์ด์ ๋ํ ํด์ค์ ์ ๊ณตํ ์ ์๋ค.
- ์ค๋์ค์ ๋น๋์ค ๋ฐ์ดํฐ๋ ๋งค์ฐ ์์ ํ๊ฒ ๊ด๋ฆฌ๋๋ค.
1. Initiating a Broadcast
func didPressBroadcastButton() {
RPBroadcastActivityViewController.load { broadcastAVC, error in
if let broadcastAVC = broadcastAVC {
broadcastAVC.delegate = self
self.present(broadcastAVC, animated: true)
}
}
}
- ์ค์๊ฐ ๋ฐฉ์ก์ ์ํด,
RPBroadcastActivityViewController
๋ผ๋ ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ค. - ๋ํ,
RPBroadcastActivityViewController
์ ๊ฐ์ฒด๋ฅผ ์ป๊ธฐ ์ํด.load()
ํจ์๋ฅผ ์ฌ์ฉํ๋ค. - ์ด๋ฅผ ํตํด ์ค์๊ฐ ๋ฐฉ์ก ์๋น์ค๋ฅผ ์ ๊ณตํ๋ ์ฑ์ ์ ํํ ์ ์๋ ํ๋ฉด์ ํ์ํ ์ ์์.
// RPBroadcastActivityViewControllerDelegate
func broadcastActivityViewController(
_ broadcastAVC: RPBroadcastActivityViewController,
didFinishWith broadcastContoller: RPBroadcastController,
error: NSError?) {
broadcastAVC.dismiss(animated: true) {
self.startCountDownTimer {
broadcastController?.startBroadcast { error in
}
}
}
}
)
- ๋ฐฉ์ก ์๋น์ค๋ฅผ ์ ํํ๊ณ , ๋ฐฉ์ก์์ ์ ์ฌ๋ฌ ์ค์ ์ ๋ง์น๊ฒ ๋๋ฉด
RPBroadcastActivityViewControllerDelegate
์ ํจ์์ธbroadcastActivityController
๋ฅผ ํธ์ถํ๋ค. - ํด๋น ํจ์์์
broadcastAVC
๋ฅผ dismissํด์ฃผ๊ณ , ์ผ์ ์นด์ดํธ๋ค์ด ์ดํ ๋ฐฉ์ก์ ์์ํ๋ค.
2. Indicating a Broadcast
์ฌ์ฉ์๊ฐ ํ์ฌ ์ค์๊ฐ ๋ฐฉ์ก์ค ์์ ์๋ ค์ฃผ๋ ๊ฒ์ด ์ค์ํ๊ธฐ ๋๋ฌธ์,broadcastController?.isBroadcasting
์ ํ์ฉํด์ ๊ด๋ จ UI๋ฅผ ๋
ธ์ถ์์ผ์ค์ผํจ
3. Finish Broadcast
func didPressBroadcastButton() {
self.broadcastController?.finishBroadcast { error in
if error == nil {
/// ๋ฐฉ์ก ์ข
๋ฃ
self.updateBroadcastUI()
}
}
}
// RPBroadcastActivityViewControllerDelegate
func broadcastActivityVeiwController(
_ broadcastActivityViewController: RPBroadcastActivityViewController,
didFinishWith broadcastController: RPBroadcastController,
error: NSError?
) {
self.broadcastController = broadcastController
/// delegate๋ฅผ ์ธํ
ํจ์ผ๋ก์จ, error๋ฅผ ๊ฐ์งํ ์ ์์
self.broadcastController?.delegate = self
}
// RPBroadcastControllerDelegate
func broadcastController(
_ broadcastController: RPBroadcastContoller,
didFinishWithError error: NSError?
) {
if error != nil {
self.showErrorMessage(text: error!.localizedDescription)
self.updateBroadcastUI()
}
}
func applicationWillResignActive() {
// ReplayKit์ ์๋์ผ๋ก ๋ฐฉ์ก์ ์ผ์ ์ค์งํ๊ฒ ๋๋ค.
}
func applicationDidBecomeActive() {
if self.broadcastController?.isBroadcasting == true {
self.promptUserToResumeBroadcast { userWantsToResume in
if userWantsToResume {
self.broadcastController?.resumeBroadcast()
self.updateBroadcastUI()
} else {
self.broadcastController?.finishBroadcast { error in
self.updateBroadcastUI()
}
}
}
}
}
- ์ถ๊ฐ์ ์ผ๋ก, ์ฌ์ฉ์๊ฐ ์ฑ์ background๋ก ๋ณด๋ด๊ฑฐ๋ ์ ํ๊ฐ ์ค๋ ๋ฑ์ ์ํฉ์ด ๋ฐ์ํ๊ฒ ๋๋ฉด ์ค์๊ฐ ๋ฐฉ์ก์ ์๋์ผ๋ก ์ผ์ ์ค๋จ๋๋ค.
- ๋ฐ๋ผ์, ๋ค์ ์ฑ์ด ํ์ฑํ ๋๋ ์์ ์ ์ฌ์ฉ์์๊ฒ ์ค์๊ฐ ๋ฐฉ์ก์ ๋ค์ ์ฌ๊ฐํ ๊ฒ์ธ์ง ๋ฌผ์ด๋ด์ผํ๋ค.
Classes and Protocols
Game API
RPBroadcastActivityViewController
- ํ์ฌ ์ค์น๋์ด์๋ broadcast ์๋น์ค๋ค์ ๋ณด์ฌ์ค๋ค
RPBroadcastActivityViewControllerDelegate
- broadcast์ ์์ ์ค๋น๊ฐ ์๋ฃ๋์์์ ์๋ ค์ฃผ๋ delegate
RPBroadcastController
- broadcast๋ฅผ ์์ / ์ค์ง ํ๋ ์ญํ ์ ํ๋ค.
- ํ์ฌ ๋ฐฉ์ก์ ์ํ(๋ฐฉ์ก ์ค ์ธ์ง)๋ฅผ ์ฒดํฌํ ์ ์๋ค.
RPBroadcastControllerDelegate
- broadcast์ค ๋ฐ์ํ๋ error๋ฅผ ๋ค๋ฃฐ ์ ์๋ delegate
Broadcast Services
Broadcast UI Extension
- broadcast๋ฅผ ์ฌ์ฉ์๊ฐ ์ค๋นํ ์ ์๋๋ก ๋ณด์ฌ์ฃผ๋ UI
Broadcast Upload Extension
- broadcast์ ๋น๋์ค / ์ค๋์ค ๋ฐ์ดํฐ๋ฅผ ์ ๋ก๋ ์ฒ๋ฆฌ ํด์ฃผ๋ non-UI extension
Broadcast Extensions
- ๋ถ๋ชจ App์ Target์ผ๋ก ์๋ฒ ๋๋ํด์ ์ฌ์ฉํ๋ค.
- ๋ณ๋์ App์ผ๋ก ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์, ๋ถ๋ชจ App๊ณผ๋ ์์ ๋ถ๋ฆฌ๋ Process์์ ์คํ๋๋ค.
- ํ์ง๋ง, ๋ถ๋ชจ App๊ณผ์ ๋ฐ์ดํฐ๋ฅผ ๊ณต์ ํ ์ ์๋ค.
- ๋ํ, Extension์ ๋ถ๋ชจ App์ ๋นํด ์ฃผ์ด์ง ๋ฆฌ์์ค๊ฐ ์ ํ์ ์ด๋ค.
- ๋ฐ๋ผ์, ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํด์ผํจ
Xcode Templates
- Add Target -> iOS/tvOS -> Application Extension -> Broadcast UI Extension & Broadcast Upload Extension
- ์์ฑํ Extension์ ๋ํ ์ ์ฒ๋ฆฌ(Pre-Configure)์์ ์ด ํ์ํ๋ค๋ฉด, Extension์ ์์ฑ๋ info.plist์์ ์ฒ๋ฆฌ ๊ฐ๋ฅํ๋ค.
1. Broadcast UI Extension
- ์ ์ ์ธ์ฆ์ ์ ๊ณตํด์ผํจ (๋ง์ฝ, ์ธ์ฆ ์ ๋ณด๊ฐ ์์ผ๋ฉด ํ์๊ฐ์
๋ ์ ๊ณต)
-> ์ ์ ์ ์ฅ์์๋, ๋ณ๋์ ์ฑ์ธ์ง ๊ตฌ๋ถ์ ๋ชปํ๊ธฐ ๋๋ฌธ์ ํผ๋์ด ์ผ์ด๋์ง ์์ - ์ด์ฉ์ฝ๊ด ๋ฑ์ ์ ๋ณด๋ ์ด ๊ณณ์์ ๋ณด์ฌ์ค์ผํ๋ค.
- ๋ฐฉ์ก์ค๋น๊ณผ์ ๋ ์ด ๊ณณ์์ ์งํํด์ผํ๋ค.
- ๋ชจ๋ ์ค๋น๊ฐ ์๋ฃ ๋์๋ค๋ฉด, ์ค๋น์๋ฃ๊ฐ ๋์์์ ReplayKit์๊ฒ ์๋ ค์ค์ผํ๋ค.
2. Broadcast Upload Extension
- ์ ๋ฌ๋ฐ์ ๋น๋์ค ๋ฐ ์ค๋์ค ๋ฐ์ดํฐ๋ฅผ ์๋ฒ์ uploadํด์ฃผ๋ ๊ณผ์ ์ ์ํ
- ์ค์ broadcast๋ฅผ ์ํ ๋ค์ํ ๋ก์ง๋ค์ ์ด ๊ณณ์์ ์ค๊ณ ๋ฐ ๊ตฌํ ํด์ผํจ
์๋์ ์ฌ์ง์, ๊ฐ Feature๋ณ๋ก ๋ด๋นํด์ผํ๋ ์ญํ ๋ค
Expanded Commentary Options
- FaceTime ์นด๋ฉ๋ผ๋ฅผ ์ง์ํ๋ค
if let cameraPreview = RPScreenRecorder.shared().cameraPreviewView {
cameraPreview.frame = CGRect(...)
self.view.addSubview(cameraPreview)
}
- ์ ์ฐํ ์นด๋ฉ๋ผ ๋ น์ ๊ธฐ๋ฅ
func enableMic {
RPScreenRecorder.shared().isMicrophoneEnabled = true
}
func disableMic {
RPScreenRecorder.shared().isMicrophoneEnabled = false
}
- iOS 10+ ์ ์ฉ