fix: always reply to track method channel calls and surface failures - #75
Open
vahidlazio wants to merge 2 commits into
Open
fix: always reply to track method channel calls and surface failures#75vahidlazio wants to merge 2 commits into
vahidlazio wants to merge 2 commits into
Conversation
`track` was the only method channel case that never replied. Every other
case calls result(...)/result.success(...), so a track call left the
Dart-side reply pending forever — on both iOS and Android. iOS also
swallowed every failure via `try?`.
- iOS: replace `try?` with do/catch, reply result("") on success and a
FlutterError(TRACK_FAILED) on failure, and log via NSLog like the other
catch blocks. Also reply on the argument-guard path, matching neighbours.
- Android: reply result.success(null), and translate a thrown exception
into result.error(TRACK_FAILED) rather than letting it escape the
channel handler.
- Dart: track() stays `void` (making it a Future would break the public
API), and the unawaited future now has a catchError so a native error
reply is logged instead of becoming an unhandled async error.
Adds two method channel tests; the error one fails without the Dart guard.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
flush had the same dangling-reply defect as track, on BOTH platforms — not Android only as previously noted. iOS replied in the guard branch but fell through to `break;` after `confidence.flush()` with no reply, and Android never replied at all. Either way the Dart future never completed. iOS now replies after flushing. Confidence.flush() is non-throwing there, so no do/catch is added — an unreachable catch would only warn. Android wraps the call and replies success or FLUSH_FAILED, matching the shape track uses. The platform interface types flush() as void, so callers discard the future. Now that Android can reply with an error, that rejection would surface as an unhandled async error in the host app, so the Dart side swallows and logs it as track already does. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This was referenced Sep 7, 2026
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
trackwas the only method channel case that never sent a reply. Every other case callsresult(...)/result.success(...), so atrackcall left the Dart-side reply pending forever — on both iOS and Android. iOS additionally swallowed every failure viatry?.Defects fixed
tracknever replies on the method channel, leaving the Dart future pendingtrackerrors silently discarded bytry?Changes
ConfidenceFlutterSdkPlugin.swift) — replacedtry?withdo/catch: replyresult("")on success,FlutterError(code: "TRACK_FAILED", ...)on failure, and log viaNSLogto match the other catch blocks in the file. The argument-guard path now also replies, consistent with neighbouring cases.ConfidenceFlutterSdkPlugin.kt) — replyresult.success(null), and translate a thrown exception intoresult.error("TRACK_FAILED", ...)instead of letting it escape the channel handler.confidence_flutter_sdk_method_channel.dart) —track()deliberately staysvoid; changing it to return aFuturewould be a breaking public API change. Because the returned future is therefore never awaited, a native error reply would surface as an unhandled async error in the host app, so acatchErrorhandler now logs it instead.Tests
Two tests added to the existing method channel suite:
trackforwards the event name and typed datatrackdoes not raise an unhandled async error when native fails — this one fails without the DartcatchErrorguard (verified by reverting the guard), so it genuinely pins the regressionVerification
flutter analyzeclean; all 4 method channel tests pass.Verified end-to-end on both platforms against a real Confidence account, driving the example app:
iOS (iPhone 15 simulator) — explicit upload status:
Android (Pixel 7 Pro, API 33):
with no error logged, and the on-disk batch file absent afterwards — the SDK only deletes a batch after a successful upload, retaining it for retry on failure.
flushhas the same defect — now fixed here tooA follow-up review found the earlier note was incomplete:
"flush"had the identicalmissing-reply defect on both platforms, not Android only.
ConfidenceFlutterSdkPlugin.kt) never replied at all.ConfidenceFlutterSdkPlugin.swift) replied in theguardbranch but fell throughto
break;afterconfidence.flush()with no reply.Both are fixed in this PR.
Confidence.flush()is non-throwing on iOS, so nodo/catchwas added there (an unreachable catch would only produce a warning); Android wraps the
call and replies
successorFLUSH_FAILED, matchingtrack.Because the platform interface types
flush()asvoid, callers discard the future — sonow that Android can reply with an error, the Dart side swallows and logs it, as
trackalready does.
Still out of scope (pre-existing, filed separately)
fetchAndActivate,activateAndFetchAsyncandreadAllFlagsreply from insidecoroutineScope.launch {}onDispatchers.IO(iOS:Task {}). If the suspending callthrows, the reply never happens and the Dart future never completes; replies should also
be marshalled to the platform thread.
toTypedValuemaps unsupported values (e.g.DateTime,null) to{'type': 'unknown'}, and iOSconvertValue'sdefault:returnsConfidenceValue(integer: 0)— so those publish as0rather than being rejected.🤖 Generated with Claude Code