iOS 웹뷰 연동
고객사 iOS 애플리케이션의 네이티브 WKWebView 또는 SauceLive_iOS SDK를 활용하여 SauceLive 플레이어를 연동하는 방법을 안내합니다.
연동 방식은 서비스 환경에 따라 크게 세 가지로 나뉘며, 프로젝트에 적합한 방식을 선택하여 구현할 수 있습니다.
1. 연동 방식
연동 방식은 서비스 환경에 따라 SauceLive 도메인 URL 직접 연동(방식 A), 커스텀 도메인 연동(라이브러리 사용 - 방식 B / 라이브러리 미사용 - 방식 C) 세 가지로 구분되며, 프로젝트에 적합한 방식을 선택하여 구현할 수 있습니다.
방식 A. SauceLive 도메인 URL 직접 연동
SauceLive에서 제공하는 기본 플레이어 URL을 WKWebView에 직접 로드합니다. 별도의 웹 개발 없이 가장 빠르고 간편하게 연동할 수 있습니다.
방식 B. 커스텀 도메인 연동 (라이브러리 사용)
고객사 도메인의 웹페이지에 SauceLive JS 라이브러리를 설치한 후, 해당 웹페이지를 WKWebView에 로드합니다.
👉소스라이브 라이브러리 연동이 필요한 경우, 별도 라이브러리 가이드를 참고해 주세요. 🔗 소스라이브 라이브러리
방식 C. 커스텀 도메인 연동 (라이브러리 미사용)
고객사가 자체 웹페이지에 라이브러리를 사용하지 않고 직접 플레이어를 임베드하여 구축한 후, 해당 페이지를 WKWebView에 로드합니다.
참고
일반적인 WebView 연동만 필요한 경우에는WKWebView직접 연동으로 충분합니다.
PIP, 홈플로팅 등 iOS 네이티브 제어가 필요한 경우에는SauceLive_iOS SDK사용을 권장합니다.
2. 사전 준비 (Prerequisites) - 공통
원활한 영상 재생과 PIP 지원을 위해 아래 설정을 확인합니다.
2.1 HTTPS 환경 사용 권장
WKWebView는 App Transport Security(ATS) 정책의 영향을 받으므로, 소스라이브 URL 또는 고객사 커스텀 웹페이지는 HTTPS 환경 사용을 권장합니다.
2.2 PIP 사용 시 Capability 설정
PIP 또는 백그라운드 오디오 재생이 필요한 경우, Xcode에서 아래 기능을 활성화합니다.
- Signing & Capabilities > Background Modes
- Audio, AirPlay, and Picture in Picture 체크
2.3 오디오 세션 설정
라이브 재생 중 앱이 백그라운드로 전환되더라도 오디오를 유지하려면 AppDelegate.swift에 아래 설정을 추가합니다.
import AVFoundation
private func setupAudioSession() {
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback)
try AVAudioSession.sharedInstance().setActive(true)
} catch {
print("Failed to set up audio session: \(error)")
}
}애플리케이션 시작 시점에 호출합니다.
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
setupAudioSession()
return true
}3. WebView 설정 (Settings) - 공통
라이브 비디오 스트리밍과 브릿지가 정상적으로 동작하기 위해서는 아래의 필수 웹 설정이 선행되어야 합니다.
import UIKit
import WebKit
final class LiveWebViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {
private lazy var webView: WKWebView = {
let userContentController = WKUserContentController()
let configuration = WKWebViewConfiguration()
configuration.userContentController = userContentController
configuration.allowsInlineMediaPlayback = true
if #available(iOS 10.0, *) {
configuration.mediaTypesRequiringUserActionForPlayback = []
}
let webView = WKWebView(frame: .zero, configuration: configuration)
webView.navigationDelegate = self
webView.uiDelegate = self
webView.scrollView.contentInsetAdjustmentBehavior = .never
return webView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(webView)
webView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
webView.topAnchor.constraint(equalTo: view.topAnchor),
webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
webView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}
}4. 로드 구현
선택한 방식에 맞게 URL을 로드합니다.
방식 A. SauceLive URL 직접 로드
let broadcastId = "YOUR_BROADCAST_ID"
let sauceLiveUrl = URL(string: "https://player.sauceflex.com/broadcast/\(broadcastId)")!
webView.load(URLRequest(url: sauceLiveUrl))방식 B / C. 고객사 커스텀 웹페이지 로드
라이브러리 사용 여부(방식 B, C)와 관계없이, iOS 앱단에서는 동일하게 고객사 도메인으로 구축된 웹페이지 URL을 로드합니다.
let clientDomainUrl = URL(string: "https://www.your-domain.com/live-player?broadcastId=YOUR_BROADCAST_ID")!
webView.load(URLRequest(url: clientDomainUrl))5. JavaScript 브릿지 연동 (Bridge Integration)
SauceLive 플레이어 내부에서 발생하는 액션을 네이티브 앱에서 처리하기 위해 WKScriptMessageHandler를 등록합니다.
웹에서 전달하는 JSON 객체인 payload 데이터를 파싱하여 네이티브 앱 동작을 구현할 수 있습니다.
5.1 브릿지 객체 생성
import UIKit
import WebKit
final class SauceLiveBridge: NSObject, WKScriptMessageHandler {
weak var viewController: UIViewController?
init(viewController: UIViewController) {
self.viewController = viewController
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
switch message.name {
case "sauceflexOnShare":
if let payload = message.body as? String {
// 전달받은 JSON(payload)을 파싱하여
// iOS 기본 공유 시트(UIActivityViewController) 실행
}
case "sauceflexCouponIosScheme":
if let urlString = message.body as? String,
let url = URL(string: urlString) {
UIApplication.shared.open(url)
}
default:
break
}
}
}👉 전체 Payload 규격과 메서드는 🔗 SauceLive Payload 스펙 가이드 문서를 참고해 주세요.
5.2 WebView에 브릿지 연결
private var bridge: SauceLiveBridge!
override func viewDidLoad() {
super.viewDidLoad()
bridge = SauceLiveBridge(viewController: self)
let controller = webView.configuration.userContentController
controller.add(bridge, name: "sauceflexOnShare")
controller.add(bridge, name: "sauceflexCouponIosScheme")
}필요 시 deinit에서 핸들러를 정리합니다.
deinit {
let controller = webView.configuration.userContentController
controller.removeScriptMessageHandler(forName: "sauceflexOnShare")
controller.removeScriptMessageHandler(forName: "sauceflexCouponIosScheme")
}6. iOS PIP(Picture-in-Picture) 구현
iOS에서 PIP 기능을 안정적으로 사용하기 위해서는 SauceLive_iOS SDK 사용을 권장합니다.
Step 1. Capability 및 오디오 세션 설정
위 2. 사전 준비 - 공통 항목의 설정을 먼저 적용합니다.
Step 2. 플레이어 내 PIP 옵션 활성화
SauceLiveViewController를 상속받아 SauceViewControllerConfig에서 PIP 관련 옵션을 활성화합니다.
import UIKit
import SauceLive_iOS
final class LivePlayerViewController: SauceLiveViewController, SauceLiveDelegate {
private let sauceLiveLib = SauceLiveLib()
var broadcastId: String = ""
override func viewDidLoad() {
super.viewDidLoad()
let config = SauceViewControllerConfig(
isEnterEnabled: true,
isExitEnabled: true,
isLoginEnabled: true,
isProductEnabled: true,
isBannerEnabled: true,
isShareEnabled: true,
isPictureInPictureEnabled: true,
isReloadingEnabled: true,
isRewardEnabled: true,
isPIPActive: true,
pipSize: CGSize(width: 120, height: 214),
pipMode: .internalMode,
delegate: self
)
configure(with: config)
sauceLiveLib.viewController = self
sauceLiveLib.setInit(broadcastId)
sauceLiveLib.load()
}
}Step 3. 네이티브 플레이어 화면 진입
플레이어 화면 전환 시 PIPKit.show(with:)를 사용하여 표시합니다.
let playerVC = LivePlayerViewController()
playerVC.broadcastId = "YOUR_BROADCAST_ID"
PIPKit.show(with: playerVC)7. 홈플로팅(Home Floating) 구현 안내
홈플로팅 기능은 고객사 앱 메인(홈) 화면 진입했을 때 진행 중인 라이브 방송이 있으면, 플레이어를 즉시 PIP 모드로 전환하여 미니 플레이어 형태로 노출해 시청을 유도하는 기능입니다.
iOS에서는 SauceLive_iOS SDK의 PIP 설정을 활용하는 방식을 권장합니다.
7.1 메인 화면에서 ViewController 호출
func showHomeFloatingIfNeeded(broadcastId: String) {
let playerVC = HomeFloatingLiveViewController()
playerVC.broadcastId = broadcastId
PIPKit.show(with: playerVC)
}7.2 HomeFloating 전용 ViewController 구현
홈플로팅 전용 ViewController 역시 SauceLiveViewController를 상속받아 동일하게 구성할 수 있습니다.
import UIKit
import SauceLive_iOS
final class HomeFloatingLiveViewController: SauceLiveViewController, SauceLiveDelegate {
private let sauceLiveLib = SauceLiveLib()
var broadcastId: String = ""
override func viewDidLoad() {
super.viewDidLoad()
let config = SauceViewControllerConfig(
isEnterEnabled: true,
isExitEnabled: true,
isLoginEnabled: true,
isProductEnabled: true,
isBannerEnabled: true,
isShareEnabled: true,
isPictureInPictureEnabled: true,
isReloadingEnabled: true,
isRewardEnabled: true,
isPIPActive: true,
pipSize: CGSize(width: 120, height: 214),
pipMode: .internalMode,
delegate: self
)
configure(with: config)
sauceLiveLib.viewController = self
sauceLiveLib.setInit(broadcastId)
sauceLiveLib.load()
}
}8. 공유하기(Share) 기능 연동
플레이어 내에서 공유하기 아이콘을 터치하면 sauceflexOnShare 브릿지로 이벤트가 전달됩니다.
iOS 기본 공유 시트(UIActivityViewController)를 띄우기 위해 아래와 같이 파싱 및 구현합니다.
func handleShare(payload: String) {
guard let data = payload.data(using: .utf8),
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
return
}
let linkUrl = json["linkUrl"] as? String ?? ""
let broadcastName = json["broadcastName"] as? String ?? "SauceLive 방송"
let activityVC = UIActivityViewController(
activityItems: ["지금 라이브 방송을 확인해보세요!\n\(broadcastName)\n\(linkUrl)"],
applicationActivities: nil
)
present(activityVC, animated: true)
}Updated about 11 hours ago