[camera] Remove iOS thread-safe result class (#6498)

`FlutterResult` is thread-safe in all versions of Flutter that this plugin now supports, so the wrapper class to move result calls to the main thread is no longer necessary.
diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md
index 10cb652..260df0c 100644
--- a/packages/camera/camera_avfoundation/CHANGELOG.md
+++ b/packages/camera/camera_avfoundation/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.9.15+1
+
+* Simplifies internal handling of method channel responses.
+
 ## 0.9.15
 
 * Adds support to control video FPS and bitrate. See `CameraController.withSettings`.
diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj
index dad7730..86296ea 100644
--- a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj
@@ -9,7 +9,6 @@
 /* Begin PBXBuildFile section */
 		033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */; };
 		03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BB766A2665316900CE5A93 /* CameraFocusTests.m */; };
-		03F6F8B226CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03F6F8B126CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m */; };
 		1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
 		236906D1621AE863A5B2E770 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 89D82918721FABF772705DB0 /* libPods-Runner.a */; };
 		25C3919135C3D981E6F800D0 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */; };
@@ -36,7 +35,6 @@
 		E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */; };
 		E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */; };
 		E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */; };
-		F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */ = {isa = PBXBuildFile; fileRef = F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -68,7 +66,6 @@
 		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 /* CameraOrientationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraOrientationTests.m; sourceTree = "<group>"; };
-		03F6F8B126CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeFlutterResultTests.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>"; };
 		14AE82C910C2A12F2ECB2094 /* 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>"; };
@@ -106,8 +103,6 @@
 		E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraTestUtils.m; sourceTree = "<group>"; };
 		E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPropertiesTests.m; sourceTree = "<group>"; };
 		E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPreviewPauseTests.m; sourceTree = "<group>"; };
-		F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockFLTThreadSafeFlutterResult.h; sourceTree = "<group>"; };
-		F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockFLTThreadSafeFlutterResult.m; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -138,7 +133,6 @@
 				03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */,
 				03BB766C2665316900CE5A93 /* Info.plist */,
 				033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */,
-				03F6F8B126CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m */,
 				E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */,
 				E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */,
 				E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */,
@@ -150,8 +144,6 @@
 				E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */,
 				E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */,
 				E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */,
-				F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */,
-				F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */,
 				E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */,
 				E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */,
 				788A065927B0E02900533D74 /* StreamingTest.m */,
@@ -287,7 +279,7 @@
 		97C146E61CF9000F007C117D /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastUpgradeCheck = 1430;
+				LastUpgradeCheck = 1510;
 				ORGANIZATIONNAME = "The Flutter Authors";
 				TargetAttributes = {
 					03BB76672665316900CE5A93 = {
@@ -445,7 +437,6 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				03F6F8B226CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m in Sources */,
 				033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */,
 				E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */,
 				E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */,
@@ -455,7 +446,6 @@
 				E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */,
 				E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */,
 				43ED1537282570DE00EB00DE /* AvailableCamerasTest.m in Sources */,
-				F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */,
 				E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */,
 				334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */,
 				CEF6611A2B5E36A500D33FD4 /* CameraSessionPresetsTests.m in Sources */,
diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index 1ff4b57..5e1cf7e 100644
--- a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1430"
+   LastUpgradeVersion = "1510"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTest.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTest.m
index 6074b87..341d532 100644
--- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTest.m
+++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTest.m
@@ -7,7 +7,6 @@
 @import XCTest;
 @import AVFoundation;
 #import <OCMock/OCMock.h>
-#import "MockFLTThreadSafeFlutterResult.h"
 
 @interface AvailableCamerasTest : XCTestCase
 @end
@@ -56,17 +55,19 @@
   }
   OCMStub([discoverySessionMock devices]).andReturn([NSArray arrayWithArray:cameras]);
 
-  MockFLTThreadSafeFlutterResult *resultObject =
-      [[MockFLTThreadSafeFlutterResult alloc] initWithExpectation:expectation];
-
   // Set up method call
   FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"availableCameras"
                                                               arguments:nil];
 
-  [camera handleMethodCallAsync:call result:resultObject];
+  __block id resultValue;
+  [camera handleMethodCallAsync:call
+                         result:^(id _Nullable result) {
+                           resultValue = result;
+                           [expectation fulfill];
+                         }];
 
   // Verify the result
-  NSDictionary *dictionaryResult = (NSDictionary *)resultObject.receivedResult;
+  NSDictionary *dictionaryResult = (NSDictionary *)resultValue;
   if (@available(iOS 13.0, *)) {
     XCTAssertTrue([dictionaryResult count] == 4);
   } else {
@@ -104,17 +105,19 @@
   [cameras addObjectsFromArray:@[ wideAngleCamera, frontFacingCamera ]];
   OCMStub([discoverySessionMock devices]).andReturn([NSArray arrayWithArray:cameras]);
 
-  MockFLTThreadSafeFlutterResult *resultObject =
-      [[MockFLTThreadSafeFlutterResult alloc] initWithExpectation:expectation];
-
   // Set up method call
   FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"availableCameras"
                                                               arguments:nil];
 
-  [camera handleMethodCallAsync:call result:resultObject];
+  __block id resultValue;
+  [camera handleMethodCallAsync:call
+                         result:^(id _Nullable result) {
+                           resultValue = result;
+                           [expectation fulfill];
+                         }];
 
   // Verify the result
-  NSDictionary *dictionaryResult = (NSDictionary *)resultObject.receivedResult;
+  NSDictionary *dictionaryResult = (NSDictionary *)resultValue;
   XCTAssertTrue([dictionaryResult count] == 2);
 }
 
diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m
index 89f4030..bc3713b 100644
--- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m
+++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m
@@ -30,10 +30,9 @@
                       [disposeExpectation fulfill];
                     }];
   [camera createCameraOnSessionQueueWithCreateMethodCall:createCall
-                                                  result:[[FLTThreadSafeFlutterResult alloc]
-                                                             initWithResult:^(id _Nullable result) {
-                                                               [createExpectation fulfill];
-                                                             }]];
+                                                  result:^(id _Nullable result) {
+                                                    [createExpectation fulfill];
+                                                  }];
   [self waitForExpectationsWithTimeout:1 handler:nil];
   // `captureSessionQueue` must not be nil after `create` call. Otherwise a nil
   // `captureSessionQueue` passed into `AVCaptureVideoDataOutput::setSampleBufferDelegate:queue:`
diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.m
index 1b6ada5..3575cd2 100644
--- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.m
+++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.m
@@ -114,11 +114,11 @@
   [_camera setValue:_mockDevice forKey:@"captureDevice"];
 
   // Run test
-  [_camera setFocusPointWithResult:[[FLTThreadSafeFlutterResult alloc]
-                                       initWithResult:^(id _Nullable result){
-                                       }]
-                                 x:1
-                                 y:1];
+  [_camera
+      setFocusPointWithResult:^(id _Nullable result) {
+      }
+                            x:1
+                            y:1];
 
   // Verify the focus point of interest has been set
   OCMVerify([_mockDevice setFocusPointOfInterest:CGPointMake(1, 1)]);
diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraMethodChannelTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraMethodChannelTests.m
index bd20134..423b8e8 100644
--- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraMethodChannelTests.m
+++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraMethodChannelTests.m
@@ -7,7 +7,6 @@
 @import XCTest;
 @import AVFoundation;
 #import <OCMock/OCMock.h>
-#import "MockFLTThreadSafeFlutterResult.h"
 
 @interface CameraMethodChannelTests : XCTestCase
 @end
@@ -28,19 +27,21 @@
   OCMStub([avCaptureSessionMock alloc]).andReturn(avCaptureSessionMock);
   OCMStub([avCaptureSessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES);
 
-  MockFLTThreadSafeFlutterResult *resultObject =
-      [[MockFLTThreadSafeFlutterResult alloc] initWithExpectation:expectation];
-
   // Set up method call
   FlutterMethodCall *call = [FlutterMethodCall
       methodCallWithMethodName:@"create"
                      arguments:@{@"resolutionPreset" : @"medium", @"enableAudio" : @(1)}];
 
-  [camera createCameraOnSessionQueueWithCreateMethodCall:call result:resultObject];
+  __block id resultValue;
+  [camera createCameraOnSessionQueueWithCreateMethodCall:call
+                                                  result:^(id _Nullable result) {
+                                                    resultValue = result;
+                                                    [expectation fulfill];
+                                                  }];
   [self waitForExpectationsWithTimeout:1 handler:nil];
 
   // Verify the result
-  NSDictionary *dictionaryResult = (NSDictionary *)resultObject.receivedResult;
+  NSDictionary *dictionaryResult = (NSDictionary *)resultValue;
   XCTAssertNotNil(dictionaryResult);
   XCTAssert([[dictionaryResult allKeys] containsObject:@"cameraId"]);
 }
diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.m
index 1dfc90b..2ce7b86 100644
--- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.m
+++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.m
@@ -7,7 +7,6 @@
 @import XCTest;
 @import AVFoundation;
 #import <OCMock/OCMock.h>
-#import "MockFLTThreadSafeFlutterResult.h"
 
 @interface CameraPreviewPauseTests : XCTestCase
 @end
@@ -16,17 +15,17 @@
 
 - (void)testPausePreviewWithResult_shouldPausePreview {
   FLTCam *camera = [[FLTCam alloc] init];
-  MockFLTThreadSafeFlutterResult *resultObject = [[MockFLTThreadSafeFlutterResult alloc] init];
 
-  [camera pausePreviewWithResult:resultObject];
+  [camera pausePreviewWithResult:^(id _Nullable result){
+  }];
   XCTAssertTrue(camera.isPreviewPaused);
 }
 
 - (void)testResumePreviewWithResult_shouldResumePreview {
   FLTCam *camera = [[FLTCam alloc] init];
-  MockFLTThreadSafeFlutterResult *resultObject = [[MockFLTThreadSafeFlutterResult alloc] init];
 
-  [camera resumePreviewWithResult:resultObject];
+  [camera resumePreviewWithResult:^(id _Nullable result){
+  }];
   XCTAssertFalse(camera.isPreviewPaused);
 }
 
diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSettingsTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSettingsTests.m
index ecf2b17..3177fe4 100644
--- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSettingsTests.m
+++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSettingsTests.m
@@ -8,7 +8,6 @@
 @import AVFoundation;
 #import <OCMock/OCMock.h>
 #import "CameraTestUtils.h"
-#import "MockFLTThreadSafeFlutterResult.h"
 
 static const char *gTestResolutionPreset = "medium";
 static const int gTestFramesPerSecond = 15;
@@ -19,44 +18,33 @@
 @interface CameraCreateWithMediaSettingsParseTests : XCTestCase
 @end
 
-@interface MockErrorFlutterResult : MockFLTThreadSafeFlutterResult
-@property(nonatomic, nullable) NSError *receivedError;
-@end
-
-@implementation MockErrorFlutterResult
-
-- (void)sendError:(NSError *)error {
-  _receivedError = error;
-  [self.expectation fulfill];
-}
-
-@end
-
 /// Expect that optional positive numbers can be parsed
 @implementation CameraCreateWithMediaSettingsParseTests
 
-- (NSError *)failingTestWithArguments:(NSDictionary *)arguments {
+- (FlutterError *)failingTestWithArguments:(NSDictionary *)arguments {
   CameraPlugin *camera = [[CameraPlugin alloc] initWithRegistry:nil messenger:nil];
 
   XCTestExpectation *expectation = [self expectationWithDescription:@"Result finished"];
 
-  MockErrorFlutterResult *resultObject =
-      [[MockErrorFlutterResult alloc] initWithExpectation:expectation];
-
   // Set up method call
   FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"create"
                                                               arguments:arguments];
 
-  [camera createCameraOnSessionQueueWithCreateMethodCall:call result:resultObject];
+  __block id resultValue;
+  [camera createCameraOnSessionQueueWithCreateMethodCall:call
+                                                  result:^(id _Nullable result) {
+                                                    resultValue = result;
+                                                    [expectation fulfill];
+                                                  }];
   [self waitForExpectationsWithTimeout:1 handler:nil];
 
   // Verify the result
-  NSError *receivedError = resultObject.receivedError;
-  XCTAssertNotNil(receivedError);
-  return receivedError;
+  XCTAssertNotNil(resultValue);
+  XCTAssertTrue([resultValue isKindOfClass:[FlutterError class]]);
+  return (FlutterError *)resultValue;
 }
 
-- (NSError *)goodTestWithArguments:(NSDictionary *)arguments {
+- (void)goodTestWithArguments:(NSDictionary *)arguments {
   CameraPlugin *camera = [[CameraPlugin alloc] initWithRegistry:nil messenger:nil];
 
   XCTestExpectation *expectation = [self expectationWithDescription:@"Result finished"];
@@ -70,93 +58,82 @@
   OCMStub([avCaptureSessionMock alloc]).andReturn(avCaptureSessionMock);
   OCMStub([avCaptureSessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES);
 
-  MockErrorFlutterResult *resultObject =
-      [[MockErrorFlutterResult alloc] initWithExpectation:expectation];
-
   // Set up method call
   FlutterMethodCall *call = [FlutterMethodCall
       methodCallWithMethodName:@"create"
                      arguments:@{@"resolutionPreset" : @"medium", @"enableAudio" : @(1)}];
 
-  [camera createCameraOnSessionQueueWithCreateMethodCall:call result:resultObject];
+  __block id resultValue;
+  [camera createCameraOnSessionQueueWithCreateMethodCall:call
+                                                  result:^(id _Nullable result) {
+                                                    resultValue = result;
+                                                    [expectation fulfill];
+                                                  }];
   [self waitForExpectationsWithTimeout:1 handler:nil];
 
   // Verify the result
-  NSDictionary *dictionaryResult = (NSDictionary *)resultObject.receivedResult;
-  XCTAssertNotNil(dictionaryResult);
+  XCTAssertNotNil(resultValue);
+  XCTAssertFalse([resultValue isKindOfClass:[FlutterError class]]);
+  NSDictionary *dictionaryResult = (NSDictionary *)resultValue;
   XCTAssert([[dictionaryResult allKeys] containsObject:@"cameraId"]);
-
-  return resultObject.receivedError;
 }
 
 - (void)testCameraCreateWithMediaSettings_shouldRejectNegativeIntNumbers {
-  id errorOrNil =
+  FlutterError *error =
       [self failingTestWithArguments:@{@"fps" : @(-1), @"resolutionPreset" : @"medium"}];
-  XCTAssertEqualObjects([errorOrNil localizedDescription], @"fps should be a positive number",
+  XCTAssertEqualObjects(error.message, @"fps should be a positive number",
                         "should reject negative int number");
 }
 
 - (void)testCameraCreateWithMediaSettings_shouldRejectNegativeFloatingPointNumbers {
-  id errorOrNil =
+  FlutterError *error =
       [self failingTestWithArguments:@{@"fps" : @(-3.7), @"resolutionPreset" : @"medium"}];
-  XCTAssertEqualObjects([errorOrNil localizedDescription], @"fps should be a positive number",
+  XCTAssertEqualObjects(error.message, @"fps should be a positive number",
                         "should reject negative floating point number");
 }
 
 - (void)testCameraCreateWithMediaSettings_nanShouldBeParsedAsNil {
-  id errorOrNil =
+  FlutterError *error =
       [self failingTestWithArguments:@{@"fps" : @(NAN), @"resolutionPreset" : @"medium"}];
-  XCTAssertEqualObjects([errorOrNil localizedDescription], @"fps should not be a nan",
-                        "should reject NAN");
+  XCTAssertEqualObjects(error.message, @"fps should not be a nan", "should reject NAN");
 }
 
 - (void)testCameraCreateWithMediaSettings_shouldNotRejectNilArguments {
-  id errorOrNil = [self goodTestWithArguments:@{@"resolutionPreset" : @"medium"}];
-  XCTAssertNil(errorOrNil, "should accept nil");
+  [self goodTestWithArguments:@{@"resolutionPreset" : @"medium"}];
 }
 
 - (void)testCameraCreateWithMediaSettings_shouldAcceptNull {
-  id errorOrNil =
-      [self goodTestWithArguments:@{@"fps" : [NSNull null], @"resolutionPreset" : @"medium"}];
-  XCTAssertNil(errorOrNil, "should accept [NSNull null]");
+  [self goodTestWithArguments:@{@"fps" : [NSNull null], @"resolutionPreset" : @"medium"}];
 }
 
 - (void)testCameraCreateWithMediaSettings_shouldAcceptPositiveDecimalNumbers {
-  id errorOrNil = [self goodTestWithArguments:@{@"fps" : @(5), @"resolutionPreset" : @"medium"}];
-  XCTAssertNil(errorOrNil, "should parse positive int number");
+  [self goodTestWithArguments:@{@"fps" : @(5), @"resolutionPreset" : @"medium"}];
 }
 
 - (void)testCameraCreateWithMediaSettings_shouldAcceptPositiveFloatingPointNumbers {
-  id errorOrNil = [self goodTestWithArguments:@{@"fps" : @(3.7), @"resolutionPreset" : @"medium"}];
-  XCTAssertNil(errorOrNil, "should accept positive floating point number");
+  [self goodTestWithArguments:@{@"fps" : @(3.7), @"resolutionPreset" : @"medium"}];
 }
 
 - (void)testCameraCreateWithMediaSettings_shouldRejectWrongVideoBitrate {
-  id errorOrNil =
+  FlutterError *error =
       [self failingTestWithArguments:@{@"videoBitrate" : @(-1), @"resolutionPreset" : @"medium"}];
-  XCTAssertEqualObjects([errorOrNil localizedDescription],
-                        @"videoBitrate should be a positive number",
+  XCTAssertEqualObjects(error.message, @"videoBitrate should be a positive number",
                         "should reject wrong video bitrate");
 }
 
 - (void)testCameraCreateWithMediaSettings_shouldRejectWrongAudioBitrate {
-  id errorOrNil =
+  FlutterError *error =
       [self failingTestWithArguments:@{@"audioBitrate" : @(-1), @"resolutionPreset" : @"medium"}];
-  XCTAssertEqualObjects([errorOrNil localizedDescription],
-                        @"audioBitrate should be a positive number",
+  XCTAssertEqualObjects(error.message, @"audioBitrate should be a positive number",
                         "should reject wrong audio bitrate");
 }
 
 - (void)testCameraCreateWithMediaSettings_shouldAcceptGoodVideoBitrate {
-  id errorOrNil =
-      [self goodTestWithArguments:@{@"videoBitrate" : @(200000), @"resolutionPreset" : @"medium"}];
-  XCTAssertNil(errorOrNil, "should accept good video bitrate");
+  [self goodTestWithArguments:@{@"videoBitrate" : @(200000), @"resolutionPreset" : @"medium"}];
 }
 
 - (void)testCameraCreateWithMediaSettings_shouldAcceptGoodAudioBitrate {
-  id errorOrNil =
-      [self goodTestWithArguments:@{@"audioBitrate" : @(32000), @"resolutionPreset" : @"medium"}];
-  XCTAssertNil(errorOrNil, "should accept good audio bitrate");
+  [self goodTestWithArguments:@{@"audioBitrate" : @(32000), @"resolutionPreset" : @"medium"}];
 }
 
 @end
@@ -298,11 +275,9 @@
                     timeout:1
                enforceOrder:YES];
 
-  FLTThreadSafeFlutterResult *result =
-      [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id result){
-      }];
+  [camera startVideoRecordingWithResult:^(id _Nullable result){
 
-  [camera startVideoRecordingWithResult:result];
+  }];
 
   [self waitForExpectations:@[
     injectedWrapper.audioSettingsExpectation, injectedWrapper.videoSettingsExpectation
@@ -324,9 +299,6 @@
   OCMStub([avCaptureSessionMock alloc]).andReturn(avCaptureSessionMock);
   OCMStub([avCaptureSessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES);
 
-  MockFLTThreadSafeFlutterResult *resultObject =
-      [[MockFLTThreadSafeFlutterResult alloc] initWithExpectation:expectation];
-
   // Set up method call
   FlutterMethodCall *call =
       [FlutterMethodCall methodCallWithMethodName:@"create"
@@ -338,11 +310,16 @@
                                           @"audioBitrate" : @(gTestAudioBitrate)
                                         }];
 
-  [camera createCameraOnSessionQueueWithCreateMethodCall:call result:resultObject];
+  __block id resultValue;
+  [camera createCameraOnSessionQueueWithCreateMethodCall:call
+                                                  result:^(id _Nullable result) {
+                                                    resultValue = result;
+                                                    [expectation fulfill];
+                                                  }];
   [self waitForExpectationsWithTimeout:1 handler:nil];
 
   // Verify the result
-  NSDictionary *dictionaryResult = (NSDictionary *)resultObject.receivedResult;
+  NSDictionary *dictionaryResult = (NSDictionary *)resultValue;
   XCTAssertNotNil(dictionaryResult);
   XCTAssert([[dictionaryResult allKeys] containsObject:@"cameraId"]);
 }
diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h
index 1982be7..57c47ac 100644
--- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h
+++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 @import camera_avfoundation;
+@import camera_avfoundation.Test;
 
 NS_ASSUME_NONNULL_BEGIN
 
diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m
index f7204f2..00c583d 100644
--- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m
+++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m
@@ -30,10 +30,6 @@
   OCMStub([mockSettings photoSettings]).andReturn(settings);
 
   NSError *error = [NSError errorWithDomain:@"test" code:0 userInfo:nil];
-  id mockResult = OCMClassMock([FLTThreadSafeFlutterResult class]);
-  OCMStub([mockResult sendError:error]).andDo(^(NSInvocation *invocation) {
-    [errorExpectation fulfill];
-  });
 
   id mockOutput = OCMClassMock([AVCapturePhotoOutput class]);
   OCMStub([mockOutput capturePhotoWithSettings:OCMOCK_ANY delegate:OCMOCK_ANY])
@@ -49,7 +45,10 @@
 
   // `FLTCam::captureToFile` runs on capture session queue.
   dispatch_async(captureSessionQueue, ^{
-    [cam captureToFile:mockResult];
+    [cam captureToFile:^(id _Nullable result) {
+      XCTAssertTrue([result isKindOfClass:[FlutterError class]]);
+      [errorExpectation fulfill];
+    }];
   });
 
   [self waitForExpectationsWithTimeout:1 handler:nil];
@@ -70,10 +69,6 @@
   OCMStub([mockSettings photoSettings]).andReturn(settings);
 
   NSString *filePath = @"test";
-  id mockResult = OCMClassMock([FLTThreadSafeFlutterResult class]);
-  OCMStub([mockResult sendSuccessWithData:filePath]).andDo(^(NSInvocation *invocation) {
-    [pathExpectation fulfill];
-  });
 
   id mockOutput = OCMClassMock([AVCapturePhotoOutput class]);
   OCMStub([mockOutput capturePhotoWithSettings:OCMOCK_ANY delegate:OCMOCK_ANY])
@@ -89,7 +84,10 @@
 
   // `FLTCam::captureToFile` runs on capture session queue.
   dispatch_async(captureSessionQueue, ^{
-    [cam captureToFile:mockResult];
+    [cam captureToFile:^(id _Nullable result) {
+      XCTAssertEqual(result, filePath);
+      [pathExpectation fulfill];
+    }];
   });
   [self waitForExpectationsWithTimeout:1 handler:nil];
 }
@@ -110,14 +108,6 @@
   id mockSettings = OCMClassMock([AVCapturePhotoSettings class]);
   OCMStub([mockSettings photoSettingsWithFormat:OCMOCK_ANY]).andReturn(settings);
 
-  id mockResult = OCMClassMock([FLTThreadSafeFlutterResult class]);
-  OCMStub([mockResult sendSuccessWithData:OCMOCK_ANY]).andDo(^(NSInvocation *invocation) {
-    NSString *filePath;
-    [invocation getArgument:&filePath atIndex:2];
-    XCTAssertEqualObjects([filePath pathExtension], @"heif");
-    [expectation fulfill];
-  });
-
   id mockOutput = OCMClassMock([AVCapturePhotoOutput class]);
   // Set availablePhotoCodecTypes to HEVC
   NSArray *codecTypes = @[ AVVideoCodecTypeHEVC ];
@@ -135,7 +125,11 @@
   cam.capturePhotoOutput = mockOutput;
   // `FLTCam::captureToFile` runs on capture session queue.
   dispatch_async(captureSessionQueue, ^{
-    [cam captureToFile:mockResult];
+    [cam captureToFile:^(id _Nullable result) {
+      NSString *filePath = (NSString *)result;
+      XCTAssertEqualObjects([filePath pathExtension], @"heif");
+      [expectation fulfill];
+    }];
   });
   [self waitForExpectationsWithTimeout:1 handler:nil];
 }
@@ -154,14 +148,6 @@
   id mockSettings = OCMClassMock([AVCapturePhotoSettings class]);
   OCMStub([mockSettings photoSettings]).andReturn(settings);
 
-  id mockResult = OCMClassMock([FLTThreadSafeFlutterResult class]);
-  OCMStub([mockResult sendSuccessWithData:OCMOCK_ANY]).andDo(^(NSInvocation *invocation) {
-    NSString *filePath;
-    [invocation getArgument:&filePath atIndex:2];
-    XCTAssertEqualObjects([filePath pathExtension], @"jpg");
-    [expectation fulfill];
-  });
-
   id mockOutput = OCMClassMock([AVCapturePhotoOutput class]);
 
   OCMStub([mockOutput capturePhotoWithSettings:OCMOCK_ANY delegate:OCMOCK_ANY])
@@ -176,7 +162,11 @@
   cam.capturePhotoOutput = mockOutput;
   // `FLTCam::captureToFile` runs on capture session queue.
   dispatch_async(captureSessionQueue, ^{
-    [cam captureToFile:mockResult];
+    [cam captureToFile:^(id _Nullable result) {
+      NSString *filePath = (NSString *)result;
+      XCTAssertEqualObjects([filePath pathExtension], @"jpg");
+      [expectation fulfill];
+    }];
   });
   [self waitForExpectationsWithTimeout:1 handler:nil];
 }
diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m
index 24e3544..9c036ad 100644
--- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m
+++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m
@@ -54,15 +54,13 @@
     [invocation setReturnValue:&status];
   });
 
-  FLTThreadSafeFlutterResult *result =
-      [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id result){
-          // no-op
-      }];
-
   // Pause then resume the recording.
-  [cam startVideoRecordingWithResult:result];
-  [cam pauseVideoRecordingWithResult:result];
-  [cam resumeVideoRecordingWithResult:result];
+  [cam startVideoRecordingWithResult:^(id _Nullable result){
+  }];
+  [cam pauseVideoRecordingWithResult:^(id _Nullable result){
+  }];
+  [cam resumeVideoRecordingWithResult:^(id _Nullable result){
+  }];
 
   [cam captureOutput:cam.captureVideoOutput
       didOutputSampleBuffer:sampleBuffer
@@ -113,10 +111,8 @@
     writtenSamples = [writtenSamples arrayByAddingObject:@"audio"];
   });
 
-  FLTThreadSafeFlutterResult *result =
-      [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id result){
-      }];
-  [cam startVideoRecordingWithResult:result];
+  [cam startVideoRecordingWithResult:^(id _Nullable result){
+  }];
 
   [cam captureOutput:nil didOutputSampleBuffer:audioSample fromConnection:connectionMock];
   [cam captureOutput:nil didOutputSampleBuffer:audioSample fromConnection:connectionMock];
diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.h b/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.h
deleted file mode 100644
index 8685f3f..0000000
--- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// 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.
-
-#ifndef MockFLTThreadSafeFlutterResult_h
-#define MockFLTThreadSafeFlutterResult_h
-
-/**
- * Extends FLTThreadSafeFlutterResult to give tests the ability to wait on the result and
- * read the received result.
- */
-@interface MockFLTThreadSafeFlutterResult : FLTThreadSafeFlutterResult
-@property(readonly, nonatomic, nonnull) XCTestExpectation *expectation;
-@property(nonatomic, nullable) id receivedResult;
-
-/**
- * Initializes the MockFLTThreadSafeFlutterResult with an expectation.
- *
- * The expectation is fullfilled when a result is called allowing tests to await the result in an
- * asynchronous manner.
- */
-- (nonnull instancetype)initWithExpectation:(nonnull XCTestExpectation *)expectation;
-@end
-
-#endif /* MockFLTThreadSafeFlutterResult_h */
diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.m
deleted file mode 100644
index d3d7b6a..0000000
--- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.m
+++ /dev/null
@@ -1,27 +0,0 @@
-// 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_avfoundation;
-@import XCTest;
-
-#import "MockFLTThreadSafeFlutterResult.h"
-
-@implementation MockFLTThreadSafeFlutterResult
-
-- (instancetype)initWithExpectation:(XCTestExpectation *)expectation {
-  self = [super init];
-  _expectation = expectation;
-  return self;
-}
-
-- (void)sendSuccessWithData:(id)data {
-  self.receivedResult = data;
-  [self.expectation fulfill];
-}
-
-- (void)sendSuccess {
-  self.receivedResult = nil;
-  [self.expectation fulfill];
-}
-@end
diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.m
index a9fc739..128b47b 100644
--- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.m
+++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.m
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 @import camera_avfoundation;
+@import camera_avfoundation.Test;
 @import XCTest;
 
 @interface QueueUtilsTests : XCTestCase
diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeEventChannelTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeEventChannelTests.m
index 2aad7e3..e445697 100644
--- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeEventChannelTests.m
+++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeEventChannelTests.m
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 @import camera_avfoundation;
+@import camera_avfoundation.Test;
 @import XCTest;
 #import <OCMock/OCMock.h>
 
diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m
deleted file mode 100644
index b8de19c..0000000
--- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m
+++ /dev/null
@@ -1,116 +0,0 @@
-// 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_avfoundation;
-@import XCTest;
-
-@interface ThreadSafeFlutterResultTests : XCTestCase
-@end
-
-@implementation ThreadSafeFlutterResultTests
-- (void)testAsyncSendSuccess_ShouldCallResultOnMainThread {
-  XCTestExpectation *expectation = [self expectationWithDescription:@"Result finished"];
-
-  FLTThreadSafeFlutterResult *threadSafeFlutterResult =
-      [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id _Nullable result) {
-        XCTAssert(NSThread.isMainThread);
-        [expectation fulfill];
-      }];
-  dispatch_queue_t dispatchQueue = dispatch_queue_create("test dispatchqueue", NULL);
-  dispatch_async(dispatchQueue, ^{
-    [threadSafeFlutterResult sendSuccess];
-  });
-
-  [self waitForExpectationsWithTimeout:1 handler:nil];
-}
-
-- (void)testSyncSendSuccess_ShouldCallResultOnMainThread {
-  XCTestExpectation *expectation = [self expectationWithDescription:@"Result finished"];
-
-  FLTThreadSafeFlutterResult *threadSafeFlutterResult =
-      [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id _Nullable result) {
-        XCTAssert(NSThread.isMainThread);
-        [expectation fulfill];
-      }];
-  [threadSafeFlutterResult sendSuccess];
-  [self waitForExpectationsWithTimeout:1 handler:nil];
-}
-
-- (void)testSendNotImplemented_ShouldSendNotImplementedToFlutterResult {
-  XCTestExpectation *expectation = [self expectationWithDescription:@"Result finished"];
-
-  FLTThreadSafeFlutterResult *threadSafeFlutterResult =
-      [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id _Nullable result) {
-        XCTAssert([result isKindOfClass:FlutterMethodNotImplemented.class]);
-        [expectation fulfill];
-      }];
-  dispatch_queue_t dispatchQueue = dispatch_queue_create("test dispatchqueue", NULL);
-  dispatch_async(dispatchQueue, ^{
-    [threadSafeFlutterResult sendNotImplemented];
-  });
-
-  [self waitForExpectationsWithTimeout:1 handler:nil];
-}
-
-- (void)testSendErrorDetails_ShouldSendErrorToFlutterResult {
-  NSString *errorCode = @"errorCode";
-  NSString *errorMessage = @"message";
-  NSString *errorDetails = @"error details";
-  XCTestExpectation *expectation = [self expectationWithDescription:@"Result finished"];
-
-  FLTThreadSafeFlutterResult *threadSafeFlutterResult =
-      [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id _Nullable result) {
-        XCTAssert([result isKindOfClass:FlutterError.class]);
-        FlutterError *error = (FlutterError *)result;
-        XCTAssertEqualObjects(error.code, errorCode);
-        XCTAssertEqualObjects(error.message, errorMessage);
-        XCTAssertEqualObjects(error.details, errorDetails);
-        [expectation fulfill];
-      }];
-  dispatch_queue_t dispatchQueue = dispatch_queue_create("test dispatchqueue", NULL);
-  dispatch_async(dispatchQueue, ^{
-    [threadSafeFlutterResult sendErrorWithCode:errorCode message:errorMessage details:errorDetails];
-  });
-
-  [self waitForExpectationsWithTimeout:1 handler:nil];
-}
-
-- (void)testSendNSError_ShouldSendErrorToFlutterResult {
-  NSError *originalError = [[NSError alloc] initWithDomain:NSURLErrorDomain code:404 userInfo:nil];
-  XCTestExpectation *expectation = [self expectationWithDescription:@"Result finished"];
-
-  FLTThreadSafeFlutterResult *threadSafeFlutterResult =
-      [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id _Nullable result) {
-        XCTAssert([result isKindOfClass:FlutterError.class]);
-        FlutterError *error = (FlutterError *)result;
-        NSString *constructedErrorCode =
-            [NSString stringWithFormat:@"Error %d", (int)originalError.code];
-        XCTAssertEqualObjects(error.code, constructedErrorCode);
-        [expectation fulfill];
-      }];
-  dispatch_queue_t dispatchQueue = dispatch_queue_create("test dispatchqueue", NULL);
-  dispatch_async(dispatchQueue, ^{
-    [threadSafeFlutterResult sendError:originalError];
-  });
-
-  [self waitForExpectationsWithTimeout:1 handler:nil];
-}
-
-- (void)testSendResult_ShouldSendResultToFlutterResult {
-  NSString *resultData = @"resultData";
-  XCTestExpectation *expectation = [self expectationWithDescription:@"Result finished"];
-
-  FLTThreadSafeFlutterResult *threadSafeFlutterResult =
-      [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id _Nullable result) {
-        XCTAssertEqualObjects(result, resultData);
-        [expectation fulfill];
-      }];
-  dispatch_queue_t dispatchQueue = dispatch_queue_create("test dispatchqueue", NULL);
-  dispatch_async(dispatchQueue, ^{
-    [threadSafeFlutterResult sendSuccessWithData:resultData];
-  });
-
-  [self waitForExpectationsWithTimeout:1 handler:nil];
-}
-@end
diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m
index ce1b641..36e87db 100644
--- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m
+++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 @import camera_avfoundation;
+@import camera_avfoundation.Test;
 @import XCTest;
 #import <OCMock/OCMock.h>
 
diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m
index 31f196f..f91896b 100644
--- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m
+++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 @import camera_avfoundation;
+@import camera_avfoundation.Test;
 @import XCTest;
 #import <OCMock/OCMock.h>
 
diff --git a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m
index 243d727..ba29dbe 100644
--- a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m
+++ b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m
@@ -11,11 +11,16 @@
 #import "CameraProperties.h"
 #import "FLTCam.h"
 #import "FLTThreadSafeEventChannel.h"
-#import "FLTThreadSafeFlutterResult.h"
 #import "FLTThreadSafeMethodChannel.h"
 #import "FLTThreadSafeTextureRegistry.h"
 #import "QueueUtils.h"
 
+static FlutterError *FlutterErrorFromNSError(NSError *error) {
+  return [FlutterError errorWithCode:[NSString stringWithFormat:@"Error %d", (int)error.code]
+                             message:error.localizedDescription
+                             details:error.domain];
+}
+
 @interface CameraPlugin ()
 @property(readonly, nonatomic) FLTThreadSafeTextureRegistry *registry;
 @property(readonly, nonatomic) NSObject<FlutterBinaryMessenger> *messenger;
@@ -95,14 +100,11 @@
   // Invoke the plugin on another dispatch queue to avoid blocking the UI.
   __weak typeof(self) weakSelf = self;
   dispatch_async(self.captureSessionQueue, ^{
-    FLTThreadSafeFlutterResult *threadSafeResult =
-        [[FLTThreadSafeFlutterResult alloc] initWithResult:result];
-    [weakSelf handleMethodCallAsync:call result:threadSafeResult];
+    [weakSelf handleMethodCallAsync:call result:result];
   });
 }
 
-- (void)handleMethodCallAsync:(FlutterMethodCall *)call
-                       result:(FLTThreadSafeFlutterResult *)result {
+- (void)handleMethodCallAsync:(FlutterMethodCall *)call result:(FlutterResult)result {
   if ([@"availableCameras" isEqualToString:call.method]) {
     NSMutableArray *discoveryDevices =
         [@[ AVCaptureDeviceTypeBuiltInWideAngleCamera, AVCaptureDeviceTypeBuiltInTelephotoCamera ]
@@ -136,18 +138,18 @@
         @"sensorOrientation" : @90,
       }];
     }
-    [result sendSuccessWithData:reply];
+    result(reply);
   } else if ([@"create" isEqualToString:call.method]) {
     [self handleCreateMethodCall:call result:result];
   } else if ([@"startImageStream" isEqualToString:call.method]) {
     [_camera startImageStreamWithMessenger:_messenger];
-    [result sendSuccess];
+    result(nil);
   } else if ([@"stopImageStream" isEqualToString:call.method]) {
     [_camera stopImageStream];
-    [result sendSuccess];
+    result(nil);
   } else if ([@"receivedImageStreamData" isEqualToString:call.method]) {
     [_camera receivedImageStreamData];
-    [result sendSuccess];
+    result(nil);
   } else {
     NSDictionary *argsMap = call.arguments;
     NSUInteger cameraId = ((NSNumber *)argsMap[@"cameraId"]).unsignedIntegerValue;
@@ -183,16 +185,16 @@
              }];
       [self sendDeviceOrientation:[UIDevice currentDevice].orientation];
       [_camera start];
-      [result sendSuccess];
+      result(nil);
     } else if ([@"takePicture" isEqualToString:call.method]) {
       [_camera captureToFile:result];
     } else if ([@"dispose" isEqualToString:call.method]) {
       [_registry unregisterTexture:cameraId];
       [_camera close];
-      [result sendSuccess];
+      result(nil);
     } else if ([@"prepareForVideoRecording" isEqualToString:call.method]) {
       [self.camera setUpCaptureSessionForAudio];
-      [result sendSuccess];
+      result(nil);
     } else if ([@"startVideoRecording" isEqualToString:call.method]) {
       BOOL enableStream = [call.arguments[@"enableStream"] boolValue];
       if (enableStream) {
@@ -227,11 +229,11 @@
       }
       [_camera setExposurePointWithResult:result x:x y:y];
     } else if ([@"getMinExposureOffset" isEqualToString:call.method]) {
-      [result sendSuccessWithData:@(_camera.captureDevice.minExposureTargetBias)];
+      result(@(_camera.captureDevice.minExposureTargetBias));
     } else if ([@"getMaxExposureOffset" isEqualToString:call.method]) {
-      [result sendSuccessWithData:@(_camera.captureDevice.maxExposureTargetBias)];
+      result(@(_camera.captureDevice.maxExposureTargetBias));
     } else if ([@"getExposureOffsetStepSize" isEqualToString:call.method]) {
-      [result sendSuccessWithData:@(0.0)];
+      result(@(0.0));
     } else if ([@"setExposureOffset" isEqualToString:call.method]) {
       [_camera setExposureOffsetWithResult:result
                                     offset:((NSNumber *)call.arguments[@"offset"]).doubleValue];
@@ -260,13 +262,12 @@
       NSString *fileFormat = call.arguments[@"fileFormat"];
       [_camera setImageFileFormat:FCPGetFileFormatFromString(fileFormat)];
     } else {
-      [result sendNotImplemented];
+      result(FlutterMethodNotImplemented);
     }
   }
 }
 
-- (void)handleCreateMethodCall:(FlutterMethodCall *)call
-                        result:(FLTThreadSafeFlutterResult *)result {
+- (void)handleCreateMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
   // Create FLTCam only if granted camera access (and audio access if audio is enabled)
   __weak typeof(self) weakSelf = self;
   FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) {
@@ -274,7 +275,7 @@
     if (!strongSelf) return;
 
     if (error) {
-      [result sendFlutterError:error];
+      result(error);
     } else {
       // Request audio permission on `create` call with `enableAudio` argument instead of the
       // `prepareForVideoRecording` call. This is because `prepareForVideoRecording` call is
@@ -287,7 +288,7 @@
           typeof(self) strongSelf = weakSelf;
           if (!strongSelf) return;
           if (error) {
-            [result sendFlutterError:error];
+            result(error);
           } else {
             [strongSelf createCameraOnSessionQueueWithCreateMethodCall:call result:result];
           }
@@ -353,7 +354,7 @@
 }
 
 - (void)createCameraOnSessionQueueWithCreateMethodCall:(FlutterMethodCall *)createMethodCall
-                                                result:(FLTThreadSafeFlutterResult *)result {
+                                                result:(FlutterResult)result {
   __weak typeof(self) weakSelf = self;
   dispatch_async(self.captureSessionQueue, ^{
     typeof(self) strongSelf = weakSelf;
@@ -367,7 +368,7 @@
                                                                        fromMethod:createMethodCall
                                                                             error:&error];
     if (error) {
-      [result sendError:error];
+      result(FlutterErrorFromNSError(error));
       return;
     }
 
@@ -375,7 +376,7 @@
                                                                     fromMethod:createMethodCall
                                                                          error:&error];
     if (error) {
-      [result sendError:error];
+      result(FlutterErrorFromNSError(error));
       return;
     }
 
@@ -383,7 +384,7 @@
                                                                     fromMethod:createMethodCall
                                                                          error:&error];
     if (error) {
-      [result sendError:error];
+      result(FlutterErrorFromNSError(error));
       return;
     }
 
@@ -406,7 +407,7 @@
                                                error:&error];
 
     if (error) {
-      [result sendError:error];
+      result(FlutterErrorFromNSError(error));
     } else {
       if (strongSelf.camera) {
         [strongSelf.camera close];
@@ -414,9 +415,9 @@
       strongSelf.camera = cam;
       [strongSelf.registry registerTexture:cam
                                 completion:^(int64_t textureId) {
-                                  [result sendSuccessWithData:@{
+                                  result(@{
                                     @"cameraId" : @(textureId),
-                                  }];
+                                  });
                                 }];
     }
   });
diff --git a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.modulemap b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.modulemap
index abdad1a..8de3cde 100644
--- a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.modulemap
+++ b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.modulemap
@@ -12,7 +12,6 @@
     header "FLTCam_Test.h"
     header "FLTSavePhotoDelegate_Test.h"
     header "FLTThreadSafeEventChannel.h"
-    header "FLTThreadSafeFlutterResult.h"
     header "FLTThreadSafeMethodChannel.h"
     header "FLTThreadSafeTextureRegistry.h"
     header "QueueUtils.h"
diff --git a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin_Test.h b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin_Test.h
index 230648f..97ccd48 100644
--- a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin_Test.h
+++ b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin_Test.h
@@ -6,7 +6,6 @@
 
 #import "CameraPlugin.h"
 #import "FLTCam.h"
-#import "FLTThreadSafeFlutterResult.h"
 
 /// APIs exposed for unit testing.
 @interface CameraPlugin ()
@@ -34,7 +33,7 @@
 /// @param call The method call command object.
 /// @param result A wrapper around the `FlutterResult` callback which ensures the callback is called
 /// on the main dispatch queue.
-- (void)handleMethodCallAsync:(FlutterMethodCall *)call result:(FLTThreadSafeFlutterResult *)result;
+- (void)handleMethodCallAsync:(FlutterMethodCall *)call result:(FlutterResult)result;
 
 /// Called by the @c NSNotificationManager each time the device's orientation is changed.
 ///
@@ -46,5 +45,5 @@
 /// @param createMethodCall the create method call
 /// @param result a thread safe flutter result wrapper object to report creation result.
 - (void)createCameraOnSessionQueueWithCreateMethodCall:(FlutterMethodCall *)createMethodCall
-                                                result:(FLTThreadSafeFlutterResult *)result;
+                                                result:(FlutterResult)result;
 @end
diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h
index e1216fd..281ff14 100644
--- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h
+++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h
@@ -10,7 +10,6 @@
 #import "FLTCamMediaSettings.h"
 #import "FLTCamMediaSettingsAVWrapper.h"
 #import "FLTThreadSafeEventChannel.h"
-#import "FLTThreadSafeFlutterResult.h"
 #import "FLTThreadSafeMethodChannel.h"
 #import "FLTThreadSafeTextureRegistry.h"
 
@@ -52,26 +51,26 @@
 - (void)start;
 - (void)stop;
 - (void)setDeviceOrientation:(UIDeviceOrientation)orientation;
-- (void)captureToFile:(FLTThreadSafeFlutterResult *)result;
+- (void)captureToFile:(FlutterResult)result;
 - (void)close;
-- (void)startVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result;
+- (void)startVideoRecordingWithResult:(FlutterResult)result;
 - (void)setImageFileFormat:(FCPFileFormat)fileFormat;
 /// Starts recording a video with an optional streaming messenger.
 /// If the messenger is non-null then it will be called for each
 /// captured frame, allowing streaming concurrently with recording.
 ///
 /// @param messenger Nullable messenger for capturing each frame.
-- (void)startVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result
+- (void)startVideoRecordingWithResult:(FlutterResult)result
                 messengerForStreaming:(nullable NSObject<FlutterBinaryMessenger> *)messenger;
-- (void)stopVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result;
-- (void)pauseVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result;
-- (void)resumeVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result;
-- (void)lockCaptureOrientationWithResult:(FLTThreadSafeFlutterResult *)result
+- (void)stopVideoRecordingWithResult:(FlutterResult)result;
+- (void)pauseVideoRecordingWithResult:(FlutterResult)result;
+- (void)resumeVideoRecordingWithResult:(FlutterResult)result;
+- (void)lockCaptureOrientationWithResult:(FlutterResult)result
                              orientation:(NSString *)orientationStr;
-- (void)unlockCaptureOrientationWithResult:(FLTThreadSafeFlutterResult *)result;
-- (void)setFlashModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr;
-- (void)setExposureModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr;
-- (void)setFocusModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr;
+- (void)unlockCaptureOrientationWithResult:(FlutterResult)result;
+- (void)setFlashModeWithResult:(FlutterResult)result mode:(NSString *)modeStr;
+- (void)setExposureModeWithResult:(FlutterResult)result mode:(NSString *)modeStr;
+- (void)setFocusModeWithResult:(FlutterResult)result mode:(NSString *)modeStr;
 - (void)applyFocusMode;
 
 /// Acknowledges the receipt of one image stream frame.
@@ -93,18 +92,17 @@
 /// @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:(FLTFocusMode)focusMode onDevice:(AVCaptureDevice *)captureDevice;
-- (void)pausePreviewWithResult:(FLTThreadSafeFlutterResult *)result;
-- (void)resumePreviewWithResult:(FLTThreadSafeFlutterResult *)result;
-- (void)setDescriptionWhileRecording:(NSString *)cameraName
-                              result:(FLTThreadSafeFlutterResult *)result;
-- (void)setExposurePointWithResult:(FLTThreadSafeFlutterResult *)result x:(double)x y:(double)y;
-- (void)setFocusPointWithResult:(FLTThreadSafeFlutterResult *)result x:(double)x y:(double)y;
-- (void)setExposureOffsetWithResult:(FLTThreadSafeFlutterResult *)result offset:(double)offset;
+- (void)pausePreviewWithResult:(FlutterResult)result;
+- (void)resumePreviewWithResult:(FlutterResult)result;
+- (void)setDescriptionWhileRecording:(NSString *)cameraName result:(FlutterResult)result;
+- (void)setExposurePointWithResult:(FlutterResult)result x:(double)x y:(double)y;
+- (void)setFocusPointWithResult:(FlutterResult)result x:(double)x y:(double)y;
+- (void)setExposureOffsetWithResult:(FlutterResult)result offset:(double)offset;
 - (void)startImageStreamWithMessenger:(NSObject<FlutterBinaryMessenger> *)messenger;
 - (void)stopImageStream;
-- (void)getMaxZoomLevelWithResult:(FLTThreadSafeFlutterResult *)result;
-- (void)getMinZoomLevelWithResult:(FLTThreadSafeFlutterResult *)result;
-- (void)setZoomLevel:(CGFloat)zoom Result:(FLTThreadSafeFlutterResult *)result;
+- (void)getMaxZoomLevelWithResult:(FlutterResult)result;
+- (void)getMinZoomLevelWithResult:(FlutterResult)result;
+- (void)setZoomLevel:(CGFloat)zoom Result:(FlutterResult)result;
 - (void)setUpCaptureSessionForAudio;
 
 @end
diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m
index b2f09e9..9544dbc 100644
--- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m
+++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m
@@ -10,6 +10,12 @@
 @import CoreMotion;
 #import <libkern/OSAtomic.h>
 
+static FlutterError *FlutterErrorFromNSError(NSError *error) {
+  return [FlutterError errorWithCode:[NSString stringWithFormat:@"Error %d", (int)error.code]
+                             message:error.localizedDescription
+                             details:error.domain];
+}
+
 @implementation FLTImageStreamHandler
 
 - (instancetype)initWithCaptureSessionQueue:(dispatch_queue_t)captureSessionQueue {
@@ -336,7 +342,7 @@
   }
 }
 
-- (void)captureToFile:(FLTThreadSafeFlutterResult *)result {
+- (void)captureToFile:(FlutterResult)result {
   AVCapturePhotoSettings *settings = [AVCapturePhotoSettings photoSettings];
 
   if (_resolutionPreset == FLTResolutionPresetMax) {
@@ -366,7 +372,7 @@
                                                     prefix:@"CAP_"
                                                      error:error];
   if (error) {
-    [result sendError:error];
+    result(FlutterErrorFromNSError(error));
     return;
   }
 
@@ -385,10 +391,10 @@
         });
 
         if (error) {
-          [result sendError:error];
+          result(FlutterErrorFromNSError(error));
         } else {
           NSAssert(path, @"Path must not be nil if no error.");
-          [result sendSuccessWithData:path];
+          result(path);
         }
       }];
 
@@ -793,11 +799,11 @@
   return pixelBuffer;
 }
 
-- (void)startVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result {
+- (void)startVideoRecordingWithResult:(FlutterResult)result {
   [self startVideoRecordingWithResult:result messengerForStreaming:nil];
 }
 
-- (void)startVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result
+- (void)startVideoRecordingWithResult:(FlutterResult)result
                 messengerForStreaming:(nullable NSObject<FlutterBinaryMessenger> *)messenger {
   if (!_isRecording) {
     if (messenger != nil) {
@@ -810,11 +816,11 @@
                                                            prefix:@"REC_"
                                                             error:error];
     if (error) {
-      [result sendError:error];
+      result(FlutterErrorFromNSError(error));
       return;
     }
     if (![self setupWriterForPath:_videoRecordingPath]) {
-      [result sendErrorWithCode:@"IOError" message:@"Setup Writer Failed" details:nil];
+      result([FlutterError errorWithCode:@"IOError" message:@"Setup Writer Failed" details:nil]);
       return;
     }
     _isRecording = YES;
@@ -823,13 +829,13 @@
     _audioTimeOffset = CMTimeMake(0, 1);
     _videoIsDisconnected = NO;
     _audioIsDisconnected = NO;
-    [result sendSuccess];
+    result(nil);
   } else {
-    [result sendErrorWithCode:@"Error" message:@"Video is already recording" details:nil];
+    result([FlutterError errorWithCode:@"Error" message:@"Video is already recording" details:nil]);
   }
 }
 
-- (void)stopVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result {
+- (void)stopVideoRecordingWithResult:(FlutterResult)result {
   if (_isRecording) {
     _isRecording = NO;
 
@@ -837,12 +843,12 @@
       [_videoWriter finishWritingWithCompletionHandler:^{
         if (self->_videoWriter.status == AVAssetWriterStatusCompleted) {
           [self updateOrientation];
-          [result sendSuccessWithData:self->_videoRecordingPath];
+          result(self->_videoRecordingPath);
           self->_videoRecordingPath = nil;
         } else {
-          [result sendErrorWithCode:@"IOError"
-                            message:@"AVAssetWriter could not finish writing!"
-                            details:nil];
+          result([FlutterError errorWithCode:@"IOError"
+                                     message:@"AVAssetWriter could not finish writing!"
+                                     details:nil]);
         }
       }];
     }
@@ -851,35 +857,35 @@
         [NSError errorWithDomain:NSCocoaErrorDomain
                             code:NSURLErrorResourceUnavailable
                         userInfo:@{NSLocalizedDescriptionKey : @"Video is not recording!"}];
-    [result sendError:error];
+    result(FlutterErrorFromNSError(error));
   }
 }
 
-- (void)pauseVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result {
+- (void)pauseVideoRecordingWithResult:(FlutterResult)result {
   _isRecordingPaused = YES;
   _videoIsDisconnected = YES;
   _audioIsDisconnected = YES;
-  [result sendSuccess];
+  result(nil);
 }
 
-- (void)resumeVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result {
+- (void)resumeVideoRecordingWithResult:(FlutterResult)result {
   _isRecordingPaused = NO;
-  [result sendSuccess];
+  result(nil);
 }
 
-- (void)lockCaptureOrientationWithResult:(FLTThreadSafeFlutterResult *)result
+- (void)lockCaptureOrientationWithResult:(FlutterResult)result
                              orientation:(NSString *)orientationStr {
   UIDeviceOrientation orientation = FLTGetUIDeviceOrientationForString(orientationStr);
   // "Unknown" should never be sent, so is used to represent an unexpected
   // value.
   if (orientation == UIDeviceOrientationUnknown) {
-    [result sendError:[NSError errorWithDomain:NSCocoaErrorDomain
-                                          code:NSURLErrorUnknown
-                                      userInfo:@{
-                                        NSLocalizedDescriptionKey : [NSString
-                                            stringWithFormat:@"Unknown device orientation %@",
-                                                             orientationStr]
-                                      }]];
+    result(FlutterErrorFromNSError([NSError
+        errorWithDomain:NSCocoaErrorDomain
+                   code:NSURLErrorUnknown
+               userInfo:@{
+                 NSLocalizedDescriptionKey :
+                     [NSString stringWithFormat:@"Unknown device orientation %@", orientationStr]
+               }]));
     return;
   }
 
@@ -888,37 +894,38 @@
     [self updateOrientation];
   }
 
-  [result sendSuccess];
+  result(nil);
 }
 
-- (void)unlockCaptureOrientationWithResult:(FLTThreadSafeFlutterResult *)result {
+- (void)unlockCaptureOrientationWithResult:(FlutterResult)result {
   _lockedCaptureOrientation = UIDeviceOrientationUnknown;
   [self updateOrientation];
-  [result sendSuccess];
+  result(nil);
 }
 
-- (void)setFlashModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr {
+- (void)setFlashModeWithResult:(FlutterResult)result mode:(NSString *)modeStr {
   FLTFlashMode mode = FLTGetFLTFlashModeForString(modeStr);
   if (mode == FLTFlashModeInvalid) {
-    [result sendError:[NSError errorWithDomain:NSCocoaErrorDomain
-                                          code:NSURLErrorUnknown
-                                      userInfo:@{
-                                        NSLocalizedDescriptionKey : [NSString
-                                            stringWithFormat:@"Unknown flash mode %@", modeStr]
-                                      }]];
+    result(FlutterErrorFromNSError([NSError
+        errorWithDomain:NSCocoaErrorDomain
+                   code:NSURLErrorUnknown
+               userInfo:@{
+                 NSLocalizedDescriptionKey :
+                     [NSString stringWithFormat:@"Unknown flash mode %@", modeStr]
+               }]));
     return;
   }
   if (mode == FLTFlashModeTorch) {
     if (!_captureDevice.hasTorch) {
-      [result sendErrorWithCode:@"setFlashModeFailed"
-                        message:@"Device does not support torch mode"
-                        details:nil];
+      result([FlutterError errorWithCode:@"setFlashModeFailed"
+                                 message:@"Device does not support torch mode"
+                                 details:nil]);
       return;
     }
     if (!_captureDevice.isTorchAvailable) {
-      [result sendErrorWithCode:@"setFlashModeFailed"
-                        message:@"Torch mode is currently not available"
-                        details:nil];
+      result([FlutterError errorWithCode:@"setFlashModeFailed"
+                                 message:@"Torch mode is currently not available"
+                                 details:nil]);
       return;
     }
     if (_captureDevice.torchMode != AVCaptureTorchModeOn) {
@@ -928,17 +935,17 @@
     }
   } else {
     if (!_captureDevice.hasFlash) {
-      [result sendErrorWithCode:@"setFlashModeFailed"
-                        message:@"Device does not have flash capabilities"
-                        details:nil];
+      result([FlutterError errorWithCode:@"setFlashModeFailed"
+                                 message:@"Device does not have flash capabilities"
+                                 details:nil]);
       return;
     }
     AVCaptureFlashMode avFlashMode = FLTGetAVCaptureFlashModeForFLTFlashMode(mode);
     if (![_capturePhotoOutput.supportedFlashModes
             containsObject:[NSNumber numberWithInt:((int)avFlashMode)]]) {
-      [result sendErrorWithCode:@"setFlashModeFailed"
-                        message:@"Device does not support this specific flash mode"
-                        details:nil];
+      result([FlutterError errorWithCode:@"setFlashModeFailed"
+                                 message:@"Device does not support this specific flash mode"
+                                 details:nil]);
       return;
     }
     if (_captureDevice.torchMode != AVCaptureTorchModeOff) {
@@ -948,23 +955,24 @@
     }
   }
   _flashMode = mode;
-  [result sendSuccess];
+  result(nil);
 }
 
-- (void)setExposureModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr {
+- (void)setExposureModeWithResult:(FlutterResult)result mode:(NSString *)modeStr {
   FLTExposureMode mode = FLTGetFLTExposureModeForString(modeStr);
   if (mode == FLTExposureModeInvalid) {
-    [result sendError:[NSError errorWithDomain:NSCocoaErrorDomain
-                                          code:NSURLErrorUnknown
-                                      userInfo:@{
-                                        NSLocalizedDescriptionKey : [NSString
-                                            stringWithFormat:@"Unknown exposure mode %@", modeStr]
-                                      }]];
+    result(FlutterErrorFromNSError([NSError
+        errorWithDomain:NSCocoaErrorDomain
+                   code:NSURLErrorUnknown
+               userInfo:@{
+                 NSLocalizedDescriptionKey :
+                     [NSString stringWithFormat:@"Unknown exposure mode %@", modeStr]
+               }]));
     return;
   }
   _exposureMode = mode;
   [self applyExposureMode];
-  [result sendSuccess];
+  result(nil);
 }
 
 - (void)applyExposureMode {
@@ -989,20 +997,21 @@
   [_captureDevice unlockForConfiguration];
 }
 
-- (void)setFocusModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr {
+- (void)setFocusModeWithResult:(FlutterResult)result mode:(NSString *)modeStr {
   FLTFocusMode mode = FLTGetFLTFocusModeForString(modeStr);
   if (mode == FLTFocusModeInvalid) {
-    [result sendError:[NSError errorWithDomain:NSCocoaErrorDomain
-                                          code:NSURLErrorUnknown
-                                      userInfo:@{
-                                        NSLocalizedDescriptionKey : [NSString
-                                            stringWithFormat:@"Unknown focus mode %@", modeStr]
-                                      }]];
+    result(FlutterErrorFromNSError([NSError
+        errorWithDomain:NSCocoaErrorDomain
+                   code:NSURLErrorUnknown
+               userInfo:@{
+                 NSLocalizedDescriptionKey :
+                     [NSString stringWithFormat:@"Unknown focus mode %@", modeStr]
+               }]));
     return;
   }
   _focusMode = mode;
   [self applyFocusMode];
-  [result sendSuccess];
+  result(nil);
 }
 
 - (void)applyFocusMode {
@@ -1033,22 +1042,21 @@
   [captureDevice unlockForConfiguration];
 }
 
-- (void)pausePreviewWithResult:(FLTThreadSafeFlutterResult *)result {
+- (void)pausePreviewWithResult:(FlutterResult)result {
   _isPreviewPaused = true;
-  [result sendSuccess];
+  result(nil);
 }
 
-- (void)resumePreviewWithResult:(FLTThreadSafeFlutterResult *)result {
+- (void)resumePreviewWithResult:(FlutterResult)result {
   _isPreviewPaused = false;
-  [result sendSuccess];
+  result(nil);
 }
 
-- (void)setDescriptionWhileRecording:(NSString *)cameraName
-                              result:(FLTThreadSafeFlutterResult *)result {
+- (void)setDescriptionWhileRecording:(NSString *)cameraName result:(FlutterResult)result {
   if (!_isRecording) {
-    [result sendErrorWithCode:@"setDescriptionWhileRecordingFailed"
-                      message:@"Device was not recording"
-                      details:nil];
+    result([FlutterError errorWithCode:@"setDescriptionWhileRecordingFailed"
+                               message:@"Device was not recording"
+                               details:nil]);
     return;
   }
 
@@ -1068,7 +1076,7 @@
   NSError *error = nil;
   AVCaptureConnection *newConnection = [self createConnection:&error];
   if (error) {
-    [result sendError:error];
+    result(FlutterErrorFromNSError(error));
     return;
   }
 
@@ -1079,17 +1087,23 @@
 
   // Add the new connections to the session.
   if (![_videoCaptureSession canAddInput:_captureVideoInput])
-    [result sendErrorWithCode:@"VideoError" message:@"Unable switch video input" details:nil];
+    result([FlutterError errorWithCode:@"VideoError"
+                               message:@"Unable switch video input"
+                               details:nil]);
   [_videoCaptureSession addInputWithNoConnections:_captureVideoInput];
   if (![_videoCaptureSession canAddOutput:_captureVideoOutput])
-    [result sendErrorWithCode:@"VideoError" message:@"Unable switch video output" details:nil];
+    result([FlutterError errorWithCode:@"VideoError"
+                               message:@"Unable switch video output"
+                               details:nil]);
   [_videoCaptureSession addOutputWithNoConnections:_captureVideoOutput];
   if (![_videoCaptureSession canAddConnection:newConnection])
-    [result sendErrorWithCode:@"VideoError" message:@"Unable switch video connection" details:nil];
+    result([FlutterError errorWithCode:@"VideoError"
+                               message:@"Unable switch video connection"
+                               details:nil]);
   [_videoCaptureSession addConnection:newConnection];
   [_videoCaptureSession commitConfiguration];
 
-  [result sendSuccess];
+  result(nil);
 }
 
 - (CGPoint)getCGPointForCoordsWithOrientation:(UIDeviceOrientation)orientation
@@ -1117,11 +1131,11 @@
   return CGPointMake(x, y);
 }
 
-- (void)setExposurePointWithResult:(FLTThreadSafeFlutterResult *)result x:(double)x y:(double)y {
+- (void)setExposurePointWithResult:(FlutterResult)result x:(double)x y:(double)y {
   if (!_captureDevice.isExposurePointOfInterestSupported) {
-    [result sendErrorWithCode:@"setExposurePointFailed"
-                      message:@"Device does not have exposure point capabilities"
-                      details:nil];
+    result([FlutterError errorWithCode:@"setExposurePointFailed"
+                               message:@"Device does not have exposure point capabilities"
+                               details:nil]);
     return;
   }
   UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
@@ -1132,14 +1146,14 @@
   [_captureDevice unlockForConfiguration];
   // Retrigger auto exposure
   [self applyExposureMode];
-  [result sendSuccess];
+  result(nil);
 }
 
-- (void)setFocusPointWithResult:(FLTThreadSafeFlutterResult *)result x:(double)x y:(double)y {
+- (void)setFocusPointWithResult:(FlutterResult)result x:(double)x y:(double)y {
   if (!_captureDevice.isFocusPointOfInterestSupported) {
-    [result sendErrorWithCode:@"setFocusPointFailed"
-                      message:@"Device does not have focus point capabilities"
-                      details:nil];
+    result([FlutterError errorWithCode:@"setFocusPointFailed"
+                               message:@"Device does not have focus point capabilities"
+                               details:nil]);
     return;
   }
   UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
@@ -1151,14 +1165,14 @@
   [_captureDevice unlockForConfiguration];
   // Retrigger auto focus
   [self applyFocusMode];
-  [result sendSuccess];
+  result(nil);
 }
 
-- (void)setExposureOffsetWithResult:(FLTThreadSafeFlutterResult *)result offset:(double)offset {
+- (void)setExposureOffsetWithResult:(FlutterResult)result offset:(double)offset {
   [_captureDevice lockForConfiguration:nil];
   [_captureDevice setExposureTargetBias:offset completionHandler:nil];
   [_captureDevice unlockForConfiguration];
-  [result sendSuccessWithData:@(offset)];
+  result(@(offset));
 }
 
 - (void)startImageStreamWithMessenger:(NSObject<FlutterBinaryMessenger> *)messenger {
@@ -1211,18 +1225,18 @@
   self.streamingPendingFramesCount--;
 }
 
-- (void)getMaxZoomLevelWithResult:(FLTThreadSafeFlutterResult *)result {
+- (void)getMaxZoomLevelWithResult:(FlutterResult)result {
   CGFloat maxZoomFactor = [self getMaxAvailableZoomFactor];
 
-  [result sendSuccessWithData:[NSNumber numberWithFloat:maxZoomFactor]];
+  result([NSNumber numberWithFloat:maxZoomFactor]);
 }
 
-- (void)getMinZoomLevelWithResult:(FLTThreadSafeFlutterResult *)result {
+- (void)getMinZoomLevelWithResult:(FlutterResult)result {
   CGFloat minZoomFactor = [self getMinAvailableZoomFactor];
-  [result sendSuccessWithData:[NSNumber numberWithFloat:minZoomFactor]];
+  result([NSNumber numberWithFloat:minZoomFactor]);
 }
 
-- (void)setZoomLevel:(CGFloat)zoom Result:(FLTThreadSafeFlutterResult *)result {
+- (void)setZoomLevel:(CGFloat)zoom Result:(FlutterResult)result {
   CGFloat maxAvailableZoomFactor = [self getMaxAvailableZoomFactor];
   CGFloat minAvailableZoomFactor = [self getMinAvailableZoomFactor];
 
@@ -1231,19 +1245,19 @@
         stringWithFormat:@"Zoom level out of bounds (zoom level should be between %f and %f).",
                          minAvailableZoomFactor, maxAvailableZoomFactor];
 
-    [result sendErrorWithCode:@"ZOOM_ERROR" message:errorMessage details:nil];
+    result([FlutterError errorWithCode:@"ZOOM_ERROR" message:errorMessage details:nil]);
     return;
   }
 
   NSError *error = nil;
   if (![_captureDevice lockForConfiguration:&error]) {
-    [result sendError:error];
+    result(FlutterErrorFromNSError(error));
     return;
   }
   _captureDevice.videoZoomFactor = zoom;
   [_captureDevice unlockForConfiguration];
 
-  [result sendSuccess];
+  result(nil);
 }
 
 - (CGFloat)getMinAvailableZoomFactor {
diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate.h b/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate.h
index cec35d2..1b3cbca 100644
--- a/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate.h
+++ b/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate.h
@@ -3,10 +3,9 @@
 // found in the LICENSE file.
 
 @import AVFoundation;
+@import Flutter;
 @import Foundation;
 
-#import "FLTThreadSafeFlutterResult.h"
-
 NS_ASSUME_NONNULL_BEGIN
 
 /// The completion handler block for save photo operations.
diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.h b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.h
deleted file mode 100644
index 09c4f43..0000000
--- a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// 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>
-
-NS_ASSUME_NONNULL_BEGIN
-
-/// A thread safe wrapper for FlutterResult that can be called from any thread, by dispatching its
-/// underlying engine calls to the main thread.
-@interface FLTThreadSafeFlutterResult : NSObject
-
-/// Gets the original FlutterResult object wrapped by this FLTThreadSafeFlutterResult instance.
-@property(readonly, nonatomic) FlutterResult flutterResult;
-
-/// Initializes with a FlutterResult object.
-/// @param result The FlutterResult object that the result will be given to.
-- (instancetype)initWithResult:(FlutterResult)result;
-
-/// Sends a successful result on the main thread without any data.
-- (void)sendSuccess;
-
-/// Sends a successful result on the main thread with data.
-/// @param data Result data that is send to the Flutter Dart side.
-- (void)sendSuccessWithData:(id)data;
-
-/// Sends an NSError as result on the main thread.
-/// @param error Error that will be send as FlutterError.
-- (void)sendError:(NSError *)error;
-
-/// Sends a FlutterError as result on the main thread.
-/// @param flutterError FlutterError that will be sent to the Flutter Dart side.
-- (void)sendFlutterError:(FlutterError *)flutterError;
-
-/// Sends a FlutterError as result on the main thread.
-- (void)sendErrorWithCode:(NSString *)code
-                  message:(nullable NSString *)message
-                  details:(nullable id)details;
-
-/// Sends FlutterMethodNotImplemented as result on the main thread.
-- (void)sendNotImplemented;
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.m b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.m
deleted file mode 100644
index ee636cd..0000000
--- a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.m
+++ /dev/null
@@ -1,62 +0,0 @@
-// 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 "FLTThreadSafeFlutterResult.h"
-#import <Foundation/Foundation.h>
-#import "QueueUtils.h"
-
-@implementation FLTThreadSafeFlutterResult {
-}
-
-- (id)initWithResult:(FlutterResult)result {
-  self = [super init];
-  if (!self) {
-    return nil;
-  }
-  _flutterResult = result;
-  return self;
-}
-
-- (void)sendSuccess {
-  [self send:nil];
-}
-
-- (void)sendSuccessWithData:(id)data {
-  [self send:data];
-}
-
-- (void)sendError:(NSError *)error {
-  [self sendErrorWithCode:[NSString stringWithFormat:@"Error %d", (int)error.code]
-                  message:error.localizedDescription
-                  details:error.domain];
-}
-
-- (void)sendErrorWithCode:(NSString *)code
-                  message:(NSString *_Nullable)message
-                  details:(id _Nullable)details {
-  FlutterError *flutterError = [FlutterError errorWithCode:code message:message details:details];
-  [self send:flutterError];
-}
-
-- (void)sendFlutterError:(FlutterError *)flutterError {
-  [self send:flutterError];
-}
-
-- (void)sendNotImplemented {
-  [self send:FlutterMethodNotImplemented];
-}
-
-/// Sends result to flutterResult on the main thread.
-- (void)send:(id _Nullable)result {
-  FLTEnsureToRunOnMainQueue(^{
-    // WARNING: Should not use weak self, because `FlutterResult`s are passed as arguments
-    // (retained within call stack, but not in the heap). FLTEnsureToRunOnMainQueue may trigger a
-    // context switch (when calling from background thread), in which case using weak self will
-    // always result in a nil self. Alternative to using strong self, we can also create a local
-    // strong variable to be captured by this block.
-    self.flutterResult(result);
-  });
-}
-
-@end
diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml
index 1757aaf..82b804b 100644
--- a/packages/camera/camera_avfoundation/pubspec.yaml
+++ b/packages/camera/camera_avfoundation/pubspec.yaml
@@ -2,7 +2,7 @@
 description: iOS implementation of the camera plugin.
 repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_avfoundation
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
-version: 0.9.15
+version: 0.9.15+1
 
 environment:
   sdk: ^3.2.3