| @@ -0,0 +1,66 @@ | |||
| # Below is a list of people and organizations that have contributed | |||
| # to the Flutter project. Names should be added to the list like so: | |||
| # | |||
| # Name/Organization <email address> | |||
| Google Inc. | |||
| The Chromium Authors | |||
| German Saprykin <saprykin.h@gmail.com> | |||
| Benjamin Sauer <sauer.benjamin@gmail.com> | |||
| larsenthomasj@gmail.com | |||
| Ali Bitek <alibitek@protonmail.ch> | |||
| Pol Batlló <pol.batllo@gmail.com> | |||
| Anatoly Pulyaevskiy | |||
| Hayden Flinner <haydenflinner@gmail.com> | |||
| Stefano Rodriguez <hlsroddy@gmail.com> | |||
| Salvatore Giordano <salvatoregiordanoo@gmail.com> | |||
| Brian Armstrong <brian@flutter.institute> | |||
| Paul DeMarco <paulmdemarco@gmail.com> | |||
| Fabricio Nogueira <feufeu@gmail.com> | |||
| Simon Lightfoot <simon@devangels.london> | |||
| Ashton Thomas <ashton@acrinta.com> | |||
| Thomas Danner <thmsdnnr@gmail.com> | |||
| Diego Velásquez <diego.velasquez.lopez@gmail.com> | |||
| Hajime Nakamura <nkmrhj@gmail.com> | |||
| Tuyển Vũ Xuân <netsoft1985@gmail.com> | |||
| Miguel Ruivo <miguel@miguelruivo.com> | |||
| Sarthak Verma <sarthak@artiosys.com> | |||
| Mike Diarmid <mike@invertase.io> | |||
| Invertase <oss@invertase.io> | |||
| Elliot Hesp <elliot@invertase.io> | |||
| Vince Varga <vince.varga@smaho.com> | |||
| Aawaz Gyawali <awazgyawali@gmail.com> | |||
| EUI Limited <ian.evans3@admiralgroup.co.uk> | |||
| Katarina Sheremet <katarina@sheremet.ch> | |||
| Thomas Stockx <thomas@stockxit.com> | |||
| Sarbagya Dhaubanjar <sarbagyastha@gmail.com> | |||
| Ozkan Eksi <ozeksi@gmail.com> | |||
| Rishab Nayak <rishab@bu.edu> | |||
| ko2ic <ko2ic.dev@gmail.com> | |||
| Jonathan Younger <jonathan@daikini.com> | |||
| Jose Sanchez <josesm82@gmail.com> | |||
| Debkanchan Samadder <debu.samadder@gmail.com> | |||
| Audrius Karosevicius <audrius.karosevicius@gmail.com> | |||
| Lukasz Piliszczuk <lukasz@intheloup.io> | |||
| SoundReply Solutions GmbH <ch@soundreply.com> | |||
| Rafal Wachol <rwachol@gmail.com> | |||
| Pau Picas <pau.picas@gmail.com> | |||
| Christian Weder <chrstian.weder@yapeal.ch> | |||
| Alexandru Tuca <salexandru.tuca@outlook.com> | |||
| Christian Weder <chrstian.weder@yapeal.ch> | |||
| Rhodes Davis Jr. <rody.davis.jr@gmail.com> | |||
| Luigi Agosti <luigi@tengio.com> | |||
| Quentin Le Guennec <quentin@tengio.com> | |||
| Koushik Ravikumar <koushik@tengio.com> | |||
| Nissim Dsilva <nissim@tengio.com> | |||
| Giancarlo Rocha <giancarloiff@gmail.com> | |||
| Ryo Miyake <ryo@miyake.id> | |||
| Théo Champion <contact.theochampion@gmail.com> | |||
| Kazuki Yamaguchi <y.kazuki0614n@gmail.com> | |||
| Eitan Schwartz <eshvartz@gmail.com> | |||
| Chris Rutkowski <chrisrutkowski89@gmail.com> | |||
| Juan Alvarez <juan.alvarez@resideo.com> | |||
| Aleksandr Yurkovskiy <sanekyy@gmail.com> | |||
| Anton Borries <mail@antonborri.es> | |||
| Alex Li <google@alexv525.com> | |||
| Rahul Raj <64.rahulraj@gmail.com> | |||
| @@ -0,0 +1,90 @@ | |||
| ## 6.3.2 | |||
| * Updates to Pigeon v22. | |||
| * Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. | |||
| ## 6.3.1 | |||
| * Fixes a compile error when comforming UIApplication to Launcher in iOS 18 Beta 3. | |||
| ## 6.3.0 | |||
| * Adds Swift Package Manager compatibility. | |||
| ## 6.2.5 | |||
| * Adds explicit imports for UIKit. | |||
| * Updates minimum iOS version to 12.0 and minimum Flutter version to 3.16.6. | |||
| ## 6.2.4 | |||
| * Adds privacy manifest. | |||
| ## 6.2.3 | |||
| * Updates minimum required plugin_platform_interface version to 2.1.7. | |||
| ## 6.2.2 | |||
| * Updates minimum supported SDK version to Flutter 3.10/Dart 3.0. | |||
| * Fixes lint warnings. | |||
| ## 6.2.1 | |||
| * Migrates plugin from Objective-C to Swift. | |||
| ## 6.2.0 | |||
| * Implements `supportsMode` and `supportsCloseForMode`. | |||
| ## 6.1.5 | |||
| * Adds pub topics to package metadata. | |||
| * Updates minimum supported SDK version to Flutter 3.7/Dart 2.19. | |||
| ## 6.1.4 | |||
| * Updates pigeon to fix warnings with clang 15. | |||
| ## 6.1.3 | |||
| * Switches to Pigeon for internal implementation. | |||
| ## 6.1.2 | |||
| * Clarifies explanation of endorsement in README. | |||
| ## 6.1.1 | |||
| * Updates links for the merge of flutter/plugins into flutter/packages. | |||
| ## 6.1.0 | |||
| * Updates minimum Flutter version to 3.3 and iOS 11. | |||
| ## 6.0.18 | |||
| * Updates code for stricter lint checks. | |||
| * Updates minimum Flutter version to 2.10. | |||
| ## 6.0.17 | |||
| * Suppresses warnings for pre-iOS-13 codepaths. | |||
| ## 6.0.16 | |||
| * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors | |||
| lint warnings. | |||
| ## 6.0.15 | |||
| * Switches to an in-package method channel implementation. | |||
| ## 6.0.14 | |||
| * Updates code for new analysis options. | |||
| * Removes dependency on `meta`. | |||
| ## 6.0.13 | |||
| * Splits from `url_launcher` as a federated implementation. | |||
| @@ -0,0 +1,25 @@ | |||
| Copyright 2013 The Flutter Authors. All rights reserved. | |||
| Redistribution and use in source and binary forms, with or without modification, | |||
| are permitted provided that the following conditions are met: | |||
| * Redistributions of source code must retain the above copyright | |||
| notice, this list of conditions and the following disclaimer. | |||
| * Redistributions in binary form must reproduce the above | |||
| copyright notice, this list of conditions and the following | |||
| disclaimer in the documentation and/or other materials provided | |||
| with the distribution. | |||
| * Neither the name of Google Inc. nor the names of its | |||
| contributors may be used to endorse or promote products derived | |||
| from this software without specific prior written permission. | |||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |||
| ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | |||
| ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||
| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |||
| ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| @@ -0,0 +1,15 @@ | |||
| # url\_launcher\_ios | |||
| The iOS implementation of [`url_launcher`][1]. | |||
| ## Usage | |||
| This package is [endorsed][2], which means you can simply use `url_launcher` | |||
| normally. This package will be automatically included in your app when you do, | |||
| so you do not need to add it to your `pubspec.yaml`. | |||
| However, if you `import` this package to use any of its APIs directly, you | |||
| should add it to your `pubspec.yaml` as usual. | |||
| [1]: https://pub.dev/packages/url_launcher | |||
| [2]: https://flutter.dev/to/endorsed-federated-plugin | |||
| @@ -0,0 +1,9 @@ | |||
| # Platform Implementation Test App | |||
| This is a test app for manual testing and automated integration testing | |||
| of this platform implementation. It is not intended to demonstrate actual use of | |||
| this package, since the intent is that plugin clients use the app-facing | |||
| package. | |||
| Unless you are making changes to this implementation package, this example is | |||
| very unlikely to be relevant. | |||
| @@ -0,0 +1,26 @@ | |||
| // 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_test/flutter_test.dart'; | |||
| import 'package:integration_test/integration_test.dart'; | |||
| import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; | |||
| void main() { | |||
| IntegrationTestWidgetsFlutterBinding.ensureInitialized(); | |||
| testWidgets('canLaunch', (WidgetTester _) async { | |||
| final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; | |||
| expect(await launcher.canLaunch('randomstring'), false); | |||
| // Generally all devices should have some default browser. | |||
| expect(await launcher.canLaunch('http://flutter.dev'), true); | |||
| // SMS handling is available by default on test devices. | |||
| expect(await launcher.canLaunch('sms:5555555555'), true); | |||
| // tel: and mailto: links may not be openable on every device. iOS | |||
| // simulators notably can't open these link types. | |||
| }); | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |||
| <plist version="1.0"> | |||
| <dict> | |||
| <key>CFBundleDevelopmentRegion</key> | |||
| <string>en</string> | |||
| <key>CFBundleExecutable</key> | |||
| <string>App</string> | |||
| <key>CFBundleIdentifier</key> | |||
| <string>io.flutter.flutter.app</string> | |||
| <key>CFBundleInfoDictionaryVersion</key> | |||
| <string>6.0</string> | |||
| <key>CFBundleName</key> | |||
| <string>App</string> | |||
| <key>CFBundlePackageType</key> | |||
| <string>FMWK</string> | |||
| <key>CFBundleShortVersionString</key> | |||
| <string>1.0</string> | |||
| <key>CFBundleSignature</key> | |||
| <string>????</string> | |||
| <key>CFBundleVersion</key> | |||
| <string>1.0</string> | |||
| <key>UIRequiredDeviceCapabilities</key> | |||
| <array> | |||
| <string>arm64</string> | |||
| </array> | |||
| <key>MinimumOSVersion</key> | |||
| <string>12.0</string> | |||
| </dict> | |||
| </plist> | |||
| @@ -0,0 +1,2 @@ | |||
| #include "Generated.xcconfig" | |||
| #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" | |||
| @@ -0,0 +1,2 @@ | |||
| #include "Generated.xcconfig" | |||
| #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" | |||
| @@ -0,0 +1,41 @@ | |||
| # Uncomment this line to define a global platform for your project | |||
| # platform :ios, '12.0' | |||
| # CocoaPods analytics sends network stats synchronously affecting flutter build latency. | |||
| ENV['COCOAPODS_DISABLE_STATS'] = 'true' | |||
| project 'Runner', { | |||
| 'Debug' => :debug, | |||
| 'Profile' => :release, | |||
| 'Release' => :release, | |||
| } | |||
| def flutter_root | |||
| generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) | |||
| unless File.exist?(generated_xcode_build_settings_path) | |||
| raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" | |||
| end | |||
| File.foreach(generated_xcode_build_settings_path) do |line| | |||
| matches = line.match(/FLUTTER_ROOT\=(.*)/) | |||
| return matches[1].strip if matches | |||
| end | |||
| raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" | |||
| end | |||
| require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) | |||
| flutter_ios_podfile_setup | |||
| target 'Runner' do | |||
| flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) | |||
| target 'RunnerTests' do | |||
| inherit! :search_paths | |||
| end | |||
| end | |||
| post_install do |installer| | |||
| installer.pods_project.targets.each do |target| | |||
| flutter_additional_ios_build_settings(target) | |||
| end | |||
| end | |||
| @@ -0,0 +1,771 @@ | |||
| // !$*UTF8*$! | |||
| { | |||
| archiveVersion = 1; | |||
| classes = { | |||
| }; | |||
| objectVersion = 54; | |||
| objects = { | |||
| /* Begin PBXBuildFile section */ | |||
| 1D9FF3342A90170000F89C85 /* URLLauncherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D9FF3332A90170000F89C85 /* URLLauncherTests.swift */; }; | |||
| 1DBE6EDE2A92479000B6525C /* URLLauncherUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DBE6EDD2A92479000B6525C /* URLLauncherUITests.swift */; }; | |||
| 2D92223F1EC1DA93007564B0 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D92223E1EC1DA93007564B0 /* GeneratedPluginRegistrant.m */; }; | |||
| 2E37D9A274B2EACB147AC51B /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 856D0913184F79C678A42603 /* libPods-Runner.a */; }; | |||
| 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; | |||
| 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; | |||
| 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; | |||
| 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; | |||
| 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; | |||
| 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; | |||
| 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; | |||
| B8140773523F70A044426500 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 487A1B5A2ECB3E406FD62FE3 /* libPods-RunnerTests.a */; }; | |||
| /* End PBXBuildFile section */ | |||
| /* Begin PBXContainerItemProxy section */ | |||
| F7151F4D26604CFB0028CB91 /* PBXContainerItemProxy */ = { | |||
| isa = PBXContainerItemProxy; | |||
| containerPortal = 97C146E61CF9000F007C117D /* Project object */; | |||
| proxyType = 1; | |||
| remoteGlobalIDString = 97C146ED1CF9000F007C117D; | |||
| remoteInfo = Runner; | |||
| }; | |||
| F7151F5B26604D060028CB91 /* PBXContainerItemProxy */ = { | |||
| isa = PBXContainerItemProxy; | |||
| containerPortal = 97C146E61CF9000F007C117D /* Project object */; | |||
| proxyType = 1; | |||
| remoteGlobalIDString = 97C146ED1CF9000F007C117D; | |||
| remoteInfo = Runner; | |||
| }; | |||
| /* End PBXContainerItemProxy section */ | |||
| /* Begin PBXCopyFilesBuildPhase section */ | |||
| 9705A1C41CF9048500538489 /* Embed Frameworks */ = { | |||
| isa = PBXCopyFilesBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| dstPath = ""; | |||
| dstSubfolderSpec = 10; | |||
| files = ( | |||
| ); | |||
| name = "Embed Frameworks"; | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| /* End PBXCopyFilesBuildPhase section */ | |||
| /* Begin PBXFileReference section */ | |||
| 1D9FF3332A90170000F89C85 /* URLLauncherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLLauncherTests.swift; sourceTree = "<group>"; }; | |||
| 1DBE6EDD2A92479000B6525C /* URLLauncherUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLLauncherUITests.swift; sourceTree = "<group>"; }; | |||
| 2D92223D1EC1DA93007564B0 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GeneratedPluginRegistrant.h; path = Runner/GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; | |||
| 2D92223E1EC1DA93007564B0 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GeneratedPluginRegistrant.m; path = Runner/GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; | |||
| 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; | |||
| 487A1B5A2ECB3E406FD62FE3 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; | |||
| 666BCD7C181C34F8BE58929B /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; }; | |||
| 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; | |||
| 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; }; | |||
| 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; }; | |||
| 836316F9AEA584411312E29F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; }; | |||
| 856D0913184F79C678A42603 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; | |||
| 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; | |||
| 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; | |||
| 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; | |||
| 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; | |||
| 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; | |||
| 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; | |||
| 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; | |||
| 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; | |||
| A84BFEE343F54B983D1B67EB /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; }; | |||
| D25C434271ACF6555E002440 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; }; | |||
| F7151F4826604CFB0028CB91 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; | |||
| F7151F4C26604CFB0028CB91 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; | |||
| F7151F5626604D060028CB91 /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; | |||
| F7151F5A26604D060028CB91 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; | |||
| /* End PBXFileReference section */ | |||
| /* Begin PBXFrameworksBuildPhase section */ | |||
| 97C146EB1CF9000F007C117D /* Frameworks */ = { | |||
| isa = PBXFrameworksBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, | |||
| 2E37D9A274B2EACB147AC51B /* libPods-Runner.a in Frameworks */, | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| F7151F4526604CFB0028CB91 /* Frameworks */ = { | |||
| isa = PBXFrameworksBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| B8140773523F70A044426500 /* libPods-RunnerTests.a in Frameworks */, | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| F7151F5326604D060028CB91 /* Frameworks */ = { | |||
| isa = PBXFrameworksBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| /* End PBXFrameworksBuildPhase section */ | |||
| /* Begin PBXGroup section */ | |||
| 840012C8B5EDBCF56B0E4AC1 /* Pods */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 836316F9AEA584411312E29F /* Pods-Runner.debug.xcconfig */, | |||
| A84BFEE343F54B983D1B67EB /* Pods-Runner.release.xcconfig */, | |||
| 666BCD7C181C34F8BE58929B /* Pods-RunnerTests.debug.xcconfig */, | |||
| D25C434271ACF6555E002440 /* Pods-RunnerTests.release.xcconfig */, | |||
| ); | |||
| name = Pods; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| 9740EEB11CF90186004384FC /* Flutter */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, | |||
| 9740EEB21CF90195004384FC /* Debug.xcconfig */, | |||
| 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, | |||
| 9740EEB31CF90195004384FC /* Generated.xcconfig */, | |||
| ); | |||
| name = Flutter; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| 97C146E51CF9000F007C117D = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 2D92223D1EC1DA93007564B0 /* GeneratedPluginRegistrant.h */, | |||
| 2D92223E1EC1DA93007564B0 /* GeneratedPluginRegistrant.m */, | |||
| 9740EEB11CF90186004384FC /* Flutter */, | |||
| 97C146F01CF9000F007C117D /* Runner */, | |||
| F7151F4926604CFB0028CB91 /* RunnerTests */, | |||
| F7151F5726604D060028CB91 /* RunnerUITests */, | |||
| 97C146EF1CF9000F007C117D /* Products */, | |||
| 840012C8B5EDBCF56B0E4AC1 /* Pods */, | |||
| CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, | |||
| ); | |||
| sourceTree = "<group>"; | |||
| }; | |||
| 97C146EF1CF9000F007C117D /* Products */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 97C146EE1CF9000F007C117D /* Runner.app */, | |||
| F7151F4826604CFB0028CB91 /* RunnerTests.xctest */, | |||
| F7151F5626604D060028CB91 /* RunnerUITests.xctest */, | |||
| ); | |||
| name = Products; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| 97C146F01CF9000F007C117D /* Runner */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, | |||
| 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, | |||
| 97C146FA1CF9000F007C117D /* Main.storyboard */, | |||
| 97C146FD1CF9000F007C117D /* Assets.xcassets */, | |||
| 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, | |||
| 97C147021CF9000F007C117D /* Info.plist */, | |||
| 97C146F11CF9000F007C117D /* Supporting Files */, | |||
| ); | |||
| path = Runner; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| 97C146F11CF9000F007C117D /* Supporting Files */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 97C146F21CF9000F007C117D /* main.m */, | |||
| ); | |||
| name = "Supporting Files"; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| CF3B75C9A7D2FA2A4C99F110 /* Frameworks */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 856D0913184F79C678A42603 /* libPods-Runner.a */, | |||
| 487A1B5A2ECB3E406FD62FE3 /* libPods-RunnerTests.a */, | |||
| ); | |||
| name = Frameworks; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| F7151F4926604CFB0028CB91 /* RunnerTests */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| F7151F4C26604CFB0028CB91 /* Info.plist */, | |||
| 1D9FF3332A90170000F89C85 /* URLLauncherTests.swift */, | |||
| ); | |||
| path = RunnerTests; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| F7151F5726604D060028CB91 /* RunnerUITests */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| F7151F5A26604D060028CB91 /* Info.plist */, | |||
| 1DBE6EDD2A92479000B6525C /* URLLauncherUITests.swift */, | |||
| ); | |||
| path = RunnerUITests; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| /* End PBXGroup section */ | |||
| /* Begin PBXNativeTarget section */ | |||
| 97C146ED1CF9000F007C117D /* Runner */ = { | |||
| isa = PBXNativeTarget; | |||
| buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; | |||
| buildPhases = ( | |||
| AB1344B0443C71CD721E1BB7 /* [CP] Check Pods Manifest.lock */, | |||
| 9740EEB61CF901F6004384FC /* Run Script */, | |||
| 97C146EA1CF9000F007C117D /* Sources */, | |||
| 97C146EB1CF9000F007C117D /* Frameworks */, | |||
| 97C146EC1CF9000F007C117D /* Resources */, | |||
| 9705A1C41CF9048500538489 /* Embed Frameworks */, | |||
| 3B06AD1E1E4923F5004D2608 /* Thin Binary */, | |||
| ); | |||
| buildRules = ( | |||
| ); | |||
| dependencies = ( | |||
| ); | |||
| name = Runner; | |||
| packageProductDependencies = ( | |||
| 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, | |||
| ); | |||
| productName = Runner; | |||
| productReference = 97C146EE1CF9000F007C117D /* Runner.app */; | |||
| productType = "com.apple.product-type.application"; | |||
| }; | |||
| F7151F4726604CFB0028CB91 /* RunnerTests */ = { | |||
| isa = PBXNativeTarget; | |||
| buildConfigurationList = F7151F5126604CFB0028CB91 /* Build configuration list for PBXNativeTarget "RunnerTests" */; | |||
| buildPhases = ( | |||
| DD4687403C4F35FCD2994FDE /* [CP] Check Pods Manifest.lock */, | |||
| F7151F4426604CFB0028CB91 /* Sources */, | |||
| F7151F4526604CFB0028CB91 /* Frameworks */, | |||
| F7151F4626604CFB0028CB91 /* Resources */, | |||
| ); | |||
| buildRules = ( | |||
| ); | |||
| dependencies = ( | |||
| F7151F4E26604CFB0028CB91 /* PBXTargetDependency */, | |||
| ); | |||
| name = RunnerTests; | |||
| productName = RunnerTests; | |||
| productReference = F7151F4826604CFB0028CB91 /* RunnerTests.xctest */; | |||
| productType = "com.apple.product-type.bundle.unit-test"; | |||
| }; | |||
| F7151F5526604D060028CB91 /* RunnerUITests */ = { | |||
| isa = PBXNativeTarget; | |||
| buildConfigurationList = F7151F5D26604D060028CB91 /* Build configuration list for PBXNativeTarget "RunnerUITests" */; | |||
| buildPhases = ( | |||
| F7151F5226604D060028CB91 /* Sources */, | |||
| F7151F5326604D060028CB91 /* Frameworks */, | |||
| F7151F5426604D060028CB91 /* Resources */, | |||
| ); | |||
| buildRules = ( | |||
| ); | |||
| dependencies = ( | |||
| F7151F5C26604D060028CB91 /* PBXTargetDependency */, | |||
| ); | |||
| name = RunnerUITests; | |||
| productName = RunnerUITests; | |||
| productReference = F7151F5626604D060028CB91 /* RunnerUITests.xctest */; | |||
| productType = "com.apple.product-type.bundle.ui-testing"; | |||
| }; | |||
| /* End PBXNativeTarget section */ | |||
| /* Begin PBXProject section */ | |||
| 97C146E61CF9000F007C117D /* Project object */ = { | |||
| isa = PBXProject; | |||
| attributes = { | |||
| LastUpgradeCheck = 1510; | |||
| ORGANIZATIONNAME = "The Flutter Authors"; | |||
| TargetAttributes = { | |||
| 97C146ED1CF9000F007C117D = { | |||
| CreatedOnToolsVersion = 7.3.1; | |||
| DevelopmentTeam = S8QB4VV633; | |||
| }; | |||
| F7151F4726604CFB0028CB91 = { | |||
| CreatedOnToolsVersion = 12.5; | |||
| ProvisioningStyle = Automatic; | |||
| TestTargetID = 97C146ED1CF9000F007C117D; | |||
| }; | |||
| F7151F5526604D060028CB91 = { | |||
| CreatedOnToolsVersion = 12.5; | |||
| ProvisioningStyle = Automatic; | |||
| TestTargetID = 97C146ED1CF9000F007C117D; | |||
| }; | |||
| }; | |||
| }; | |||
| buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; | |||
| compatibilityVersion = "Xcode 3.2"; | |||
| developmentRegion = en; | |||
| hasScannedForEncodings = 0; | |||
| knownRegions = ( | |||
| en, | |||
| Base, | |||
| ); | |||
| mainGroup = 97C146E51CF9000F007C117D; | |||
| packageReferences = ( | |||
| 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, | |||
| ); | |||
| productRefGroup = 97C146EF1CF9000F007C117D /* Products */; | |||
| projectDirPath = ""; | |||
| projectRoot = ""; | |||
| targets = ( | |||
| 97C146ED1CF9000F007C117D /* Runner */, | |||
| F7151F4726604CFB0028CB91 /* RunnerTests */, | |||
| F7151F5526604D060028CB91 /* RunnerUITests */, | |||
| ); | |||
| }; | |||
| /* End PBXProject section */ | |||
| /* Begin PBXResourcesBuildPhase section */ | |||
| 97C146EC1CF9000F007C117D /* Resources */ = { | |||
| isa = PBXResourcesBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, | |||
| 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, | |||
| 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, | |||
| 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| F7151F4626604CFB0028CB91 /* Resources */ = { | |||
| isa = PBXResourcesBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| F7151F5426604D060028CB91 /* Resources */ = { | |||
| isa = PBXResourcesBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| /* End PBXResourcesBuildPhase section */ | |||
| /* Begin PBXShellScriptBuildPhase section */ | |||
| 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { | |||
| isa = PBXShellScriptBuildPhase; | |||
| alwaysOutOfDate = 1; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| ); | |||
| inputPaths = ( | |||
| "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", | |||
| ); | |||
| name = "Thin Binary"; | |||
| outputPaths = ( | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| shellPath = /bin/sh; | |||
| shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n"; | |||
| }; | |||
| 9740EEB61CF901F6004384FC /* Run Script */ = { | |||
| isa = PBXShellScriptBuildPhase; | |||
| alwaysOutOfDate = 1; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| ); | |||
| inputPaths = ( | |||
| ); | |||
| name = "Run Script"; | |||
| outputPaths = ( | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| shellPath = /bin/sh; | |||
| shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; | |||
| }; | |||
| AB1344B0443C71CD721E1BB7 /* [CP] Check Pods Manifest.lock */ = { | |||
| isa = PBXShellScriptBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| ); | |||
| inputPaths = ( | |||
| "${PODS_PODFILE_DIR_PATH}/Podfile.lock", | |||
| "${PODS_ROOT}/Manifest.lock", | |||
| ); | |||
| name = "[CP] Check Pods Manifest.lock"; | |||
| outputPaths = ( | |||
| "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| shellPath = /bin/sh; | |||
| shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; | |||
| showEnvVarsInLog = 0; | |||
| }; | |||
| DD4687403C4F35FCD2994FDE /* [CP] Check Pods Manifest.lock */ = { | |||
| isa = PBXShellScriptBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| ); | |||
| inputFileListPaths = ( | |||
| ); | |||
| inputPaths = ( | |||
| "${PODS_PODFILE_DIR_PATH}/Podfile.lock", | |||
| "${PODS_ROOT}/Manifest.lock", | |||
| ); | |||
| name = "[CP] Check Pods Manifest.lock"; | |||
| outputFileListPaths = ( | |||
| ); | |||
| outputPaths = ( | |||
| "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| shellPath = /bin/sh; | |||
| shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; | |||
| showEnvVarsInLog = 0; | |||
| }; | |||
| /* End PBXShellScriptBuildPhase section */ | |||
| /* Begin PBXSourcesBuildPhase section */ | |||
| 97C146EA1CF9000F007C117D /* Sources */ = { | |||
| isa = PBXSourcesBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, | |||
| 97C146F31CF9000F007C117D /* main.m in Sources */, | |||
| 2D92223F1EC1DA93007564B0 /* GeneratedPluginRegistrant.m in Sources */, | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| F7151F4426604CFB0028CB91 /* Sources */ = { | |||
| isa = PBXSourcesBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| 1D9FF3342A90170000F89C85 /* URLLauncherTests.swift in Sources */, | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| F7151F5226604D060028CB91 /* Sources */ = { | |||
| isa = PBXSourcesBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| 1DBE6EDE2A92479000B6525C /* URLLauncherUITests.swift in Sources */, | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| /* End PBXSourcesBuildPhase section */ | |||
| /* Begin PBXTargetDependency section */ | |||
| F7151F4E26604CFB0028CB91 /* PBXTargetDependency */ = { | |||
| isa = PBXTargetDependency; | |||
| target = 97C146ED1CF9000F007C117D /* Runner */; | |||
| targetProxy = F7151F4D26604CFB0028CB91 /* PBXContainerItemProxy */; | |||
| }; | |||
| F7151F5C26604D060028CB91 /* PBXTargetDependency */ = { | |||
| isa = PBXTargetDependency; | |||
| target = 97C146ED1CF9000F007C117D /* Runner */; | |||
| targetProxy = F7151F5B26604D060028CB91 /* PBXContainerItemProxy */; | |||
| }; | |||
| /* End PBXTargetDependency section */ | |||
| /* Begin PBXVariantGroup section */ | |||
| 97C146FA1CF9000F007C117D /* Main.storyboard */ = { | |||
| isa = PBXVariantGroup; | |||
| children = ( | |||
| 97C146FB1CF9000F007C117D /* Base */, | |||
| ); | |||
| name = Main.storyboard; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { | |||
| isa = PBXVariantGroup; | |||
| children = ( | |||
| 97C147001CF9000F007C117D /* Base */, | |||
| ); | |||
| name = LaunchScreen.storyboard; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| /* End PBXVariantGroup section */ | |||
| /* Begin XCBuildConfiguration section */ | |||
| 97C147031CF9000F007C117D /* Debug */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| ALWAYS_SEARCH_USER_PATHS = NO; | |||
| CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; | |||
| CLANG_ANALYZER_NONNULL = YES; | |||
| CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; | |||
| CLANG_CXX_LIBRARY = "libc++"; | |||
| CLANG_ENABLE_MODULES = YES; | |||
| CLANG_ENABLE_OBJC_ARC = YES; | |||
| CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; | |||
| CLANG_WARN_BOOL_CONVERSION = YES; | |||
| CLANG_WARN_COMMA = YES; | |||
| CLANG_WARN_CONSTANT_CONVERSION = YES; | |||
| CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; | |||
| CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; | |||
| CLANG_WARN_EMPTY_BODY = YES; | |||
| CLANG_WARN_ENUM_CONVERSION = YES; | |||
| CLANG_WARN_INFINITE_RECURSION = YES; | |||
| CLANG_WARN_INT_CONVERSION = YES; | |||
| CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; | |||
| CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; | |||
| CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; | |||
| CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; | |||
| CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; | |||
| CLANG_WARN_STRICT_PROTOTYPES = YES; | |||
| CLANG_WARN_SUSPICIOUS_MOVE = YES; | |||
| CLANG_WARN_UNREACHABLE_CODE = YES; | |||
| CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | |||
| "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; | |||
| COPY_PHASE_STRIP = NO; | |||
| DEBUG_INFORMATION_FORMAT = dwarf; | |||
| ENABLE_STRICT_OBJC_MSGSEND = YES; | |||
| ENABLE_TESTABILITY = YES; | |||
| GCC_C_LANGUAGE_STANDARD = gnu99; | |||
| GCC_DYNAMIC_NO_PIC = NO; | |||
| GCC_NO_COMMON_BLOCKS = YES; | |||
| GCC_OPTIMIZATION_LEVEL = 0; | |||
| GCC_PREPROCESSOR_DEFINITIONS = ( | |||
| "DEBUG=1", | |||
| "$(inherited)", | |||
| ); | |||
| GCC_WARN_64_TO_32_BIT_CONVERSION = YES; | |||
| GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; | |||
| GCC_WARN_UNDECLARED_SELECTOR = YES; | |||
| GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | |||
| GCC_WARN_UNUSED_FUNCTION = YES; | |||
| GCC_WARN_UNUSED_VARIABLE = YES; | |||
| IPHONEOS_DEPLOYMENT_TARGET = 12.0; | |||
| MTL_ENABLE_DEBUG_INFO = YES; | |||
| ONLY_ACTIVE_ARCH = YES; | |||
| SDKROOT = iphoneos; | |||
| TARGETED_DEVICE_FAMILY = "1,2"; | |||
| }; | |||
| name = Debug; | |||
| }; | |||
| 97C147041CF9000F007C117D /* Release */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| ALWAYS_SEARCH_USER_PATHS = NO; | |||
| CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; | |||
| CLANG_ANALYZER_NONNULL = YES; | |||
| CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; | |||
| CLANG_CXX_LIBRARY = "libc++"; | |||
| CLANG_ENABLE_MODULES = YES; | |||
| CLANG_ENABLE_OBJC_ARC = YES; | |||
| CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; | |||
| CLANG_WARN_BOOL_CONVERSION = YES; | |||
| CLANG_WARN_COMMA = YES; | |||
| CLANG_WARN_CONSTANT_CONVERSION = YES; | |||
| CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; | |||
| CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; | |||
| CLANG_WARN_EMPTY_BODY = YES; | |||
| CLANG_WARN_ENUM_CONVERSION = YES; | |||
| CLANG_WARN_INFINITE_RECURSION = YES; | |||
| CLANG_WARN_INT_CONVERSION = YES; | |||
| CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; | |||
| CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; | |||
| CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; | |||
| CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; | |||
| CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; | |||
| CLANG_WARN_STRICT_PROTOTYPES = YES; | |||
| CLANG_WARN_SUSPICIOUS_MOVE = YES; | |||
| CLANG_WARN_UNREACHABLE_CODE = YES; | |||
| CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | |||
| "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; | |||
| COPY_PHASE_STRIP = NO; | |||
| DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; | |||
| ENABLE_NS_ASSERTIONS = NO; | |||
| ENABLE_STRICT_OBJC_MSGSEND = YES; | |||
| GCC_C_LANGUAGE_STANDARD = gnu99; | |||
| GCC_NO_COMMON_BLOCKS = YES; | |||
| GCC_WARN_64_TO_32_BIT_CONVERSION = YES; | |||
| GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; | |||
| GCC_WARN_UNDECLARED_SELECTOR = YES; | |||
| GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | |||
| GCC_WARN_UNUSED_FUNCTION = YES; | |||
| GCC_WARN_UNUSED_VARIABLE = YES; | |||
| IPHONEOS_DEPLOYMENT_TARGET = 12.0; | |||
| MTL_ENABLE_DEBUG_INFO = NO; | |||
| SDKROOT = iphoneos; | |||
| TARGETED_DEVICE_FAMILY = "1,2"; | |||
| VALIDATE_PRODUCT = YES; | |||
| }; | |||
| name = Release; | |||
| }; | |||
| 97C147061CF9000F007C117D /* Debug */ = { | |||
| isa = XCBuildConfiguration; | |||
| baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; | |||
| buildSettings = { | |||
| ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | |||
| ENABLE_BITCODE = NO; | |||
| FRAMEWORK_SEARCH_PATHS = ( | |||
| "$(inherited)", | |||
| "$(PROJECT_DIR)/Flutter", | |||
| ); | |||
| INFOPLIST_FILE = Runner/Info.plist; | |||
| LD_RUNPATH_SEARCH_PATHS = ( | |||
| "$(inherited)", | |||
| "@executable_path/Frameworks", | |||
| ); | |||
| LIBRARY_SEARCH_PATHS = ( | |||
| "$(inherited)", | |||
| "$(PROJECT_DIR)/Flutter", | |||
| ); | |||
| PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.urlLauncher; | |||
| PRODUCT_NAME = "$(TARGET_NAME)"; | |||
| }; | |||
| name = Debug; | |||
| }; | |||
| 97C147071CF9000F007C117D /* Release */ = { | |||
| isa = XCBuildConfiguration; | |||
| baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; | |||
| buildSettings = { | |||
| ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | |||
| ENABLE_BITCODE = NO; | |||
| FRAMEWORK_SEARCH_PATHS = ( | |||
| "$(inherited)", | |||
| "$(PROJECT_DIR)/Flutter", | |||
| ); | |||
| INFOPLIST_FILE = Runner/Info.plist; | |||
| LD_RUNPATH_SEARCH_PATHS = ( | |||
| "$(inherited)", | |||
| "@executable_path/Frameworks", | |||
| ); | |||
| LIBRARY_SEARCH_PATHS = ( | |||
| "$(inherited)", | |||
| "$(PROJECT_DIR)/Flutter", | |||
| ); | |||
| PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.urlLauncher; | |||
| PRODUCT_NAME = "$(TARGET_NAME)"; | |||
| }; | |||
| name = Release; | |||
| }; | |||
| F7151F4F26604CFB0028CB91 /* Debug */ = { | |||
| isa = XCBuildConfiguration; | |||
| baseConfigurationReference = 666BCD7C181C34F8BE58929B /* Pods-RunnerTests.debug.xcconfig */; | |||
| buildSettings = { | |||
| BUNDLE_LOADER = "$(TEST_HOST)"; | |||
| CLANG_ENABLE_MODULES = YES; | |||
| CODE_SIGN_STYLE = Automatic; | |||
| INFOPLIST_FILE = RunnerTests/Info.plist; | |||
| LD_RUNPATH_SEARCH_PATHS = ( | |||
| "$(inherited)", | |||
| "@executable_path/Frameworks", | |||
| "@loader_path/Frameworks", | |||
| ); | |||
| MTL_FAST_MATH = YES; | |||
| PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; | |||
| PRODUCT_NAME = "$(TARGET_NAME)"; | |||
| SWIFT_VERSION = 5.0; | |||
| TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; | |||
| }; | |||
| name = Debug; | |||
| }; | |||
| F7151F5026604CFB0028CB91 /* Release */ = { | |||
| isa = XCBuildConfiguration; | |||
| baseConfigurationReference = D25C434271ACF6555E002440 /* Pods-RunnerTests.release.xcconfig */; | |||
| buildSettings = { | |||
| BUNDLE_LOADER = "$(TEST_HOST)"; | |||
| CLANG_ENABLE_MODULES = YES; | |||
| CODE_SIGN_STYLE = Automatic; | |||
| INFOPLIST_FILE = RunnerTests/Info.plist; | |||
| LD_RUNPATH_SEARCH_PATHS = ( | |||
| "$(inherited)", | |||
| "@executable_path/Frameworks", | |||
| "@loader_path/Frameworks", | |||
| ); | |||
| MTL_FAST_MATH = YES; | |||
| PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; | |||
| PRODUCT_NAME = "$(TARGET_NAME)"; | |||
| SWIFT_VERSION = 5.0; | |||
| TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; | |||
| }; | |||
| name = Release; | |||
| }; | |||
| F7151F5E26604D060028CB91 /* Debug */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| CODE_SIGN_STYLE = Automatic; | |||
| INFOPLIST_FILE = RunnerUITests/Info.plist; | |||
| LD_RUNPATH_SEARCH_PATHS = ( | |||
| "$(inherited)", | |||
| "@executable_path/Frameworks", | |||
| "@loader_path/Frameworks", | |||
| ); | |||
| MTL_FAST_MATH = YES; | |||
| PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests; | |||
| PRODUCT_NAME = "$(TARGET_NAME)"; | |||
| SWIFT_VERSION = 5.0; | |||
| TEST_TARGET_NAME = Runner; | |||
| }; | |||
| name = Debug; | |||
| }; | |||
| F7151F5F26604D060028CB91 /* Release */ = { | |||
| isa = XCBuildConfiguration; | |||
| buildSettings = { | |||
| CODE_SIGN_STYLE = Automatic; | |||
| INFOPLIST_FILE = RunnerUITests/Info.plist; | |||
| LD_RUNPATH_SEARCH_PATHS = ( | |||
| "$(inherited)", | |||
| "@executable_path/Frameworks", | |||
| "@loader_path/Frameworks", | |||
| ); | |||
| MTL_FAST_MATH = YES; | |||
| PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests; | |||
| PRODUCT_NAME = "$(TARGET_NAME)"; | |||
| SWIFT_VERSION = 5.0; | |||
| TEST_TARGET_NAME = Runner; | |||
| }; | |||
| name = Release; | |||
| }; | |||
| /* End XCBuildConfiguration section */ | |||
| /* Begin XCConfigurationList section */ | |||
| 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { | |||
| isa = XCConfigurationList; | |||
| buildConfigurations = ( | |||
| 97C147031CF9000F007C117D /* Debug */, | |||
| 97C147041CF9000F007C117D /* Release */, | |||
| ); | |||
| defaultConfigurationIsVisible = 0; | |||
| defaultConfigurationName = Release; | |||
| }; | |||
| 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { | |||
| isa = XCConfigurationList; | |||
| buildConfigurations = ( | |||
| 97C147061CF9000F007C117D /* Debug */, | |||
| 97C147071CF9000F007C117D /* Release */, | |||
| ); | |||
| defaultConfigurationIsVisible = 0; | |||
| defaultConfigurationName = Release; | |||
| }; | |||
| F7151F5126604CFB0028CB91 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { | |||
| isa = XCConfigurationList; | |||
| buildConfigurations = ( | |||
| F7151F4F26604CFB0028CB91 /* Debug */, | |||
| F7151F5026604CFB0028CB91 /* Release */, | |||
| ); | |||
| defaultConfigurationIsVisible = 0; | |||
| defaultConfigurationName = Release; | |||
| }; | |||
| F7151F5D26604D060028CB91 /* Build configuration list for PBXNativeTarget "RunnerUITests" */ = { | |||
| isa = XCConfigurationList; | |||
| buildConfigurations = ( | |||
| F7151F5E26604D060028CB91 /* Debug */, | |||
| F7151F5F26604D060028CB91 /* Release */, | |||
| ); | |||
| defaultConfigurationIsVisible = 0; | |||
| defaultConfigurationName = Release; | |||
| }; | |||
| /* End XCConfigurationList section */ | |||
| /* Begin XCLocalSwiftPackageReference section */ | |||
| 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { | |||
| isa = XCLocalSwiftPackageReference; | |||
| relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; | |||
| }; | |||
| /* End XCLocalSwiftPackageReference section */ | |||
| /* Begin XCSwiftPackageProductDependency section */ | |||
| 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { | |||
| isa = XCSwiftPackageProductDependency; | |||
| productName = FlutterGeneratedPluginSwiftPackage; | |||
| }; | |||
| /* End XCSwiftPackageProductDependency section */ | |||
| }; | |||
| rootObject = 97C146E61CF9000F007C117D /* Project object */; | |||
| } | |||
| @@ -0,0 +1,7 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <Workspace | |||
| version = "1.0"> | |||
| <FileRef | |||
| location = "self:"> | |||
| </FileRef> | |||
| </Workspace> | |||
| @@ -0,0 +1,126 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <Scheme | |||
| LastUpgradeVersion = "1510" | |||
| version = "1.3"> | |||
| <BuildAction | |||
| parallelizeBuildables = "YES" | |||
| buildImplicitDependencies = "YES"> | |||
| <PreActions> | |||
| <ExecutionAction | |||
| ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction"> | |||
| <ActionContent | |||
| title = "Run Prepare Flutter Framework Script" | |||
| scriptText = "/bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" prepare "> | |||
| <EnvironmentBuildable> | |||
| <BuildableReference | |||
| BuildableIdentifier = "primary" | |||
| BlueprintIdentifier = "97C146ED1CF9000F007C117D" | |||
| BuildableName = "Runner.app" | |||
| BlueprintName = "Runner" | |||
| ReferencedContainer = "container:Runner.xcodeproj"> | |||
| </BuildableReference> | |||
| </EnvironmentBuildable> | |||
| </ActionContent> | |||
| </ExecutionAction> | |||
| </PreActions> | |||
| <BuildActionEntries> | |||
| <BuildActionEntry | |||
| buildForTesting = "YES" | |||
| buildForRunning = "YES" | |||
| buildForProfiling = "YES" | |||
| buildForArchiving = "YES" | |||
| buildForAnalyzing = "YES"> | |||
| <BuildableReference | |||
| BuildableIdentifier = "primary" | |||
| BlueprintIdentifier = "97C146ED1CF9000F007C117D" | |||
| BuildableName = "Runner.app" | |||
| BlueprintName = "Runner" | |||
| ReferencedContainer = "container:Runner.xcodeproj"> | |||
| </BuildableReference> | |||
| </BuildActionEntry> | |||
| </BuildActionEntries> | |||
| </BuildAction> | |||
| <TestAction | |||
| buildConfiguration = "Debug" | |||
| selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" | |||
| selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" | |||
| shouldUseLaunchSchemeArgsEnv = "YES"> | |||
| <MacroExpansion> | |||
| <BuildableReference | |||
| BuildableIdentifier = "primary" | |||
| BlueprintIdentifier = "97C146ED1CF9000F007C117D" | |||
| BuildableName = "Runner.app" | |||
| BlueprintName = "Runner" | |||
| ReferencedContainer = "container:Runner.xcodeproj"> | |||
| </BuildableReference> | |||
| </MacroExpansion> | |||
| <Testables> | |||
| <TestableReference | |||
| skipped = "NO"> | |||
| <BuildableReference | |||
| BuildableIdentifier = "primary" | |||
| BlueprintIdentifier = "F7151F4726604CFB0028CB91" | |||
| BuildableName = "RunnerTests.xctest" | |||
| BlueprintName = "RunnerTests" | |||
| ReferencedContainer = "container:Runner.xcodeproj"> | |||
| </BuildableReference> | |||
| </TestableReference> | |||
| <TestableReference | |||
| skipped = "NO"> | |||
| <BuildableReference | |||
| BuildableIdentifier = "primary" | |||
| BlueprintIdentifier = "F7151F5526604D060028CB91" | |||
| BuildableName = "RunnerUITests.xctest" | |||
| BlueprintName = "RunnerUITests" | |||
| ReferencedContainer = "container:Runner.xcodeproj"> | |||
| </BuildableReference> | |||
| </TestableReference> | |||
| </Testables> | |||
| </TestAction> | |||
| <LaunchAction | |||
| buildConfiguration = "Debug" | |||
| selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" | |||
| selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" | |||
| launchStyle = "0" | |||
| useCustomWorkingDirectory = "NO" | |||
| ignoresPersistentStateOnLaunch = "NO" | |||
| debugDocumentVersioning = "YES" | |||
| debugServiceExtension = "internal" | |||
| enableGPUValidationMode = "1" | |||
| allowLocationSimulation = "YES"> | |||
| <BuildableProductRunnable | |||
| runnableDebuggingMode = "0"> | |||
| <BuildableReference | |||
| BuildableIdentifier = "primary" | |||
| BlueprintIdentifier = "97C146ED1CF9000F007C117D" | |||
| BuildableName = "Runner.app" | |||
| BlueprintName = "Runner" | |||
| ReferencedContainer = "container:Runner.xcodeproj"> | |||
| </BuildableReference> | |||
| </BuildableProductRunnable> | |||
| </LaunchAction> | |||
| <ProfileAction | |||
| buildConfiguration = "Release" | |||
| shouldUseLaunchSchemeArgsEnv = "YES" | |||
| savedToolIdentifier = "" | |||
| useCustomWorkingDirectory = "NO" | |||
| debugDocumentVersioning = "YES"> | |||
| <BuildableProductRunnable | |||
| runnableDebuggingMode = "0"> | |||
| <BuildableReference | |||
| BuildableIdentifier = "primary" | |||
| BlueprintIdentifier = "97C146ED1CF9000F007C117D" | |||
| BuildableName = "Runner.app" | |||
| BlueprintName = "Runner" | |||
| ReferencedContainer = "container:Runner.xcodeproj"> | |||
| </BuildableReference> | |||
| </BuildableProductRunnable> | |||
| </ProfileAction> | |||
| <AnalyzeAction | |||
| buildConfiguration = "Debug"> | |||
| </AnalyzeAction> | |||
| <ArchiveAction | |||
| buildConfiguration = "Release" | |||
| revealArchiveInOrganizer = "YES"> | |||
| </ArchiveAction> | |||
| </Scheme> | |||
| @@ -0,0 +1,10 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <Workspace | |||
| version = "1.0"> | |||
| <FileRef | |||
| location = "group:Runner.xcodeproj"> | |||
| </FileRef> | |||
| <FileRef | |||
| location = "group:Pods/Pods.xcodeproj"> | |||
| </FileRef> | |||
| </Workspace> | |||
| @@ -0,0 +1,8 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |||
| <plist version="1.0"> | |||
| <dict> | |||
| <key>IDEDidComputeMac32BitWarning</key> | |||
| <true/> | |||
| </dict> | |||
| </plist> | |||
| @@ -0,0 +1,10 @@ | |||
| // 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 <Flutter/Flutter.h> | |||
| #import <UIKit/UIKit.h> | |||
| @interface AppDelegate : FlutterAppDelegate | |||
| @end | |||
| @@ -0,0 +1,17 @@ | |||
| // 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. | |||
| #include "AppDelegate.h" | |||
| #include "GeneratedPluginRegistrant.h" | |||
| @implementation AppDelegate | |||
| - (BOOL)application:(UIApplication *)application | |||
| didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { | |||
| [GeneratedPluginRegistrant registerWithRegistry:self]; | |||
| [super application:application didFinishLaunchingWithOptions:launchOptions]; | |||
| return YES; | |||
| } | |||
| @end | |||
| @@ -0,0 +1,116 @@ | |||
| { | |||
| "images" : [ | |||
| { | |||
| "size" : "20x20", | |||
| "idiom" : "iphone", | |||
| "filename" : "Icon-App-20x20@2x.png", | |||
| "scale" : "2x" | |||
| }, | |||
| { | |||
| "size" : "20x20", | |||
| "idiom" : "iphone", | |||
| "filename" : "Icon-App-20x20@3x.png", | |||
| "scale" : "3x" | |||
| }, | |||
| { | |||
| "size" : "29x29", | |||
| "idiom" : "iphone", | |||
| "filename" : "Icon-App-29x29@1x.png", | |||
| "scale" : "1x" | |||
| }, | |||
| { | |||
| "size" : "29x29", | |||
| "idiom" : "iphone", | |||
| "filename" : "Icon-App-29x29@2x.png", | |||
| "scale" : "2x" | |||
| }, | |||
| { | |||
| "size" : "29x29", | |||
| "idiom" : "iphone", | |||
| "filename" : "Icon-App-29x29@3x.png", | |||
| "scale" : "3x" | |||
| }, | |||
| { | |||
| "size" : "40x40", | |||
| "idiom" : "iphone", | |||
| "filename" : "Icon-App-40x40@2x.png", | |||
| "scale" : "2x" | |||
| }, | |||
| { | |||
| "size" : "40x40", | |||
| "idiom" : "iphone", | |||
| "filename" : "Icon-App-40x40@3x.png", | |||
| "scale" : "3x" | |||
| }, | |||
| { | |||
| "size" : "60x60", | |||
| "idiom" : "iphone", | |||
| "filename" : "Icon-App-60x60@2x.png", | |||
| "scale" : "2x" | |||
| }, | |||
| { | |||
| "size" : "60x60", | |||
| "idiom" : "iphone", | |||
| "filename" : "Icon-App-60x60@3x.png", | |||
| "scale" : "3x" | |||
| }, | |||
| { | |||
| "size" : "20x20", | |||
| "idiom" : "ipad", | |||
| "filename" : "Icon-App-20x20@1x.png", | |||
| "scale" : "1x" | |||
| }, | |||
| { | |||
| "size" : "20x20", | |||
| "idiom" : "ipad", | |||
| "filename" : "Icon-App-20x20@2x.png", | |||
| "scale" : "2x" | |||
| }, | |||
| { | |||
| "size" : "29x29", | |||
| "idiom" : "ipad", | |||
| "filename" : "Icon-App-29x29@1x.png", | |||
| "scale" : "1x" | |||
| }, | |||
| { | |||
| "size" : "29x29", | |||
| "idiom" : "ipad", | |||
| "filename" : "Icon-App-29x29@2x.png", | |||
| "scale" : "2x" | |||
| }, | |||
| { | |||
| "size" : "40x40", | |||
| "idiom" : "ipad", | |||
| "filename" : "Icon-App-40x40@1x.png", | |||
| "scale" : "1x" | |||
| }, | |||
| { | |||
| "size" : "40x40", | |||
| "idiom" : "ipad", | |||
| "filename" : "Icon-App-40x40@2x.png", | |||
| "scale" : "2x" | |||
| }, | |||
| { | |||
| "size" : "76x76", | |||
| "idiom" : "ipad", | |||
| "filename" : "Icon-App-76x76@1x.png", | |||
| "scale" : "1x" | |||
| }, | |||
| { | |||
| "size" : "76x76", | |||
| "idiom" : "ipad", | |||
| "filename" : "Icon-App-76x76@2x.png", | |||
| "scale" : "2x" | |||
| }, | |||
| { | |||
| "size" : "83.5x83.5", | |||
| "idiom" : "ipad", | |||
| "filename" : "Icon-App-83.5x83.5@2x.png", | |||
| "scale" : "2x" | |||
| } | |||
| ], | |||
| "info" : { | |||
| "version" : 1, | |||
| "author" : "xcode" | |||
| } | |||
| } | |||
| @@ -0,0 +1,27 @@ | |||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
| <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM"> | |||
| <dependencies> | |||
| <deployment identifier="iOS"/> | |||
| <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/> | |||
| </dependencies> | |||
| <scenes> | |||
| <!--View Controller--> | |||
| <scene sceneID="EHf-IW-A2E"> | |||
| <objects> | |||
| <viewController id="01J-lp-oVM" sceneMemberID="viewController"> | |||
| <layoutGuides> | |||
| <viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/> | |||
| <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/> | |||
| </layoutGuides> | |||
| <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> | |||
| <rect key="frame" x="0.0" y="0.0" width="600" height="600"/> | |||
| <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | |||
| <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/> | |||
| </view> | |||
| </viewController> | |||
| <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> | |||
| </objects> | |||
| <point key="canvasLocation" x="53" y="375"/> | |||
| </scene> | |||
| </scenes> | |||
| </document> | |||
| @@ -0,0 +1,26 @@ | |||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
| <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r"> | |||
| <dependencies> | |||
| <deployment identifier="iOS"/> | |||
| <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/> | |||
| </dependencies> | |||
| <scenes> | |||
| <!--Flutter View Controller--> | |||
| <scene sceneID="tne-QT-ifu"> | |||
| <objects> | |||
| <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController"> | |||
| <layoutGuides> | |||
| <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/> | |||
| <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/> | |||
| </layoutGuides> | |||
| <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> | |||
| <rect key="frame" x="0.0" y="0.0" width="600" height="600"/> | |||
| <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | |||
| <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/> | |||
| </view> | |||
| </viewController> | |||
| <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> | |||
| </objects> | |||
| </scene> | |||
| </scenes> | |||
| </document> | |||
| @@ -0,0 +1,51 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |||
| <plist version="1.0"> | |||
| <dict> | |||
| <key>CFBundleDevelopmentRegion</key> | |||
| <string>en</string> | |||
| <key>CFBundleExecutable</key> | |||
| <string>$(EXECUTABLE_NAME)</string> | |||
| <key>CFBundleIdentifier</key> | |||
| <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | |||
| <key>CFBundleInfoDictionaryVersion</key> | |||
| <string>6.0</string> | |||
| <key>CFBundleName</key> | |||
| <string>url_launcher_example</string> | |||
| <key>CFBundlePackageType</key> | |||
| <string>APPL</string> | |||
| <key>CFBundleShortVersionString</key> | |||
| <string>1.0</string> | |||
| <key>CFBundleSignature</key> | |||
| <string>????</string> | |||
| <key>CFBundleVersion</key> | |||
| <string>1</string> | |||
| <key>LSRequiresIPhoneOS</key> | |||
| <true/> | |||
| <key>UILaunchStoryboardName</key> | |||
| <string>LaunchScreen</string> | |||
| <key>UIMainStoryboardFile</key> | |||
| <string>Main</string> | |||
| <key>UIRequiredDeviceCapabilities</key> | |||
| <array> | |||
| <string>arm64</string> | |||
| </array> | |||
| <key>UISupportedInterfaceOrientations</key> | |||
| <array> | |||
| <string>UIInterfaceOrientationPortrait</string> | |||
| <string>UIInterfaceOrientationLandscapeLeft</string> | |||
| <string>UIInterfaceOrientationLandscapeRight</string> | |||
| </array> | |||
| <key>UISupportedInterfaceOrientations~ipad</key> | |||
| <array> | |||
| <string>UIInterfaceOrientationPortrait</string> | |||
| <string>UIInterfaceOrientationPortraitUpsideDown</string> | |||
| <string>UIInterfaceOrientationLandscapeLeft</string> | |||
| <string>UIInterfaceOrientationLandscapeRight</string> | |||
| </array> | |||
| <key>CADisableMinimumFrameDurationOnPhone</key> | |||
| <true/> | |||
| <key>UIApplicationSupportsIndirectInputEvents</key> | |||
| <true/> | |||
| </dict> | |||
| </plist> | |||
| @@ -0,0 +1,13 @@ | |||
| // 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 <Flutter/Flutter.h> | |||
| #import <UIKit/UIKit.h> | |||
| #import "AppDelegate.h" | |||
| int main(int argc, char *argv[]) { | |||
| @autoreleasepool { | |||
| return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); | |||
| } | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |||
| <plist version="1.0"> | |||
| <dict> | |||
| <key>CFBundleDevelopmentRegion</key> | |||
| <string>$(DEVELOPMENT_LANGUAGE)</string> | |||
| <key>CFBundleExecutable</key> | |||
| <string>$(EXECUTABLE_NAME)</string> | |||
| <key>CFBundleIdentifier</key> | |||
| <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | |||
| <key>CFBundleInfoDictionaryVersion</key> | |||
| <string>6.0</string> | |||
| <key>CFBundleName</key> | |||
| <string>$(PRODUCT_NAME)</string> | |||
| <key>CFBundlePackageType</key> | |||
| <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> | |||
| <key>CFBundleShortVersionString</key> | |||
| <string>1.0</string> | |||
| <key>CFBundleVersion</key> | |||
| <string>1</string> | |||
| </dict> | |||
| </plist> | |||
| @@ -0,0 +1,151 @@ | |||
| // 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 Flutter | |||
| import XCTest | |||
| @testable import url_launcher_ios | |||
| // Tests whether NSURL parsing is strict. When linking against the iOS 17 SDK or later, | |||
| // NSURL uses a more lenient parser which will not return nil. | |||
| private func urlParsingIsStrict() -> Bool { | |||
| return URL(string: "b a d U R L") == nil | |||
| } | |||
| final class URLLauncherTests: XCTestCase { | |||
| private func createPlugin() -> URLLauncherPlugin { | |||
| let launcher = FakeLauncher() | |||
| return URLLauncherPlugin(launcher: launcher) | |||
| } | |||
| private func createPlugin(launcher: FakeLauncher) -> URLLauncherPlugin { | |||
| return URLLauncherPlugin(launcher: launcher) | |||
| } | |||
| func testCanLaunchSuccess() { | |||
| let result = createPlugin().canLaunchUrl(url: "good://url") | |||
| XCTAssertEqual(result, .success) | |||
| } | |||
| func testCanLaunchFailure() { | |||
| let result = createPlugin().canLaunchUrl(url: "bad://url") | |||
| XCTAssertEqual(result, .failure) | |||
| } | |||
| func testCanLaunchFailureWithInvalidURL() { | |||
| let result = createPlugin().canLaunchUrl(url: "urls can't have spaces") | |||
| if urlParsingIsStrict() { | |||
| XCTAssertEqual(result, .invalidUrl) | |||
| } else { | |||
| XCTAssertEqual(result, .failure) | |||
| } | |||
| } | |||
| func testLaunchSuccess() { | |||
| let expectation = XCTestExpectation(description: "completion called") | |||
| createPlugin().launchUrl(url: "good://url", universalLinksOnly: false) { result in | |||
| switch result { | |||
| case .success(let details): | |||
| XCTAssertEqual(details, .success) | |||
| case .failure(let error): | |||
| XCTFail("Unexpected error: \(error)") | |||
| } | |||
| expectation.fulfill() | |||
| } | |||
| wait(for: [expectation], timeout: 1) | |||
| } | |||
| func testLaunchFailure() { | |||
| let expectation = XCTestExpectation(description: "completion called") | |||
| createPlugin().launchUrl(url: "bad://url", universalLinksOnly: false) { result in | |||
| switch result { | |||
| case .success(let details): | |||
| XCTAssertEqual(details, .failure) | |||
| case .failure(let error): | |||
| XCTFail("Unexpected error: \(error)") | |||
| } | |||
| expectation.fulfill() | |||
| } | |||
| wait(for: [expectation], timeout: 1) | |||
| } | |||
| func testLaunchFailureWithInvalidURL() { | |||
| let expectation = XCTestExpectation(description: "completion called") | |||
| createPlugin().launchUrl(url: "urls can't have spaces", universalLinksOnly: false) { result in | |||
| switch result { | |||
| case .success(let details): | |||
| if urlParsingIsStrict() { | |||
| XCTAssertEqual(details, .invalidUrl) | |||
| } else { | |||
| XCTAssertEqual(details, .failure) | |||
| } | |||
| case .failure(let error): | |||
| XCTFail("Unexpected error: \(error)") | |||
| } | |||
| expectation.fulfill() | |||
| } | |||
| wait(for: [expectation], timeout: 1) | |||
| } | |||
| func testLaunchWithoutUniversalLinks() { | |||
| let launcher = FakeLauncher() | |||
| let plugin = createPlugin(launcher: launcher) | |||
| let expectation = XCTestExpectation(description: "completion called") | |||
| plugin.launchUrl(url: "good://url", universalLinksOnly: false) { result in | |||
| switch result { | |||
| case .success(let details): | |||
| XCTAssertEqual(details, .success) | |||
| case .failure(let error): | |||
| XCTFail("Unexpected error: \(error)") | |||
| } | |||
| expectation.fulfill() | |||
| } | |||
| wait(for: [expectation], timeout: 1) | |||
| XCTAssertEqual(launcher.passedOptions?[.universalLinksOnly] as? Bool, false) | |||
| } | |||
| func testLaunchWithUniversalLinks() { | |||
| let launcher = FakeLauncher() | |||
| let plugin = createPlugin(launcher: launcher) | |||
| let expectation = XCTestExpectation(description: "completion called") | |||
| plugin.launchUrl(url: "good://url", universalLinksOnly: true) { result in | |||
| switch result { | |||
| case .success(let details): | |||
| XCTAssertEqual(details, .success) | |||
| case .failure(let error): | |||
| XCTFail("Unexpected error: \(error)") | |||
| } | |||
| expectation.fulfill() | |||
| } | |||
| wait(for: [expectation], timeout: 1) | |||
| XCTAssertEqual(launcher.passedOptions?[.universalLinksOnly] as? Bool, true) | |||
| } | |||
| } | |||
| final private class FakeLauncher: NSObject, Launcher { | |||
| var passedOptions: [UIApplication.OpenExternalURLOptionsKey: Any]? | |||
| func canOpenURL(_ url: URL) -> Bool { | |||
| url.scheme == "good" | |||
| } | |||
| func open( | |||
| _ url: URL, | |||
| options: [UIApplication.OpenExternalURLOptionsKey: Any], | |||
| completionHandler completion: ((Bool) -> Void)? | |||
| ) { | |||
| self.passedOptions = options | |||
| completion?(url.scheme == "good") | |||
| } | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |||
| <plist version="1.0"> | |||
| <dict> | |||
| <key>CFBundleDevelopmentRegion</key> | |||
| <string>$(DEVELOPMENT_LANGUAGE)</string> | |||
| <key>CFBundleExecutable</key> | |||
| <string>$(EXECUTABLE_NAME)</string> | |||
| <key>CFBundleIdentifier</key> | |||
| <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | |||
| <key>CFBundleInfoDictionaryVersion</key> | |||
| <string>6.0</string> | |||
| <key>CFBundleName</key> | |||
| <string>$(PRODUCT_NAME)</string> | |||
| <key>CFBundlePackageType</key> | |||
| <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> | |||
| <key>CFBundleShortVersionString</key> | |||
| <string>1.0</string> | |||
| <key>CFBundleVersion</key> | |||
| <string>1</string> | |||
| </dict> | |||
| </plist> | |||
| @@ -0,0 +1,42 @@ | |||
| // 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 XCTest | |||
| class URLLauncherUITests: XCTestCase { | |||
| var app: XCUIApplication! | |||
| override func setUp() { | |||
| continueAfterFailure = false | |||
| app = XCUIApplication() | |||
| app.launch() | |||
| } | |||
| func testLaunch() { | |||
| let app = self.app! | |||
| let buttonNames: [String] = [ | |||
| "Launch in app", "Launch in app(JavaScript ON)", "Launch in app(DOM storage ON)", | |||
| "Launch a universal link in a native app, fallback to Safari.(Youtube)", | |||
| ] | |||
| for buttonName in buttonNames { | |||
| let button = app.buttons[buttonName] | |||
| XCTAssertTrue(button.waitForExistence(timeout: 30.0)) | |||
| XCTAssertEqual(app.webViews.count, 0) | |||
| button.tap() | |||
| let webView = app.webViews.firstMatch | |||
| XCTAssertTrue(webView.waitForExistence(timeout: 30.0)) | |||
| XCTAssertTrue(app.buttons["ForwardButton"].waitForExistence(timeout: 30.0)) | |||
| XCTAssertTrue(app.buttons["Share"].exists) | |||
| XCTAssertTrue(app.buttons["OpenInSafariButton"].exists) | |||
| let doneButton = app.buttons["Done"] | |||
| XCTAssertTrue(doneButton.waitForExistence(timeout: 30.0)) | |||
| // This should just be doneButton.tap, but for some reason that stopped working in Xcode 15; | |||
| // tapping via coordinate works, however. | |||
| doneButton.coordinate(withNormalizedOffset: CGVector()).withOffset(CGVector(dx: 10, dy: 10)) | |||
| .tap() | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,242 @@ | |||
| // 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. | |||
| // ignore_for_file: public_member_api_docs | |||
| import 'dart:async'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; | |||
| void main() { | |||
| runApp(const MyApp()); | |||
| } | |||
| class MyApp extends StatelessWidget { | |||
| const MyApp({super.key}); | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return MaterialApp( | |||
| title: 'URL Launcher', | |||
| theme: ThemeData( | |||
| primarySwatch: Colors.blue, | |||
| ), | |||
| home: const MyHomePage(title: 'URL Launcher'), | |||
| ); | |||
| } | |||
| } | |||
| class MyHomePage extends StatefulWidget { | |||
| const MyHomePage({super.key, required this.title}); | |||
| final String title; | |||
| @override | |||
| State<MyHomePage> createState() => _MyHomePageState(); | |||
| } | |||
| class _MyHomePageState extends State<MyHomePage> { | |||
| Future<void>? _launched; | |||
| String _phone = ''; | |||
| Future<void> _launchInBrowser(String url) async { | |||
| final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; | |||
| if (await launcher.canLaunch(url)) { | |||
| await launcher.launch( | |||
| url, | |||
| useSafariVC: false, | |||
| useWebView: false, | |||
| enableJavaScript: false, | |||
| enableDomStorage: false, | |||
| universalLinksOnly: false, | |||
| headers: <String, String>{'my_header_key': 'my_header_value'}, | |||
| ); | |||
| } else { | |||
| throw Exception('Could not launch $url'); | |||
| } | |||
| } | |||
| Future<void> _launchInWebViewOrVC(String url) async { | |||
| final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; | |||
| if (await launcher.canLaunch(url)) { | |||
| await launcher.launch( | |||
| url, | |||
| useSafariVC: true, | |||
| useWebView: true, | |||
| enableJavaScript: false, | |||
| enableDomStorage: false, | |||
| universalLinksOnly: false, | |||
| headers: <String, String>{'my_header_key': 'my_header_value'}, | |||
| ); | |||
| } else { | |||
| throw Exception('Could not launch $url'); | |||
| } | |||
| } | |||
| Future<void> _launchInWebViewWithJavaScript(String url) async { | |||
| final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; | |||
| if (await launcher.canLaunch(url)) { | |||
| await launcher.launch( | |||
| url, | |||
| useSafariVC: true, | |||
| useWebView: true, | |||
| enableJavaScript: true, | |||
| enableDomStorage: false, | |||
| universalLinksOnly: false, | |||
| headers: <String, String>{}, | |||
| ); | |||
| } else { | |||
| throw Exception('Could not launch $url'); | |||
| } | |||
| } | |||
| Future<void> _launchInWebViewWithDomStorage(String url) async { | |||
| final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; | |||
| if (await launcher.canLaunch(url)) { | |||
| await launcher.launch( | |||
| url, | |||
| useSafariVC: true, | |||
| useWebView: true, | |||
| enableJavaScript: false, | |||
| enableDomStorage: true, | |||
| universalLinksOnly: false, | |||
| headers: <String, String>{}, | |||
| ); | |||
| } else { | |||
| throw Exception('Could not launch $url'); | |||
| } | |||
| } | |||
| Future<void> _launchUniversalLinkIos(String url) async { | |||
| final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; | |||
| if (await launcher.canLaunch(url)) { | |||
| final bool nativeAppLaunchSucceeded = await launcher.launch( | |||
| url, | |||
| useSafariVC: false, | |||
| useWebView: false, | |||
| enableJavaScript: false, | |||
| enableDomStorage: false, | |||
| universalLinksOnly: true, | |||
| headers: <String, String>{}, | |||
| ); | |||
| if (!nativeAppLaunchSucceeded) { | |||
| await launcher.launch( | |||
| url, | |||
| useSafariVC: true, | |||
| useWebView: true, | |||
| enableJavaScript: false, | |||
| enableDomStorage: false, | |||
| universalLinksOnly: true, | |||
| headers: <String, String>{}, | |||
| ); | |||
| } | |||
| } | |||
| } | |||
| Widget _launchStatus(BuildContext context, AsyncSnapshot<void> snapshot) { | |||
| if (snapshot.hasError) { | |||
| return Text('Error: ${snapshot.error}'); | |||
| } else { | |||
| return const Text(''); | |||
| } | |||
| } | |||
| Future<void> _makePhoneCall(String url) async { | |||
| final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; | |||
| if (await launcher.canLaunch(url)) { | |||
| await launcher.launch( | |||
| url, | |||
| useSafariVC: false, | |||
| useWebView: false, | |||
| enableJavaScript: false, | |||
| enableDomStorage: false, | |||
| universalLinksOnly: true, | |||
| headers: <String, String>{}, | |||
| ); | |||
| } else { | |||
| throw Exception('Could not launch $url'); | |||
| } | |||
| } | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| const String toLaunch = 'https://www.cylog.org/headers/'; | |||
| return Scaffold( | |||
| appBar: AppBar( | |||
| title: Text(widget.title), | |||
| ), | |||
| body: ListView( | |||
| children: <Widget>[ | |||
| Column( | |||
| mainAxisAlignment: MainAxisAlignment.center, | |||
| children: <Widget>[ | |||
| Padding( | |||
| padding: const EdgeInsets.all(16.0), | |||
| child: TextField( | |||
| onChanged: (String text) => _phone = text, | |||
| decoration: const InputDecoration( | |||
| hintText: 'Input the phone number to launch')), | |||
| ), | |||
| ElevatedButton( | |||
| onPressed: () => setState(() { | |||
| _launched = _makePhoneCall('tel:$_phone'); | |||
| }), | |||
| child: const Text('Make phone call'), | |||
| ), | |||
| const Padding( | |||
| padding: EdgeInsets.all(16.0), | |||
| child: Text(toLaunch), | |||
| ), | |||
| ElevatedButton( | |||
| onPressed: () => setState(() { | |||
| _launched = _launchInBrowser(toLaunch); | |||
| }), | |||
| child: const Text('Launch in browser'), | |||
| ), | |||
| const Padding(padding: EdgeInsets.all(16.0)), | |||
| ElevatedButton( | |||
| onPressed: () => setState(() { | |||
| _launched = _launchInWebViewOrVC(toLaunch); | |||
| }), | |||
| child: const Text('Launch in app'), | |||
| ), | |||
| ElevatedButton( | |||
| onPressed: () => setState(() { | |||
| _launched = _launchInWebViewWithJavaScript(toLaunch); | |||
| }), | |||
| child: const Text('Launch in app(JavaScript ON)'), | |||
| ), | |||
| ElevatedButton( | |||
| onPressed: () => setState(() { | |||
| _launched = _launchInWebViewWithDomStorage(toLaunch); | |||
| }), | |||
| child: const Text('Launch in app(DOM storage ON)'), | |||
| ), | |||
| const Padding(padding: EdgeInsets.all(16.0)), | |||
| ElevatedButton( | |||
| onPressed: () => setState(() { | |||
| _launched = _launchUniversalLinkIos(toLaunch); | |||
| }), | |||
| child: const Text( | |||
| 'Launch a universal link in a native app, fallback to Safari.(Youtube)'), | |||
| ), | |||
| const Padding(padding: EdgeInsets.all(16.0)), | |||
| ElevatedButton( | |||
| onPressed: () => setState(() { | |||
| _launched = _launchInWebViewOrVC(toLaunch); | |||
| Timer(const Duration(seconds: 5), () { | |||
| UrlLauncherPlatform.instance.closeWebView(); | |||
| }); | |||
| }), | |||
| child: const Text('Launch in app + close after 5 seconds'), | |||
| ), | |||
| const Padding(padding: EdgeInsets.all(16.0)), | |||
| FutureBuilder<void>(future: _launched, builder: _launchStatus), | |||
| ], | |||
| ), | |||
| ], | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| name: url_launcher_example | |||
| description: Demonstrates how to use the url_launcher plugin. | |||
| publish_to: none | |||
| environment: | |||
| sdk: ^3.3.0 | |||
| flutter: ">=3.19.0" | |||
| dependencies: | |||
| flutter: | |||
| sdk: flutter | |||
| url_launcher_ios: | |||
| # When depending on this package from a real application you should use: | |||
| # url_launcher_ios: ^x.y.z | |||
| # See https://dart.dev/tools/pub/dependencies#version-constraints | |||
| # The example app is bundled with the plugin so we use a path dependency on | |||
| # the parent directory to use the current plugin's version. | |||
| path: ../ | |||
| url_launcher_platform_interface: ^2.2.0 | |||
| dev_dependencies: | |||
| flutter_test: | |||
| sdk: flutter | |||
| integration_test: | |||
| sdk: flutter | |||
| plugin_platform_interface: ^2.1.7 | |||
| flutter: | |||
| uses-material-design: true | |||
| @@ -0,0 +1,7 @@ | |||
| // 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:integration_test/integration_test_driver.dart'; | |||
| Future<void> main() => integrationDriver(); | |||
| @@ -0,0 +1,26 @@ | |||
| # | |||
| # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html | |||
| # | |||
| Pod::Spec.new do |s| | |||
| s.name = 'url_launcher_ios' | |||
| s.version = '0.0.1' | |||
| s.summary = 'Flutter plugin for launching a URL.' | |||
| s.description = <<-DESC | |||
| A Flutter plugin for making the underlying platform (Android or iOS) launch a URL. | |||
| DESC | |||
| s.homepage = 'https://github.com/flutter/packages/tree/main/packages/url_launcher' | |||
| s.license = { :type => 'BSD', :file => '../LICENSE' } | |||
| s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } | |||
| s.source = { :http => 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_ios' } | |||
| s.documentation_url = 'https://pub.dev/packages/url_launcher' | |||
| s.swift_version = '5.0' | |||
| s.source_files = 'url_launcher_ios/Sources/**/*.swift' | |||
| s.xcconfig = { | |||
| 'LIBRARY_SEARCH_PATHS' => '$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)/ $(SDKROOT)/usr/lib/swift', | |||
| 'LD_RUNPATH_SEARCH_PATHS' => '/usr/lib/swift', | |||
| } | |||
| s.dependency 'Flutter' | |||
| s.platform = :ios, '12.0' | |||
| s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } | |||
| s.resource_bundles = {'url_launcher_ios_privacy' => ['url_launcher_ios/Sources/url_launcher_ios/Resources/PrivacyInfo.xcprivacy']} | |||
| end | |||
| @@ -0,0 +1,28 @@ | |||
| // swift-tools-version: 5.9 | |||
| // The swift-tools-version declares the minimum version of Swift required to build this package. | |||
| // 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 PackageDescription | |||
| let package = Package( | |||
| name: "url_launcher_ios", | |||
| platforms: [ | |||
| .iOS("12.0") | |||
| ], | |||
| products: [ | |||
| .library(name: "url-launcher-ios", targets: ["url_launcher_ios"]) | |||
| ], | |||
| dependencies: [], | |||
| targets: [ | |||
| .target( | |||
| name: "url_launcher_ios", | |||
| dependencies: [], | |||
| resources: [ | |||
| .process("Resources") | |||
| ] | |||
| ) | |||
| ] | |||
| ) | |||
| @@ -0,0 +1,38 @@ | |||
| // 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 UIKit | |||
| /// Protocol for UIApplication methods relating to launching URLs. | |||
| /// | |||
| /// This protocol exists to allow injecting an alternate implementation for testing. | |||
| protocol Launcher { | |||
| /// Returns a Boolean value that indicates whether an app is available to handle a URL scheme. | |||
| func canOpenURL(_ url: URL) -> Bool | |||
| /// Attempts to asynchronously open the resource at the specified URL. | |||
| func open( | |||
| _ url: URL, | |||
| options: [UIApplication.OpenExternalURLOptionsKey: Any], | |||
| completionHandler completion: ((Bool) -> Void)?) | |||
| } | |||
| // TODO(hellohuanlin): This wrapper is a workaround for iOS 18 Beta 3 where completionHandler is annotated with @MainActor @Sendable, resulting in compile error when conforming UIApplication to Launcher. We should try again in newer betas. | |||
| /// A default URL launcher. | |||
| final class DefaultLauncher: Launcher { | |||
| func canOpenURL(_ url: URL) -> Bool { | |||
| return UIApplication.shared.canOpenURL(url) | |||
| } | |||
| func open( | |||
| _ url: URL, | |||
| options: [UIApplication.OpenExternalURLOptionsKey: Any], | |||
| completionHandler completion: ((Bool) -> Void)? | |||
| ) { | |||
| UIApplication.shared.open( | |||
| url, | |||
| options: options, | |||
| completionHandler: completion) | |||
| } | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |||
| <plist version="1.0"> | |||
| <dict> | |||
| <key>NSPrivacyTrackingDomains</key> | |||
| <array/> | |||
| <key>NSPrivacyAccessedAPITypes</key> | |||
| <array/> | |||
| <key>NSPrivacyCollectedDataTypes</key> | |||
| <array/> | |||
| <key>NSPrivacyTracking</key> | |||
| <false/> | |||
| </dict> | |||
| </plist> | |||
| @@ -0,0 +1,63 @@ | |||
| // 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 Flutter | |||
| import SafariServices | |||
| typealias OpenInSafariCompletionHandler = (Result<InAppLoadResult, Error>) -> Void | |||
| /// A session responsible for launching a URL in Safari and handling its events. | |||
| final class URLLaunchSession: NSObject, SFSafariViewControllerDelegate { | |||
| private let completion: OpenInSafariCompletionHandler | |||
| private let url: URL | |||
| /// The Safari view controller used for displaying the URL. | |||
| let safariViewController: SFSafariViewController | |||
| // A closure to be executed after the Safari view controller finishes. | |||
| var didFinish: (() -> Void)? | |||
| /// Initializes a new URLLaunchSession with the provided URL and completion handler. | |||
| /// | |||
| /// - Parameters: | |||
| /// - url: The URL to be opened in Safari. | |||
| /// - completion: The completion handler to be called after attempting to open the URL. | |||
| init(url: URL, completion: @escaping OpenInSafariCompletionHandler) { | |||
| self.url = url | |||
| self.completion = completion | |||
| self.safariViewController = SFSafariViewController(url: url) | |||
| super.init() | |||
| self.safariViewController.delegate = self | |||
| } | |||
| /// Called when the Safari view controller completes the initial load. | |||
| /// | |||
| /// - Parameters: | |||
| /// - controller: The Safari view controller. | |||
| /// - didLoadSuccessfully: Indicates if the initial load was successful. | |||
| func safariViewController( | |||
| _ controller: SFSafariViewController, | |||
| didCompleteInitialLoad didLoadSuccessfully: Bool | |||
| ) { | |||
| if didLoadSuccessfully { | |||
| completion(.success(.success)) | |||
| } else { | |||
| completion(.success(.failedToLoad)) | |||
| } | |||
| } | |||
| /// Called when the user finishes using the Safari view controller. | |||
| /// | |||
| /// - Parameter controller: The Safari view controller. | |||
| func safariViewControllerDidFinish(_ controller: SFSafariViewController) { | |||
| controller.dismiss(animated: true, completion: nil) | |||
| didFinish?() | |||
| } | |||
| /// Closes the Safari view controller. | |||
| func close() { | |||
| safariViewControllerDidFinish(safariViewController) | |||
| } | |||
| } | |||
| @@ -0,0 +1,100 @@ | |||
| // 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 Flutter | |||
| import UIKit | |||
| public final class URLLauncherPlugin: NSObject, FlutterPlugin, UrlLauncherApi { | |||
| public static func register(with registrar: FlutterPluginRegistrar) { | |||
| let plugin = URLLauncherPlugin() | |||
| UrlLauncherApiSetup.setUp(binaryMessenger: registrar.messenger(), api: plugin) | |||
| registrar.publish(plugin) | |||
| } | |||
| private var currentSession: URLLaunchSession? | |||
| private let launcher: Launcher | |||
| private var topViewController: UIViewController? { | |||
| // TODO(stuartmorgan) Provide a non-deprecated codepath. See | |||
| // https://github.com/flutter/flutter/issues/104117 | |||
| UIApplication.shared.keyWindow?.rootViewController?.topViewController | |||
| } | |||
| init(launcher: Launcher = DefaultLauncher()) { | |||
| self.launcher = launcher | |||
| } | |||
| func canLaunchUrl(url: String) -> LaunchResult { | |||
| guard let url = URL(string: url) else { | |||
| return .invalidUrl | |||
| } | |||
| let canOpen = launcher.canOpenURL(url) | |||
| return canOpen ? .success : .failure | |||
| } | |||
| func launchUrl( | |||
| url: String, | |||
| universalLinksOnly: Bool, | |||
| completion: @escaping (Result<LaunchResult, Error>) -> Void | |||
| ) { | |||
| guard let url = URL(string: url) else { | |||
| completion(.success(.invalidUrl)) | |||
| return | |||
| } | |||
| let options = [UIApplication.OpenExternalURLOptionsKey.universalLinksOnly: universalLinksOnly] | |||
| launcher.open(url, options: options) { result in | |||
| completion(.success(result ? .success : .failure)) | |||
| } | |||
| } | |||
| func openUrlInSafariViewController( | |||
| url: String, | |||
| completion: @escaping (Result<InAppLoadResult, Error>) -> Void | |||
| ) { | |||
| guard let url = URL(string: url) else { | |||
| completion(.success(.invalidUrl)) | |||
| return | |||
| } | |||
| let session = URLLaunchSession(url: url, completion: completion) | |||
| currentSession = session | |||
| session.didFinish = { [weak self] in | |||
| self?.currentSession = nil | |||
| } | |||
| topViewController?.present(session.safariViewController, animated: true, completion: nil) | |||
| } | |||
| func closeSafariViewController() { | |||
| currentSession?.close() | |||
| } | |||
| } | |||
| /// This method recursively iterates through the view hierarchy | |||
| /// to return the top-most view controller. | |||
| /// | |||
| /// It supports the following scenarios: | |||
| /// | |||
| /// - The view controller is presenting another view. | |||
| /// - The view controller is a UINavigationController. | |||
| /// - The view controller is a UITabBarController. | |||
| /// | |||
| /// @return The top most view controller. | |||
| extension UIViewController { | |||
| var topViewController: UIViewController { | |||
| if let navigationController = self as? UINavigationController { | |||
| return navigationController.viewControllers.last?.topViewController | |||
| ?? navigationController | |||
| .visibleViewController ?? navigationController | |||
| } | |||
| if let tabBarController = self as? UITabBarController { | |||
| return tabBarController.selectedViewController?.topViewController ?? tabBarController | |||
| } | |||
| if let presented = presentedViewController { | |||
| return presented.topViewController | |||
| } | |||
| return self | |||
| } | |||
| } | |||
| @@ -0,0 +1,242 @@ | |||
| // 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. | |||
| // Autogenerated from Pigeon (v22.6.4), do not edit directly. | |||
| // See also: https://pub.dev/packages/pigeon | |||
| import Foundation | |||
| #if os(iOS) | |||
| import Flutter | |||
| #elseif os(macOS) | |||
| import FlutterMacOS | |||
| #else | |||
| #error("Unsupported platform.") | |||
| #endif | |||
| /// Error class for passing custom error details to Dart side. | |||
| final class PigeonError: Error { | |||
| let code: String | |||
| let message: String? | |||
| let details: Any? | |||
| init(code: String, message: String?, details: Any?) { | |||
| self.code = code | |||
| self.message = message | |||
| self.details = details | |||
| } | |||
| var localizedDescription: String { | |||
| return | |||
| "PigeonError(code: \(code), message: \(message ?? "<nil>"), details: \(details ?? "<nil>")" | |||
| } | |||
| } | |||
| private func wrapResult(_ result: Any?) -> [Any?] { | |||
| return [result] | |||
| } | |||
| private func wrapError(_ error: Any) -> [Any?] { | |||
| if let pigeonError = error as? PigeonError { | |||
| return [ | |||
| pigeonError.code, | |||
| pigeonError.message, | |||
| pigeonError.details, | |||
| ] | |||
| } | |||
| if let flutterError = error as? FlutterError { | |||
| return [ | |||
| flutterError.code, | |||
| flutterError.message, | |||
| flutterError.details, | |||
| ] | |||
| } | |||
| return [ | |||
| "\(error)", | |||
| "\(type(of: error))", | |||
| "Stacktrace: \(Thread.callStackSymbols)", | |||
| ] | |||
| } | |||
| private func isNullish(_ value: Any?) -> Bool { | |||
| return value is NSNull || value == nil | |||
| } | |||
| private func nilOrValue<T>(_ value: Any?) -> T? { | |||
| if value is NSNull { return nil } | |||
| return value as! T? | |||
| } | |||
| /// Possible outcomes of launching a URL. | |||
| enum LaunchResult: Int { | |||
| /// The URL was successfully launched (or could be, for `canLaunchUrl`). | |||
| case success = 0 | |||
| /// There was no handler available for the URL. | |||
| case failure = 1 | |||
| /// The URL could not be launched because it is invalid. | |||
| case invalidUrl = 2 | |||
| } | |||
| /// Possible outcomes of handling a URL within the application. | |||
| enum InAppLoadResult: Int { | |||
| /// The URL was successfully loaded. | |||
| case success = 0 | |||
| /// The URL did not load successfully. | |||
| case failedToLoad = 1 | |||
| /// The URL could not be launched because it is invalid. | |||
| case invalidUrl = 2 | |||
| } | |||
| private class messagesPigeonCodecReader: FlutterStandardReader { | |||
| override func readValue(ofType type: UInt8) -> Any? { | |||
| switch type { | |||
| case 129: | |||
| let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) | |||
| if let enumResultAsInt = enumResultAsInt { | |||
| return LaunchResult(rawValue: enumResultAsInt) | |||
| } | |||
| return nil | |||
| case 130: | |||
| let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) | |||
| if let enumResultAsInt = enumResultAsInt { | |||
| return InAppLoadResult(rawValue: enumResultAsInt) | |||
| } | |||
| return nil | |||
| default: | |||
| return super.readValue(ofType: type) | |||
| } | |||
| } | |||
| } | |||
| private class messagesPigeonCodecWriter: FlutterStandardWriter { | |||
| override func writeValue(_ value: Any) { | |||
| if let value = value as? LaunchResult { | |||
| super.writeByte(129) | |||
| super.writeValue(value.rawValue) | |||
| } else if let value = value as? InAppLoadResult { | |||
| super.writeByte(130) | |||
| super.writeValue(value.rawValue) | |||
| } else { | |||
| super.writeValue(value) | |||
| } | |||
| } | |||
| } | |||
| private class messagesPigeonCodecReaderWriter: FlutterStandardReaderWriter { | |||
| override func reader(with data: Data) -> FlutterStandardReader { | |||
| return messagesPigeonCodecReader(data: data) | |||
| } | |||
| override func writer(with data: NSMutableData) -> FlutterStandardWriter { | |||
| return messagesPigeonCodecWriter(data: data) | |||
| } | |||
| } | |||
| class messagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { | |||
| static let shared = messagesPigeonCodec(readerWriter: messagesPigeonCodecReaderWriter()) | |||
| } | |||
| /// Generated protocol from Pigeon that represents a handler of messages from Flutter. | |||
| protocol UrlLauncherApi { | |||
| /// Checks whether a URL can be loaded. | |||
| func canLaunchUrl(url: String) throws -> LaunchResult | |||
| /// Opens the URL externally, returning the status of launching it. | |||
| func launchUrl( | |||
| url: String, universalLinksOnly: Bool, | |||
| completion: @escaping (Result<LaunchResult, Error>) -> Void) | |||
| /// Opens the URL in an in-app SFSafariViewController, returning the results | |||
| /// of loading it. | |||
| func openUrlInSafariViewController( | |||
| url: String, completion: @escaping (Result<InAppLoadResult, Error>) -> Void) | |||
| /// Closes the view controller opened by [openUrlInSafariViewController]. | |||
| func closeSafariViewController() throws | |||
| } | |||
| /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. | |||
| class UrlLauncherApiSetup { | |||
| static var codec: FlutterStandardMessageCodec { messagesPigeonCodec.shared } | |||
| /// Sets up an instance of `UrlLauncherApi` to handle messages through the `binaryMessenger`. | |||
| static func setUp( | |||
| binaryMessenger: FlutterBinaryMessenger, api: UrlLauncherApi?, messageChannelSuffix: String = "" | |||
| ) { | |||
| let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" | |||
| /// Checks whether a URL can be loaded. | |||
| let canLaunchUrlChannel = FlutterBasicMessageChannel( | |||
| name: "dev.flutter.pigeon.url_launcher_ios.UrlLauncherApi.canLaunchUrl\(channelSuffix)", | |||
| binaryMessenger: binaryMessenger, codec: codec) | |||
| if let api = api { | |||
| canLaunchUrlChannel.setMessageHandler { message, reply in | |||
| let args = message as! [Any?] | |||
| let urlArg = args[0] as! String | |||
| do { | |||
| let result = try api.canLaunchUrl(url: urlArg) | |||
| reply(wrapResult(result)) | |||
| } catch { | |||
| reply(wrapError(error)) | |||
| } | |||
| } | |||
| } else { | |||
| canLaunchUrlChannel.setMessageHandler(nil) | |||
| } | |||
| /// Opens the URL externally, returning the status of launching it. | |||
| let launchUrlChannel = FlutterBasicMessageChannel( | |||
| name: "dev.flutter.pigeon.url_launcher_ios.UrlLauncherApi.launchUrl\(channelSuffix)", | |||
| binaryMessenger: binaryMessenger, codec: codec) | |||
| if let api = api { | |||
| launchUrlChannel.setMessageHandler { message, reply in | |||
| let args = message as! [Any?] | |||
| let urlArg = args[0] as! String | |||
| let universalLinksOnlyArg = args[1] as! Bool | |||
| api.launchUrl(url: urlArg, universalLinksOnly: universalLinksOnlyArg) { result in | |||
| switch result { | |||
| case .success(let res): | |||
| reply(wrapResult(res)) | |||
| case .failure(let error): | |||
| reply(wrapError(error)) | |||
| } | |||
| } | |||
| } | |||
| } else { | |||
| launchUrlChannel.setMessageHandler(nil) | |||
| } | |||
| /// Opens the URL in an in-app SFSafariViewController, returning the results | |||
| /// of loading it. | |||
| let openUrlInSafariViewControllerChannel = FlutterBasicMessageChannel( | |||
| name: | |||
| "dev.flutter.pigeon.url_launcher_ios.UrlLauncherApi.openUrlInSafariViewController\(channelSuffix)", | |||
| binaryMessenger: binaryMessenger, codec: codec) | |||
| if let api = api { | |||
| openUrlInSafariViewControllerChannel.setMessageHandler { message, reply in | |||
| let args = message as! [Any?] | |||
| let urlArg = args[0] as! String | |||
| api.openUrlInSafariViewController(url: urlArg) { result in | |||
| switch result { | |||
| case .success(let res): | |||
| reply(wrapResult(res)) | |||
| case .failure(let error): | |||
| reply(wrapError(error)) | |||
| } | |||
| } | |||
| } | |||
| } else { | |||
| openUrlInSafariViewControllerChannel.setMessageHandler(nil) | |||
| } | |||
| /// Closes the view controller opened by [openUrlInSafariViewController]. | |||
| let closeSafariViewControllerChannel = FlutterBasicMessageChannel( | |||
| name: | |||
| "dev.flutter.pigeon.url_launcher_ios.UrlLauncherApi.closeSafariViewController\(channelSuffix)", | |||
| binaryMessenger: binaryMessenger, codec: codec) | |||
| if let api = api { | |||
| closeSafariViewControllerChannel.setMessageHandler { _, reply in | |||
| do { | |||
| try api.closeSafariViewController() | |||
| reply(wrapResult(nil)) | |||
| } catch { | |||
| reply(wrapError(error)) | |||
| } | |||
| } | |||
| } else { | |||
| closeSafariViewControllerChannel.setMessageHandler(nil) | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,208 @@ | |||
| // 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. | |||
| // Autogenerated from Pigeon (v22.6.4), do not edit directly. | |||
| // See also: https://pub.dev/packages/pigeon | |||
| // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers | |||
| import 'dart:async'; | |||
| import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; | |||
| import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; | |||
| import 'package:flutter/services.dart'; | |||
| PlatformException _createConnectionError(String channelName) { | |||
| return PlatformException( | |||
| code: 'channel-error', | |||
| message: 'Unable to establish connection on channel: "$channelName".', | |||
| ); | |||
| } | |||
| /// Possible outcomes of launching a URL. | |||
| enum LaunchResult { | |||
| /// The URL was successfully launched (or could be, for `canLaunchUrl`). | |||
| success, | |||
| /// There was no handler available for the URL. | |||
| failure, | |||
| /// The URL could not be launched because it is invalid. | |||
| invalidUrl, | |||
| } | |||
| /// Possible outcomes of handling a URL within the application. | |||
| enum InAppLoadResult { | |||
| /// The URL was successfully loaded. | |||
| success, | |||
| /// The URL did not load successfully. | |||
| failedToLoad, | |||
| /// The URL could not be launched because it is invalid. | |||
| invalidUrl, | |||
| } | |||
| class _PigeonCodec extends StandardMessageCodec { | |||
| const _PigeonCodec(); | |||
| @override | |||
| void writeValue(WriteBuffer buffer, Object? value) { | |||
| if (value is int) { | |||
| buffer.putUint8(4); | |||
| buffer.putInt64(value); | |||
| } else if (value is LaunchResult) { | |||
| buffer.putUint8(129); | |||
| writeValue(buffer, value.index); | |||
| } else if (value is InAppLoadResult) { | |||
| buffer.putUint8(130); | |||
| writeValue(buffer, value.index); | |||
| } else { | |||
| super.writeValue(buffer, value); | |||
| } | |||
| } | |||
| @override | |||
| Object? readValueOfType(int type, ReadBuffer buffer) { | |||
| switch (type) { | |||
| case 129: | |||
| final int? value = readValue(buffer) as int?; | |||
| return value == null ? null : LaunchResult.values[value]; | |||
| case 130: | |||
| final int? value = readValue(buffer) as int?; | |||
| return value == null ? null : InAppLoadResult.values[value]; | |||
| default: | |||
| return super.readValueOfType(type, buffer); | |||
| } | |||
| } | |||
| } | |||
| class UrlLauncherApi { | |||
| /// Constructor for [UrlLauncherApi]. The [binaryMessenger] named argument is | |||
| /// available for dependency injection. If it is left null, the default | |||
| /// BinaryMessenger will be used which routes to the host platform. | |||
| UrlLauncherApi( | |||
| {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) | |||
| : pigeonVar_binaryMessenger = binaryMessenger, | |||
| pigeonVar_messageChannelSuffix = | |||
| messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; | |||
| final BinaryMessenger? pigeonVar_binaryMessenger; | |||
| static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec(); | |||
| final String pigeonVar_messageChannelSuffix; | |||
| /// Checks whether a URL can be loaded. | |||
| Future<LaunchResult> canLaunchUrl(String url) async { | |||
| final String pigeonVar_channelName = | |||
| 'dev.flutter.pigeon.url_launcher_ios.UrlLauncherApi.canLaunchUrl$pigeonVar_messageChannelSuffix'; | |||
| final BasicMessageChannel<Object?> pigeonVar_channel = | |||
| BasicMessageChannel<Object?>( | |||
| pigeonVar_channelName, | |||
| pigeonChannelCodec, | |||
| binaryMessenger: pigeonVar_binaryMessenger, | |||
| ); | |||
| final List<Object?>? pigeonVar_replyList = | |||
| await pigeonVar_channel.send(<Object?>[url]) as List<Object?>?; | |||
| if (pigeonVar_replyList == null) { | |||
| throw _createConnectionError(pigeonVar_channelName); | |||
| } else if (pigeonVar_replyList.length > 1) { | |||
| throw PlatformException( | |||
| code: pigeonVar_replyList[0]! as String, | |||
| message: pigeonVar_replyList[1] as String?, | |||
| details: pigeonVar_replyList[2], | |||
| ); | |||
| } else if (pigeonVar_replyList[0] == null) { | |||
| throw PlatformException( | |||
| code: 'null-error', | |||
| message: 'Host platform returned null value for non-null return value.', | |||
| ); | |||
| } else { | |||
| return (pigeonVar_replyList[0] as LaunchResult?)!; | |||
| } | |||
| } | |||
| /// Opens the URL externally, returning the status of launching it. | |||
| Future<LaunchResult> launchUrl(String url, bool universalLinksOnly) async { | |||
| final String pigeonVar_channelName = | |||
| 'dev.flutter.pigeon.url_launcher_ios.UrlLauncherApi.launchUrl$pigeonVar_messageChannelSuffix'; | |||
| final BasicMessageChannel<Object?> pigeonVar_channel = | |||
| BasicMessageChannel<Object?>( | |||
| pigeonVar_channelName, | |||
| pigeonChannelCodec, | |||
| binaryMessenger: pigeonVar_binaryMessenger, | |||
| ); | |||
| final List<Object?>? pigeonVar_replyList = await pigeonVar_channel | |||
| .send(<Object?>[url, universalLinksOnly]) as List<Object?>?; | |||
| if (pigeonVar_replyList == null) { | |||
| throw _createConnectionError(pigeonVar_channelName); | |||
| } else if (pigeonVar_replyList.length > 1) { | |||
| throw PlatformException( | |||
| code: pigeonVar_replyList[0]! as String, | |||
| message: pigeonVar_replyList[1] as String?, | |||
| details: pigeonVar_replyList[2], | |||
| ); | |||
| } else if (pigeonVar_replyList[0] == null) { | |||
| throw PlatformException( | |||
| code: 'null-error', | |||
| message: 'Host platform returned null value for non-null return value.', | |||
| ); | |||
| } else { | |||
| return (pigeonVar_replyList[0] as LaunchResult?)!; | |||
| } | |||
| } | |||
| /// Opens the URL in an in-app SFSafariViewController, returning the results | |||
| /// of loading it. | |||
| Future<InAppLoadResult> openUrlInSafariViewController(String url) async { | |||
| final String pigeonVar_channelName = | |||
| 'dev.flutter.pigeon.url_launcher_ios.UrlLauncherApi.openUrlInSafariViewController$pigeonVar_messageChannelSuffix'; | |||
| final BasicMessageChannel<Object?> pigeonVar_channel = | |||
| BasicMessageChannel<Object?>( | |||
| pigeonVar_channelName, | |||
| pigeonChannelCodec, | |||
| binaryMessenger: pigeonVar_binaryMessenger, | |||
| ); | |||
| final List<Object?>? pigeonVar_replyList = | |||
| await pigeonVar_channel.send(<Object?>[url]) as List<Object?>?; | |||
| if (pigeonVar_replyList == null) { | |||
| throw _createConnectionError(pigeonVar_channelName); | |||
| } else if (pigeonVar_replyList.length > 1) { | |||
| throw PlatformException( | |||
| code: pigeonVar_replyList[0]! as String, | |||
| message: pigeonVar_replyList[1] as String?, | |||
| details: pigeonVar_replyList[2], | |||
| ); | |||
| } else if (pigeonVar_replyList[0] == null) { | |||
| throw PlatformException( | |||
| code: 'null-error', | |||
| message: 'Host platform returned null value for non-null return value.', | |||
| ); | |||
| } else { | |||
| return (pigeonVar_replyList[0] as InAppLoadResult?)!; | |||
| } | |||
| } | |||
| /// Closes the view controller opened by [openUrlInSafariViewController]. | |||
| Future<void> closeSafariViewController() async { | |||
| final String pigeonVar_channelName = | |||
| 'dev.flutter.pigeon.url_launcher_ios.UrlLauncherApi.closeSafariViewController$pigeonVar_messageChannelSuffix'; | |||
| final BasicMessageChannel<Object?> pigeonVar_channel = | |||
| BasicMessageChannel<Object?>( | |||
| pigeonVar_channelName, | |||
| pigeonChannelCodec, | |||
| binaryMessenger: pigeonVar_binaryMessenger, | |||
| ); | |||
| final List<Object?>? pigeonVar_replyList = | |||
| await pigeonVar_channel.send(null) as List<Object?>?; | |||
| if (pigeonVar_replyList == null) { | |||
| throw _createConnectionError(pigeonVar_channelName); | |||
| } else if (pigeonVar_replyList.length > 1) { | |||
| throw PlatformException( | |||
| code: pigeonVar_replyList[0]! as String, | |||
| message: pigeonVar_replyList[1] as String?, | |||
| details: pigeonVar_replyList[2], | |||
| ); | |||
| } else { | |||
| return; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,172 @@ | |||
| // 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, unreachable_switch_default | |||
| default: | |||
| // By default, open web URLs in the application. | |||
| inApp = url.startsWith('http:') || url.startsWith('https:'); | |||
| } | |||
| 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, unreachable_switch_default | |||
| 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', | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,3 @@ | |||
| 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. | |||
| @@ -0,0 +1,56 @@ | |||
| // 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:pigeon/pigeon.dart'; | |||
| @ConfigurePigeon(PigeonOptions( | |||
| dartOut: 'lib/src/messages.g.dart', | |||
| swiftOut: 'ios/url_launcher_ios/Sources/url_launcher_ios/messages.g.swift', | |||
| copyrightHeader: 'pigeons/copyright.txt', | |||
| )) | |||
| /// Possible outcomes of launching a URL. | |||
| enum LaunchResult { | |||
| /// The URL was successfully launched (or could be, for `canLaunchUrl`). | |||
| success, | |||
| /// There was no handler available for the URL. | |||
| failure, | |||
| /// The URL could not be launched because it is invalid. | |||
| invalidUrl, | |||
| } | |||
| /// Possible outcomes of handling a URL within the application. | |||
| enum InAppLoadResult { | |||
| /// The URL was successfully loaded. | |||
| success, | |||
| /// The URL did not load successfully. | |||
| failedToLoad, | |||
| /// The URL could not be launched because it is invalid. | |||
| invalidUrl, | |||
| } | |||
| @HostApi() | |||
| abstract class UrlLauncherApi { | |||
| /// Checks whether a URL can be loaded. | |||
| @ObjCSelector('canLaunchURL:') | |||
| LaunchResult canLaunchUrl(String url); | |||
| /// Opens the URL externally, returning the status of launching it. | |||
| @async | |||
| @ObjCSelector('launchURL:universalLinksOnly:') | |||
| LaunchResult launchUrl(String url, bool universalLinksOnly); | |||
| /// Opens the URL in an in-app SFSafariViewController, returning the results | |||
| /// of loading it. | |||
| @async | |||
| @ObjCSelector('openSafariViewControllerWithURL:') | |||
| InAppLoadResult openUrlInSafariViewController(String url); | |||
| /// Closes the view controller opened by [openUrlInSafariViewController]. | |||
| void closeSafariViewController(); | |||
| } | |||
| @@ -0,0 +1,37 @@ | |||
| name: url_launcher_ios | |||
| description: iOS implementation of the url_launcher plugin. | |||
| repository: https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_ios | |||
| issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 | |||
| version: 6.3.2 | |||
| environment: | |||
| sdk: ^3.1.0 | |||
| flutter: ">=3.19.0" | |||
| flutter: | |||
| plugin: | |||
| implements: url_launcher | |||
| platforms: | |||
| ios: | |||
| pluginClass: URLLauncherPlugin | |||
| dartPluginClass: UrlLauncherIOS | |||
| dependencies: | |||
| flutter: | |||
| sdk: flutter | |||
| url_launcher_platform_interface: ^2.2.0 | |||
| dev_dependencies: | |||
| build_runner: ^2.3.3 | |||
| flutter_test: | |||
| sdk: flutter | |||
| mockito: ^5.4.4 | |||
| pigeon: ^22.6.4 | |||
| plugin_platform_interface: ^2.1.7 | |||
| test: ^1.16.3 | |||
| topics: | |||
| - links | |||
| - os-integration | |||
| - url-launcher | |||
| - urls | |||
| @@ -0,0 +1,370 @@ | |||
| // 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/services.dart'; | |||
| import 'package:flutter_test/flutter_test.dart'; | |||
| import 'package:mockito/annotations.dart'; | |||
| import 'package:mockito/mockito.dart'; | |||
| import 'package:url_launcher_ios/src/messages.g.dart'; | |||
| import 'package:url_launcher_ios/url_launcher_ios.dart'; | |||
| import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; | |||
| import 'url_launcher_ios_test.mocks.dart'; | |||
| // A web URL to use in tests where the specifics of the URL don't matter. | |||
| const String _webUrl = 'https://example.com/'; | |||
| @GenerateMocks(<Type>[UrlLauncherApi]) | |||
| void main() { | |||
| late MockUrlLauncherApi api; | |||
| setUp(() { | |||
| api = MockUrlLauncherApi(); | |||
| }); | |||
| test('registers instance', () { | |||
| UrlLauncherIOS.registerWith(); | |||
| expect(UrlLauncherPlatform.instance, isA<UrlLauncherIOS>()); | |||
| }); | |||
| group('canLaunch', () { | |||
| test('handles success', () async { | |||
| when(api.canLaunchUrl(_webUrl)) | |||
| .thenAnswer((_) async => LaunchResult.success); | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| expect(await launcher.canLaunch(_webUrl), true); | |||
| }); | |||
| test('handles failure', () async { | |||
| when(api.canLaunchUrl(_webUrl)) | |||
| .thenAnswer((_) async => LaunchResult.failure); | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| expect(await launcher.canLaunch(_webUrl), false); | |||
| }); | |||
| test('throws PlatformException for invalid URL', () async { | |||
| when(api.canLaunchUrl(_webUrl)) | |||
| .thenAnswer((_) async => LaunchResult.invalidUrl); | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| await expectLater( | |||
| launcher.canLaunch(_webUrl), | |||
| throwsA(isA<PlatformException>().having( | |||
| (PlatformException e) => e.code, 'code', 'argument_error'))); | |||
| }); | |||
| }); | |||
| group('legacy launch', () { | |||
| test('handles success', () async { | |||
| when(api.launchUrl(_webUrl, any)) | |||
| .thenAnswer((_) async => LaunchResult.success); | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| expect( | |||
| await launcher.launch( | |||
| _webUrl, | |||
| useSafariVC: false, | |||
| useWebView: false, | |||
| enableJavaScript: false, | |||
| enableDomStorage: false, | |||
| universalLinksOnly: false, | |||
| headers: const <String, String>{}, | |||
| ), | |||
| true); | |||
| verifyNever(api.openUrlInSafariViewController(any)); | |||
| }); | |||
| test('handles failure', () async { | |||
| when(api.launchUrl(_webUrl, any)) | |||
| .thenAnswer((_) async => LaunchResult.failure); | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| expect( | |||
| await launcher.launch( | |||
| _webUrl, | |||
| useSafariVC: false, | |||
| useWebView: false, | |||
| enableJavaScript: false, | |||
| enableDomStorage: false, | |||
| universalLinksOnly: false, | |||
| headers: const <String, String>{}, | |||
| ), | |||
| false); | |||
| verifyNever(api.openUrlInSafariViewController(any)); | |||
| }); | |||
| test('throws PlatformException for invalid URL', () async { | |||
| when(api.launchUrl(_webUrl, any)) | |||
| .thenAnswer((_) async => LaunchResult.invalidUrl); | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| await expectLater( | |||
| launcher.launch( | |||
| _webUrl, | |||
| useSafariVC: false, | |||
| useWebView: false, | |||
| enableJavaScript: false, | |||
| enableDomStorage: false, | |||
| universalLinksOnly: false, | |||
| headers: const <String, String>{}, | |||
| ), | |||
| throwsA(isA<PlatformException>().having( | |||
| (PlatformException e) => e.code, 'code', 'argument_error'))); | |||
| }); | |||
| test('force SafariVC is handled', () async { | |||
| when(api.openUrlInSafariViewController(_webUrl)) | |||
| .thenAnswer((_) async => InAppLoadResult.success); | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| expect( | |||
| await launcher.launch( | |||
| _webUrl, | |||
| useSafariVC: true, | |||
| useWebView: false, | |||
| enableJavaScript: false, | |||
| enableDomStorage: false, | |||
| universalLinksOnly: false, | |||
| headers: const <String, String>{}, | |||
| ), | |||
| true); | |||
| verifyNever(api.launchUrl(any, any)); | |||
| }); | |||
| test('universal links only is handled', () async { | |||
| when(api.launchUrl(_webUrl, any)) | |||
| .thenAnswer((_) async => LaunchResult.success); | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| expect( | |||
| await launcher.launch( | |||
| _webUrl, | |||
| useSafariVC: false, | |||
| useWebView: false, | |||
| enableJavaScript: false, | |||
| enableDomStorage: false, | |||
| universalLinksOnly: true, | |||
| headers: const <String, String>{}, | |||
| ), | |||
| true); | |||
| verifyNever(api.openUrlInSafariViewController(any)); | |||
| }); | |||
| test('disallowing SafariVC is handled', () async { | |||
| when(api.launchUrl(_webUrl, any)) | |||
| .thenAnswer((_) async => LaunchResult.success); | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| expect( | |||
| await launcher.launch( | |||
| _webUrl, | |||
| useSafariVC: false, | |||
| useWebView: false, | |||
| enableJavaScript: false, | |||
| enableDomStorage: false, | |||
| universalLinksOnly: false, | |||
| headers: const <String, String>{}, | |||
| ), | |||
| true); | |||
| verifyNever(api.openUrlInSafariViewController(any)); | |||
| }); | |||
| }); | |||
| test('closeWebView calls through', () async { | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| await launcher.closeWebView(); | |||
| verify(api.closeSafariViewController()).called(1); | |||
| }); | |||
| group('launch without webview', () { | |||
| test('calls through', () async { | |||
| when(api.launchUrl(_webUrl, any)) | |||
| .thenAnswer((_) async => LaunchResult.success); | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| final bool launched = await launcher.launchUrl( | |||
| _webUrl, | |||
| const LaunchOptions(mode: PreferredLaunchMode.externalApplication), | |||
| ); | |||
| expect(launched, true); | |||
| verifyNever(api.openUrlInSafariViewController(any)); | |||
| }); | |||
| test('throws PlatformException for invalid URL', () async { | |||
| when(api.launchUrl(_webUrl, any)) | |||
| .thenAnswer((_) async => LaunchResult.invalidUrl); | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| await expectLater( | |||
| launcher.launchUrl( | |||
| _webUrl, | |||
| const LaunchOptions(mode: PreferredLaunchMode.externalApplication), | |||
| ), | |||
| throwsA(isA<PlatformException>().having( | |||
| (PlatformException e) => e.code, 'code', 'argument_error'))); | |||
| }); | |||
| }); | |||
| group('launch with Safari view controller', () { | |||
| test('calls through with inAppWebView', () async { | |||
| when(api.openUrlInSafariViewController(_webUrl)) | |||
| .thenAnswer((_) async => InAppLoadResult.success); | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| final bool launched = await launcher.launchUrl( | |||
| _webUrl, const LaunchOptions(mode: PreferredLaunchMode.inAppWebView)); | |||
| expect(launched, true); | |||
| verifyNever(api.launchUrl(any, any)); | |||
| }); | |||
| test('calls through with inAppBrowserView', () async { | |||
| when(api.openUrlInSafariViewController(_webUrl)) | |||
| .thenAnswer((_) async => InAppLoadResult.success); | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| final bool launched = await launcher.launchUrl(_webUrl, | |||
| const LaunchOptions(mode: PreferredLaunchMode.inAppBrowserView)); | |||
| expect(launched, true); | |||
| verifyNever(api.launchUrl(any, any)); | |||
| }); | |||
| test('throws PlatformException for invalid URL', () async { | |||
| when(api.openUrlInSafariViewController(_webUrl)) | |||
| .thenAnswer((_) async => InAppLoadResult.invalidUrl); | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| await expectLater( | |||
| launcher.launchUrl(_webUrl, | |||
| const LaunchOptions(mode: PreferredLaunchMode.inAppWebView)), | |||
| throwsA(isA<PlatformException>().having( | |||
| (PlatformException e) => e.code, 'code', 'argument_error'))); | |||
| }); | |||
| test('throws PlatformException for load failure', () async { | |||
| when(api.openUrlInSafariViewController(_webUrl)) | |||
| .thenAnswer((_) async => InAppLoadResult.failedToLoad); | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| await expectLater( | |||
| launcher.launchUrl(_webUrl, | |||
| const LaunchOptions(mode: PreferredLaunchMode.inAppWebView)), | |||
| throwsA(isA<PlatformException>() | |||
| .having((PlatformException e) => e.code, 'code', 'Error'))); | |||
| }); | |||
| }); | |||
| group('launch with universal links', () { | |||
| test('calls through', () async { | |||
| when(api.launchUrl(_webUrl, any)) | |||
| .thenAnswer((_) async => LaunchResult.success); | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| final bool launched = await launcher.launchUrl( | |||
| _webUrl, | |||
| const LaunchOptions( | |||
| mode: PreferredLaunchMode.externalNonBrowserApplication), | |||
| ); | |||
| expect(launched, true); | |||
| verifyNever(api.openUrlInSafariViewController(any)); | |||
| }); | |||
| test('throws PlatformException for invalid URL', () async { | |||
| when(api.launchUrl(_webUrl, any)) | |||
| .thenAnswer((_) async => LaunchResult.invalidUrl); | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| await expectLater( | |||
| launcher.launchUrl( | |||
| _webUrl, | |||
| const LaunchOptions( | |||
| mode: PreferredLaunchMode.externalNonBrowserApplication)), | |||
| throwsA(isA<PlatformException>().having( | |||
| (PlatformException e) => e.code, 'code', 'argument_error'))); | |||
| }); | |||
| }); | |||
| group('launch with platform default', () { | |||
| test('uses Safari view controller for http', () async { | |||
| const String httpUrl = 'http://example.com/'; | |||
| when(api.openUrlInSafariViewController(httpUrl)) | |||
| .thenAnswer((_) async => InAppLoadResult.success); | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| final bool launched = | |||
| await launcher.launchUrl(httpUrl, const LaunchOptions()); | |||
| expect(launched, true); | |||
| verifyNever(api.launchUrl(any, any)); | |||
| }); | |||
| test('uses Safari view controller for https', () async { | |||
| const String httpsUrl = 'https://example.com/'; | |||
| when(api.openUrlInSafariViewController(httpsUrl)) | |||
| .thenAnswer((_) async => InAppLoadResult.success); | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| final bool launched = | |||
| await launcher.launchUrl(httpsUrl, const LaunchOptions()); | |||
| expect(launched, true); | |||
| verifyNever(api.launchUrl(any, any)); | |||
| }); | |||
| test('uses standard external for other schemes', () async { | |||
| const String nonWebUrl = 'supportedcustomscheme://example.com/'; | |||
| when(api.launchUrl(nonWebUrl, any)) | |||
| .thenAnswer((_) async => LaunchResult.success); | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| final bool launched = | |||
| await launcher.launchUrl(nonWebUrl, const LaunchOptions()); | |||
| expect(launched, true); | |||
| verifyNever(api.openUrlInSafariViewController(any)); | |||
| }); | |||
| }); | |||
| group('supportsMode', () { | |||
| test('returns true for platformDefault', () async { | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| expect(await launcher.supportsMode(PreferredLaunchMode.platformDefault), | |||
| true); | |||
| }); | |||
| test('returns true for external application', () async { | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| expect( | |||
| await launcher.supportsMode(PreferredLaunchMode.externalApplication), | |||
| true); | |||
| }); | |||
| test('returns true for external non-browser application', () async { | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| expect( | |||
| await launcher | |||
| .supportsMode(PreferredLaunchMode.externalNonBrowserApplication), | |||
| true); | |||
| }); | |||
| test('returns true for in app web view', () async { | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| expect( | |||
| await launcher.supportsMode(PreferredLaunchMode.inAppWebView), true); | |||
| }); | |||
| test('returns true for in app browser view', () async { | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| expect(await launcher.supportsMode(PreferredLaunchMode.inAppBrowserView), | |||
| true); | |||
| }); | |||
| }); | |||
| group('supportsCloseForMode', () { | |||
| test('returns true for in app web view', () async { | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| expect( | |||
| await launcher.supportsCloseForMode(PreferredLaunchMode.inAppWebView), | |||
| true); | |||
| }); | |||
| test('returns true for in app browser view', () async { | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| expect( | |||
| await launcher | |||
| .supportsCloseForMode(PreferredLaunchMode.inAppBrowserView), | |||
| true); | |||
| }); | |||
| test('returns false for other modes', () async { | |||
| final UrlLauncherIOS launcher = UrlLauncherIOS(api: api); | |||
| expect( | |||
| await launcher | |||
| .supportsCloseForMode(PreferredLaunchMode.externalApplication), | |||
| false); | |||
| expect( | |||
| await launcher.supportsCloseForMode( | |||
| PreferredLaunchMode.externalNonBrowserApplication), | |||
| false); | |||
| }); | |||
| }); | |||
| } | |||
| @@ -0,0 +1,89 @@ | |||
| // Mocks generated by Mockito 5.4.4 from annotations | |||
| // in url_launcher_ios/test/url_launcher_ios_test.dart. | |||
| // Do not manually edit this file. | |||
| // ignore_for_file: no_leading_underscores_for_library_prefixes | |||
| import 'dart:async' as _i4; | |||
| import 'package:mockito/mockito.dart' as _i1; | |||
| import 'package:mockito/src/dummies.dart' as _i3; | |||
| import 'package:url_launcher_ios/src/messages.g.dart' as _i2; | |||
| // ignore_for_file: type=lint | |||
| // ignore_for_file: avoid_redundant_argument_values | |||
| // ignore_for_file: avoid_setters_without_getters | |||
| // ignore_for_file: comment_references | |||
| // ignore_for_file: deprecated_member_use | |||
| // ignore_for_file: deprecated_member_use_from_same_package | |||
| // ignore_for_file: implementation_imports | |||
| // ignore_for_file: invalid_use_of_visible_for_testing_member | |||
| // ignore_for_file: prefer_const_constructors | |||
| // ignore_for_file: unnecessary_parenthesis | |||
| // ignore_for_file: camel_case_types | |||
| // ignore_for_file: subtype_of_sealed_class | |||
| /// A class which mocks [UrlLauncherApi]. | |||
| /// | |||
| /// See the documentation for Mockito's code generation for more information. | |||
| class MockUrlLauncherApi extends _i1.Mock implements _i2.UrlLauncherApi { | |||
| MockUrlLauncherApi() { | |||
| _i1.throwOnMissingStub(this); | |||
| } | |||
| @override | |||
| String get pigeonVar_messageChannelSuffix => (super.noSuchMethod( | |||
| Invocation.getter(#pigeonVar_messageChannelSuffix), | |||
| returnValue: _i3.dummyValue<String>( | |||
| this, | |||
| Invocation.getter(#pigeonVar_messageChannelSuffix), | |||
| ), | |||
| ) as String); | |||
| @override | |||
| _i4.Future<_i2.LaunchResult> canLaunchUrl(String? url) => (super.noSuchMethod( | |||
| Invocation.method( | |||
| #canLaunchUrl, | |||
| [url], | |||
| ), | |||
| returnValue: | |||
| _i4.Future<_i2.LaunchResult>.value(_i2.LaunchResult.success), | |||
| ) as _i4.Future<_i2.LaunchResult>); | |||
| @override | |||
| _i4.Future<_i2.LaunchResult> launchUrl( | |||
| String? url, | |||
| bool? universalLinksOnly, | |||
| ) => | |||
| (super.noSuchMethod( | |||
| Invocation.method( | |||
| #launchUrl, | |||
| [ | |||
| url, | |||
| universalLinksOnly, | |||
| ], | |||
| ), | |||
| returnValue: | |||
| _i4.Future<_i2.LaunchResult>.value(_i2.LaunchResult.success), | |||
| ) as _i4.Future<_i2.LaunchResult>); | |||
| @override | |||
| _i4.Future<_i2.InAppLoadResult> openUrlInSafariViewController(String? url) => | |||
| (super.noSuchMethod( | |||
| Invocation.method( | |||
| #openUrlInSafariViewController, | |||
| [url], | |||
| ), | |||
| returnValue: | |||
| _i4.Future<_i2.InAppLoadResult>.value(_i2.InAppLoadResult.success), | |||
| ) as _i4.Future<_i2.InAppLoadResult>); | |||
| @override | |||
| _i4.Future<void> closeSafariViewController() => (super.noSuchMethod( | |||
| Invocation.method( | |||
| #closeSafariViewController, | |||
| [], | |||
| ), | |||
| returnValue: _i4.Future<void>.value(), | |||
| returnValueForMissingStub: _i4.Future<void>.value(), | |||
| ) as _i4.Future<void>); | |||
| } | |||