iOS & macOS

Flutter libraries targeting iOS and macOS use cocoapods for dependencies, so this page will walk you through setting that up with FRB.

The simplist way the author of this page found to integrate with cocoapods for all Apple platforms (iOS, iOS Simulator, and macOS) is to create an XCFramework. While you don't need to know what an XCFramework is to follow this guide, if you want to understand how this all works behind the scenes, I'd recommend doing a quick Google search on "What is an XCFramework?".

Also, this page will introduce the iOS & macOS build script (build-apple.sh) to compile your Rust library for all Apple platforms. Note: unlike all of the other build scripts presented in this guide (which we can run on any host OS), build-apple.sh must be run on macOS.

Directory Tree

We will need to create several files for both iOS and macOS to:

  • Prevent our Rust symbols from being accidentally stripped
  • Bundle our "XCFramework" with our Flutter library

ios/Classes/EnforceBundling.swift and macos/Classes/EnforceBundling.swift

public func dummyMethodToEnforceBundling() -> Int64 {
  return dummy_method_to_enforce_bundling()
}
let dummyVar = dummyMethodToEnforceBundling();

ios/Frameworks/.gitkeep and macos/Frameworks/.gitkeep

No file contents here; simply add a blank file (i.e., touch .gitkeep in bash).

ios/.gitignore

Flutter/
Runner/
Frameworks/*
!Frameworks/.gitkeep

macos/.gitignore

Flutter/
Frameworks/*
!Frameworks/.gitkeep

ios/flutter_library_name.podspec and macos/flutter_library_name.podspec (for Cocoapods)

We cannot use the CMake approach taken on other platforms with Cocoapods, so we do something a bit different here. .podspec files are actually just ruby files; due to this observation, we can access the system shell to make arbitrary changes. While we could download and extract our Rust binaries for iOS/macOS in ruby directly, it is much more straightforward to simply use bash/zsh.

Replace all instances of library_name and LibraryName below with your library name. Also, replace other variables (i.e. YourGitHubAccount and repo_name) as needed.

Note: the same exact flutter_library_name.podspec is used for both iOS and macOS; you can thank the XCFramework for this simplicity.

release_tag_name = 'library_name-v0.0.0' # generated; do not edit

# We cannot distribute the XCFramework alongside the library directly,
# so we have to fetch the correct version here.
framework_name = 'LibraryName.xcframework'
remote_zip_name = "#{framework_name}.zip"
url = "https://github.com/YourGitHubAccount/repo_name/releases/download/#{release_tag_name}/#{remote_zip_name}"
local_zip_name = "#{release_tag_name}.zip"
`
cd Frameworks
rm -rf #{framework_name}

if [ ! -f #{local_zip_name} ]
then
  curl -L #{url} -o #{local_zip_name}
fi

unzip #{local_zip_name}
cd -
`

Pod::Spec.new do |spec|
  spec.name          = 'library_name'
  spec.version       = '0.0.1'
  spec.license       = { :file => '../LICENSE' }
  spec.homepage      = 'https://github.com/YourGitHubAccount/repo_name'
  spec.authors       = { 'Your Name' => 'your-email@example.com' }
  spec.summary       = 'iOS/macOS Flutter bindings for library_name'

  spec.source              = { :path => '.' }
  spec.source_files        = 'Classes/**/*'
  spec.public_header_files = 'Classes/**/*.h'
  spec.vendored_frameworks = "Frameworks/#{framework_name}"

  spec.ios.deployment_target = '11.0'
  spec.osx.deployment_target = '10.11'
end

Build Script (/scripts/build-apple.sh)

Replace library_name and LibraryName below as needed.

#!/bin/bash

# Setup
BUILD_DIR=platform-build
mkdir $BUILD_DIR
cd $BUILD_DIR

# Build static libs
for TARGET in \
        aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim \
        x86_64-apple-darwin aarch64-apple-darwin
do
    rustup target add $TARGET
    cargo build -r --target=$TARGET
done

# Create XCFramework zip
FRAMEWORK="LibraryName.xcframework"
LIBNAME=liblibrary_name.a
mkdir mac-lipo ios-sim-lipo
IOS_SIM_LIPO=ios-sim-lipo/$LIBNAME
MAC_LIPO=mac-lipo/$LIBNAME
lipo -create -output $IOS_SIM_LIPO \
        ../target/aarch64-apple-ios-sim/release/$LIBNAME \
        ../target/x86_64-apple-ios/release/$LIBNAME
lipo -create -output $MAC_LIPO \
        ../target/aarch64-apple-darwin/release/$LIBNAME \
        ../target/x86_64-apple-darwin/release/$LIBNAME
xcodebuild -create-xcframework \
        -library $IOS_SIM_LIPO \
        -library $MAC_LIPO \
        -library ../target/aarch64-apple-ios/release/$LIBNAME \
        -output $FRAMEWORK
zip -r $FRAMEWORK.zip $FRAMEWORK

# Cleanup
rm -rf ios-sim-lipo mac-lipo $FRAMEWORK