Skip to content

Commit

Permalink
revert: don't use ios pods package until we publish it (#17)
Browse files Browse the repository at this point in the history
* feat: remove env for compatibility

* revert: don't use ios pods package until we publish it
  • Loading branch information
andrejborstnik authored Jan 9, 2025
1 parent e96e177 commit 3f51337
Show file tree
Hide file tree
Showing 8 changed files with 279 additions and 12 deletions.
5 changes: 0 additions & 5 deletions ios/Sources/passkeys-mobile-ios/Environment.swift

This file was deleted.

3 changes: 1 addition & 2 deletions ios/Sources/passkeys-mobile-ios/PasskeysMobile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ enum CustomError: Error {
}

public struct PasskeysMobileView: View {
@Environment(\.embeddedWalletUrl) private var embeddedWalletUrl: String
@ObservedObject private var viewModel: WebViewModel

public init() {
Expand All @@ -23,7 +22,7 @@ public struct PasskeysMobileView: View {
let delegate = WebviewDelegate()

Group {
if let url = URL(string: embeddedWalletUrl) {
if let url = URL(string: "https://wallet-d.passkeys.foundation?relay") {
Webview(
url: url,
uiDelegate: delegate,
Expand Down
35 changes: 35 additions & 0 deletions lefthook.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# EXAMPLE USAGE:
#
# Refer for explanation to following link:
# https://github.com/evilmartians/lefthook/blob/master/docs/configuration.md
#
# pre-push:
# commands:
# packages-audit:
# tags: frontend security
# run: yarn audit
# gems-audit:
# tags: backend security
# run: bundle audit
#
# pre-commit:
# parallel: true
# commands:
# eslint:
# glob: "*.{js,ts,jsx,tsx}"
# run: yarn eslint {staged_files}
# rubocop:
# tags: backend style
# glob: "*.rb"
# exclude: '(^|/)(application|routes)\.rb$'
# run: bundle exec rubocop --force-exclusion {all_files}
# govet:
# tags: backend style
# files: git ls-files -m
# glob: "*.go"
# run: go vet {files}
# scripts:
# "hello.js":
# runner: node
# "any.go":
# runner: go run
3 changes: 1 addition & 2 deletions react-native/example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ PODS:
- DoubleConversion
- glog
- hermes-engine
- PasskeysMobile (~> 0.1.0)
- RCT-Folly (= 2024.01.01.00)
- RCTRequired
- RCTTypeSafety
Expand Down Expand Up @@ -1748,7 +1747,7 @@ SPEC CHECKSUMS:
fmt: 10c6e61f4be25dc963c36bd73fc7b1705fe975be
glog: 08b301085f15bcbb6ff8632a8ebaf239aae04e6a
hermes-engine: 0555a84ea495e8e3b4bde71b597cd87fbb382888
passkeys-react-native: 97079132e571bf9b433f5fd2bc674a4923a012d3
passkeys-react-native: c300667d41506b27523ee1f5b11ae44b0c747934
PasskeysMobile: 5b0f800e55ced3ed520a4dbc302b24802596878f
RCT-Folly: bf5c0376ffe4dd2cf438dcf86db385df9fdce648
RCTDeprecation: 2c5e1000b04ab70b53956aa498bf7442c3c6e497
Expand Down
161 changes: 161 additions & 0 deletions react-native/ios/PasskeysMobile.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import SafariServices
import WebKit
import SwiftUI

class WebViewModel: ObservableObject {
@Published var webView: WKWebView? = nil
public init() {}
}

enum CustomError: Error {
case message(String)
}

public struct PasskeysMobileView: View {
@ObservedObject private var viewModel: WebViewModel

public init() {
self.viewModel = WebViewModel()
}

public var body: some View {
let delegate = WebviewDelegate()

Group {
if let url = URL(string: "https://wallet-d.passkeys.foundation?relay") {
Webview(
url: url,
uiDelegate: delegate,
onWebViewCreated: { webView in
self.viewModel.webView = webView
}
)
.ignoresSafeArea()
.navigationTitle("Passkeys")
.navigationBarTitleDisplayMode(.inline)
} else {
Text("Error: Invalid URL")
}
}
}

public func callAsyncJavaScript(_ script: String, completion: @escaping (Result<Any?, Error>) -> Void) {
guard let webviewInstance = viewModel.webView else {
completion(.failure(CustomError.message("WebView not found")))
return
}

Task {
do {
let jsResult = try await webviewInstance.callAsyncJavaScript(
script,
arguments: [:],
contentWorld: .page
)

if let jsResult = jsResult as? String,
let jsonData = jsResult.data(using: .utf8),
let jsonObject = try? JSONSerialization.jsonObject(with: jsonData) {
completion(.success(jsonObject))
} else {
completion(.failure(CustomError.message("Invalid JavaScript response format")))
}
} catch {
completion(.failure(error))
}
}
}

public func callMethod(_ method: String, data: [String: Any]?, completion: @escaping (Result<Any?, Error>) -> Void) {
let dataJSON: String
if let data = data,
let jsonData = try? JSONSerialization.data(withJSONObject: data, options: []),
let jsonString = String(data: jsonData, encoding: .utf8) {
dataJSON = jsonString
} else {
dataJSON = "{}"
}

let script = """
const result = window.\(method)(\(dataJSON));
if (result instanceof Promise) {
return result
.then(resolved => JSON.stringify(resolved))
.catch(error => { throw error; });
} else {
return JSON.stringify(result);
}
"""
callAsyncJavaScript(script, completion: completion)
}
}

struct SafariView: UIViewControllerRepresentable {
let url: URL
let onDismiss: () -> Void

func makeUIViewController(context: Context) -> SFSafariViewController {
let safariVC = SFSafariViewController(url: url)
safariVC.delegate = context.coordinator
return safariVC
}

func updateUIViewController(_ uiViewController: SFSafariViewController, context: Context) {}

func makeCoordinator() -> Coordinator {
Coordinator(onDismiss: onDismiss)
}

class Coordinator: NSObject, SFSafariViewControllerDelegate {
let onDismiss: () -> Void

init(onDismiss: @escaping () -> Void) {
self.onDismiss = onDismiss
}

func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
onDismiss()
}
}
}

class WebviewDelegate: NSObject, WKUIDelegate {
private weak var hostingController: UIViewController?

func presentSafariView(from viewController: UIViewController, url: URL) {
let safariView = SafariView(url: url, onDismiss: {
viewController.dismiss(animated: true)
self.hostingController = nil
})
let hostingController = UIHostingController(rootView: safariView)
self.hostingController = hostingController
viewController.present(hostingController, animated: true)
}

func closeSafariView() {
hostingController?.dismiss(animated: true) {
self.hostingController = nil
}
}

func getPresentedViewController() -> UIViewController? {
if let reactNativeController = (NSClassFromString("RCTPresentedViewController") as? () -> UIViewController)?() {
return reactNativeController
}

var topController = UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.rootViewController
while let presentedController = topController?.presentedViewController {
topController = presentedController
}
return topController
}

func openSafariView(url: String) {
guard let viewController = getPresentedViewController(),
let safariURL = URL(string: url) else {
print("Failed to retrieve presented view controller or invalid URL.")
return
}
presentSafariView(from: viewController, url: safariURL)
}
}
1 change: 0 additions & 1 deletion react-native/ios/PasskeysViewManager.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import SwiftUI
import PasskeysMobile

class HostingAwareView<T: View>: UIView {
var hostingController: UIHostingController<T>?
Expand Down
81 changes: 81 additions & 0 deletions react-native/ios/Webview.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import WebKit
import SwiftUI

struct Webview: UIViewRepresentable {
let url: URL
let uiDelegate: WebviewDelegate
var onWebViewCreated: ((WKWebView) -> Void)?

func makeCoordinator() -> Coordinator {
Coordinator(self)
}

func makeUIView(context: Context) -> WKWebView {
let configuration = WKWebViewConfiguration()
configuration.websiteDataStore = WKWebsiteDataStore.default()

let contentController = WKUserContentController()
contentController.add(context.coordinator, name: "closeSigner")
contentController.add(context.coordinator, name: "openSigner")
configuration.userContentController = contentController

let js = """
if (!window.nativeBridge) {
window.nativeBridge = {};
}
window.nativeBridge.closeSigner = function() {
window.webkit.messageHandlers.closeSigner.postMessage(null);
};
window.nativeBridge.openSigner = function(url) {
if (typeof url !== 'string') throw new Error('url is not a string')
window.webkit.messageHandlers.openSigner.postMessage(url);
}
"""
contentController.addUserScript(WKUserScript(source: js, injectionTime: .atDocumentEnd, forMainFrameOnly: false))

let webView = WKWebView(frame: .zero, configuration: configuration)
webView.uiDelegate = uiDelegate
if #available(iOS 16.4, *) {
webView.isInspectable = true
}

webView.load(URLRequest(url: url))

DispatchQueue.main.async {
self.onWebViewCreated?(webView)
}

return webView
}

func updateUIView(_ webView: WKWebView, context: Context) {
DispatchQueue.main.async {
self.onWebViewCreated?(webView)
}
}

class Coordinator: NSObject, WKScriptMessageHandler {
var parent: Webview

init(_ parent: Webview) {
self.parent = parent
}

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "closeSigner" {
DispatchQueue.main.async {
self.parent.uiDelegate.closeSafariView()
}
}
if message.name == "openSigner" {
if let url = message.body as? String {
DispatchQueue.main.async {
self.parent.uiDelegate.openSafariView(url: url)
}
} else {
print("url is not a String")
}
}
}
}
}
2 changes: 0 additions & 2 deletions react-native/passkeys-react-native.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ Pod::Spec.new do |s|

s.source_files = "ios/**/*.{h,m,mm,swift}"

s.dependency 'PasskeysMobile', '~> 0.1.0'

# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
# See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
if respond_to?(:install_modules_dependencies, true)
Expand Down

0 comments on commit 3f51337

Please sign in to comment.