-
Notifications
You must be signed in to change notification settings - Fork 3.6k
[camera_avfoundation] Pigeon swift migration - part 2 #10980
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[camera_avfoundation] Pigeon swift migration - part 2 #10980
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request completes the migration of the camera_avfoundation package to a Swift-based Pigeon interface, removing the last Objective-C-based Pigeon interfaces. The changes are extensive and primarily involve:
- Replacing Objective-C generated Pigeon files with new Swift-generated ones (
Messages.swift). - Updating the
CameraApiimplementation inCameraPlugin.swiftandDefaultCamera.swiftto use the new Swift types andResult-based completion handlers. - Removing the Objective-C Pigeon target and related source files.
- Updating all unit and integration tests to align with the new Swift API, including changes to mock objects and assertions to handle
ResultandPigeonErrortypes. - Adjusting the
podspecandPackage.swiftfiles to reflect the removal of the Objective-C target.
My feedback focuses on improving the maintainability of the updated test files by reducing code duplication.
| permissionManager.requestCameraPermission { error in | ||
| XCTAssertEqual(error, expectedError) | ||
| XCTAssertEqual(error?.code, "CameraAccessDeniedWithoutPrompt") | ||
| XCTAssertEqual( | ||
| error?.message, | ||
| "User has previously denied the camera access request. Go to Settings to enable camera access." | ||
| ) | ||
| expectation.fulfill() | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a lot of repeated code for asserting error properties across this test file. To improve maintainability and reduce duplication, consider adding a helper function to assert the properties of a PigeonError.
For example:
private func assertPermissionError(
_ error: PigeonError?,
hasCode expectedCode: String,
hasMessage expectedMessage: String,
file: StaticString = #file,
line: UInt = #line
) {
XCTAssertNotNil(error, "Expected a permission error, but got nil", file: file, line: line)
XCTAssertEqual(error?.code, expectedCode, file: file, line: line)
XCTAssertEqual(error?.message, expectedMessage, file: file, line: line)
}You could then simplify this test case and others like it:
permissionManager.requestCameraPermission { error in
assertPermissionError(
error,
hasCode: "CameraAccessDeniedWithoutPrompt",
hasMessage: "User has previously denied the camera access request. Go to Settings to enable camera access."
)
expectation.fulfill()
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pls let me know if you're in favor or agains a helper in those cases. I remember that you were against in an earlier PR but it would be a much simpler helper here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm ok with either approach. Both seem readable to me.
| cameraPlugin.lockCaptureOrientation(orientation: targetOrientation) { result in | ||
| switch result { | ||
| case .success: | ||
| break | ||
| case .failure: | ||
| XCTFail("Unexpected error") | ||
| } | ||
| expectation.fulfill() | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Throughout this test file, there are many switch statements to check for a successful Result and fail the test on failure. This pattern is repeated frequently. To make the tests more concise and reduce boilerplate, you could introduce a helper function.
For example, a helper could unwrap the success value or fail the test:
func assertSuccess<T>(_ result: Result<T, any Error>, file: StaticString = #file, line: UInt = #line) -> T? {
switch result {
case .success(let value):
return value
case .failure(let error):
XCTFail("Expected success, but got failure: \(error)", file: file, line: line)
return nil
}
}This would simplify tests that expect success. For this test, it would look like:
cameraPlugin.lockCaptureOrientation(orientation: targetOrientation) { result in
_ = assertSuccess(result)
expectation.fulfill()
}For tests that also check the returned value, it would be even cleaner:
// Example from another test in this file
cameraPlugin.stopVideoRecording { result in
if let path = assertSuccess(result) {
XCTAssertEqual(path, targetPath)
}
expectation.fulfill()
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems nice cleanup that saves lots of lines
|
@hellohuanlin pls take a look at this PR when you have some time |
|
@RobertOdrowaz Hello, this PR is 2000 LOC and all in a single commit. Could you give a tour of the change and how i can start reviewing it, if possible? |
|
I know it's over 2000 LOC, but the vast majority are trivial changes:
camera.setZoomLevel(CGFloat(1.0)) { error in
XCTAssertNotNil(error)
XCTAssertEqual(error?.code, "ZOOM_ERROR")
expectation.fulfill()
}to camera.setZoomLevel(CGFloat(1.0)) { result in
switch result {
case .failure(let error as PigeonError):
XCTAssertEqual(error.code, "ZOOM_ERROR")
default:
XCTFail("Expected failure")
}
expectation.fulfill()
}because methods now pass a
I can split 1, 2 and 3+4 into separate commits if it will help you but IMO going file by file is effectively the same. |
|
@RobertOdrowaz Thanks for the tour. Don't worry about splitting it. Taking a look now. |
hellohuanlin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with just a few questions
| settings: FCPPlatformMediaSettings.make( | ||
| with: FCPPlatformResolutionPreset.medium, | ||
| settings: PlatformMediaSettings( | ||
| resolutionPreset: PlatformResolutionPreset.medium, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can infer type
| permissionManager.requestCameraPermission { error in | ||
| XCTAssertEqual(error, expectedError) | ||
| XCTAssertEqual(error?.code, "CameraAccessDeniedWithoutPrompt") | ||
| XCTAssertEqual( | ||
| error?.message, | ||
| "User has previously denied the camera access request. Go to Settings to enable camera access." | ||
| ) | ||
| expectation.fulfill() | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm ok with either approach. Both seem readable to me.
| handler(false) | ||
| } | ||
| permissionManager.requestCameraPermission { error in | ||
| XCTAssertEqual(error, expectedError) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
curious why are we changing this? I don't see any problem with this approach. Is it related to equatable?
| cameraPlugin.lockCaptureOrientation(orientation: targetOrientation) { result in | ||
| switch result { | ||
| case .success: | ||
| break | ||
| case .failure: | ||
| XCTFail("Unexpected error") | ||
| } | ||
| expectation.fulfill() | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems nice cleanup that saves lots of lines
| with: testResolutionPreset, | ||
| framesPerSecond: NSNumber(value: testFramesPerSecond), | ||
| videoBitrate: NSNumber(value: testVideoBitrate), | ||
| audioBitrate: NSNumber(value: testAudioBitrate), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice to see these gone
| /// @param messenger Nullable messenger for capturing each frame. | ||
| func startVideoRecording( | ||
| completion: @escaping (_ error: FlutterError?) -> Void, | ||
| completion: @escaping (Result<Void, any Error>) -> Void, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is it changing from FlutterError to any Error?
| # Combine camera_avfoundation and camera_avfoundation_objc sources into a single pod, unlike | ||
| # SwiftPM, where separate Swift and Objective-C targets are required. | ||
| s.source_files = 'camera_avfoundation/Sources/camera_avfoundation*/**/*.{h,m,swift}' | ||
| s.public_header_files = 'camera_avfoundation/Sources/camera_avfoundation_objc/include/**/*.h' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💯
Migrates camera package to the Swift-based pigeon interface as the last part of flutter/flutter#119109
This PR replaces the ObjC-based pigeon interfaces with Swift-based ones, thus removing the last remaining pieces of ObjC code in the package 🎉
I know there are a lot of changes in this PR, but there is no way to split it into smaller parts.
Pre-Review Checklist
[shared_preferences]pubspec.yamlwith an appropriate new version according to the pub versioning philosophy, or I have commented below to indicate which version change exemption this PR falls under1.CHANGELOG.mdto add a description of the change, following repository CHANGELOG style, or I have commented below to indicate which CHANGELOG exemption this PR falls under1.///).If you need help, consider asking for advice on the #hackers-new channel on Discord.
Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the
gemini-code-assistbot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.Footnotes
Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. ↩ ↩2 ↩3