iOS
Last updated
Last updated
iOS 가이드 문서는 Swift UIKit 기준으로 작성되었습니다.
사용언어 : Swift UI
Objective-C
형태의 가이드는 제공되지 않습니다.
세로모드 고정
게임은 세로모드 해상도에 최적화 되어 세로모드 잠금 기능을 추가해야 합니다.
// blp_sample_app_iosApp.swift
import SwiftUI
@main
struct blp_sample_app_iosApp: App {
// 앱 세로모드 잠금 처리
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return .portrait
}
}
최상위 ContentView 파일 내 NavigationStack
으로 화면 단위 View를 컨트롤 합니다.
홈화면을 HomeContentView
로 선언 하였으며, navigationDestination
메서드를 통해 런처를 실행할 LauncherContentView
로 이동할 케이스를 선언합니다.
// ContentView.swift
import SwiftUI
// 최상위 View
struct ContentView: View {
@StateObject private var navigationModel = NavigationModel()
var body: some View {
NavigationStack(path: $navigationModel.path) {
HomeContentView(navigationModel: navigationModel)
.navigationDestination(for: NavigationDestination.self) { destination in
switch destination {
case .launcher:
// 런처 화면
LauncherContentView(navigationModel: navigationModel)
}
}
}
}
}
#Preview {
ContentView()
}
Navigation 전환을 위해 NavigationModel
클래스 파일을 선언합니다.
// NavigationModel.swift
import Foundation
// 화면 전환 case들 (ContentView)
enum NavigationDestination: String, Identifiable {
case launcher
var id: String { rawValue }
}
class NavigationModel: ObservableObject {
@Published var path: [NavigationDestination] = []
}
런처 호출 URL을 로드할 WebView
구조체가 필요합니다.
Coordinator
클래스 내 userContentController
에서 script 메세지를 핸들링 하는 로직을 추가합니다.
// LauncherWebView.swift
import WebKit
import SwiftUI
// UIViewRepresentable를 사용하여 WKWebView를 wrapping해서 사용
struct LauncherWebView: UIViewRepresentable {
let url: URL
// 뒤로가기 처리
let onCloseLauncher: () -> Void
class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
var parent: LauncherWebView
init(parent: LauncherWebView) {
self.parent = parent
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// WebView finished loading
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
// 런처 뒤로가기 이벤트 수신 시 처리
if message.name == "closeLauncher" {
parent.onCloseLauncher()
}
// 런처 로드 완료
if message.name == "launcherLoaded" {
parent.onLauncherLoaded()
}
// 타이머 미션 완료
if message.name == "timerMissionComplete" {
parent.onTimerMissionComplete()
}
// 육성완료
if message.name == "giftReceived" {
parent.onGiftReceived()
}
}
}
func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
}
func makeUIView(context: Context) -> WKWebView {
let webView = WKWebView()
let contentController = webView.configuration.userContentController
// javascript에서 전달하는 이벤트 등록
contentController.add(context.coordinator, name: "closeLauncher")
contentController.add(context.coordinator, name: "launcherLoaded")
contentController.add(context.coordinator, name: "timerMissionComplete")
contentController.add(context.coordinator, name: "giftReceived")
webView.navigationDelegate = context.coordinator
let request = URLRequest(url: url, cachePolicy:NSURLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 10.0)
webView.load(request)
return webView
}
func updateUIView(_ webView: WKWebView, context: Context) {
//
}
static func dismantleUIView(_ uiView: WKWebView, coordinator: Self.Coordinator) {
uiView.configuration.userContentController.removeScriptMessageHandler(forName: "closeLauncher")
uiView.configuration.userContentController.removeScriptMessageHandler(forName: "launcherLoaded")
uiView.configuration.userContentController.removeScriptMessageHandler(forName: "timerMissionComplete")
uiView.configuration.userContentController.removeScriptMessageHandler(forName: "giftReceived")
}
}
런처를 실행할 LauncherContentView
파일을 생성합니다.
LauncherContentView
구조체를 선언합니다.
body 변수에는 런처가 로드될 WebView
를 선언합니다.
2단계에서 선언된 LauncherWebView
구조체를 사용합니다.
런처 호출 URL
을 로드합니다.
런처 이벤트 처리를 위한 핸들러를 구현합니다.
// LauncherContentView.swift
import SwiftUI
import WebKit
struct LauncherContentView: View {
// navigation model observer
@ObservedObject var navigationModel: NavigationModel
var body: some View {
LauncherWebView(
url: URL(string: "런처 URL 정보"),
onCloseLauncher: {
// 런처 뒤로가기 처리
handleCloseLauncher()
},
onLauncherLoaded: {
// 런처 로드 완료
handleLauncherLoaded()
},
onTimerMissionComplete: {
// 타이머 미션 완료
handleTimerMissionComplete()
},
onGiftReceived: {
// 육성완료
handleGiftReceived()
},
)
.navigationBarBackButtonHidden(true)
}
// 뒤로가기 처리
private func handleCloseLauncher() {
navigationModel.path.removeLast()
}
// 런처 로드 완료
private func handleLauncherLoaded() {
// 런처의 로드가 완료된 후 추가적인 처리 필요 시 사용
}
// 타이머 미션 완료
private func handleTimerMissionComplete() {
// 타이머 미션 완료 후 추가적인 처리 필요 시 사용
}
// 육성완료
private func handleGiftReceived() {
// 육성완료 후 추가적인 처리 필요 시 사용
}
}
#Preview {
LauncherContentView(navigationModel: NavigationModel())
}
WKWebView
를 사용하며 userContentController
에서 javascript를 전달 받는 이벤트를 등록합니다.
런처에서 딥링크 호출 시 makeUIView
함수 내에서 추가로 스크립트를 주입합니다.
해당 가이드에서는 deepLinkHandler
라는 이름으로 메시지 핸들러 네이밍을 정의하였습니다.
Coordinator
클래스 내 userContentController
함수에서 부모의 onDeepLink
콜백을 호출합니다.
// LauncherWebView.swift
import WebKit
import SwiftUI
struct LauncherWebView: UIViewRepresentable {
...
// Deep Link 처리할 콜백 함수
let onDeepLink: (URL) -> Void
class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
...
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
// deepLinkHandler 케이스 추가
if message.name == "deepLinkHandler", let urlString = message.body as? String, let url = URL(string: urlString) {
parent.onDeepLink(url)
}
}
...
}
...
func makeUIView(context: Context) -> WKWebView {
let webView = WKWebView()
let contentController = webView.configuration.userContentController
// javascript에서 전달하는 이벤트 등록
...
contentController.add(context.coordinator, name: "deepLinkHandler")
// script 주입
let script = """
window.open = function(url) {
window.webkit.messageHandlers.deepLinkHandler.postMessage(url);
};
"""
let scriptToInject = WKUserScript(source: script, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
contentController.addUserScript(scriptToInject)
webView.navigationDelegate = context.coordinator
let request = URLRequest(url: url)
webView.load(request)
return webView
}
...
static func dismantleUIView(_ uiView: WKWebView, coordinator: Self.Coordinator) {
...
uiView.configuration.userContentController.removeScriptMessageHandler(forName: "deepLinkHandler")
}
}
WebView 내에서 딥링크 이벤트가 발생하면 상위 ContentView에서 해당 이벤트를 전달 받아 네비게이션 이동을 수행합니다.
queryParameters
로 넘어오는 데이터는 화면 전환 시 NavigationModel
에 추가로 정의하여 전달합니다.
// LauncherContentView.swift
import SwiftUI
import WebKit
struct LauncherContentView: View {
...
var body: some View {
LauncherWebView(
url: URL(string: "런처 URL"),
// Deep Link 이벤트 핸들링 처리
onDeepLink: { url in
handleDeepLink(url)
}
)
.navigationBarBackButtonHidden(true)
}
...
// Deep Link 처리
private func handleDeepLink(_ url: URL) {
// Deep Link URL 내 포함된 스키마, 호스트, 파라미터 데이터로 네비게이션 이동 처리 추가
if url.scheme == "{스키마}"{
if url.host == "{호스트명}" {
if let queryItems = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems,
let exampleValue = queryItems.first(where: { $0.name == "{파라미터명}" })?.value {
navigationModel.exampleValue = exampleValue
navigationModel.path.append(.{호스트명})
}
}
}
}
}
...
개발에 대한 추가 설명이 더 필요하신가요?
"[Client Admin] 로그인 → 오른쪽 하단 채널톡 위젯" 클릭 후 개발 카테고리에 문의 남겨주시면 기술 개발팀에서 확인 후 연락드리겠습니다.