<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: saimskywalker</title>
    <description>The latest articles on DEV Community by saimskywalker (@saimskywalker).</description>
    <link>https://dev.to/saimskywalker</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F690157%2F7ad9b6a3-df30-4157-ab9c-a97c73162be3.jpeg</url>
      <title>DEV Community: saimskywalker</title>
      <link>https://dev.to/saimskywalker</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/saimskywalker"/>
    <language>en</language>
    <item>
      <title>Making a vendor's closed-source binary SDK optional at build time</title>
      <dc:creator>saimskywalker</dc:creator>
      <pubDate>Thu, 27 Aug 2026 08:31:51 +0000</pubDate>
      <link>https://dev.to/saimskywalker/making-a-vendors-closed-source-binary-sdk-optional-at-build-time-570d</link>
      <guid>https://dev.to/saimskywalker/making-a-vendors-closed-source-binary-sdk-optional-at-build-time-570d</guid>
      <description>&lt;p&gt;You have an app to ship, and it depends on a vendor's SDK. The vendor sends you a &lt;code&gt;.xcframework&lt;/code&gt; for iOS and a bare &lt;code&gt;.aar&lt;/code&gt; for Android, both prebuilt, both proprietary, often issued per customer, and both too large and too licensed to go in git.&lt;/p&gt;

&lt;p&gt;So the first question is where the binary lives, and every answer is bad:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Commit it, and you have a licensed binary in your history forever, plus a repository that clones slowly for everyone.&lt;/li&gt;
&lt;li&gt;Do not commit it, and a fresh checkout cannot build. Neither can CI, which has no vendor credentials and should not have them.&lt;/li&gt;
&lt;li&gt;Put it behind a fetch script, and now the build only works on machines that ran the script — which is exactly the class of failure that shows up as "works on my machine" three weeks later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then the iOS half splits in two, depending on a decision you may have already made. If the app is on CocoaPods, the vendor's podspec drops in. If the app is on Swift Package Manager, it does not — SPM and CocoaPods have no shared resolver, so if the vendor pod and a pub.dev plugin both depend on the same underlying SDK, nothing can negotiate the version between them. The usual advice ("just wrap it in a pod") quietly means &lt;em&gt;turn SPM off for the whole app&lt;/em&gt;, and that trade gets worse every month: &lt;strong&gt;CocoaPods trunk goes permanently read-only on 2026-12-02.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the write-up of a small generator that came out of doing this integration once by hand: [binary-sdk-bridge (&lt;a href="https://github.com/saimskywalker/binary-sdk-bridge" rel="noopener noreferrer"&gt;https://github.com/saimskywalker/binary-sdk-bridge&lt;/a&gt;). It emits an SPM package plus a Gradle module that wrap the vendor binary, with the binary kept out of version control and &lt;em&gt;optional at build time&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The failures, in the order you hit them
&lt;/h2&gt;

&lt;p&gt;These are the specific things that go wrong. If you searched your way here, one of them is probably in your terminal right now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A missing binary target fails the whole graph, not just the target.&lt;/strong&gt; A&lt;br&gt;
&lt;code&gt;.binaryTarget(path:)&lt;/code&gt; pointing at a file that is not there does not degrade —&lt;br&gt;
it fails the resolve of the &lt;em&gt;entire&lt;/em&gt; SPM package graph. One absent&lt;br&gt;
&lt;code&gt;.xcframework&lt;/code&gt; and nothing in the app builds, which is why "just gitignore it"&lt;br&gt;
does not work on its own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gradle refuses a local &lt;code&gt;.aar&lt;/code&gt; inside a library module.&lt;/strong&gt; Add the vendor&lt;br&gt;
binary with &lt;code&gt;implementation(files("libs/AcmeSDK.aar"))&lt;/code&gt; in a library module and&lt;br&gt;
&lt;code&gt;bundleDebugAar&lt;/code&gt; fails with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Direct local .aar file dependencies are not supported when building an AAR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AGP refuses because those classes would be silently dropped from the published&lt;br&gt;
artifact. If you are producing a Flutter plugin this bites the moment anyone&lt;br&gt;
tries &lt;code&gt;flutter build aar&lt;/code&gt; for add-to-app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A bare &lt;code&gt;.aar&lt;/code&gt; carries no transitive dependency metadata.&lt;/strong&gt; Every SDK the&lt;br&gt;
vendor binary expects at runtime has to be declared and version-managed by hand,&lt;br&gt;
and a missing one is a &lt;code&gt;ClassNotFoundException&lt;/code&gt; at call time rather than a build&lt;br&gt;
error. If the host app minifies, R8 will also strip classes the vendor loads&lt;br&gt;
reflectively by name, and the symptom of missing keep rules is a silent runtime&lt;br&gt;
failure in release builds only.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The deployment target mismatch.&lt;/strong&gt; Setting the package's &lt;code&gt;platforms:&lt;/code&gt; above the&lt;br&gt;
host app's own is what produces &lt;code&gt;required a higher minimum deployment target&lt;/code&gt; at&lt;br&gt;
link time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And the worst one, which produces no error at all.&lt;/strong&gt; SwiftPM caches manifest&lt;br&gt;
evaluation by manifest &lt;strong&gt;content&lt;/strong&gt;, not by filesystem state. So if your&lt;br&gt;
&lt;code&gt;Package.swift&lt;/code&gt; decides something by looking at the filesystem, dropping the&lt;br&gt;
binary in afterwards does not flip that decision — the manifest text has not&lt;br&gt;
changed. Worse, the cache lives in more than one place. Clearing only Flutter's&lt;br&gt;
ephemeral directory leaves Xcode's cloned &lt;code&gt;SourcePackages&lt;/code&gt; and SwiftPM's&lt;br&gt;
&lt;em&gt;global&lt;/em&gt; manifest cache intact. That produced three consecutive green builds&lt;br&gt;
with no SDK linked and nothing in any log to say so.&lt;/p&gt;
&lt;h2&gt;
  
  
  The idea: probe the filesystem from the manifest
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Package.swift&lt;/code&gt; is not a data file. It is a Swift program that SwiftPM runs, and&lt;br&gt;
it can read the filesystem. So the binary target can be declared conditionally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// swift-tools-version: 5.9&lt;/span&gt;
&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Foundation&lt;/span&gt;
&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;PackageDescription&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;sdkPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;packageDirectory&lt;/span&gt;
    &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"/Frameworks/AcmeSDK.xcframework"&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;sdkPresent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;FileManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fileExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;atPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sdkPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Target&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;kitDependencies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Target&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Dependency&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;kitSwiftSettings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;SwiftSetting&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sdkPresent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;targets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;binaryTarget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"AcmeSDK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Frameworks/AcmeSDK.xcframework"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;kitDependencies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AcmeSDK"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;kitSwiftSettings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ACME_ADS_SDK"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;targets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;target&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"AcmeAdsKit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;kitDependencies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;swiftSettings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;kitSwiftSettings&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things fall out of that. The package resolves whether or not the binary is&lt;br&gt;
present, so CI and a fresh checkout are fine. And &lt;code&gt;ACME_ADS_SDK&lt;/code&gt; is a&lt;br&gt;
compilation condition, so the vendor-facing code can sit behind &lt;code&gt;#if&lt;/code&gt; and simply&lt;br&gt;
not exist in a build without the binary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="kt"&gt;AcmeAdsKit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cp"&gt;#if ACME_ADS_SDK&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;probe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;AcmeAdsKitState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// the vendor's real API goes here&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="cp"&gt;#else&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;probe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;AcmeAdsKitState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unavailable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"sdk_not_bundled"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="cp"&gt;#endif&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Making the target conditional while leaving the &lt;em&gt;product&lt;/em&gt; and the &lt;em&gt;dependency&lt;/em&gt;&lt;br&gt;
unconditional is a real trap, incidentally: you get a manifest referencing a&lt;br&gt;
target that does not exist, which is a package that cannot resolve at all.&lt;br&gt;
There is a test for that.&lt;/p&gt;

&lt;p&gt;Android has no &lt;code&gt;#if&lt;/code&gt;. The two options were &lt;code&gt;Class.forName&lt;/code&gt; reflection or two&lt;br&gt;
source sets declaring the same object, and the generator picks source sets —&lt;br&gt;
reflection would compile fine without the SDK while throwing away every&lt;br&gt;
compile-time check against the vendor API, which is precisely the surface least&lt;br&gt;
worth leaving unchecked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;vendorAar&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"libs/AcmeSDK.aar"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;sdkPresent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vendorAar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;android&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;sourceSets&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;getByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;directories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sdkPresent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="s"&gt;"src/withSdk/kotlin"&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;"src/noSdk/kotlin"&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sdkPresent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;runtimeOnly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;files&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vendorAar&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;runtimeOnly&lt;/code&gt;, not &lt;code&gt;implementation&lt;/code&gt; — that is the &lt;code&gt;bundleDebugAar&lt;/code&gt; fix from&lt;br&gt;
above, and it is also the honest declaration of what is actually required at&lt;br&gt;
compile time. &lt;code&gt;java.directories.add&lt;/code&gt; rather than &lt;code&gt;java.srcDir&lt;/code&gt;, and a top-level&lt;br&gt;
&lt;code&gt;kotlin { compilerOptions { } }&lt;/code&gt; rather than &lt;code&gt;kotlinOptions&lt;/code&gt; inside &lt;code&gt;android { }&lt;/code&gt;,&lt;br&gt;
because AGP 9.1 rejects the deprecated forms at &lt;em&gt;script compilation&lt;/em&gt; — failing&lt;br&gt;
the module before a single source file is read.&lt;/p&gt;

&lt;p&gt;The two &lt;code&gt;VendorBridge.kt&lt;/code&gt; files must keep identical signatures or &lt;code&gt;main&lt;/code&gt;&lt;br&gt;
compiles in one configuration and not the other, a break nobody sees until the&lt;br&gt;
binary lands. A generated test asserts that.&lt;/p&gt;
&lt;h2&gt;
  
  
  Running it
&lt;/h2&gt;

&lt;p&gt;Not on pub.dev yet, so install it from git:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dart pub global activate &lt;span class="nt"&gt;--source&lt;/span&gt; git &lt;span class="se"&gt;\&lt;/span&gt;
  https://github.com/saimskywalker/binary-sdk-bridge.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That puts &lt;code&gt;binary-sdk-bridge&lt;/code&gt; on your &lt;code&gt;PATH&lt;/code&gt; via &lt;code&gt;~/.pub-cache/bin&lt;/code&gt;. From a&lt;br&gt;
clone, &lt;code&gt;dart run bin/binary_sdk_bridge.dart&lt;/code&gt; works with no install at all.&lt;/p&gt;

&lt;p&gt;Two flavours from one generator. A Flutter plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;binary-sdk-bridge &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; acme_ads &lt;span class="nt"&gt;--org&lt;/span&gt; com.example &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ios-framework&lt;/span&gt; AcmeSDK &lt;span class="nt"&gt;--android-aar&lt;/span&gt; AcmeSDK &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--out&lt;/span&gt; packages
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or native only — an SPM package and a Gradle module, no pubspec, no Dart, no&lt;br&gt;
Flutter dependency anywhere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;binary-sdk-bridge &lt;span class="nt"&gt;--flavor&lt;/span&gt; native &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; acme_sdk &lt;span class="nt"&gt;--org&lt;/span&gt; com.example &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ios-framework&lt;/span&gt; AcmeSDK &lt;span class="nt"&gt;--android-aar&lt;/span&gt; AcmeSDK &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--out&lt;/span&gt; vendor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--dry-run&lt;/code&gt; lists the files without writing them. The generated package ships&lt;br&gt;
&lt;code&gt;tool/fetch_ios_sdk.sh&lt;/code&gt; and &lt;code&gt;tool/fetch_android_sdk.sh&lt;/code&gt;, which take either a&lt;br&gt;
local path or a URL pinned to a SHA-256 — they refuse an unpinned download&lt;br&gt;
rather than trusting it on first use, since the artifact links into a shipping&lt;br&gt;
app. The iOS script also clears all three SwiftPM manifest caches, for the&lt;br&gt;
reason above, and prints the two checks that actually prove the binary linked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;swift package &lt;span class="nt"&gt;--package-path&lt;/span&gt; ios/acme_sdk &lt;span class="se"&gt;\&lt;/span&gt;
  describe &lt;span class="nt"&gt;--type&lt;/span&gt; json | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'"type" : "binary"'&lt;/span&gt;

&lt;span class="nb"&gt;ls &lt;/span&gt;build/ios/iphonesimulator/Runner.app/Frameworks/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use those rather than a green build. &lt;code&gt;flutter build&lt;/code&gt; passes &lt;code&gt;-quiet&lt;/code&gt; to&lt;br&gt;
xcodebuild, which suppresses the &lt;code&gt;#warning&lt;/code&gt; the generated bridge emits, so the&lt;br&gt;
absence of that warning proves nothing at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this does not do
&lt;/h2&gt;

&lt;p&gt;It does not write the vendor's calls for you. No generator can — that API is&lt;br&gt;
whatever the vendor shipped, and exactly one generated file carries a &lt;code&gt;TODO&lt;/code&gt;&lt;br&gt;
for it. Everything around that file is already decided.&lt;/p&gt;

&lt;p&gt;It does not fix publishing. While a local &lt;code&gt;.aar&lt;/code&gt; is in place the module cannot&lt;br&gt;
be published as a standalone AAR; the real fix is a Maven coordinate from the&lt;br&gt;
vendor, and that is the vendor's decision, not yours.&lt;/p&gt;

&lt;p&gt;It does not invent transitive dependency metadata. If the vendor ships a bare&lt;br&gt;
&lt;code&gt;.aar&lt;/code&gt;, you are still declaring its runtime dependencies by hand.&lt;/p&gt;

&lt;p&gt;It cannot get you a checksum. The fetch scripts refuse to pin what the vendor&lt;br&gt;
will not tell you.&lt;/p&gt;

&lt;p&gt;And sometimes there is nothing to call at all. One SDK this was built against&lt;br&gt;
turned out to expose no initialisation surface whatsoever — its adapters were&lt;br&gt;
instantiated by a host SDK from a server response. That is why the generated&lt;br&gt;
bridge starts life as a runtime presence check: it is the one question worth&lt;br&gt;
answering even when there is nothing else to ask.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you want to poke at it
&lt;/h2&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/saimskywalker/binary-sdk-bridge" rel="noopener noreferrer"&gt;https://github.com/saimskywalker/binary-sdk-bridge&lt;/a&gt; (MIT). It is early —&lt;br&gt;
the structure is running against a real vendor SDK in a production app, but the&lt;br&gt;
generator itself has rough edges, and they are written down as issues rather&lt;br&gt;
than hidden.&lt;/p&gt;

&lt;p&gt;A few are tagged&lt;br&gt;
&lt;a href="https://github.com/saimskywalker/binary-sdk-bridge/labels/good%20first%20issue" rel="noopener noreferrer"&gt;&lt;code&gt;good first issue&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
handling a vendor SDK delivered as a &lt;code&gt;.zip&lt;/code&gt;, making the generator work on&lt;br&gt;
Windows, and covering the CLI with tests. Everything lands through a reviewed&lt;br&gt;
pull request, including from forks. If your vendor's SDK does not fit the shape&lt;br&gt;
above, that is worth an issue on its own — there is a template for it.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>ios</category>
      <category>android</category>
      <category>dart</category>
    </item>
  </channel>
</rss>
