Skip to content

Instantly share code, notes, and snippets.

@adityadaniel
Created March 17, 2025 16:44
Show Gist options
  • Save adityadaniel/bdc3ad6e6bfe2b847f27fdeaaf18daf9 to your computer and use it in GitHub Desktop.
Save adityadaniel/bdc3ad6e6bfe2b847f27fdeaaf18daf9 to your computer and use it in GitHub Desktop.
Webview Midtrans blob
//
// MyWebViewController.swift
// midtrans-snap-WebView-sample
//
// Created by Zaki Ibrahim on 14/09/21.
//
import UIKit
import WebKit
class MyWebViewController: UIViewController, WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "imageHandler", let base64String = message.body as? String {
saveBase64Image(base64String)
}
}
var selectedName: String = ""
var webView: WKWebView!
private var activityIndicatorContainer: UIView!
private var activityIndicator: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
var urlS : URL
// Get URL from text field, if empty get Snap URL from Sample web
if !selectedName.isEmpty {
urlS = URL(string: selectedName)!
} else {
urlS = URL(string: "https://sample-demo-dot-midtrans-support-tools.et.r.appspot.com/snap-redirect")!
}
let contentController = WKUserContentController()
contentController.add(self, name: "imageHandler")
let config = WKWebViewConfiguration()
config.userContentController = contentController
self.webView = WKWebView(frame: .zero, configuration: config)
self.webView.navigationDelegate = self
view.addSubview(webView)
webView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
webView.topAnchor.constraint(equalTo: view.topAnchor),
webView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
webView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
webView.load(URLRequest(url: urlS))
}
func saveBase64Image(_ base64String: String) {
guard let imageData = Data(base64Encoded: base64String.replacingOccurrences(of: "data:image/png;base64,", with: "")) else {
print("Failed to decode base64")
return
}
do {
UIImageWriteToSavedPhotosAlbum(UIImage(data: imageData)!, nil, nil, nil)
print("image saved to photo")
} catch {
print("Failed to save image: \(error.localizedDescription)")
}
}
// WKWebView Configuration
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
let url = navigationAction.request.url
if let url = url, url.absoluteString.contains("blob") {
print("URL", url)
let jsCode = """
(async function() {
try {
let response = await fetch("\(url)"); // Ensure this is a valid string
let blob = await response.blob();
let reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = function() {
window.webkit.messageHandlers.imageHandler.postMessage(reader.result);
};
} catch (error) {
window.webkit.messageHandlers.imageHandler.postMessage("JavaScript Error: " + error.message);
}
})();
"""
print("jscode", jsCode)
webView.evaluateJavaScript(jsCode) { result, error in
if let error = error {
print("JavaScript Error: \(error)")
}
}
decisionHandler(.cancel)
}
// detect these specified deeplinks and e_money simulator to be handled by OS
if url!.absoluteString.hasPrefix("https://simulator.sandbox.midtrans.com/gopay/partner/")
|| url!.absoluteString.hasPrefix("https://simulator.sandbox.midtrans.com/shopeepay/")
|| url!.absoluteString.hasPrefix("shopeeid://")
|| url!.absoluteString.hasPrefix("gojek://")
|| url!.absoluteString.hasPrefix("//wsa.wallet.airpay.co.id/") {
decisionHandler(.cancel)
UIApplication.shared.open(url!)
// any other url will be loaded normally by the WebView
} else {
decisionHandler(.allow)
}
}
// Loading animator
fileprivate func setActivityIndicator() {
activityIndicatorContainer = UIView(frame: CGRect(x: 0, y: 0, width: 80, height: 80))
activityIndicatorContainer.center.x = webView.center.x
activityIndicatorContainer.center.y = webView.center.y - 44
activityIndicatorContainer.backgroundColor = UIColor.black
activityIndicatorContainer.alpha = 0.8
activityIndicatorContainer.layer.cornerRadius = 10
// Configure the activity indicator
activityIndicator = UIActivityIndicatorView()
activityIndicator.hidesWhenStopped = true
activityIndicator.style = UIActivityIndicatorView.Style.whiteLarge
activityIndicator.translatesAutoresizingMaskIntoConstraints = false
activityIndicatorContainer.addSubview(activityIndicator)
webView.addSubview(activityIndicatorContainer)
// Constraints
activityIndicator.centerXAnchor.constraint(equalTo: activityIndicatorContainer.centerXAnchor).isActive = true
activityIndicator.centerYAnchor.constraint(equalTo: activityIndicatorContainer.centerYAnchor).isActive = true
}
// Helper function to control activityIndicator's animation
fileprivate func showActivityIndicator(show: Bool) {
if show {
activityIndicator.startAnimating()
} else {
activityIndicator.stopAnimating()
activityIndicatorContainer.removeFromSuperview()
}
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
if let url = webView.url?.absoluteString, url.contains("blob") {
print("Found blob")
}
self.showActivityIndicator(show: false)
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
// Set the indicator everytime webView started loading
self.setActivityIndicator()
self.showActivityIndicator(show: true)
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
self.showActivityIndicator(show: false)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment