Skip to content

A Cargo subcommand for building iOS and macOS apps.

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT
Notifications You must be signed in to change notification settings

loki-chat/cargo-ipa

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cargo-ipa

A cargo subcommand for compiling .ipa and .app files, for iOS and macOS (respectively).

cargo-ipa exclusively supports macOS, and will only ever support macOS. Because Apple is Apple, you can't compile iOS apps on non-mac systems (doing so requires the iOS SDK, which is closed source and only on macOS).

Installation

cargo-ipa isn't in a "finished" state yet, so it's not on crates.io. You can, however, still install from Git:

cargo install --git https://github.com/loki-chat/cargo-ipa.git

To install with swift-bridge integration:

cargo install --git https://github.com/loki-chat/cargo-ipa.git --feature swift-bridge

Usage

Currently, cargo-ipa can only build unsigned IPA and .app files. Hopefully, in the future, it'll support signing and installing IPAs as well.

Building IPAs & apps

For binary projects, just run cargo ipa build. For library examples, run cargo ipa build -e <example_name> (or --example instead of -e).

By default, cargo-ipa will make 4 files: an IPA for x86_64 iOS, an IPA for aarch64 iOS, an app for x86_64 macOS, and an app for aarch64 macOS. You can limit these with the -p/--platform and -a/--architecture flags; you can set the platform to just iOS or just macOS, and the architecture to just x86_64 or just aarch64 devices. For example, to compile your cool app for M1 (and later) macs, you could run:

cargo ipa build --platform macos --architecture aarch64

(or, if you're a normal person and find architecture impossible to spell: cargo ipa build -p macos -a aarch64.)

App Name

In the Info.plist, Apple requires both an app name (as an ID, eg "my-app"), and a human readable name (eg "My App"). cargo-ipa will set the ID to the package name in Cargo.toml, but needs a human readable name. You can either set this via the name setting (see Configuration), or pass the -n (or --name) argument to cargo-ipa.

Configuration

cargo-ipa reads settings directly from your Cargo.toml. Simply add a package.metadata.cargo-ipa section in your Cargo.toml, and it'll read all the settings from there. For example, to set your app's name, you could add this to your Cargo.toml:

[package.metadata.cargo-ipa]
name = "My App"

Info.plist Overrides

Every macOS/iOS app has an Info.plist file. By defualt, cargo-ipa will automatically set these settings in the Info.plist:

  • CFBundleExecutable: This is the name of the executable in the app. This gets set to the project's name (or library example's name, if you're compiling an example).
  • CFBundleIdentifier: This is the bundle identifier for the app. By default, cargo-ipa sets this to com.<binary-name>, where binary name is the project's name or library example's name.
  • CFBundleName: This is a human-readable bundle identifier, and what appears as the app's name on the device's home screen/app list. cargo-ipa will load this from the -n/--name argument, or the name setting in your configuration.
  • CFBundleVersion: This is the app's version. cargo-ipa will load this from the version listed in your Cargo.toml.
  • CFBundleShortVersionString: This is basically the same as above, but requires a <major version>.<minor version>.<patch version> format. cargo-ipa will load this from Cargo.toml just like above; this can lead to issues if the Cargo.toml version is not in the correct format, and in the future, cargo-ipa should be able to convert your Cargo.toml version into that format, so it's in the valid.

cargo-ipa won't set any other settings in the Info.plist. To set (or override) more settings in the Info.plist, you can use the properties section of cargo-ipa's configuration, like so:

[package.metadata.cargo-ipa.properties]
CFBundleShortVersionString = 0.1.0

Swift-bridge integration

Since many Apple APIs still rely on Swift code, cargo-ipa can integrate with swift-bridge to compile Swift and Rust together. To use it, you need to install cargo-ipa with the swift-bridge feature, and then configure the swift-library and swift-bridges settings.

swift-library is literally just the folder that has your library in it; for example, if your project has my-swift-library/Sources/my-swift-library in it, then set swift-library = "my-swift-library in your Cargo.toml.

swift-bridges is a list of Rust files that actually use swift-bridge's FFI (via the #[swift_bridge::bridge] macro). These are indexed from the project root (the folder that houses Cargo.toml). For example, if you have a file called swift.rs that handles FFI, your setting will probably look like this: swift-bridges = ["src/swift.rs"].

Swift-bridge integration is disabled by default because it adds lots of dependencies, which hurts build times.

Complete list of settings

Here's an example of all the cargo-ipa settings:

[package]
name = "my-app"
description = "My fancy app, compiled for iOS with cargo-ipa"
authors = ["Me"]
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
repository = "https://github.com/loki-chat/cargo-ipa"

[package.metadata.cargo-ipa]
name = "My App"
swift-bridges = ["src/swift.rs"]
swift-library = "swift-library"

[package.metadata.cargo-ipa.properties]
MinimumOSVersion = "14.0.0"

Signing IPAs

Currently, cargo-ipa doesn't support signing the IPA files. It'll just spit out a raw, unsigned IPA file, which you can sign using any existing IPA signer. IPA will hopefully be added in the future.

Build Scripts

If you need to link against a Swift library in build.rs, you can add cargo-ipa as a build dependency for macOS &/ iOS, then run cargo_ipa::compile_and_link_swift() in your build.rs. cargo-ipa will read what package to compile from your Cargo.toml, just like normal. This does not require the swift-bridge feature; to cut down compile times, you can generate your bindings beforehand and then use this afterwards to always link with those same bindings.

Example:

// In build.rs
#[cfg(any(target_os = "macos", target_os = "ios"))]
fn main() {
    cargo_ipa::compile_and_link_swift().unwrap();
}

#[cfg(not(any(target_os = "macos", target_os = "ios")))]
fn main() {}
# In Cargo.toml
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.build-dependencies]
cargo-ipa = { git = "https://github.com/loki-chat/cargo-ipa.git" }

Note that this will only compile and link a Swift package; if you also need to generate FFI bindings, you'll need to run cargo_ipa::generate_bindings(), which requires the swift-bridge feature. To do that:

// In build.rs
#[cfg(any(target_os = "macos", target_os = "ios"))]
fn main() {
    cargo_ipa::generate_bindings().unwrap();
    cargo_ipa::compile_and_link_swift().unwrap();
}

#[cfg(not(any(target_os = "macos", target_os = "ios")))]
fn main() {}
# In Cargo.toml
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.build-dependencies]
cargo-ipa = { git = "https://github.com/loki-chat/cargo-ipa.git", features = ["swift-bridge"] }

This is not recommended, because the dependencies added by swift-bridge will hurt compile times.

About

A Cargo subcommand for building iOS and macOS apps.

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages