ํ์ด๋ธ๋ฆฌ๋ ์ฑ(Hybid Application) ?
๊ทธ์ ์ ๋จผ์ ์์์ผ ํ ๊ฒ:
1. ๋ค์ดํฐ๋ธ ์ฑ(Native App)
: Android ๋๋ iOS ๊ฐ์ ์ด๋ค ๊ตฌ์ฒด์ ์ธ ํ๋ซํผ๋ง์ ์ํด ๋ง๋ค์ด์ง ์์ฉ ํ๋ก๊ทธ๋จ์ ๋ค์ดํฐ๋ธ ์ฑ(Native App)์ผ๋ก ๋๋ฐ์ด์ค์ ๋ค์ด๋ก๋ํ์ฌ ์ค์นํ ์ ์๋ ์ฑ
- ์ฅ
- ๊ฐ ์ด์์ฒด์ ์ ์ต์ ํ๋ ๋ฐฉ์์ผ๋ก ๋ง๋ค์ด์ง๋ฏ๋ก ์ฑ์ ๊ตฌ๋ ์๋๊ฐ ๋น ๋ฅด๊ณ ์์ ์
- ๋์ ์ฌ์์ ๊ทธ๋ํฝ์ผ๋ก ์ํ๋ ๋์์ธ์ ๊ตฌํ ๊ฐ๋ฅ
- ๋จ
- ๋ค๋ฅธ ์ด์์ฒด์ ์์ ํธํ์ด ๋์ง ์๊ธฐ ๋๋ฌธ์ ์๋๋ก์ด๋์ iOS ์ฑ์ ๋ณ๋๋ก ๊ฐ๋ฐํด์ผ ํจ
- ์ฑ์ ์์ ์ฌํญ์ด ์๊ธฐ๋ ๊ฒฝ์ฐ ์ฑ ๋ง์ผ์ ์ฌ์ฌ๋ฅผ ๊ฑฐ์น๊ณ ์ ์ฒด ์ ๋ฐ์ดํธ๋ฅผ ์งํ
2. ์น ์ฑ(Web App)
: ๋ฐ์คํฌํฑ ๋๋ ๋ชจ๋ฐ์ผ ๋๋ฐ์ด์ค์ ๋ธ๋ผ์ฐ์ ์์ ์ก์ธ์ค ํ ์ ์๋ ์ฑ
- ์ฅ
- ์ธํฐ๋ท ๋ธ๋ผ์ฐ์ ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์๋ํ๊ธฐ ๋๋ฌธ์ ๋ณ๋์ ์ฑ์ ์ค์นํ์ง ์์๋ ๋จ
- ํ์ค ์น ์ธ์ด๋ก ๋ง๋ค ์ ์๊ธฐ ๋๋ฌธ์ ์๋์ ์ผ๋ก ์ ์ ๋น์ฉ์ด ์ ๋ ดํ๊ณ ๊ฐ๋ฐ ๊ธฐ๊ฐ๋ ์งง์ ํธ
- ๋จ
- ๋๋ฐ์ด์ค์ ์ ๊ทผ ๊ถํ์ด ์๊ธฐ ๋๋ฌธ์ ์นด๋ฉ๋ผ๋ ์์ฑ ์ธ์ ๋ฑ์ ๊ธฐ๋ฅ์ ํ์ฉํ ์ ์์
- ๋ค์ดํฐ๋ธ ์ฑ์ ๋นํด ์๋์ ์ผ๋ก ๊ตฌ๋ ์๋๊ฐ ๋๋ฆฌ๊ณ ์์ ์ฑ๋ ๋จ์ด์ง
ํ์ด๋ธ๋ฆฌ๋ ์ฑ์ ์ด ๋ ๊ฐ์ง๋ฅผ ์งฌ๋ฝํ ๊ฒ, 100% ์น์ฑ, 100% ๋ค์ดํฐ๋ธ ์ฑ์ด ์๋ ์ฑ์ด๋ค.
์ฆ, JavaScript, HTML ๋ฐ CSS์ ๊ฐ์ด ์ ์๋ ค์ง ์ธ์ด์ ํ๋ ์ ์ํฌ๋ฅผ ์ฌ์ฉํ์ฌ ๋ค์ํ ํ๋ซํผ์์ ์ฌ์ฉํ ์ ์๋ ํ์ด๋ธ๋ฆฌ๋ ์ฑ์ ๋น ๋ฅด๊ฒ ๋ง๋ค ์ ์๋ค.
๋น์ฉ๋ ์๊ฐ๋ ๋ ๋ค๊ณ ์ ์ง ๋ณด์๋ ์ฝ๊ณ ๊ธฐ๋ณธ API ๊ธฐ๋ฅ๋ ์ฌ์ฉ ๊ฐ๋ฅํ๋ ์คํ๋ผ์ธ(offline)์ผ๋ก๋ ์๋ํ์ง ์๊ณ ๋๊ฐ์ ์ฝ๋๋ฅผ ๋ฒ ์ด์ค๋ก ํ๊ธฐ ๋๋ฌธ์ ์๋๋ก์ด๋๋ ์์ดํฐ ๋ฑ ๊ฐ ๋๋ฐ์ด์ค์ ํน์ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ ๋ฐ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์๋ ์๋ค.
- ์ฅ
- ์น ๊ฐ๋ฐ์ด ์๋ฃ๋์ด ์๋ ๊ฒฝ์ฐ ๊ฐ๋ฐ ์์์๊ฐ์ด ์ ๋ค.
์ด๋ฏธ ์น์ผ๋ก ๊ฐ๋ฐ๋์ด ์๋ ์๋น์ค๋ฅผ ์ฑ์ผ๋ก ์ถ์ํ๊ธฐ๋ง ํ๋ฉด ๋๊ธฐ ๋๋ฌธ์ ๋ค์ดํฐ๋ธ ์ฑ์ ๋นํ์ฌ ์ข ๋ ๋น ๋ฅด๊ฒ ์ ์ํ ์ ์์ - ๋ชจ๋ฐ์ผ ์น์ ํธ์ ์๋ฆผ, ์์น๊ธฐ๋ฐ ๊ธฐ๋ฅ์ ํ์ฅํ ์ ์๋ค.
ํ์ด๋ธ๋ฆฌ๋๋ก ์ ์๋ ์ฑ์ ๋ค์ดํฐ๋ธ์ ๋ง์ฐฌ๊ฐ์ง๋ก ํธ์ ์๋ฆผ, ์ธ๋ถ ์ฑ ์ฐ๋, ์์น๊ธฐ๋ฐ ๊ธฐ๋ฅ์ ๊ตฌํํ ์ ์์ - ์
๋ฐ์ดํธ ์ ๋งค๋ฒ ์ฌ์ฌ๋ฐ์ ํ์๊ฐ ์๋ค.
ํ๋ซํผ ๊ฐ๋ฐ ์ ์ด๊ธฐ์ ๋ฐ์ํ๋ UI ๋ณ๊ฒฝ ๋ฐ ๊ธฐ๋ฅ๊ฐํธ์ด ์์ฃผ ๋ฐ์ํ๊ฒ ๋๋๋ฐ
ํ์ด๋ธ๋ฆฌ๋ ์ฑ์ ๋ค์ดํฐ๋ธ์ ๊ด๋ จ๋ ๊ธฐ๋ฅ์ด ๋ณ๊ฒฝ๋ ๊ฒ์ด ์๋๋ผ๋ฉด ๋งค๋ฒ ์ฑ์ ์ฌ์ฌ๋ฐ์ ํ์๊ฐ ์์
- ์น ๊ฐ๋ฐ์ด ์๋ฃ๋์ด ์๋ ๊ฒฝ์ฐ ๊ฐ๋ฐ ์์์๊ฐ์ด ์ ๋ค.
- ๋จ
- ๋ค์ดํฐ๋ธ์ ๋นํด ๋งค๋๋ฝ์ง ๋ชปํ UI
ํ์ด๋ธ๋ฆฌ๋ ์ฑ์ ๋ด๋ถ ๊ตฌํ์ ์ ๋ถ ์น์ผ๋ก ํ๊ธฐ ๋๋ฌธ์ ๋ค์ดํฐ๋ธ ์ฑ์ฒ๋ผ ๋ถ๋๋ฌ์ด UI์ ํ์ ๊ตฌํํ๊ธฐ๊ฐ ํ๋ฆ - ๋คํธ์ํฌ๊ฐ ์ฐ๊ฒฐ๋ ์ํ์์๋ง ์ฌ์ฉ ๊ฐ๋ฅํ๋ค.
์ฐ๋ฆฌ๊ฐ ํํ ์ฌ์ฉํ๋ ์ฑ๋ค์ ๋คํธ์ํฌ ์ฐ๊ฒฐ ์ํ์๋ ๋ณ๊ฐ๋ก ์ฑ์ ์ฌ์ฉ ๊ฐ๋ฅ
ํ์ด๋ธ๋ฆฌ๋ ์ฑ์ ๊ฒฝ์ฐ ๋คํธ์ํฌ๊ฐ ์ฐ๊ฒฐ๋ผ์์ง ์์ ๊ฒฝ์ฐ ์ฑ์ ์ผ๋ถ ๊ธฐ๋ฅ๋ค์ ์ด์ฉํ๊ฒ๋ ํ ์๋ ์์ง๋ง
๋๋ถ๋ถ์ ๊ธฐ๋ฅ๋ค์ ์น์ผ๋ก ๊ตฌํ๋๊ธฐ ๋๋ฌธ์ ๋คํธ์ํฌ ์ํ์ ๋ฐ๋ผ ์ํํ ์๋น์ค ์ ๊ณต์ด ์๋ ์ ์์
- ๋ค์ดํฐ๋ธ์ ๋นํด ๋งค๋๋ฝ์ง ๋ชปํ UI
ํ์ด๋ธ๋ฆฌ๋ ์ฑ(Hybid Application) ์น๋ทฐ<->๋ค์ดํฐ๋ธ ํต์ ๊ตฌ์กฐ
ํ์ด๋ธ๋ฆฌ๋ ์ฑ(Hybid Application) ๊ตฌํ GOAL
ํ์ด๋ธ๋ฆฌ๋ ์ฑ์ ๊ฐ๋ฐํ ๋๋ ๋ค์ดํฐ๋ธ(iOS)์ ์น(JS) ๊ฐ์ ํต์ ์ ํตํด ์น์์ ๋ค์ดํฐ๋ธ์ ๊ธฐ๋ฅ์, ๋ค์ดํฐ๋ธ์์ ์น์ ํธ์ถํ ์ ์๋ ๊ฒ์ด ํต์ฌ์ด๋ค. ๋ฌผ๋ก ๊ทธ ๋ฐํ์ ์น๋ทฐ์์ ์ด๋ฃจ์ด์ง๋ค.
์๋ ์คํ ์ ๋ฐ๋ผ js์ native ๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ๊ณผ์ ์ ์์๋ณด์~
๐ Step1. js -> native ๋ฐ์ดํฐ ์ ์ก
- webview์ js์ Bridge ์ธํ
ํ๊ธฐ
- WKUserContentController ์ฌ์ฉํ๊ธฐ
- js์์ webview๋ก ์ ์กํ ๋ฐ์ดํฐ native์์ ์ฒ๋ฆฌํ๊ธฐ
- messageHandlers ์ฌ์ฉํ๊ธฐ
๐ Step2. native -> js ๋ฐ์ดํฐ ์ ์ก
- ์น ๋ทฐ์์ ์ํ๋ ์์ ์ js ํจ์ ํธ์ถํ๊ธฐ
- evaluateJavaScript(_:) ์ฌ์ฉํ๊ธฐ
๐ Step1. js -> webview ๋ฐ์ดํฐ ์ ์ก
js์์ webview์ ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๊ธฐ ์ํด์๋ swift์ javascipt ์ฌ์ด ์ฐ๊ฒฐํด ์ฃผ๋ Bridge๊ฐ ํ์ํ๋ค. ํต์ฌ!
Bridge๋ฅผ ํตํด ์น ํ์ด์ง์ ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ native์์ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํ๋ค!!!!
1. webview์ js์ Bridge ์ธํ ํ๊ธฐ
๋ฐ๋ผ์ ๋จผ์ , ์น ๋ทฐ(WKWebView) ์ด๊ธฐํ + Webview์ js์ Bridge ๋ฑ๋ก์ ํด์ค๋ค.
1) UIViewRepresentable์ ์ค์(conform)ํ๊ณ ์น ๋ทฐ๋ฅผ ๊ฐ์ธ๋ WebView๋ฅผ ์์ฑ + Webview์ Bridge ๋ฑ๋ก
์น ๋ทฐ(WKWebView) ์์ฑํ๋ฉฐ, ๋ฉ์์งํธ๋ค๋ฌ๋ฅผ ๋ฐ๊ธฐ ์ํ ๋ธ๋ฆฟ์ง๋ฅผ ๋ฑ๋กํด ์ค๋ค.
how? WKUserContentController์ ์์ฑํ์ฌ ๊ทธ ์์ ์ฌ์ฉํ Bridge ์ด๋ฆ์ ์ถ๊ฐํด ์ค๋ค.
๋ด๊ฐ ์ฐ๊ฒฐํ ๋ธ๋ฆฟ์ง ์ด๋ฆ : WKBridge >> ์ด ๋ถ๋ถ์ ์ฑ๊ณผ ์น์์ ๋์ผํ๊ฒ ๋ง์ถ์ด์ผ ์ ์ ์๋ํ๋ค!*์ฃผ์*
WKUserContentController์ ํด๋น ํธ๋ค๋ฌ๋ฅผ ๋ฑ๋กํด ์ฃผ๊ณ
WKWebViewConfiguration์ ์์ฑํ์ฌ WKUserContentController์ userContentController๋ก ๋ฃ์ด์ค๋ค.
struct WebView: UIViewRepresentable {
typealias UIViewType = WKWebView
var url: URL?
func makeUIView(context: Context) -> UIViewType {
let contentController = WKUserContentController()
contentController.add(self.makeCoordinator(),
name: "WKBridge")
let configuration = WKWebViewConfiguration()
configuration.preferences = preferences
configuration.userContentController = contentController
...
์์ฑํ webview์ ์ํ๋ remote url๋ก ํ์ด์ง๋ฅผ ๋ก๋ํ๋ค.
์ง๋ Firebase ๊ธฐ๋ฐ ์น ํธ์คํ ๋ฐฐํฌํ ํ๋ฉด์ผ๋ก ํ์ด๋ธ๋ฆฌ๋ ์ฑ์ ๋ง๋ค์๋ค.
let hostURL = "https://hostingdemo-a9104.web.app/"
var body: some View {
VStack {
WebView(url: hostURL, viewModel: viewModel)
2) js์ Bridge ๋ฑ๋ก
์น์์๋ window.webkit.messageHandlers๋ก ์ ๊ทผํ์ฌ ์ฑ์ ๋ฉ์์ง๋ฅผ ๋ณด๋ผ ์ ์๋ค.
๋ฐ๋ผ์ js์์๋ "MessageHanlder"๋ฅผ ์ฌ์ฉํ์ฌ ๋ฑ๋กํ๋ค.
- messageHandler๋ javascript์์ native๋ก ์ด๋ฒคํธ๋ฅผ ๋ณด๋ด๊ณ ์ถ์ ๊ฒฝ์ฐ, javascript ์ชฝ์์ ์ ์ํ ๋ฉ์๋ window.webkit.messageHandlers.{๋ฉ์์ง ํธ๋ค๋ฌ ์ด๋ฆ}.postMessage("์ ๋ฌํ ๋ฉ์์ง ์ ๋ ฅ")
<. js ํ์ผ >
์น ๋ทฐ์์ ์ค์ ํ ๋ธ๋ฆฟ์ง ์ด๋ฆ๊ณผ ๋์ผํ "WKBridge"๋ก Bridge ํจ์๋ฅผ sendScriptMessage ํจ์ ์์ ์ ์ธํ๋ค.
function sendScriptMessage(data) {
// ์น๋ทฐ์์ ๋ค์ดํฐ๋ธ๋ก ๋ฉ์์ง ์ ๋ฌ
const sendData = { message: data };
window.webkit.messageHandlers.WKBridge.postMessage(sendData);
}
javascript ํจ์๊ฐ ์คํํ ๊ฒฐ๊ณผ๋ฅผ native์ ์ ๋ฌํ์ฌ ์ฒ๋ฆฌํ๋ ๋ก์ง์ ์ํด
html์์ ๊ทธ๋ฆฐ ํ ์ผ ์ถ๊ฐํ๋ ๋ฒํผ ํด๋ฆญ ์ ์ํํ๋ ํจ์(addTodo()) ์์์ sendScriptMessage๋ฅผ ๋ถ๋ฌ์ฃผ๊ณ ์ ๋ ฅํ ๋ฐ์ดํฐ๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋ฃ์ด์ฃผ์๋ค.
<. js ํ์ผ >
function addTodo(){
...
sendScriptMessage(addValue.value)
...
}
<. html ํ์ผ >
<button type="button" id = "btn" onclick="addTodo()"><i class="xi-plus xi-2x"></i></button>
</div>
2. js์์ webview๋ก ๋ณด๋ธ ๋ฐ์ดํฐ native code๋ก ์ฒ๋ฆฌํ๊ธฐ
๊ทธ๋ผ js์์ ๋ณด๋ธ ๋ฐ์ดํฐ๊ฐ ์ ์๋์ง ํ์ธํด ๋ณด์~
์ฐ๊ฒฐํ ๋ฒํผ์ ํด๋ฆญํด์ ์น ํ์ด์ง(js)์์ sendScriptMessage ํจ์๊ฐ ํธ์ถํ์ ๋ Bridge๋ WKWebView์์ ํธ๋ค๋งํ๋ค.
WKWebView์์ WKScriptMessageHandlerํ๋กํ ์ฝ์ ์์๋ฐ์ ํ userContentController ๋ฉ์๋๋ฅผ ์์ฑํ๋ค.
์น๋ทฐ์์ ํธ์ถํ sendScriptMessage์ด ์คํ๋๋ฉด, ๋ค์ดํฐ๋ธ์์ ์คํ๋๋ ๋ถ๋ถ์ด๋ค.
message.name์ผ๋ก ์ด๋ค ๋ธ๋ฆฟ์ง๊ฐ ๋ค์ด์๋์ง ํ์ธ ๊ฐ๋ฅํ๊ณ ,
๋ธ๋ฆฟ์ง ๋ณ๋ก ๋ค์ด์จ message.body๋ฅผ handling ํด์ฃผ๋ฉด ๋๋ค!!!
๋๋ "WKBridge"๋ก ๋ค์ด์จ ๋ธ๋ฆฟ์ง๋ฅผ ๋ถ๋ฆฌํ์ฌ receivedJsonValueFromWebView ํจ์๋ก ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ค.
extension WebView.Coordinator: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage) {
if message.name == "WKBridge" {
delegate?.receivedJsonValueFromWebView(value: message.body as! [String:Any?])
} else if let body = message.body as? String {
delegate?.receivedStringValueFromWebView(value: body)
}
}
}
receivedJsonValueFromWebView ํจ์์์ ์ ๋ฌ๋ฐ์ ๋ฐ์ดํฐ๋ฅผ ํธ๋ค๋งํด ์ค๋ค~~!!
func receivedJsonValueFromWebView(value: [String : Any?]) {
if let data = value["message"] {
print("JSON ๋ฐ์ดํฐ๊ฐ ์น์ผ๋ก๋ถํฐ ์ด: \(String(describing: data!))")
}
}
[๊ตฌํ ๊ฒฐ๊ณผ]
์น๋ทฐ์์ ์ ๋ ฅํ ๋ฐ์ดํฐ๊ฐ ๋ธ๋ฆฟ์ง ํ๊ณ native์ message.body์ ๋์ฐฉํ ๋ฐ์ดํฐ๋ฅผ ์ถ๋ ฅํด ๋ณด์๋ค.
๐ Step2. webview -> js ๋ฐ์ดํฐ ์ ์ก
๋ฐ๋๋ก native์์ ์ฒ๋ฆฌํ ๊ฐ์ js๋ก ๋ณด๋ด๋ณด์ !
๋ฐฉ๋ฒ์ native code์์ javascript code์ ์ ์๋์ด ์๋ ํจ์ ์คํ ์ํค๊ณ ๋ฐ์ดํฐ๋ ํด๋น ํจ์์์ ํธ๋ค๋งํ๋ฉด ๋๋ค.
๊ทธ๋ผ ๋จผ์ ,
js ์ฝ๋์ ํจ์๋ฅผ ์ด๋ป๊ฒ ๋ถ๋ฅด๋ ์น ๋ทฐ์์ evaluateJavaScript(_:)๋ฅผ ํตํด ํธ์ถ ๊ฐ๋ฅํ๋ค!!
์๋ js์์ ์ ์ธํ callFromNative(data) ํจ์๋ฅผ ํธ์ถํ๊ธฐ ์ํด์
<. js ํ์ผ >
function callFromNative(data){
alert("Received :", data);
return "OK";
}
ํด๋น ํจ์๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ํธ์ถํ๋ฉด ๋๋ค.
let args: Any = "test"
webView.evaluateJavaScript("callFromNative('\(args)');") { result, error in
if let error = error {
print(error.localizedDescription)
} else if let result = result {
print(result)
}
}
ํด๋น evaluateJavaScript(_:)๋ฅผ ์ํ๋ ์์ ์ ํธ์ถํ๋ ค๋ฉด? (with SwiftUI)
UIKit ๋จ์ํ ๋ฒํผ ์ด๋ฒคํธ๋ก ํธ์ถํ๋ฉด ๋์ง๋ง, SwiftUI์์๋ ๋ณต์กํ๋คใ
ContentView์ ๋ฒํผ์ ํด๋ฆญํด์ js์์ ์ ์ธํ callFromNative(data) ํจ์๋ฅผ ํธ์ถํด ๋ณด์!
1) ObservableObject์ ์์๋ฐ๋ WebViewModel ํด๋์ค ์์ฑ
- functionCaller : send๋ฅผ ํธ์ถํด์ ๊ตฌ๋ ์๋ค์๊ฒ ๊ฐ์ ์ ํํ๋ publisher
- shouldUpldateView : updateView๋ฅผ ์คํํด์ผ ํ๋์ง ์ฌ๋ถ๋ฅผ ์๋ ค์ฃผ๋ Bool ๊ฐ
import Combine
class WebViewModel: ObservableObject {
var functionCaller = PassthroughSubject<String, Never>()
var shouldUpdateView = true
}
2) WebView ๋ด์ ์์์ ๋ง๋ WebViewModel์ ๋ํ ์ํ ๋ณ์๋ฅผ ์ถ๊ฐํ๋ค.
struct WebView: UIViewRepresentable {
// ... //
@StateObject var data: WebViewModel
// ... //
}
3) WebView ๋ด์ ์ฝ๋๋ค์ดํฐ๋ฅผ ์ถ๊ฐ
- tieFunctionCaller(…)
- WebViewData์ functionCalller๋ฅผ ์ํํ๋ ์ญํ
- functionCalller๋ PassthroughSubject์ด๋ฏ๋ก sink๋ฅผ ํธ์ถ ๊ฐ๋ฅ
- ์ด๋ ํน์ ์กฐ๊ฑด์ด ๋๋ฉด(์: ๋ฒํผ์ ๋๋ฅธ ๊ฒฝ์ฐ) String ๊ฐ์ด ๋ฐฐ์ถ
- sink๋ฅผ ํตํด ๋ฐฐ์ถ๋ js๊ฐ(String ํ์ )์ webView?.evaluateJavaScript(js)๋ก ์คํ
import Combine
// ... //
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
class Coordinator: NSObject, WKNavigationDelegate {
/// WebView Representable
var parentWebView: WebView
var webView: WKWebView? = nil
private var cancellable: AnyCancellable?
init(_ parentWebView: WebView) {
self.parentWebView = parentWebView
super.init()
}
//webViewmodel ์ functionCaller ๋ฅผ ์ํ
func tieFunctionCaller(data: WebViewModel) {
print("Passthrough:", #function)
//functionCaller ๋ PassthroughSubject์ด๋ฏ๋ก Sink๋ฅผ ํธ์ถํ ์ ์๋ค
cancellable = data.functionCaller.sink(receiveValue: { js in
self.webView?.evaluateJavaScript(js)
print("js ๋ด \(js) ํธ์ถ ์๋ฃ")
})
}
}
4) WebView ๋ด์ updateUIView๋ฅผ ์์ฑ
updateUIView๋ ์น๋ทฐ๊ฐ ์คํ๋ ์์ ์ ๋ฐ๋ก ์คํ๋๋ฉฐ, makeUIView ๋ค์์ ์คํ๋๋ค.
์ง์ ๋ ๋ทฐ์ ์ํ๋ฅผ ๋ด์ ์ ์ ๋ณด๋ก ์ ๋ฐ์ดํธํ๋ ๋ฉ์๋!
func updateUIView(_ uiView: UIViewType, context: Context) {
guard data.shouldUpdateView else {
data.shouldUpdateView = false
return
}
context.coordinator.tieFunctionCaller(data: data)
context.coordinator.webView = uiView
}
5) SwiftUI์ ๋ทฐ(ContentView ๋ฑ) ๋ด๋ถ์ WebViewModel๋ฅผ ์ถ๊ฐ
struct ContentView: View {
// ... //
@StateObject var webViewData = WebViewModel()
// ... //
}
6) SwiftUI์ ๋ทฐ(ContentView ๋ฑ) ๋ด๋ถ์ WebView๋ฅผ ์ถ๊ฐ
์์์ ๋ง๋ webViewData๋ฅผ WebView์ ํ๋ผ๋ฏธํฐ๋ก ์ถ๊ฐ
var body: some View {
WebView(url: URL(string: "https://example.con"), data: webViewData)
}
7) webViewData๋ฅผ ์ด์ฉํ์ฌ ๋ฒํผ์ ๋๋ฅด๋ฉด ํน์ ์๋ฐ์คํฌ๋ฆฝํธ ํจ์๊ฐ ์คํ๋๋๋ก ํ๋ค!
let args: Any = "test"
...
Button {
webViewData.functionCaller.send(
"""
callFromNative('\(args)')
"""
)
} label: {
Image(systemName: "icloud.and.arrow.up.fill")
}
[๊ตฌํ ๊ฒฐ๊ณผ]
safari๋ก ๋๋ฒ๊น ํ์ฌ ๊ฐ์ด js์ ์ ๋๋ก ๋ค์ด์์์ ํ์ธํ๋ค.
์ด์ js <-> webview ์๋ฑกํฅ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ๋ฐฉ๋ฒ์ ํฐ๋ํ์ต๋๋ค!!!!
*์น๋ทฐ ์ฌํ๋ฆฌ๋ก ๋๋ฒ๊น ํ๊ธฐ
์น๋ทฐ ๋๋ฒ๊น ์ด ์ฌํ๋ฆฌ์์ ์ ๋์์๋๋ฐ, ์๋ผ์ ํ์ธํด ๋ณด๋
ios 16.4 ์ดํ inspectable ํจ์๊ฐ ์ถ๊ฐ๋๋ฉด์, webview์ ์ค์ ํด ์ค์ผ์ง ๋๋ฒ๊น ์ด ๊ฐ๋ฅํด์ก๋ค.
@property(nonatomic, getter=isInspectable) BOOL inspectable;
1. ์น๋ทฐ ์ค์ ํ๋ ๋ถ๋ถ์ ์๋ ์ฝ๋ ์ถ๊ฐํด ์ค๋ค.
if #available(iOS 16.4, *) {
#if DEBUG
webView.isInspectable = true // webview inspector ๊ฐ๋ฅํ๋๋ก ์ค์
#endif
}
์ด์ , ์ฌํ๋ฆฌ๋ก ๋๋ฒ๊น ๊ฐ๋ฅ~~
๋ฐฉ๋ฒ์
2. ๋น๋ > ์ฌํ๋ฆฌ ์คํ > ์๋จ ๋ฉ๋ด์ '๊ฐ๋ฐ์์ฉ' ํด๋ฆญ > ์๋ฎฌ๋ ์ดํฐ ํด๋ฆญ!!
ํด๋ฆญํ๋ฉด ์๋์ฒ๋ผ ์น ๋๋ฒ๊น ํ ์ ์๋ ํ๋ฉด์ด ๋์ต๋๋ค~!