Integrasi Mobile•iOS (Swift)
Implementasi Native iOS (Swift)
Panduan integrasi lengkap untuk aplikasi **iOS Native** menggunakan bahasa Swift, framework WebKit (WKWebView), dan WKScriptMessageHandler.
1. Konfigurasi Info.plist
Info.plist
<key>NSLocationWhenInUseUsageDescription</key>
<string>Aplikasi membutuhkan izin lokasi untuk validasi keamanan dompet digital QoinCash.</string>
<key>NSCameraUsageDescription</key>
<string>Aplikasi membutuhkan izin kamera untuk memindai kode QRIS.</string>2. ViewController Lengkap (QoinWebviewViewController.swift)
QoinWebviewViewController.swift
import UIKit
import WebKit
import CoreLocation
class QoinWebviewViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler, CLLocationManagerDelegate {
var signature: String = ""
var palette: String = "original"
var baseUrl: String = "https://seamless.qoin.id"
private var webView: WKWebView!
private let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
// Minta izin lokasi GPS perangkat iOS
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
setupWebView()
loadWebview()
}
private func setupWebView() {
let contentController = WKUserContentController()
// Daftarkan handler bridge ke script webkit
contentController.add(self, name: "QoinBridge")
let config = WKWebViewConfiguration()
config.userContentController = contentController
config.allowsInlineMediaPlayback = true
webView = WKWebView(frame: view.bounds, configuration: config)
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
webView.navigationDelegate = self
view.addSubview(webView)
}
private func loadWebview() {
guard let url = URL(string: "\(baseUrl)/") else { return }
webView.load(URLRequest(url: url))
}
// Event saat halaman selesai dimuat
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
injectPostMessage()
}
private func injectPostMessage() {
let jsonPayload: [String: Any] = [
"Signature": signature,
"Palette": palette
]
guard let jsonData = try? JSONSerialization.data(withJSONObject: jsonPayload),
let jsonString = String(data: jsonData, encoding: .utf8) else { return }
let bridgeScript = """
window.addEventListener('message', function(event) {
if (window.webkit && window.webkit.messageHandlers.QoinBridge) {
var d = typeof event.data === 'string' ? event.data : JSON.stringify(event.data);
window.webkit.messageHandlers.QoinBridge.postMessage(d);
}
});
window.postMessage(\(jsonString), '*');
"""
webView.evaluateJavaScript(bridgeScript, completionHandler: nil)
// Retry 500ms kemudian
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.webView.evaluateJavaScript("window.postMessage(\(jsonString), '*');", completionHandler: nil)
}
}
// Menangkap pesan balasan dari webview bridge
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
guard message.name == "QoinBridge", let bodyString = message.body as? String else { return }
if let data = bodyString.data(using: .utf8),
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
if let isClose = json["close"] as? Bool, isClose == true {
// Tutup screen webview
dismiss(animated: true, completion: nil)
}
}
}
}Pembersihan Memory
Ingat untuk memanggil
contentController.removeScriptMessageHandler(forName: "QoinBridge") saat view controller di-deallocate agar tidak terjadi retain cycle memory leak.