Skip to content

[native assets] Respect app deployment targets on iOS and macOS #194145

Open

Description

@leehack

Use case

Native code assets may need a newer Apple deployment target than Flutter's baseline. The consuming app can already set IPHONEOS_DEPLOYMENT_TARGET or MACOSX_DEPLOYMENT_TARGET, but those values do not reach the corresponding native asset hook configuration.

Flutter currently supplies iOS 15 and macOS 13 through IOSCodeConfig and MacOSCodeConfig. In code_assets 2.1.0 both target-version fields are integers, so targets such as iOS 16.4 or macOS 13.3 cannot be represented accurately.

There is also a reproduced iOS packaging mismatch. With an app targeting 16.4 and a hook-provided binary requiring 16.4, Flutter generates a native framework plist declaring 15.0:

Artifact Minimum OS version
Runner.app/Info.plist 16.4
Native binary LC_BUILD_VERSION / minos 16.4
Generated native framework Info.plist 15.0

This was reproduced in successful unsigned release builds on stable 3.47.6 (5fc346839b) and master 3.49.0-0.2.pre (a381eea6b1) using a small C function, without a third-party native runtime or model download. The same reduced sample's device and simulator framework plists become 16.4 with an iOS packaging patch. This evidence does not establish an App Store rejection or a runtime failure.

A workaround in llamadart is a separate Flutter companion package linking an XCFramework through SwiftPM. Hook packages cannot set the generated framework's plist themselves. The hook in this use case downloads prebuilt libraries, so fixing iOS packaging can help independently of changing the compiler hook input.

macOS's fixed hook configuration is confirmed in source, but an equivalent packaging or runtime failure has not been reproduced. macOS has a different framework layout, and its app uses LSMinimumSystemVersion rather than the iOS MinimumOSVersion path.

Proposal

  • Pass the consuming app's iOS/macOS deployment target into native asset configuration, preserving major, minor, and patch versions. Full hook-version support will need coordination with dart-lang/native.
  • Keep iOS framework metadata consistent with the app deployment target rather than a fixed baseline. Preserve Flutter's supported floor and a documented fallback when no app target is available.
  • Take settings from the app build context; dependency packages should not need their own ios/ or macos/ directories.
  • Invalidate affected cached output when the deployment target changes, and test defaults, malformed/unsupported values, iOS device/simulator builds, and supported macOS architectures. macOS needs its own reproduction and validation before extending the packaging change.

Partial implementation: #194146 fixes iOS framework metadata without changing the hooks protocol. It should be a partial fix, leaving this issue open for hook input and macOS work. This proposal does not claim that deployment-version changes alone replace every function of companion packages, including privacy manifest packaging.

Related: #145104 consolidates minimum-version constants; this proposal concerns propagation of a higher app-specific target. dart-lang/native#3594 discusses the Flutter 3.47 baseline, and #191964 raised the fixed iOS value to 15.

Verified reduced iOS reproducer

Create an app with flutter create --platforms=ios native_asset_deployment_repro. Set IPHONEOS_DEPLOYMENT_TARGET = 16.4 for all Runner configurations, add the files below, and add this dependency to the app's pubspec.yaml:

ios_native_asset_probe:
  path: probe

The hook deliberately compiles at a fixed 16.4 minimum to model a prebuilt library with that requirement, rather than deriving it from the current integer hook input. It supports arm64 devices and arm64/x86_64 simulators.

probe/pubspec.yaml:

name: ios_native_asset_probe
description: A tiny native library with a fixed iOS 16.4 requirement.
publish_to: none
environment:
  sdk: ^3.13.0
dependencies:
  hooks: 2.2.0
  code_assets: 2.1.0

probe/src/probe.c:

int native_asset_probe(void) { return 42; }

probe/lib/probe.dart:

import 'dart:ffi';

@Native<Int32 Function()>(symbol: 'native_asset_probe')
external int nativeAssetProbe();

probe/hook/build.dart:

import 'dart:io';

import 'package:code_assets/code_assets.dart';
import 'package:hooks/hooks.dart';

void main(List<String> args) async {
  await build(args, (input, output) async {
    if (!input.config.buildCodeAssets) return;
    final code = input.config.code;
    if (code.targetOS != OS.iOS) {
      throw UnsupportedError('This reduced sample supports iOS only.');
    }
    final simulator = code.iOS.targetSdk == IOSSdk.iPhoneSimulator;
    final architecture = switch (code.targetArchitecture) {
      Architecture.arm64 => 'arm64',
      Architecture.x64 when simulator => 'x86_64',
      _ => throw UnsupportedError('Unsupported iOS architecture.'),
    };
    final sdk = simulator ? 'iphonesimulator' : 'iphoneos';
    final sdkResult = await Process.run('xcrun', [
      '--sdk',
      sdk,
      '--show-sdk-path',
    ]);
    if (sdkResult.exitCode != 0) throw StateError('${sdkResult.stderr}');
    final library = input.outputDirectory.resolve('libdeployment_probe.dylib');
    final source = input.packageRoot.resolve('src/probe.c');
    await Directory.fromUri(input.outputDirectory).create(recursive: true);
    final result = await Process.run('xcrun', [
      '--sdk',
      sdk,
      'clang',
      '-dynamiclib',
      '-target',
      '$architecture-apple-ios16.4${simulator ? '-simulator' : ''}',
      '-isysroot',
      (sdkResult.stdout as String).trim(),
      '-Wl,-install_name,@rpath/libdeployment_probe.dylib',
      source.toFilePath(),
      '-o',
      library.toFilePath(),
    ]);
    if (result.exitCode != 0) throw StateError('${result.stderr}');
    output.dependencies.add(source);
    output.assets.code.add(
      CodeAsset(
        package: input.packageName,
        name: 'probe.dart',
        linkMode: DynamicLoadingBundled(),
        file: library,
      ),
    );
  });
}

lib/main.dart:

import 'package:flutter/material.dart';
import 'package:ios_native_asset_probe/probe.dart';

void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        body: Center(child: Text('Native result: ${nativeAssetProbe()}')),
      ),
    ),
  );
}

Run:

flutter pub get
flutter build ios --release --no-codesign
/usr/libexec/PlistBuddy -c 'Print :MinimumOSVersion' build/ios/iphoneos/Runner.app/Info.plist
/usr/libexec/PlistBuddy -c 'Print :MinimumOSVersion' build/ios/iphoneos/Runner.app/Frameworks/deployment_probe.framework/Info.plist
xcrun vtool -show-build build/ios/iphoneos/Runner.app/Frameworks/deployment_probe.framework/deployment_probe

Unpatched result: app 16.4, binary minos 16.4, native framework plist 15.0. Requires Xcode with the iOS SDK; tested on an Apple Silicon Mac with Xcode 27.0 (27A266a), macOS 26.7.1. Builds require no connected device. No simulator or device execution was performed.

Activity

  1. dcharkes commented on Oct 11, 2026

    @dcharkes
    Contributor

    Native code assets may need a newer Apple deployment target than Flutter's baseline. The consuming app can already set IPHONEOS_DEPLOYMENT_TARGET or MACOSX_DEPLOYMENT_TARGET, but those values do not reach the corresponding native asset hook configuration.

    Yes, this is a known issue. The min os target is hardcoded in various places in the codebase with TODOs to have it from one place. And that one place should come from the native build system invoking flutter assemble. (We had some similar fixes that wired through the c-compiler from CMake in Windows/Linux builds to the hooks inputs.)

    Happy to receive PRs to fix this! 🙏

    cc @bkonyi

  2. leehack commented on Oct 11, 2026

    @leehack
    ContributorAuthor

    @dcharkes here is the PR :)

  3. added
    in triagePresently being triaged by the triage team
    team-iosOwned by iOS platform team
    c: proposalA detailed proposal for a change to Flutter
    toolAffects the "flutter" command-line tool. See also t: labels.
    fyi-toolFor the attention of Flutter Tool team
    and removed
    in triagePresently being triaged by the triage team
    on Oct 11, 2026
  4. puneetkukreja98 commented on Oct 11, 2026

    @puneetkukreja98
    Contributor

    @leehack Thanks for reporting this, routing this to team-ios for further investigation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    c: proposalA detailed proposal for a change to Flutterfyi-toolFor the attention of Flutter Tool teamteam-iosOwned by iOS platform teamtoolAffects the "flutter" command-line tool. See also t: labels.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions