blob: 24b25acb3896c5038884187af5d7eccd3161df46 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart' show visibleForTesting;
import 'package:flutter/services.dart';
import 'package:url_launcher_platform_interface/link.dart';
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
import 'src/messages.g.dart';
/// An implementation of [UrlLauncherPlatform] for iOS.
class UrlLauncherIOS extends UrlLauncherPlatform {
/// Creates a new plugin implementation instance.
UrlLauncherIOS({
@visibleForTesting UrlLauncherApi? api,
}) : _hostApi = api ?? UrlLauncherApi();
final UrlLauncherApi _hostApi;
/// Registers this class as the default instance of [UrlLauncherPlatform].
static void registerWith() {
UrlLauncherPlatform.instance = UrlLauncherIOS();
}
@override
final LinkDelegate? linkDelegate = null;
@override
Future<bool> canLaunch(String url) async {
final LaunchResult result = await _hostApi.canLaunchUrl(url);
return _mapLaunchResult(result);
}
@override
Future<void> closeWebView() {
return _hostApi.closeSafariViewController();
}
@override
Future<bool> launch(
String url, {
required bool useSafariVC,
required bool useWebView,
required bool enableJavaScript,
required bool enableDomStorage,
required bool universalLinksOnly,
required Map<String, String> headers,
String? webOnlyWindowName,
}) async {
final PreferredLaunchMode mode;
if (useSafariVC) {
mode = PreferredLaunchMode.inAppBrowserView;
} else if (universalLinksOnly) {
mode = PreferredLaunchMode.externalNonBrowserApplication;
} else {
mode = PreferredLaunchMode.externalApplication;
}
return launchUrl(
url,
LaunchOptions(
mode: mode,
webViewConfiguration: InAppWebViewConfiguration(
enableDomStorage: enableDomStorage,
enableJavaScript: enableJavaScript,
headers: headers)));
}
@override
Future<bool> launchUrl(String url, LaunchOptions options) async {
final bool inApp;
switch (options.mode) {
case PreferredLaunchMode.inAppWebView:
case PreferredLaunchMode.inAppBrowserView:
// The iOS implementation doesn't distinguish between these two modes;
// both are treated as inAppBrowserView.
inApp = true;
case PreferredLaunchMode.externalApplication:
case PreferredLaunchMode.externalNonBrowserApplication:
inApp = false;
case PreferredLaunchMode.platformDefault:
// Intentionally treat any new values as platformDefault; support for any
// new mode requires intentional opt-in, otherwise falling back is the
// documented behavior.
// ignore: no_default_cases
default:
// By default, open web URLs in the application.
inApp = url.startsWith('http:') || url.startsWith('https:');
break;
}
if (inApp) {
return _mapInAppLoadResult(
await _hostApi.openUrlInSafariViewController(url),
url: url);
} else {
return _mapLaunchResult(await _hostApi.launchUrl(url,
options.mode == PreferredLaunchMode.externalNonBrowserApplication));
}
}
@override
Future<bool> supportsMode(PreferredLaunchMode mode) async {
switch (mode) {
case PreferredLaunchMode.platformDefault:
case PreferredLaunchMode.inAppWebView:
case PreferredLaunchMode.inAppBrowserView:
case PreferredLaunchMode.externalApplication:
case PreferredLaunchMode.externalNonBrowserApplication:
return true;
// Default is a desired behavior here since support for new modes is
// always opt-in, and the enum lives in a different package, so silently
// adding "false" for new values is the correct behavior.
// ignore: no_default_cases
default:
return false;
}
}
@override
Future<bool> supportsCloseForMode(PreferredLaunchMode mode) async {
return mode == PreferredLaunchMode.inAppWebView ||
mode == PreferredLaunchMode.inAppBrowserView;
}
bool _mapLaunchResult(LaunchResult result) {
switch (result) {
case LaunchResult.success:
return true;
case LaunchResult.failure:
return false;
case LaunchResult.invalidUrl:
throw _invalidUrlException();
}
}
bool _mapInAppLoadResult(InAppLoadResult result, {required String url}) {
switch (result) {
case InAppLoadResult.success:
return true;
case InAppLoadResult.failedToLoad:
throw _failedSafariViewControllerLoadException(url);
case InAppLoadResult.invalidUrl:
throw _invalidUrlException();
}
}
// TODO(stuartmorgan): Remove this as part of standardizing error handling.
// See https://github.com/flutter/flutter/issues/127665
//
// This PlatformException (including the exact string details, since those
// are a defacto part of the API) is for compatibility with the previous
// native implementation.
PlatformException _invalidUrlException() {
throw PlatformException(
code: 'argument_error',
message: 'Unable to parse URL',
);
}
// TODO(stuartmorgan): Remove this as part of standardizing error handling.
// See https://github.com/flutter/flutter/issues/127665
//
// This PlatformException (including the exact string details, since those
// are a defacto part of the API) is for compatibility with the previous
// native implementation.
PlatformException _failedSafariViewControllerLoadException(String url) {
throw PlatformException(
code: 'Error',
message: 'Error while launching $url',
);
}
}