fix(*): translate native compress failures to CompressError instead of null - #397
Merged
Conversation
Introduce an optional `code` field on `CompressError` so callers can branch on failure kind without string-matching the message. Stable values emitted by the native side: `unsupported_format`, `decode_failed`, `encode_failed`, `io_failed`, `unknown`. Unknown codes pass through verbatim for forward-compat. Additive change — `code` is nullable and the existing single-arg constructor is unchanged. Refs #252 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…f null
Native compress failures on Android/iOS/macOS previously reported back
to Dart as `reply(null)` / `result(nil)`, forcing callers to do a bare
null-check with no diagnostic — and creating a latent `TypeError` on
`compressWithList` (common), whose signature is non-nullable but which
happily returned the native null unchanged.
Native side (Android Kotlin / iOS Objective-C / macOS Swift):
- `reply(null)` / `result(nil)` on the failure path becomes
`replyError(code, msg)` / `result([FlutterError ...])` /
`FlutterError(code:..., message:...)` with a stable code:
`unsupported_format`, `decode_failed`, `encode_failed`, `io_failed`,
`unknown`.
- iOS `handleCompressFileToFile:` also translates `writeToURL:`
failure from a silent `result(nil)` to `io_failed` with the target
path in the message.
Dart side (common / macOS / ohos):
- Introduce a private `_invoke<T>` helper on each platform impl that
catches `PlatformException` and rethrows `CompressError(msg, code:
...)`. Every compress-related `_channel.invokeMethod` call now
goes through it.
- Delete three unreachable `if (!support) return null;` branches in
common — `FlutterImageCompressValidator.checkSupportPlatform`
already throws `UnsupportedError` when a format is not supported;
it never actually returns `false`.
- Add a defensive `if (result == null) throw CompressError(...)` in
`compressWithList` (common), aligning with the guard macOS and ohos
already had. This closes the latent `TypeError` — the native side
should no longer emit a `success(null)` on the failure path, but
the guard keeps the non-nullable return type honest.
- Replace `throw Exception('Compress failed')` in macOS and ohos
`compressWithList` with `throw CompressError('Compress failed')`
for consistency.
Existing null-checking callers still compile — public signatures
unchanged. Callers who already catch `CompressError` now get a real
error kind + message instead of an opaque null return.
Refs #252, refs #396
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Re-wrap CompressError.toString ternary to match `dart format` output. No behavior change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
reply(null)/result(nil), forcing Dart callers to bare null-checks with no diagnostic — and creating a latentTypeErroroncompressWithList(common), whose signature is non-nullable but which returned the native null unchanged.unsupported_format,decode_failed,encode_failed,io_failed,unknown) viareplyError/FlutterError; Dart platform impls catchPlatformExceptionand rethrowCompressError(message, code: ...).CompressError(documented in the platform interface) now get a real error kind + message; callers doingresult == nullstill compile.if (!support) return null;branches in common (checkSupportPlatformalready throwsUnsupportedError), and adds a defensive null guard tocompressWithList(common) matching macOS/ohos.Related issues
CompressErrorto be exposed on the Flutter side. The class was already exported since 🎨 Add melos and support platform interface #242, but this PR addresses the underlying pain: native failures are now surfaced asCompressErrorinstead of silent nulls.compressWithFile/compressAndGetFile/compressAssetImagefromFuture<T?>toFuture<T>), which is a v-next candidate and deliberately out of scope here.Wire protocol
Failure codes emitted by the native side (documented on
CompressError.code):unsupported_formatCompressFormatdecode_failedencode_failedio_failedunknownmessagecarries the reason)Test plan
melos run analyzeclean across all 7 packagesmelos run verify_ios_behavior) — golden path unchanged, only failure paths touchedmelos run verify_macos_behavior)compressWithListon an Android device; before this change,BitmapFactory.decodeByteArrayreturning null caused a DartTypeError(non-nullableUint8List= null). After this change, callers receiveCompressError(code: 'unknown', message: '...').compressWithFilewith a non-image file — before:result == nullwith no info. After:CompressError(code: 'decode_failed', message: 'Input file is not a decodable image (mime=application/octet-stream, path=...)').Note: the repo has no
test/unit-test scaffolding today — behavioral coverage lives inpackages/flutter_image_compress/example/integration_test/compress_test.dart. Adding unit tests for the_invoketranslator would require setting upflutter_test+test/in three platform packages, which is a bigger separate change; happy to add it if reviewers prefer.🤖 Generated with Claude Code