[camera] Prevent crash when setting unsupported FocusMode (#3992)

* Check if chosen focus mode is supported

* adding unit tests

* Added tests for camera focus

* Fail 1 test on purpose

* formatting

* Fix copyright notion

* Fix test run

* Add documentation and changelog

* Update packages/camera/camera/CHANGELOG.md

Co-authored-by: Maurits van Beusekom <maurits@vnbskm.nl>

* Improve documentation

Co-authored-by: Maurits van Beusekom <maurits@vnbskm.nl>
diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md
index e16a14c..193feec 100644
--- a/packages/camera/camera/CHANGELOG.md
+++ b/packages/camera/camera/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.8.1+2
+
+* Fix iOS crash when selecting an unsupported FocusMode.
+
 ## 0.8.1+1
 
 * Migrate maven repository from jcenter to mavenCentral.
diff --git a/packages/camera/camera/example/ios/Podfile b/packages/camera/camera/example/ios/Podfile
index f7d6a5e..884573b 100644
--- a/packages/camera/camera/example/ios/Podfile
+++ b/packages/camera/camera/example/ios/Podfile
@@ -29,6 +29,13 @@
 
 target 'Runner' do
   flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+  
+  target 'UnitTests' do
+    platform :ios, '9.0'
+    inherit! :search_paths
+    # Pods for testing
+    pod 'OCMock', '~> 3.8.1'
+  end
 end
 
 post_install do |installer|
diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj
index c865799..d39ed6a 100644
--- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj
@@ -7,16 +7,29 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		01010359265BEB94FD7CE839 /* libPods-UnitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6CCBF0769BA2C53F6AED0F17 /* libPods-UnitTests.a */; };
+		03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BB766A2665316900CE5A93 /* CameraFocusTests.m */; };
+		03BB767326653ABE00CE5A93 /* CameraPluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BB767226653ABE00CE5A93 /* CameraPluginTests.m */; };
 		1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
 		3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
-		75201D617916C49BDEDF852A /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 620DDA07C00B5FF2F937CB5B /* libPods-Runner.a */; };
 		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 */; };
+		D065CD815D405ECB22FB1BBA /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A4F2DE74AE0C572296A00BF /* libPods-Runner.a */; };
 /* End PBXBuildFile section */
 
+/* Begin PBXContainerItemProxy section */
+		03BB766D2665316900CE5A93 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 97C146E61CF9000F007C117D /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 97C146ED1CF9000F007C117D;
+			remoteInfo = Runner;
+		};
+/* End PBXContainerItemProxy section */
+
 /* Begin PBXCopyFilesBuildPhase section */
 		9705A1C41CF9048500538489 /* Embed Frameworks */ = {
 			isa = PBXCopyFilesBuildPhase;
@@ -31,14 +44,19 @@
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
+		03BB76682665316900CE5A93 /* UnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		03BB766A2665316900CE5A93 /* CameraFocusTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraFocusTests.m; sourceTree = "<group>"; };
+		03BB766C2665316900CE5A93 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		03BB767226653ABE00CE5A93 /* CameraPluginTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CameraPluginTests.m; path = ../../../ios/Tests/CameraPluginTests.m; sourceTree = "<group>"; };
 		1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
 		1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
+		2A4F2DE74AE0C572296A00BF /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
-		483D985F075B951ADBAD218E /* 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>"; };
-		620DDA07C00B5FF2F937CB5B /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		6CCBF0769BA2C53F6AED0F17 /* libPods-UnitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-UnitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		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>"; };
+		8F7D83D0CFC9B51065F87CE1 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
 		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; };
@@ -47,25 +65,46 @@
 		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>"; };
-		9AC7510327AD6A32B7CBD9A5 /* 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>"; };
+		A4725B4F24805CD3CA67828F /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
+		A903DC9BC9D1CB89BD4FB3CB /* Pods-UnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnitTests.debug.xcconfig"; path = "Target Support Files/Pods-UnitTests/Pods-UnitTests.debug.xcconfig"; sourceTree = "<group>"; };
+		C2D350ADCDFC81FCB0D6F12C /* Pods-UnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnitTests.release.xcconfig"; path = "Target Support Files/Pods-UnitTests/Pods-UnitTests.release.xcconfig"; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
+		03BB76652665316900CE5A93 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				01010359265BEB94FD7CE839 /* libPods-UnitTests.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		97C146EB1CF9000F007C117D /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				75201D617916C49BDEDF852A /* libPods-Runner.a in Frameworks */,
+				D065CD815D405ECB22FB1BBA /* libPods-Runner.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
-		8A1387E89A6BBC071B75FD6F /* Frameworks */ = {
+		03BB76692665316900CE5A93 /* UnitTests */ = {
 			isa = PBXGroup;
 			children = (
-				620DDA07C00B5FF2F937CB5B /* libPods-Runner.a */,
+				03BB767226653ABE00CE5A93 /* CameraPluginTests.m */,
+				03BB766A2665316900CE5A93 /* CameraFocusTests.m */,
+				03BB766C2665316900CE5A93 /* Info.plist */,
+			);
+			path = UnitTests;
+			sourceTree = "<group>";
+		};
+		78D1009194BD06C03BED950D /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				2A4F2DE74AE0C572296A00BF /* libPods-Runner.a */,
+				6CCBF0769BA2C53F6AED0F17 /* libPods-UnitTests.a */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -86,9 +125,10 @@
 			children = (
 				9740EEB11CF90186004384FC /* Flutter */,
 				97C146F01CF9000F007C117D /* Runner */,
+				03BB76692665316900CE5A93 /* UnitTests */,
 				97C146EF1CF9000F007C117D /* Products */,
-				C52D9D4A70956403860EBEB5 /* Pods */,
-				8A1387E89A6BBC071B75FD6F /* Frameworks */,
+				FD386F00E98D73419C929072 /* Pods */,
+				78D1009194BD06C03BED950D /* Frameworks */,
 			);
 			sourceTree = "<group>";
 		};
@@ -96,6 +136,7 @@
 			isa = PBXGroup;
 			children = (
 				97C146EE1CF9000F007C117D /* Runner.app */,
+				03BB76682665316900CE5A93 /* UnitTests.xctest */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -124,23 +165,44 @@
 			name = "Supporting Files";
 			sourceTree = "<group>";
 		};
-		C52D9D4A70956403860EBEB5 /* Pods */ = {
+		FD386F00E98D73419C929072 /* Pods */ = {
 			isa = PBXGroup;
 			children = (
-				9AC7510327AD6A32B7CBD9A5 /* Pods-Runner.debug.xcconfig */,
-				483D985F075B951ADBAD218E /* Pods-Runner.release.xcconfig */,
+				8F7D83D0CFC9B51065F87CE1 /* Pods-Runner.debug.xcconfig */,
+				A4725B4F24805CD3CA67828F /* Pods-Runner.release.xcconfig */,
+				A903DC9BC9D1CB89BD4FB3CB /* Pods-UnitTests.debug.xcconfig */,
+				C2D350ADCDFC81FCB0D6F12C /* Pods-UnitTests.release.xcconfig */,
 			);
-			name = Pods;
+			path = Pods;
 			sourceTree = "<group>";
 		};
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
+		03BB76672665316900CE5A93 /* UnitTests */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 03BB76712665316900CE5A93 /* Build configuration list for PBXNativeTarget "UnitTests" */;
+			buildPhases = (
+				604FC00FF5713F40F2A4441D /* [CP] Check Pods Manifest.lock */,
+				03BB76642665316900CE5A93 /* Sources */,
+				03BB76652665316900CE5A93 /* Frameworks */,
+				03BB76662665316900CE5A93 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				03BB766E2665316900CE5A93 /* PBXTargetDependency */,
+			);
+			name = UnitTests;
+			productName = camera_exampleTests;
+			productReference = 03BB76682665316900CE5A93 /* UnitTests.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
 		97C146ED1CF9000F007C117D /* Runner */ = {
 			isa = PBXNativeTarget;
 			buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
 			buildPhases = (
-				3E30118C54AB12C3EB9EDF27 /* [CP] Check Pods Manifest.lock */,
+				1D0D227A6719C1144CAE5AB5 /* [CP] Check Pods Manifest.lock */,
 				9740EEB61CF901F6004384FC /* Run Script */,
 				97C146EA1CF9000F007C117D /* Sources */,
 				97C146EB1CF9000F007C117D /* Frameworks */,
@@ -166,6 +228,11 @@
 				LastUpgradeCheck = 1100;
 				ORGANIZATIONNAME = "The Flutter Authors";
 				TargetAttributes = {
+					03BB76672665316900CE5A93 = {
+						CreatedOnToolsVersion = 12.5;
+						ProvisioningStyle = Automatic;
+						TestTargetID = 97C146ED1CF9000F007C117D;
+					};
 					97C146ED1CF9000F007C117D = {
 						CreatedOnToolsVersion = 7.3.1;
 						DevelopmentTeam = 7624MWN53C;
@@ -186,11 +253,19 @@
 			projectRoot = "";
 			targets = (
 				97C146ED1CF9000F007C117D /* Runner */,
+				03BB76672665316900CE5A93 /* UnitTests */,
 			);
 		};
 /* End PBXProject section */
 
 /* Begin PBXResourcesBuildPhase section */
+		03BB76662665316900CE5A93 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		97C146EC1CF9000F007C117D /* Resources */ = {
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -205,6 +280,28 @@
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXShellScriptBuildPhase section */
+		1D0D227A6719C1144CAE5AB5 /* [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-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;
+		};
 		3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
@@ -219,18 +316,22 @@
 			shellPath = /bin/sh;
 			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
 		};
-		3E30118C54AB12C3EB9EDF27 /* [CP] Check Pods Manifest.lock */ = {
+		604FC00FF5713F40F2A4441D /* [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-Runner-checkManifestLockResult.txt",
+				"$(DERIVED_FILE_DIR)/Pods-UnitTests-checkManifestLockResult.txt",
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
@@ -254,6 +355,15 @@
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
+		03BB76642665316900CE5A93 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */,
+				03BB767326653ABE00CE5A93 /* CameraPluginTests.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		97C146EA1CF9000F007C117D /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -266,6 +376,14 @@
 		};
 /* End PBXSourcesBuildPhase section */
 
+/* Begin PBXTargetDependency section */
+		03BB766E2665316900CE5A93 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 97C146ED1CF9000F007C117D /* Runner */;
+			targetProxy = 03BB766D2665316900CE5A93 /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
 /* Begin PBXVariantGroup section */
 		97C146FA1CF9000F007C117D /* Main.storyboard */ = {
 			isa = PBXVariantGroup;
@@ -286,6 +404,55 @@
 /* End PBXVariantGroup section */
 
 /* Begin XCBuildConfiguration section */
+		03BB766F2665316900CE5A93 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = A903DC9BC9D1CB89BD4FB3CB /* Pods-UnitTests.debug.xcconfig */;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_STYLE = Automatic;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				INFOPLIST_FILE = UnitTests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+				MTL_FAST_MATH = YES;
+				PRODUCT_BUNDLE_IDENTIFIER = "io.flutter.plugins.cameraExample.camera-exampleTests";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				TARGETED_DEVICE_FAMILY = "1,2";
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner";
+			};
+			name = Debug;
+		};
+		03BB76702665316900CE5A93 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = C2D350ADCDFC81FCB0D6F12C /* Pods-UnitTests.release.xcconfig */;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_STYLE = Automatic;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				INFOPLIST_FILE = UnitTests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MTL_FAST_MATH = YES;
+				PRODUCT_BUNDLE_IDENTIFIER = "io.flutter.plugins.cameraExample.camera-exampleTests";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				TARGETED_DEVICE_FAMILY = "1,2";
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner";
+			};
+			name = Release;
+		};
 		97C147031CF9000F007C117D /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
@@ -439,6 +606,15 @@
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
+		03BB76712665316900CE5A93 /* Build configuration list for PBXNativeTarget "UnitTests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				03BB766F2665316900CE5A93 /* Debug */,
+				03BB76702665316900CE5A93 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 		97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index 44b8736..d9bece2 100644
--- a/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -37,6 +37,16 @@
          </BuildableReference>
       </MacroExpansion>
       <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "03BB76672665316900CE5A93"
+               BuildableName = "UnitTests.xctest"
+               BlueprintName = "UnitTests"
+               ReferencedContainer = "container:Runner.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
       </Testables>
    </TestAction>
    <LaunchAction
diff --git a/packages/camera/camera/example/ios/UnitTests/CameraFocusTests.m b/packages/camera/camera/example/ios/UnitTests/CameraFocusTests.m
new file mode 100644
index 0000000..5d93bdf
--- /dev/null
+++ b/packages/camera/camera/example/ios/UnitTests/CameraFocusTests.m
@@ -0,0 +1,120 @@
+// 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 camera;
+@import XCTest;
+@import AVFoundation;
+#import <OCMock/OCMock.h>
+
+// Mirrors FocusMode in camera.dart
+typedef enum {
+  FocusModeAuto,
+  FocusModeLocked,
+} FocusMode;
+
+@interface FLTCam : NSObject <FlutterTexture,
+                              AVCaptureVideoDataOutputSampleBufferDelegate,
+                              AVCaptureAudioDataOutputSampleBufferDelegate>
+
+- (void)applyFocusMode;
+- (void)applyFocusMode:(FocusMode)focusMode onDevice:(AVCaptureDevice *)captureDevice;
+@end
+
+@interface CameraFocusTests : XCTestCase
+@property(readonly, nonatomic) FLTCam *camera;
+@property(readonly, nonatomic) id mockDevice;
+
+@end
+
+@implementation CameraFocusTests
+
+- (void)setUp {
+  _camera = [[FLTCam alloc] init];
+  _mockDevice = OCMClassMock([AVCaptureDevice class]);
+}
+
+- (void)tearDown {
+  // Put teardown code here. This method is called after the invocation of each test method in the
+  // class.
+}
+
+- (void)testAutoFocusWithContinuousModeSupported_ShouldSetContinuousAutoFocus {
+  // AVCaptureFocusModeContinuousAutoFocus is supported
+  OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]).andReturn(true);
+  // AVCaptureFocusModeContinuousAutoFocus is supported
+  OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]).andReturn(true);
+
+  // Don't expect setFocusMode:AVCaptureFocusModeAutoFocus
+  [[_mockDevice reject] setFocusMode:AVCaptureFocusModeAutoFocus];
+
+  // Run test
+  [_camera applyFocusMode:FocusModeAuto onDevice:_mockDevice];
+
+  // Expect setFocusMode:AVCaptureFocusModeContinuousAutoFocus
+  OCMVerify([_mockDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus]);
+}
+
+- (void)testAutoFocusWithContinuousModeNotSupported_ShouldSetAutoFocus {
+  // AVCaptureFocusModeContinuousAutoFocus is not supported
+  OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus])
+      .andReturn(false);
+  // AVCaptureFocusModeContinuousAutoFocus is supported
+  OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]).andReturn(true);
+
+  // Don't expect setFocusMode:AVCaptureFocusModeContinuousAutoFocus
+  [[_mockDevice reject] setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
+
+  // Run test
+  [_camera applyFocusMode:FocusModeAuto onDevice:_mockDevice];
+
+  // Expect setFocusMode:AVCaptureFocusModeAutoFocus
+  OCMVerify([_mockDevice setFocusMode:AVCaptureFocusModeAutoFocus]);
+}
+
+- (void)testAutoFocusWithNoModeSupported_ShouldSetNothing {
+  // AVCaptureFocusModeContinuousAutoFocus is not supported
+  OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus])
+      .andReturn(false);
+  // AVCaptureFocusModeContinuousAutoFocus is not supported
+  OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]).andReturn(false);
+
+  // Don't expect any setFocus
+  [[_mockDevice reject] setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
+  [[_mockDevice reject] setFocusMode:AVCaptureFocusModeAutoFocus];
+
+  // Run test
+  [_camera applyFocusMode:FocusModeAuto onDevice:_mockDevice];
+}
+
+- (void)testLockedFocusWithModeSupported_ShouldSetModeAutoFocus {
+  // AVCaptureFocusModeContinuousAutoFocus is supported
+  OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]).andReturn(true);
+  // AVCaptureFocusModeContinuousAutoFocus is supported
+  OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]).andReturn(true);
+
+  // Don't expect any setFocus
+  [[_mockDevice reject] setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
+
+  // Run test
+  [_camera applyFocusMode:FocusModeLocked onDevice:_mockDevice];
+
+  // Expect setFocusMode:AVCaptureFocusModeAutoFocus
+  OCMVerify([_mockDevice setFocusMode:AVCaptureFocusModeAutoFocus]);
+}
+
+- (void)testLockedFocusWithModeNotSupported_ShouldSetNothing {
+  // AVCaptureFocusModeContinuousAutoFocus is supported
+  OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]).andReturn(true);
+  // AVCaptureFocusModeContinuousAutoFocus is not supported
+  OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]).andReturn(false);
+
+  // Don't expect any setFocus
+  [[_mockDevice reject] setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
+  [[_mockDevice reject] setFocusMode:AVCaptureFocusModeAutoFocus];
+
+  // Run test
+  [_camera applyFocusMode:FocusModeLocked onDevice:_mockDevice];
+}
+
+@end
diff --git a/packages/camera/camera/example/ios/UnitTests/Info.plist b/packages/camera/camera/example/ios/UnitTests/Info.plist
new file mode 100644
index 0000000..64d65ca
--- /dev/null
+++ b/packages/camera/camera/example/ios/UnitTests/Info.plist
@@ -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>
diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m
index ac84405..1e818ab 100644
--- a/packages/camera/camera/ios/Classes/CameraPlugin.m
+++ b/packages/camera/camera/ios/Classes/CameraPlugin.m
@@ -994,20 +994,40 @@
 }
 
 - (void)applyFocusMode {
-  [_captureDevice lockForConfiguration:nil];
-  switch (_focusMode) {
+  [self applyFocusMode:_focusMode onDevice:_captureDevice];
+}
+
+/**
+ * Applies FocusMode on the AVCaptureDevice.
+ *
+ * If the @c focusMode is set to FocusModeAuto the AVCaptureDevice is configured to use
+ * AVCaptureFocusModeContinuousModeAutoFocus when supported, otherwise it is set to
+ * AVCaptureFocusModeAutoFocus. If neither AVCaptureFocusModeContinuousModeAutoFocus nor
+ * AVCaptureFocusModeAutoFocus are supported focus mode will not be set.
+ * If @c focusMode is set to FocusModeLocked the AVCaptureDevice is configured to use
+ * AVCaptureFocusModeAutoFocus. If AVCaptureFocusModeAutoFocus is not supported focus mode will not
+ * be set.
+ *
+ * @param focusMode The focus mode that should be applied to the @captureDevice instance.
+ * @param captureDevice The AVCaptureDevice to which the @focusMode will be applied.
+ */
+- (void)applyFocusMode:(FocusMode)focusMode onDevice:(AVCaptureDevice *)captureDevice {
+  [captureDevice lockForConfiguration:nil];
+  switch (focusMode) {
     case FocusModeLocked:
-      [_captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
+      if ([captureDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
+        [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
+      }
       break;
     case FocusModeAuto:
-      if ([_captureDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
-        [_captureDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
-      } else {
-        [_captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
+      if ([captureDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
+        [captureDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
+      } else if ([captureDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
+        [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
       }
       break;
   }
-  [_captureDevice unlockForConfiguration];
+  [captureDevice unlockForConfiguration];
 }
 
 - (void)setExposurePointWithResult:(FlutterResult)result x:(double)x y:(double)y {
diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml
index dc7bf8e..de28683 100644
--- a/packages/camera/camera/pubspec.yaml
+++ b/packages/camera/camera/pubspec.yaml
@@ -4,7 +4,7 @@
   and streaming image buffers to dart.
 repository: https://github.com/flutter/plugins/tree/master/packages/camera/camera
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
-version: 0.8.1+1
+version: 0.8.1+2
 
 environment:
   sdk: ">=2.12.0 <3.0.0"