Skip to content

Commit

Permalink
Merge pull request #191 from droibit/feature/close_custom_tabs
Browse files Browse the repository at this point in the history
Enhance README and Refactor SFSafariViewController dismissal handling
  • Loading branch information
droibit authored Apr 27, 2024
2 parents 0bd39a3 + 13be529 commit ce82a34
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 27 deletions.
14 changes: 13 additions & 1 deletion flutter_custom_tabs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ On Android, the plugin defaults to launching Chrome, which supports all Custom T
You can prioritize launching the default browser on the device that supports Custom Tabs over Chrome.

> [!NOTE]
> Some browsers may not support the options specified in CustomTabsOptions.
> Some browsers may not support the options specified in `CustomTabsOptions`.
> - See: [Custom Tabs Browser Support](https://developer.chrome.com/docs/android/custom-tabs/browser-support/).
```dart
Expand All @@ -245,6 +245,18 @@ Future<void> _launchURLInDefaultBrowserOnAndroid() async {
}
```

### Close the Custom Tabs

You can manually close the Custom Tabs.

```dart
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart';
Future<void> _closeCustomTabsManually() async {
await closeCustomTabs();
}
```

## License

Copyright (C) 2015 The Android Open Source Project
Expand Down
6 changes: 4 additions & 2 deletions flutter_custom_tabs_ios/ios/Classes/CustomTabsPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ public class CustomTabsPlugin: NSObject, FlutterPlugin, CustomTabsApi {
}
}

func closeAllIfPossible() throws {
launcher.dismissAll()
func closeAllIfPossible(completion: @escaping (Result<Void, any Error>) -> Void) {
launcher.dismissAll {
completion(.success(()))
}
}

// MARK: - Private
Expand Down
67 changes: 49 additions & 18 deletions flutter_custom_tabs_ios/ios/Classes/Laucher.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import SafariServices
import UIKit

open class Launcher {
private var dismissStack = [() -> Void]()

open func open(
_ url: URL,
options: [UIApplication.OpenExternalURLOptionsKey: Any] = [:],
Expand All @@ -13,9 +12,6 @@ open class Launcher {

open func present(_ viewControllerToPresent: UIViewController, completion: ((Bool) -> Void)? = nil) {
if let topViewController = UIWindow.keyWindow?.topViewController() {
dismissStack.append { [weak viewControllerToPresent] in
viewControllerToPresent?.dismiss(animated: true)
}
topViewController.present(viewControllerToPresent, animated: true) {
completion?(true)
}
Expand All @@ -24,10 +20,25 @@ open class Launcher {
}
}

open func dismissAll() {
while let task = dismissStack.popLast() {
task()
open func dismissAll(completion: (() -> Void)? = nil) {
guard let rootViewController = UIWindow.keyWindow?.rootViewController else {
completion?()
return
}

var presentedViewController = rootViewController.presentedViewController
var presentedViewControllers = [UIViewController]()
while presentedViewController != nil {
if presentedViewController is SFSafariViewController {
presentedViewControllers.append(presentedViewController!)
}
presentedViewController = presentedViewController!.presentedViewController
}
recursivelyDismissViewControllers(
presentedViewControllers,
animated: true,
completion: completion
)
}
}

Expand All @@ -42,18 +53,38 @@ private extension UIWindow {
func topViewController() -> UIViewController? {
recursivelyFindTopViewController(from: rootViewController)
}
}

private func recursivelyFindTopViewController(from viewController: UIViewController?) -> UIViewController? {
if let navigationController = viewController as? UINavigationController {
return recursivelyFindTopViewController(from: navigationController.visibleViewController)
} else if let tabBarController = viewController as? UITabBarController,
let selected = tabBarController.selectedViewController
{
return recursivelyFindTopViewController(from: selected)
} else if let presentedViewController = viewController?.presentedViewController {
return recursivelyFindTopViewController(from: presentedViewController)
} else {
return viewController
}
}

private func recursivelyDismissViewControllers(
_ viewControllers: [UIViewController],
animated flag: Bool,
completion: (() -> Void)? = nil
) {
var viewControllers = viewControllers
guard let vc = viewControllers.popLast() else {
completion?()
return
}

private func recursivelyFindTopViewController(from viewController: UIViewController?) -> UIViewController? {
if let navigationController = viewController as? UINavigationController {
return recursivelyFindTopViewController(from: navigationController.visibleViewController)
} else if let tabBarController = viewController as? UITabBarController,
let selected = tabBarController.selectedViewController
{
return recursivelyFindTopViewController(from: selected)
} else if let presentedViewController = viewController?.presentedViewController {
return recursivelyFindTopViewController(from: presentedViewController)
vc.dismiss(animated: flag) {
if viewControllers.isEmpty {
completion?()
} else {
return viewController
recursivelyDismissViewControllers(viewControllers, animated: flag, completion: completion)
}
}
}
14 changes: 8 additions & 6 deletions flutter_custom_tabs_ios/ios/Classes/messages.g.swift
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ class CustomTabsApiCodec: FlutterStandardMessageCodec {
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
protocol CustomTabsApi {
func launchURL(_ urlString: String, prefersDeepLink: Bool, options: SFSafariViewControllerOptions?, completion: @escaping (Result<Void, Error>) -> Void)
func closeAllIfPossible() throws
func closeAllIfPossible(completion: @escaping (Result<Void, Error>) -> Void)
}

/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
Expand Down Expand Up @@ -197,11 +197,13 @@ class CustomTabsApiSetup {
let closeAllIfPossibleChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.flutter_custom_tabs_ios.CustomTabsApi.closeAllIfPossible", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
closeAllIfPossibleChannel.setMessageHandler { _, reply in
do {
try api.closeAllIfPossible()
reply(wrapResult(nil))
} catch {
reply(wrapError(error))
api.closeAllIfPossible { result in
switch result {
case .success:
reply(wrapResult(nil))
case .failure(let error):
reply(wrapError(error))
}
}
}
} else {
Expand Down
1 change: 1 addition & 0 deletions flutter_custom_tabs_ios/pigeons/messages.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ abstract class CustomTabsApi {
SFSafariViewControllerOptions? options,
});

@async
void closeAllIfPossible();
}

Expand Down

0 comments on commit ce82a34

Please sign in to comment.