f-did library go ver. → go mobile iOS 빌드
📌 사전설치(Mac OS)
gomobile 설치하기
ios로 빌드
![](https://blog.kakaocdn.net/dn/dVOxjH/btsLN9RhEbl/gBP48MGefyb6cEv7XimW8K/img.png)
webview 구현을 위한 라이브러리 추가
[iOS] SwiftUI 사용하여 WebView 띄우기
SwiftUI로 WebView 어떻게 띄우지?
velog.io
SwiftUI에서 WebView를 사용해보자
현재 많은 앱 서비스들은 웹 뷰를 사용하고 있다. 그 이유에 대해서는 상당히 다양할 것이다. 업데이트가 너무 빈번해서 앱 스토어를 통해서 업데이트하는 것보다 웹을 업데이트하면 간편하게
velog.io
https://phillip5094.tistory.com/133
SwiftUI에서 WKWebView <-> JavaScript 상호작용
안녕하세요. 이번에는 SwiftUI 환경에서 WKWebView가 JavaScript의 함수를 호출하고, JavaScript가 WKWebView의 함수를 호출하는 방법에 대해 알아볼게요. iOS 전체 코드: https://github.com/phillip5094/SwiftUI-WebView-JS J
phillip5094.tistory.com
빌드된 라이브러리를 추가하고, webview 구현을 위해 Webkit.framework 라이브러리를 추가한다.
웹뷰 구성
// Global.swift
class Global {
static let shared = Global()
let dbPath: String
private init() {
let fileManager = FileManager.default
let urls = fileManager.urls(for: .documentDirectory, in: .userDomainMask)
dbPath = urls[0].appendingPathComponent("secureData").path
// 폴더가 존재하지 않으면 생성
if !fileManager.fileExists(atPath: dbPath) {
do {
try fileManager.createDirectory(atPath: dbPath, withIntermediateDirectories: true, attributes: nil)
} catch {
print("Error creating directory: \(error)")
}
}
}
}
// WebView.swift
import SwiftUI
import WebKit
import DidLibrary
// WebViewMananger 싱글톤 클래스 정의
class WebViewManager {
static let shared = WebViewManager()
var webView: WKWebView
private init() {
let config = WKWebViewConfiguration()
let contentController = WKUserContentController()
// 자바스크립트 -> swift 넘겨오는 함수명과 일치해야 함
contentController.add(ContentController(), name: "createKey")
config.userContentController = contentController
// webView 객체 초기화
self.webView = WKWebView(frame: .zero, configuration: config)
// 웹뷰에서 자바스크립트 설정 허용
self.webView.configuration.preferences.javaScriptEnabled = true
// WKWebView에서 발생하는 JavaScript 오류와 로그 메시지를 iOS 애플리케이션으로 전달하기 위한 설정
let scriptSource = """
window.onerror = function(message, source, lineno, colno, error) {
window.webkit.messageHandlers.logHandler.postMessage({
message: message,
source: source,
lineno: lineno,
colno: colno,
error: error
});
};
console.log = function(message) {
window.webkit.messageHandlers.logHandler.postMessage({
message: message
});
};
console.error = console.log;
console.debug = console.log;
console.warn = console.log;
"""
let script = WKUserScript(source: scriptSource, injectionTime: .atDocumentStart, forMainFrameOnly: false)
contentController.addUserScript(script)
contentController.add(LogHandler(), name: "logHandler")
}
}
// 로그 핸들러: 웹에서 에러 메시지를 로그로 찍기 위한 핸들러
class LogHandler: NSObject, WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if let log = message.body as? [String: Any] {
if let message = log["message"] as? String {
print("JavaScript log: \(message)")
}
if let source = log["source"] as? String,
let lineno = log["lineno"] as? Int,
let colno = log["colno"] as? Int,
let error = log["error"] as? String {
print("JavaScript error: \(message) at \(source):\(lineno):\(colno), error: \(error)")
}
}
}
}
class ContentController: NSObject, WKScriptMessageHandler {
// 데이터베이스 경로
private var dbPath = Global.shared.dbPath
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
guard let body = message.body as? [String: Any] else {
print("Invalid message format")
return
}
var error: NSError?
var result: Any?
switch message.name {
case "createKey":
if let algoType = body["algoType"] as? String,
let keyName = body["keyName"] as? String,
let keyPass = body["keyPass"] as? String,
let keyType = body["keyType"] as? String {
result = KeymanagerCreateKey(self.dbPath, algoType, keyPass, keyName, keyType, &error)
print(result as Any)
let sanitizedResult = (result as? String)?.replacingOccurrences(of: "'", with: "\\'") ?? ""
WebViewManager.shared.webView.evaluateJavaScript("document.getElementById('createKeyResult').innerText = `Result: \(sanitizedResult)`;") { result, error in
if let error = error {
print("A JavaScript exception occurred: \(error.localizedDescription)")
}
}
}
default:
print("Unhandled message: \(message.name)")
}
}
}
// SwiftUI WebView 구조체 정의
struct WebView: UIViewRepresentable {
let request: URLRequest
init(request: URLRequest) {
self.request = request
}
func makeUIView(context: Context) -> WKWebView {
let webView = WebViewManager.shared.webView
webView.load(request)
return webView
}
func updateUIView(_ uiView: WKWebView, context: Context) {
uiView.load(request)
}
func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
}
class Coordinator: NSObject {
let parent: WebView
init(parent: WebView) {
self.parent = parent
}
}
}
외부 라이브러리 사용
예시
let result = VcmanagerVPdefinitionVerifiy(verifyPublicKey, signature, vpDefinition, &error)
SPA으로 구현한 웹에서는 네이티브 → 웹뷰 통신이 어려워요🥲 그렇다면 어떻게?
네이티브 →
WebViewManager.shared.webView.evaluateJavaScript("globalFunction('getCreateKeyResult', `\(sanitizedResult)`)")
네이티브에서는 WebViewManager.shared.webView.evaluateJavaScript(자바스크립트에서 호출할 함수명)을 사용하여 자바스크립트 함수를 호출하는데
이를 위해 전역 변수를 등록하고 해당 페이지가 실행될 때 (onMounted) 전역 함수 이벤트리스너를 등록하고 컴포넌트 인스턴스가 해제되기 전에 이벤트리스너를 제거하면 된다.
// app.vue
<script setup lang="ts>
// ... 생략 ...
// 전역 함수 정의
const globalFunction = (method: string, arg?: any) => {
const event = new CustomEvent('global-function-called', { detail: { method: method, arg: arg } });
window.dispatchEvent(event);
};
// 전역 변수로 제공
nuxtApp.provide('globalFunction', globalFunction);
// 전역 함수로 등록
if (typeof window !== 'undefined') {
window.globalFunction = globalFunction;
}
</script>
//ios-test.vue
<script setup lang="ts>
// ...생략...
const callbackFunctions: Record<string, Function> = {
getCreateKeyResult: (result: string) => {
createKeyResult.value = JSON.parse(result) as IResult<CreateKeyResult>
},
getDeleteKeyResult: (result: string) => {
deleteKeyResult.value = JSON.parse(result) as IResult<boolean>
},
getChangeVcStatResult: (result: string) => {
getChangeVcStatResult.value = JSON.parse(result) as IResult<null>
} ...
}
/**
* ios -> javascript 호출을 감지하는 이벤트 핸들러
*
*
* 컴포넌트 및 페이지 mounted될 때 window.addEventLister('global-function-called', (event: Event) => handleGlobalFuncionCalled(event as CustomEvent))
* 컴포넌트 인스턴스가 마운트 해제되기 직전에 해당 이벤트 리스너 해제
*
*/
function handleGlobalFuncionCalled(event: CustomEvent) {
callbackFunctions[event.detail.method](event.detail.arg)
}
onMounted(() => {
console.log("agent:::" + didLibrary.agent)
// 전역 이벤트 리스너 등록
window.addEventListener('global-function-called', (event: Event) => handleGlobalFuncionCalled(event as CustomEvent))
onBeforeUnmount(() => {
// 전역 이벤트 리스너 해제
window.removeEventListener('global-function-called', (event: Event) => handleGlobalFuncionCalled(event as CustomEvent))
});
})
</script>
'기타 개발지식' 카테고리의 다른 글
Indexed DB와 dexie.js (feat. 브라우저에 데이터를 저장하는 방법 비교) (0) | 2024.03.29 |
---|---|
코드 스플릿팅(Code splitting) 이란? (0) | 2023.03.14 |
ERC20 / ERC721 (0) | 2023.01.09 |
[블록체인] 커스터디/CBDC/멀티시그/MPC 용어 및 현황 정리 (0) | 2023.01.09 |
OAuth 2.0 동작 방식 (0) | 2022.08.10 |
댓글