Flutter for iOS setup not working

Hi,
I can’t get a flake configured, which is compiling a flutter app for iOS.
Wen issuing flutter run
I get

Launching lib/main.dart on iPhone 15 Pro Max in debug mode...
Running Xcode build...                                                  
Xcode build done.                                            5.1s
Failed to build iOS app

with the error

Lexical or Preprocessor Issue (Xcode): 'UIKit/UIKit.h' file not found
/Users/patmuk/code/own/sherry/system/sherry/shell_flutter/ios/Pods/Target%20Support%20Files/wakelock_plus/wakelock_plus-prefix.pch:1:8

Basically the cocapod installed 3rd party lib can’t compile, as it does not find the UIKit.h from the iOS SDK.

My flake looks like this:

{
  description = "Flutter toolchain. Installs all tools needed for flutter, with versions pinned for this project. Rust's own tooling handles the rust toolchain.";
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
    android-nixpkgs.url = "github:tadfisher/android-nixpkgs";
  };

  outputs = { self, nixpkgs, flake-utils, android-nixpkgs, ... }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = import nixpkgs {
          inherit system;
          config = {
            allowUnfree = true;
            android_sdk = {
              accept_license = true;
            };
          };
        };
        androidCustomPackage = android-nixpkgs.sdk.${system} (
          sdkPkgs: with sdkPkgs; [
            cmdline-tools-latest
            build-tools-30-0-3
            build-tools-33-0-2
            build-tools-34-0-0
            platform-tools
            emulator
            #patcher-v4
            platforms-android-28
            platforms-android-29
            platforms-android-30
            platforms-android-31
            platforms-android-32
            platforms-android-33
            platforms-android-34
          ]
        );
        pinnedJDK = pkgs.jdk17;
        xcode_version = "15.3.0";
        flutter_rust_bridge_codegen = import ./nix/flutter_rust_bridge_codegen.nix {
          inherit pkgs;
        };
      in
      {
        devShells. default = pkgs.mkShell {
          name = "My-flutter-dev-shell";
          buildInputs = with pkgs; [
            # packages needed for this project
            flutter
            cocoapods
            # darwin.xcode_15_1
            xcodes
            # fastlane
            # android-studio
            pinnedJDK
            androidCustomPackage
            flutter_rust_bridge_codegen
            # needed by rust
            llvmPackages.bintools # use lld instead of ld (faster)
            libiconv
            darwin.apple_sdk.frameworks.CoreServices
            # list of frameworks: https://github.com/NixOS/nixpkgs/blob/4fb6fe0206261ee4a5849ab06daad265478eac2c/pkgs/os-specific/darwin/apple-sdk/frameworks.nix
            # needed by cocapods for 'UIKit/UIKit.h' file not found
            darwin.apple_sdk.frameworks.Foundation
            # darwin.apple_sdk.frameworks.SystemConfiguration
          ];
          shellHook = ''
            # installs or checks for the right xcode version
            echo "installing xcode ${xcode_version}"
            xcodes install ${xcode_version} --experimental-unxip
            xcodes select ${xcode_version}
            #  GRADLE_USER_HOME=$HOME/gradle-user-home
            #  GRADLE_HOME=$HOME/gradle-home
          '';
          JAVA_HOME = pinnedJDK;
          # ANDROID_HOME = "${androidCustomPackage}/share/android-sdk";
          # ANDROID_SDK_HOME = "${androidCustomPackage}/share/android-sdk";
          # GRADLE_USER_HOME = "/home/admin0101/.gradle";
          # GRADLE_OPTS = "-Dorg.gradle.project.android.aapt2FromMavenOverride=${androidCustomPackage}/share/android-sdk/build-tools/34.0.0/aapt2";
        };
      }
    );
}

Note that I did not try to get a flutter build for Android running yet. The error I am getting there is that it tries to install the idk and fails, as it can’t write to the /nix/store … but first things first - getting the iOS build running :slight_smile:

So, I am unsure what the problem is. If there a package missing, like " darwin.apple_sdk.frameworks.Foundation"? I could not find a “iOS UIKit” one though. Is that even the iOS SDK? Or the MacSDK?
Or is it a nix/store problem? As building in Xcode gives me this error message:

Target debug_unpack_ios failed: Error: Flutter failed to create a directory at "/nix/store/09v9fm2zq664rhdnbvyg2wr9j4j7ak2i-flutter-3.19.0-unwrapped/bin/cache/artifacts".

Is this ok to ignore or supposed to work? I know nix/store is read-only - I assume that flutter run/flutter build ios is doing it differently, but the env is not providing the iOS SDK, thus UIKit.h is missing for the 3rd party cocapod to be compiled …

Did anyone got it running? When looking for issues with ‘nix flutter’ I only find Android related problems (I am looking forward to … :frowning: ).

Many thanks for reading through my wall of text!

I set up a new flutter project with flutter create, and it looks like the “UiKit.h not found” is a general issue with Xcode 15.3 … I will update on that once I solved it.

So, in the new project I removed cocapods 3rd party libs.
I use the same flake posted above:
Xcode 15.3, flutter 3.19
and ran
flutter create new_flutter
flutter build ios
so a pretty striped-down setup, but get this error:

Error (Xcode): Target debug_unpack_ios failed: Error: Flutter failed to create a directory at
"/nix/store/09v9fm2zq664rhdnbvyg2wr9j4j7ak2i-flutter-3.19.0-unwrapped/bin/cache/artifacts".

Now I am looking why this happens besides a solved issue and if I can symlink the cache directory myself using nixpkgs.config.lib.file.mkOutOfStoreSymlink.

I will keep updating here - but if anyone has any pointers I would be very grateful :slight_smile:

A small update, which is even more confusing:
Android build works:

flutter build apk                          

Font asset "MaterialIcons-Regular.otf" was tree-shaken, reducing it from 1645184 to 1384 bytes (99.9% reduction). Tree-shaking can be disabled by providing the --no-tree-shake-icons flag when building your app.
Running Gradle task 'assembleRelease'...                           25.7s
✓  Built build/app/outputs/flutter-apk/app-release.apk (17.6MB).

iOS still has the problem that it tries to create a folder in the cache folder in the nix/store … did no one build for iOS?

Following the advice from @brokenpylons in nix github, I installed flutter locally in the project folder.

However, I can build in Xcode, but from CLI I am getting

Lexical or Preprocessor Issue (Xcode): 'UIKit/UIKit.h' file not found
clean_flutter/new_flutter/build/ios/Release-iphoneos/Flutter.framework/Headers/FlutterAppDelegate.h:7:8

I am assuming that this is due to some env variables not present. Maybe because I installed Xcode via xcodes.

Has anybody a working setup for compiling flutter apps for iOS?

I am about to give up and install everything globally. I would really appreciate any help.

If it helps, here are my project local nix files:

flake.nix

{
  description = "Flutter toolchain. Installs all tools needed for flutter, with versions pinned for this project. Rust's own tooling handles the rust toolchain.";
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
    android-nixpkgs.url = "github:tadfisher/android-nixpkgs";
  };

  outputs = { nixpkgs, flake-utils, android-nixpkgs, ... }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = import nixpkgs {
          inherit system;
          config = {
            allowUnfree = true;
            android_sdk = {
              accept_license = true;
            };
          };
        };
        androidCustomPackage = android-nixpkgs.sdk.${system} (
          sdkPkgs: with sdkPkgs; [
            cmdline-tools-latest
            build-tools-30-0-3
            build-tools-33-0-2
            build-tools-34-0-0
            platform-tools
            emulator
            #patcher-v4
            platforms-android-28
            platforms-android-29
            platforms-android-30
            platforms-android-31
            platforms-android-32
            platforms-android-33
            platforms-android-34
          ]
        );
        pinnedJDK = pkgs.jdk17;
        xcode_version = "15.3.0";
        flutter_rust_bridge_codegen = import ./nix/flutter_rust_bridge_codegen.nix {
          inherit pkgs;
        };
        local_flutter_path = "$PWD/.flutter-local";
        flutter-local = import ./nix/flutter-local.nix {
          inherit pkgs local_flutter_path;
        };
      in
      {
        devShells. default = pkgs.mkShell {
          name = "My-flutter-dev-shell";
          buildInputs = with pkgs; [
            cocoapods
            xcodes
            pinnedJDK
            androidCustomPackage
            flutter_rust_bridge_codegen
            # darwin.apple_sdk.frameworks.UIFoundation
            # darwin.apple_sdk.frameworks.AppKit
            # darwin.apple_sdk.frameworks.CoreServices
            # list of frameworks: https://github.com/NixOS/nixpkgs/blob/2b6ee326ad047870526d9a3ae88dfd0197da898d/pkgs/os-specific/darwin/apple-sdk/frameworks.nix
            # needed by cocapods for 'UIKit/UIKit.h' file not found
            # darwin.apple_sdk.frameworks.Foundation
            # darwin.apple_sdk.frameworks.CoreServices
            # darwin.apple_sdk.frameworks.CoreGraphics
            # darwin.apple_sdk.frameworks.SystemConfiguration
          ];
          shellHook = ''
            # installs flutter locally, if not there already
            ${flutter-local.unpack_flutter}/bin/unpack_flutter
            export PATH="${local_flutter_path}/flutter/bin:$PATH"
            # installs or checks for the right xcode version
            echo "installing xcode ${xcode_version}"
            mkdir -p "$PWD/.xcode"zi
            xcodes install ${xcode_version} --experimental-unxip
            xcodes select ${xcode_version}
            #  GRADLE_USER_HOME=$HOME/gradle-user-home
            #  GRADLE_HOME=$HOME/gradle-home
          '';
          JAVA_HOME = pinnedJDK;
          # ANDROID_HOME = "${androidCustomPackage}/share/android-sdk";
          # ANDROID_SDK_HOME = "${androidCustomPackage}/share/android-sdk";
          # GRADLE_USER_HOME = "/home/admin0101/.gradle";
          # GRADLE_OPTS = "-Dorg.gradle.project.android.aapt2FromMavenOverride=${androidCustomPackage}/share/android-sdk/build-tools/34.0.0/aapt2";
        };
      }
    );
}

nix/flutter-local.nix

{ pkgs ? import <nixpkgs> { }, local_flutter_path }:
# Downloads flutter into a local directory (passed as local_flutter_path) and returns the bin path so the calling shellHook can set it to the PATH
# call (e.g. in ShellHook) with
#   ${flutter-local.unpack_flutter}/bin/unpack_flutter
#   export PATH="${local_flutter_path}/flutter/bin:$PATH"
rec {
  flutterSource-aarch64-darwin = pkgs.fetchurl {
    url = "https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_arm64_3.19.5-stable.zip";
    hash = "sha256-HXHsHs2bzt9Xaqp6cUyK/S/Qk028jqCfSx3DF31HX/Q=";
  };

  unpack_flutter = pkgs.writeShellApplication {
    name = "unpack_flutter";
    runtimeInputs = with pkgs; [
      unzip
    ];

    text = ''
      flutter_local_dir=${local_flutter_path}
      flutter_bin_dir="$flutter_local_dir"/flutter/bin
      flutter_bin_file="$flutter_bin_dir"/flutter

      echo "flutter needs local installation? ..."

      if [ -f "$flutter_bin_file" ]; then
        echo "flutter is already installed locally in '$flutter_local_dir'"
      else
        echo "... installing flutter locally in '$flutter_local_dir'"
        unzip ${flutterSource-aarch64-darwin} -d "$flutter_local_dir"
      fi
    '';
  };
}

Meanwhile I installed all tooling manually the traditional way, using home-brew (at least from a nix file).
It started to work, when I installed cocoapods manually ( sudo gem install cocoapods), for which I needed a more recent version of ruby, which I installed and PATHed using nix.

I activated my flake.nix again, uncommenting one item at a time. But without libiconv and darwin.apple_sdk.frameworks.CoreServices the rust part of my project does not build - and with only these and nothing else (in an empty shellHook) it still doesn’t work (Lexical or Preprocessor Issue (Xcode): 'UIKit/UIKit.h' file not found).

Unfortunately I can’t use nix for my flutter-rust-bridge project - and I wonder if anyone is able to use nix for flutter build ios.
If so, please post your configuration here!

I think flutter is using Xcode so heavily, which is probably so much tied to the MacOS (one can’t install certain old versions on new processors), so that ‘freeze the versions with nix for later reproducibility’ is practically not needed - as one might anyways need to support the in-future-latest mobile os, which would require the latest toolchain. And regarding 3rd party libs, etc, cargo.lock and pubspec.lock (and Podfile.lock) do a good job already.

Not going too philosophical I wonder if nix is needed with quite modern toolchains … though I love the idea of deterministic environments.

Anyhow, if somebody made it work - please let me know!

You need to change mkShell to makShellNoCC
to use c compiler from xcode instead the one from nix

1 Like

@ianluo wow, thanks, that is actually the solution!
Did not think of the compiler being bundled in mkShell and actually being the problem! But in hindsight it is totally logical. Thanks a lot!
(For anyone reading this solution: There is a small typo, it is mkShellNoCC.)

1 Like

Just wanted to add that I experienced this bug when using devenv for a Flutter project. While I was not able to solve the error, this forum post and the reply was essential to understanding my bug. Thanks a lot!

Update: I have filed a bug report with Devenv: `flutter run` with target IOS fails with message Lexical or Preprocessor Issue (Xcode): 'UIKit/UIKit.h' file not found · Issue #1174 · cachix/devenv · GitHub

1 Like