initInternal static method
Internal hook used by Sankofa.init — DO NOT call directly. The
per-product enableDeploy flag triggers this. Idempotent.
Native platform plugin init is best-effort. When the platform
plugin's initialize() throws (e.g. on iOS Path C where the native
half of the libapp.so binary-diff updater doesn't exist),
_instance is still set + _ready is left false. Pure-Dart
methods (the KBC patch apply pipeline — applyKbcPatchFromBytes,
applyKbcPatchFromFile, fetchAndApplyKbcPatch) don't gate on
_ready and remain usable. Methods that DO require the platform
plugin (Android baseline OTA — checkForUpdate, notifyAppReady,
reportCrash, disableCurrentPatch, getActiveVersion,
getCurrentLibappPath) throw via _assertReady() with a clear
message explaining the platform is unavailable.
Before η polish (2026-05-24): platform-init failure inside the
unawaited() wrapper that calls this function would silently
leave _instance == null and Sankofa.deploy returned null —
hello_sankofa hit this on iOS, fell back to the standalone
applyKbcEnvelopeFromFile helper.
Implementation
static Future<SankofaDeploy> initInternal({
required String apiKey,
required String endpoint,
required SankofaDeployOptions options,
String? appVersion,
String? flavor,
String Function()? distinctIdResolver,
void Function(String eventType, {String? label, String? releaseId})?
onLifecycleEvent,
}) async {
if (_instance != null) return _instance!;
final deploy = SankofaDeploy._(options: options);
deploy._ingestEndpoint = endpoint;
deploy._ingestApiKey = apiKey;
deploy._defaultAppVersion = appVersion;
deploy._flavor = flavor;
deploy._distinctIdResolver = distinctIdResolver;
deploy._onLifecycleEvent = onLifecycleEvent;
// Register with the Traffic Cop BEFORE the platform init kicks off
// so the first unified handshake (fired from Sankofa.init right
// after this call returns) has a route for `modules.deploy`. The
// server is the source of truth for whether this module operates;
// host-side `enableDeploy: true` is just intent.
SankofaModuleRegistry.instance.register(deploy);
// Publish the instance EAGERLY so a slow / failing platform plugin
// init never strands `Sankofa.deploy` at null. The Path C methods
// are pure Dart and don't need the native plugin; the Android
// methods gate on `_ready` and throw a clear message when it's
// false. Either way `Sankofa.deploy` is the canonical entry point.
_instance = deploy;
try {
await SankofaDeployPlatform.instance.initialize(
apiKey: apiKey,
endpoint: endpoint,
options: options,
);
deploy._ready = true;
} on MissingPluginException {
// No native Deploy plugin registered in this build. This is the
// EXPECTED, benign case under plain `flutter run` (the native
// baseline ships via the Sankofa CLI build / fork engine) and on
// iOS (Path C is pure-Dart). KBC / Path C OTA is fully unaffected.
// Degrade silently — no error, no scary log, no "BROKEN" report.
deploy._platformUnavailable = true;
} catch (_) {
// A genuinely unexpected platform failure (plugin present but
// threw). Still non-fatal — Path C doesn't gate on _ready — and we
// deliberately stay quiet so a dev run never surfaces a deploy
// error. _ready stays false; native baseline methods will throw a
// clear _assertReady message only if the host actually calls them.
}
if (options.autoCheckOnStartup && deploy._ready) {
// Fire-and-forget. Errors flow into the platform's logger and
// the next checkForUpdate() call will surface the state. Skip
// on iOS where _ready stays false because the legacy libapp.so
// path is Android-only.
// ignore: deprecated_member_use_from_same_package
unawaited(deploy.legacyAndroidCheckForUpdate());
}
return deploy;
}