Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 22 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,12 @@ npx cap sync
### openInWebView(...)

```typescript
openInWebView(url: string, options: WebViewOptions) => void
openInWebView(model: OpenInWebViewParameterModel) => void
```

| Param | Type |
| ------------- | --------------------------------------------------------- |
| **`url`** | <code>string</code> |
| **`options`** | <code><a href="#webviewoptions">WebViewOptions</a></code> |
| Param | Type |
| ----------- | ----------------------------------------------------------------------------------- |
| **`model`** | <code><a href="#openinwebviewparametermodel">OpenInWebViewParameterModel</a></code> |

--------------------

Expand Down Expand Up @@ -104,6 +103,15 @@ addListener(eventName: 'browserClosed' | 'browserPageLoaded', listenerFunc: () =
### Interfaces


#### OpenInWebViewParameterModel

Defines the options for opening a URL in the web view.

| Prop | Type |
| ------------- | --------------------------------------------------------- |
| **`options`** | <code><a href="#webviewoptions">WebViewOptions</a></code> |


#### WebViewOptions

| Prop | Type |
Expand Down Expand Up @@ -132,20 +140,19 @@ addListener(eventName: 'browserClosed' | 'browserPageLoaded', listenerFunc: () =

#### iOSWebViewOptions

| Prop | Type |
| --------------------------------------- | ----------------------------------------------------- |
| **`allowOverScroll`** | <code>boolean</code> |
| **`enableViewportScale`** | <code>boolean</code> |
| **`allowInLineMediaPlayback`** | <code>boolean</code> |
| **`keyboardDisplayRequiresUserAction`** | <code>boolean</code> |
| **`surpressIncrementalRendering`** | <code>boolean</code> |
| **`viewStyle`** | <code><a href="#iosviewstyle">iOSViewStyle</a></code> |
| **`animation`** | <code><a href="#iosanimation">iOSAnimation</a></code> |
| Prop | Type |
| ---------------------------------- | ----------------------------------------------------- |
| **`allowOverScroll`** | <code>boolean</code> |
| **`enableViewportScale`** | <code>boolean</code> |
| **`allowInLineMediaPlayback`** | <code>boolean</code> |
| **`surpressIncrementalRendering`** | <code>boolean</code> |
| **`viewStyle`** | <code><a href="#iosviewstyle">iOSViewStyle</a></code> |
| **`animationEffect`** | <code><a href="#iosanimation">iOSAnimation</a></code> |


#### OpenInSystemBrowserParameterModel

Defines the options for opening a URL in the syste, browser.
Defines the options for opening a URL in the system browser.

| Prop | Type |
| ------------- | --------------------------------------------------------------------- |
Expand Down
37 changes: 36 additions & 1 deletion example-app/src/pages/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IonButton, IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react';
import { InAppBrowser, SystemBrowserOptions, DefaultSystemBrowserOptions, DefaultAndroidSystemBrowserOptions, DismissStyle, iOSViewStyle, iOSAnimation } from '@capacitor/os-inappbrowser';
import { InAppBrowser, SystemBrowserOptions, DefaultSystemBrowserOptions, DefaultAndroidSystemBrowserOptions, WebViewOptions, DefaultWebViewOptions, DefaultAndroidWebViewOptions, DismissStyle, iOSViewStyle, iOSAnimation, ToolbarPosition } from '@capacitor/os-inappbrowser';
import './Home.css';

const Home: React.FC = () => {
Expand Down Expand Up @@ -33,6 +33,39 @@ const Home: React.FC = () => {
});
}

const openInWebViewWithDefaults = () => {
InAppBrowser.openInWebView({
url: "https://www.google.com",
options: DefaultWebViewOptions
});
}

const openInWebViewWithCustomValues = () => {
InAppBrowser.openInWebView({
url: "https://www.outsystems.com/",
options: {
showURL: false,
showToolbar: true,
clearCache: false,
clearSessionCache: false,
mediaPlaybackRequiresUserAction: true,
closeButtonText: "Done",
toolbarPosition: ToolbarPosition.BOTTOM,
showNavigationButtons: false,
leftToRight: true,
android: DefaultAndroidWebViewOptions,
iOS: {
allowOverScroll: false,
enableViewportScale: true,
allowInLineMediaPlayback: true,
surpressIncrementalRendering: true,
viewStyle: iOSViewStyle.PAGE_SHEET,
animationEffect: iOSAnimation.CROSS_DISSOLVE
}
}
});
}

InAppBrowser.addListener('browserClosed', () => {
console.log("browser was closed.");
});
Expand All @@ -58,6 +91,8 @@ const Home: React.FC = () => {
<IonButton onClick={test}>TEST</IonButton>
<IonButton onClick={openInSystemBrowserWithDefaults}>System Browser with Defaults</IonButton>
<IonButton onClick={openInSystemBrowserWithCustomValues}>System Browser with Custom Values</IonButton>
<IonButton onClick={openInWebViewWithDefaults}>Web View with Defaults</IonButton>
<IonButton onClick={openInWebViewWithCustomValues}>Web View with Custom Values</IonButton>
</div>
</IonContent>
</IonPage>
Expand Down
160 changes: 121 additions & 39 deletions ios/Sources/InAppBrowserPlugin/InAppBrowserPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import Capacitor
import OSInAppBrowserLib
import UIKit

typealias OSInAppBrowserEngine = OSIABEngine<OSIABApplicationRouterAdapter, OSIABSafariViewControllerRouterAdapter, OSIABWebViewRouterAdapter>

/**
* Please read the Capacitor iOS Plugin Development Guide
* here: https://capacitorjs.com/docs/plugins/ios
Expand All @@ -12,62 +14,52 @@ public class InAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
public let jsName = "InAppBrowser"
public let pluginMethods: [CAPPluginMethod] = [
.init(name: "openInExternalBrowser", returnType: CAPPluginReturnPromise),
.init(name: "openInSystemBrowser", returnType: CAPPluginReturnPromise)
.init(name: "openInSystemBrowser", returnType: CAPPluginReturnPromise),
.init(name: "openInWebView", returnType: CAPPluginReturnPromise)
]

private var plugin: OSIABEngine<OSIABApplicationRouterAdapter, OSIABSafariViewControllerRouterAdapter>?
private var currentlyOpenedBrowser: (any OSIABRouter)?

private var plugin: OSInAppBrowserEngine?
private var openedRouter: (any OSIABRouter)?
override public func load() {
self.plugin = .init()
}

@objc func openInExternalBrowser(_ call: CAPPluginCall) {
if self.plugin == nil {
self.load()
self.load()
}

guard let plugin else {
return self.error(call, type: .bridgeNotInitialised)
}

let target = OSInAppBrowserTarget.openInExternalBrowser

func delegateExternalBrowser(_ url: String) {
DispatchQueue.main.async {
plugin.openExternalBrowser(url, { [weak self] success in
guard let self else { return }

if success {
self.success(call)
} else {
self.error(call, type: .failedToOpen(url: url, onTarget: target))
}
})
}
}

guard let url = call.getString("url") else {
let target = OSInAppBrowserTarget.externalBrowser

guard
let urlString = call.getString("url"),
let url = URL(string: urlString)
else {
return self.error(call, type: .inputArgumentsIssue(target: target))
}

delegateExternalBrowser(url)
delegateExternalBrowser(plugin, url, call)
}

@objc func openInSystemBrowser(_ call: CAPPluginCall) {
if self.plugin == nil {
self.load()
}

guard let plugin else {
return self.error(call, type: .bridgeNotInitialised)
}

let target = OSInAppBrowserTarget.openInSystemBrowser
let target = OSInAppBrowserTarget.systemBrowser

func delegateSystemBrowser(_ url: String, _ options: OSIABSystemBrowserOptions) {
func delegateSystemBrowser(_ url: URL, _ options: OSIABSystemBrowserOptions) {
DispatchQueue.main.async {
self.currentlyOpenedBrowser = plugin.openSystemBrowser(url, options, { [weak self] event, safariViewController in
self.openedRouter = plugin.openSystemBrowser(url, options, { [weak self] event, safariViewController in
guard let self else { return }

if let event {
Expand All @@ -76,18 +68,86 @@ public class InAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
self.bridge?.viewController?.show(safariViewController, sender: nil)
self.success(call)
} else {
self.error(call, type: .failedToOpen(url: url, onTarget: target))
self.error(call, type: .failedToOpen(url: url.absoluteString, onTarget: target))
}
})
}
}

guard let url = call.getString("url"),
let options: OSInAppBrowserInputArgumentsSystemBrowserModel = self.createModel(for: call.getObject("options"))
guard
let urlString = call.getString("url"),
let options: OSInAppBrowserSystemBrowserModel = self.createModel(for: call.getObject("options")),
let url = URL(string: urlString)
else { return self.error(call, type: .inputArgumentsIssue(target: target)) }

delegateSystemBrowser(url, options.toSystemBrowserOptions())
}

@objc func openInWebView(_ call: CAPPluginCall) {
if self.plugin == nil {
self.load()
}

guard let plugin else {
return self.error(call, type: .bridgeNotInitialised)
}

let target = OSInAppBrowserTarget.webView

func delegateWebView(_ url: URL, _ options: OSIABWebViewOptions) {
DispatchQueue.main.async {
self.openedRouter = plugin.openWebView(
url,
options,
onDelegateClose: { [weak self] in
self?.bridge?.viewController?.dismiss(animated: true)
},
onDelegateURL: { [weak self] url in
self?.delegateExternalBrowser(plugin, url, call)
},
onDelegateAlertController: { [weak self] alert in
self?.bridge?.viewController?.presentedViewController?.show(alert, sender: nil)
}, { [weak self] event, hostingController in
guard let self else { return }

if let event {
self.notifyListeners(event.rawValue, data: nil)
} else if let hostingController {
self.bridge?.viewController?.show(hostingController, sender: nil)
self.success(call)
} else {
self.error(call, type: .failedToOpen(url: url.absoluteString, onTarget: target))
}
}
)
}
}

guard
let urlString = call.getString("url"),
let options: OSInAppBrowserWebViewModel = self.createModel(for: call.getObject("options")),
let url = URL(string: urlString)
else { return self.error(call, type: .inputArgumentsIssue(target: target)) }

let customUserAgent = self.bridge?.config.overridenUserAgentString
delegateWebView(url, options.toWebViewOptions(with: customUserAgent))
}
}

private extension InAppBrowserPlugin {
func delegateExternalBrowser(_ plugin: OSInAppBrowserEngine, _ url: URL, _ call: CAPPluginCall) {
DispatchQueue.main.async {
plugin.openExternalBrowser(url, { [weak self] success in
guard let self else { return }

if success {
self.success(call)
} else {
self.error(call, type: .failedToOpen(url: url.absoluteString, onTarget: .externalBrowser))
}
})
}
}
}

private extension InAppBrowserPlugin {
Expand All @@ -108,15 +168,13 @@ private extension InAppBrowserPlugin {
}
}

private extension OSIABEngine where ExternalBrowser == OSIABApplicationRouterAdapter {
func openExternalBrowser(_ url: String, _ completionHandler: @escaping (Bool) -> Void) {
private extension OSInAppBrowserEngine {
func openExternalBrowser(_ url: URL, _ completionHandler: @escaping (Bool) -> Void) {
let router = OSIABApplicationRouterAdapter(UIApplication.shared)
self.openExternalBrowser(url, routerDelegate: router, completionHandler)
}
}

private extension OSIABEngine where SystemBrowser == OSIABSafariViewControllerRouterAdapter {
func openSystemBrowser(_ url: String, _ options: OSIABSystemBrowserOptions, _ completionHandler: @escaping (OSIABEventType?, UIViewController?) -> Void) -> SystemBrowser {

func openSystemBrowser(_ url: URL, _ options: OSIABSystemBrowserOptions, _ completionHandler: @escaping (OSIABEventType?, UIViewController?) -> Void) -> SystemBrowser {
let router = OSIABSafariViewControllerRouterAdapter(
options,
onBrowserPageLoad: { completionHandler(.pageLoadCompleted, nil) },
Expand All @@ -125,6 +183,30 @@ private extension OSIABEngine where SystemBrowser == OSIABSafariViewControllerRo
self.openSystemBrowser(url, routerDelegate: router) { completionHandler(nil, $0) }
return router
}

func openWebView(
_ url: URL,
_ options: OSIABWebViewOptions,
onDelegateClose: @escaping () -> Void,
onDelegateURL: @escaping (URL) -> Void,
onDelegateAlertController: @escaping (UIAlertController) -> Void,
_ completionHandler: @escaping (OSIABEventType?, UIViewController?) -> Void
) -> WebView {
let callbackHandler = OSIABWebViewCallbackHandler(
onDelegateURL: onDelegateURL,
onDelegateAlertController: onDelegateAlertController,
onBrowserPageLoad: { completionHandler(.pageLoadCompleted, nil) },
onBrowserClosed: { isAlreadyClosed in
if !isAlreadyClosed {
onDelegateClose()
}
completionHandler(.pageClosed, nil)
}
)
let router = OSIABWebViewRouterAdapter(options, cacheManager: OSIABBrowserCacheManager(dataStore: .default()), callbackHandler: callbackHandler)
self.openWebView(url, routerDelegate: router) { completionHandler(nil, $0) }
return router
}
}

enum OSIABEventType: String {
Expand Down
44 changes: 44 additions & 0 deletions ios/Sources/InAppBrowserPlugin/LibraryModels+Decodable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import OSInAppBrowserLib

extension OSIABDismissStyle: Decodable {
init(_ value: Int) {
switch value {
case 0: self = .close
case 1: self = .cancel
case 2: self = .done
default: self = .defaultValue
}
}
}

extension OSIABViewStyle: Decodable {
init(_ value: Int) {
switch value {
case 0: self = .pageSheet
case 1: self = .formSheet
case 2: self = .fullScreen
default: self = .defaultValue
}
}
}

extension OSIABAnimationEffect: Decodable {
init(_ value: Int) {
switch value {
case 0: self = .flipHorizontal
case 1: self = .crossDissolve
case 2: self = .coverVertical
default: self = .defaultValue
}
}
}

extension OSIABToolbarPosition: Decodable {
init(_ value: Int) {
switch value {
case 0: self = .top
case 1: self = .bottom
default: self = .defaultValue
}
}
}
Loading