Files
self/packages/native-shell-ios/Sources/SelfNativeShell/API/SelfSdk.swift
Justin Hernandez f29130587b Harden WebView bridge and asset serving across native shells (#1924)
* security fix

* more security fixes

* fixes

* pr feedback

* Restore remote URL loading in native-shell-ios and native-shell-android

Remove bundled-asset-only loading and SHA-256 integrity checks from both
native shell packages. WebViews now load directly from the remote URL
(default: https://self-app-alpha.vercel.app) over HTTPS, matching the
pattern already implemented in kmp-sdk and self-sdk-swift.

Also fixes ObjC selector mismatch in self-sdk-swift WebViewProviderImpl
for configureRemoteLoading.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Restore remote URL loading in kmp-sdk and self-sdk-swift

Remove bundled-asset-only loading from kmp-sdk AndroidWebViewHost and
self-sdk-swift WebViewProviderImpl. Both now load directly from the
remote URL (default: https://self-app-alpha.vercel.app) over HTTPS.

Adds remoteWebAppBaseUrl to SelfSdkConfig and pipes it through
IosWebViewHost via the new configureRemoteLoading protocol method.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* coderabbit comments

* lint

* coderabbit comments

---------

Co-authored-by: seshanthS <seshanth@protonmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 22:39:27 +05:30

86 lines
2.6 KiB
Swift

// SPDX-License-Identifier: BUSL-1.1
import Foundation
import UIKit
public final class SelfSdk {
public static func createViewController(
config: SelfSdkConfig,
callback: SelfSdkCallback
) -> UIViewController {
let viewController = SelfSdkViewController(config: config, callback: callback)
viewController.modalPresentationStyle = .fullScreen
return viewController
}
}
final class SelfSdkViewController: UIViewController {
private let config: SelfSdkConfig
private let callback: SelfSdkCallback
private var webViewHost: SelfWebViewHost?
init(config: SelfSdkConfig, callback: SelfSdkCallback) {
self.config = config
self.callback = callback
super.init(nibName: nil, bundle: nil)
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) is not supported")
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .black
setupWebView()
}
private func setupWebView() {
let callback = self.callback
let lifecycleHandler = LifecycleHandler(
viewController: self,
onResult: { result in
if let dict = result as? [String: Any] {
callback.onSuccess(result: dict)
} else {
callback.onSuccess(result: [:])
}
},
onFailure: { error in
callback.onFailure(error: error)
},
onDismiss: {
callback.onCancelled()
}
)
let router = MessageRouter { [weak self] js in
self?.webViewHost?.evaluateJs(js)
}
router.register(handler: SecureStorageHandler(provider: config.secureStorageProvider))
router.register(handler: CryptoHandler())
router.register(handler: lifecycleHandler)
let host = SelfWebViewHost(
router: router,
isDebugMode: config.isDebugMode,
remoteWebAppBaseURL: config.remoteWebAppBaseURL
)
self.webViewHost = host
let webView = host.createWebView()
webView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(webView)
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)
])
host.loadContent(queryParams: config.toQueryParams())
}
}