diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index 10cb652e121..260df0c751c 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 dad7730b1b5..86296ea6bc5 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 = ""; }; 03BB766C2665316900CE5A93 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraOrientationTests.m; sourceTree = ""; }; - 03F6F8B126CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeFlutterResultTests.m; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 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 = ""; }; @@ -106,8 +103,6 @@ E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraTestUtils.m; sourceTree = ""; }; E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPropertiesTests.m; sourceTree = ""; }; E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPreviewPauseTests.m; sourceTree = ""; }; - F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockFLTThreadSafeFlutterResult.h; sourceTree = ""; }; - F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockFLTThreadSafeFlutterResult.m; sourceTree = ""; }; /* 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 1ff4b573d76..5e1cf7e14ad 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 @@ -#import "MockFLTThreadSafeFlutterResult.h" @interface AvailableCamerasTest : XCTestCase @end @@ -56,17 +55,19 @@ - (void)testAvailableCamerasShouldReturnAllCamerasOnMultiCameraIPhone { } 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 @@ - (void)testAvailableCamerasShouldReturnOneCameraOnSingleCameraIPhone { [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 89f40307933..bc3713b7478 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 @@ - (void)testFixForCaptureSessionQueueNullPointerCrashDueToRaceCondition { [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 1b6ada564dd..3575cd20e12 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 @@ - (void)testSetFocusPointWithResult_SetsFocusPointOfInterest { [_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 bd20134db56..423b8e88989 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 -#import "MockFLTThreadSafeFlutterResult.h" @interface CameraMethodChannelTests : XCTestCase @end @@ -28,19 +27,21 @@ - (void)testCreate_ShouldCallResultOnMainThread { 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 1dfc90b27f1..2ce7b8676d3 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 -#import "MockFLTThreadSafeFlutterResult.h" @interface CameraPreviewPauseTests : XCTestCase @end @@ -16,17 +15,17 @@ @implementation CameraPreviewPauseTests - (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 ecf2b17e04e..3177fe460ea 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 #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 @@ - (NSError *)goodTestWithArguments:(NSDictionary *)arguments { 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 @@ - (void)testSettings_shouldPassConfigurationToCameraDeviceAndWriter { 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 @@ - (void)testSettings_ShouldBeSupportedByMethodCall { 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 @@ - (void)testSettings_ShouldBeSupportedByMethodCall { @"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 1982be72f11..57c47ac1fdd 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 f7204f27711..00c583d2412 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 @@ - (void)testCaptureToFile_mustReportErrorToResultIfSavePhotoDelegateCompletionsW 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 @@ - (void)testCaptureToFile_mustReportErrorToResultIfSavePhotoDelegateCompletionsW // `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 @@ - (void)testCaptureToFile_mustReportPathToResultIfSavePhotoDelegateCompletionsWi 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 @@ - (void)testCaptureToFile_mustReportPathToResultIfSavePhotoDelegateCompletionsWi // `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 @@ - (void)testCaptureToFile_mustReportFileExtensionWithHeifWhenHEVCIsAvailableAndF 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 @@ - (void)testCaptureToFile_mustReportFileExtensionWithHeifWhenHEVCIsAvailableAndF 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 @@ - (void)testCaptureToFile_mustReportFileExtensionWithJpgWhenHEVCNotAvailableAndF 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 @@ - (void)testCaptureToFile_mustReportFileExtensionWithJpgWhenHEVCNotAvailableAndF 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 24e35449114..9c036adeaca 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 @@ - (void)testDidOutputSampleBuffer_mustNotChangeSampleBufferRetainCountAfterPause [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 @@ - (void)testDidOutputSampleBufferIgnoreAudioSamplesBeforeVideoSamples { 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 8685f3fd610..00000000000 --- 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 d3d7b6ac15b..00000000000 --- 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 a9fc7396bb9..128b47b9089 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 2aad7e3de9d..e445697d805 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 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 b8de19ce4ab..00000000000 --- 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 ce1b641cef6..36e87db7441 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 diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m index 31f196ffdb9..f91896b5ff5 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 diff --git a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m index 243d7271869..ba29dbef75b 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 *messenger; @@ -95,14 +100,11 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result // 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 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call @"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 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call }]; [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 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call } [_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 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call 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 @@ - (void)handleCreateMethodCall:(FlutterMethodCall *)call 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 @@ - (void)handleCreateMethodCall:(FlutterMethodCall *)call typeof(self) strongSelf = weakSelf; if (!strongSelf) return; if (error) { - [result sendFlutterError:error]; + result(error); } else { [strongSelf createCameraOnSessionQueueWithCreateMethodCall:call result:result]; } @@ -353,7 +354,7 @@ + (NSNumber *)positiveNumberValueOrNilForArgument:(NSString *)argument } - (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 @@ - (void)createCameraOnSessionQueueWithCreateMethodCall:(FlutterMethodCall *)crea fromMethod:createMethodCall error:&error]; if (error) { - [result sendError:error]; + result(FlutterErrorFromNSError(error)); return; } @@ -375,7 +376,7 @@ - (void)createCameraOnSessionQueueWithCreateMethodCall:(FlutterMethodCall *)crea fromMethod:createMethodCall error:&error]; if (error) { - [result sendError:error]; + result(FlutterErrorFromNSError(error)); return; } @@ -383,7 +384,7 @@ - (void)createCameraOnSessionQueueWithCreateMethodCall:(FlutterMethodCall *)crea fromMethod:createMethodCall error:&error]; if (error) { - [result sendError:error]; + result(FlutterErrorFromNSError(error)); return; } @@ -406,7 +407,7 @@ - (void)createCameraOnSessionQueueWithCreateMethodCall:(FlutterMethodCall *)crea error:&error]; if (error) { - [result sendError:error]; + result(FlutterErrorFromNSError(error)); } else { if (strongSelf.camera) { [strongSelf.camera close]; @@ -414,9 +415,9 @@ - (void)createCameraOnSessionQueueWithCreateMethodCall:(FlutterMethodCall *)crea 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 abdad1ab575..8de3cde2a53 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.modulemap +++ b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.modulemap @@ -12,7 +12,6 @@ framework module camera_avfoundation { 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 230648ff550..97ccd481094 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 e1216fd7dc9..281ff143bcb 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 @@ NS_ASSUME_NONNULL_BEGIN - (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 *)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 @@ NS_ASSUME_NONNULL_BEGIN /// @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 *)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 b2f09e9fed7..9544dbc7b8d 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 +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)updateOrientation:(UIDeviceOrientation)orientation } } -- (void)captureToFile:(FLTThreadSafeFlutterResult *)result { +- (void)captureToFile:(FlutterResult)result { AVCapturePhotoSettings *settings = [AVCapturePhotoSettings photoSettings]; if (_resolutionPreset == FLTResolutionPresetMax) { @@ -366,7 +372,7 @@ - (void)captureToFile:(FLTThreadSafeFlutterResult *)result { prefix:@"CAP_" error:error]; if (error) { - [result sendError:error]; + result(FlutterErrorFromNSError(error)); return; } @@ -385,10 +391,10 @@ - (void)captureToFile:(FLTThreadSafeFlutterResult *)result { }); 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 @@ - (CVPixelBufferRef)copyPixelBuffer { 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 *)messenger { if (!_isRecording) { if (messenger != nil) { @@ -810,11 +816,11 @@ - (void)startVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result 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 @@ - (void)startVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result _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 @@ - (void)stopVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result { [_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 @@ - (void)stopVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result { [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 @@ - (void)lockCaptureOrientationWithResult:(FLTThreadSafeFlutterResult *)result [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 @@ - (void)setFlashModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSStri } } 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 @@ - (void)setFlashModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSStri } } _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 @@ - (void)applyExposureMode { [_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 @@ - (void)applyFocusMode:(FLTFocusMode)focusMode onDevice:(AVCaptureDevice *)captu [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 @@ - (void)setDescriptionWhileRecording:(NSString *)cameraName NSError *error = nil; AVCaptureConnection *newConnection = [self createConnection:&error]; if (error) { - [result sendError:error]; + result(FlutterErrorFromNSError(error)); return; } @@ -1079,17 +1087,23 @@ - (void)setDescriptionWhileRecording:(NSString *)cameraName // 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 @@ - (CGPoint)getCGPointForCoordsWithOrientation:(UIDeviceOrientation)orientation 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 @@ - (void)setExposurePointWithResult:(FLTThreadSafeFlutterResult *)result x:(doubl [_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 @@ - (void)setFocusPointWithResult:(FLTThreadSafeFlutterResult *)result x:(double)x [_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 *)messenger { @@ -1211,18 +1225,18 @@ - (void)receivedImageStreamData { 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 @@ - (void)setZoomLevel:(CGFloat)zoom Result:(FLTThreadSafeFlutterResult *)result { 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 cec35d28f40..1b3cbcaa64f 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 09c4f43d535..00000000000 --- 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 - -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 ee636cdd867..00000000000 --- 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 -#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 1757aafdc2d..82b804b9ff0 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_avfoundation 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