<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://johnwick.cc/index.php?action=history&amp;feed=atom&amp;title=Developing_macOS_Applications_in_Rust</id>
	<title>Developing macOS Applications in Rust - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://johnwick.cc/index.php?action=history&amp;feed=atom&amp;title=Developing_macOS_Applications_in_Rust"/>
	<link rel="alternate" type="text/html" href="https://johnwick.cc/index.php?title=Developing_macOS_Applications_in_Rust&amp;action=history"/>
	<updated>2026-05-06T20:12:53Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.44.1</generator>
	<entry>
		<id>https://johnwick.cc/index.php?title=Developing_macOS_Applications_in_Rust&amp;diff=46&amp;oldid=prev</id>
		<title>PC: Created page with &quot;For training AI models, I use an Ubuntu server with 16 cores alongside a MacBook Pro M4 Max. During training, I frequently monitor CPU and memory usage to ensure all cores are utilized efficiently and to estimate memory consumption. However, I missed having on macOS a system monitor that provides a visualization similar to what I’m used to on Ubuntu. So, I built one — a simple System Monitor application that displays CPU and memory usage.  Although it’s easy to d...&quot;</title>
		<link rel="alternate" type="text/html" href="https://johnwick.cc/index.php?title=Developing_macOS_Applications_in_Rust&amp;diff=46&amp;oldid=prev"/>
		<updated>2025-11-14T05:16:40Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;For training AI models, I use an Ubuntu server with 16 cores alongside a MacBook Pro M4 Max. During training, I frequently monitor CPU and memory usage to ensure all cores are utilized efficiently and to estimate memory consumption. However, I missed having on macOS a system monitor that provides a visualization similar to what I’m used to on Ubuntu. So, I built one — a simple System Monitor application that displays CPU and memory usage.  Although it’s easy to d...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;For training AI models, I use an Ubuntu server with 16 cores alongside a MacBook Pro M4 Max. During training, I frequently monitor CPU and memory usage to ensure all cores are utilized efficiently and to estimate memory consumption.&lt;br /&gt;
However, I missed having on macOS a system monitor that provides a visualization similar to what I’m used to on Ubuntu. So, I built one — a simple System Monitor application that displays CPU and memory usage.&lt;br /&gt;
&lt;br /&gt;
Although it’s easy to develop such a Rust-based application, I wanted to go a step further and create a fully native macOS app, complete with an icon, bundle metadata, and Launchpad integration. Fortunately, this only takes a few additional steps once your Rust application is working.&lt;br /&gt;
&lt;br /&gt;
Step 1: Install cargo-bundle&lt;br /&gt;
cargo install cargo-bundle&lt;br /&gt;
This tool packages your Rust application into a proper .app bundle for macOS.&lt;br /&gt;
&lt;br /&gt;
Step 2: Prepare an App Icon&lt;br /&gt;
Create or download a 1024×1024 icon. In my case, I generated one using ChatGPT.&lt;br /&gt;
Save it as&lt;br /&gt;
&lt;br /&gt;
Step 3: Add Bundle Metadata to Cargo.toml&lt;br /&gt;
[package.metadata.bundle]&lt;br /&gt;
name = &amp;quot;SysMonitor&amp;quot;&lt;br /&gt;
identifier = &amp;quot;xx.yyy.sysmonitor&amp;quot;&lt;br /&gt;
icon = [&amp;quot;assets/SysMonitor.icns&amp;quot;]       &lt;br /&gt;
#resources = [&amp;quot;assets/&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
Here you define the app name, bundle identifier, and icon set. Optional: you can later add a resources section for assets.&lt;br /&gt;
&lt;br /&gt;
Step 4: Generate the .icns Icon Set&lt;br /&gt;
Use the following shell script (make_icns.sh) to convert your 1024×1024 PNG into an Apple .icns bundle.&lt;br /&gt;
#!/usr/bin/env bash&lt;br /&gt;
set -euo pipefail&lt;br /&gt;
&lt;br /&gt;
# make_icns.sh&lt;br /&gt;
# Usage: ./make_icns.sh &amp;lt;input-image&amp;gt; &amp;lt;output-dir&amp;gt;&lt;br /&gt;
# Example: ./make_icns.sh SysMonitor_1024.png ./assets&lt;br /&gt;
&lt;br /&gt;
if [[ $# -ne 2 ]]; then&lt;br /&gt;
  echo &amp;quot;Usage: $0 &amp;lt;input-image&amp;gt; &amp;lt;output-dir&amp;gt;&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
INPUT=&amp;quot;$1&amp;quot;&lt;br /&gt;
OUTDIR=&amp;quot;$2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# ---- Preflight checks -------------------------------------------------------&lt;br /&gt;
if [[ ! -f &amp;quot;$INPUT&amp;quot; ]]; then&lt;br /&gt;
  echo &amp;quot;Error: input file not found: $INPUT&amp;quot; &amp;gt;&amp;amp;2&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if ! command -v sips &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then&lt;br /&gt;
  echo &amp;quot;Error: &amp;#039;sips&amp;#039; not found (macOS only). Install Xcode command line tools.&amp;quot; &amp;gt;&amp;amp;2&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if ! command -v iconutil &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then&lt;br /&gt;
  echo &amp;quot;Error: &amp;#039;iconutil&amp;#039; not found. Install Xcode or its command line tools.&amp;quot; &amp;gt;&amp;amp;2&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
mkdir -p &amp;quot;$OUTDIR&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Derive names&lt;br /&gt;
BASENAME=&amp;quot;$(basename &amp;quot;$INPUT&amp;quot;)&amp;quot;&lt;br /&gt;
STEM=&amp;quot;${BASENAME%.*}&amp;quot;&lt;br /&gt;
ICONSET_DIR=&amp;quot;$OUTDIR/${STEM}.iconset&amp;quot;&lt;br /&gt;
ICNS_PATH=&amp;quot;$OUTDIR/$STEM.icns&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# ---- Validate input dimensions ---------------------------------------------&lt;br /&gt;
# We want at least 1024x1024 for best results.&lt;br /&gt;
WIDTH=$(sips -g pixelWidth &amp;quot;$INPUT&amp;quot; 2&amp;gt;/dev/null | awk &amp;#039;/pixelWidth:/ {print $2}&amp;#039;)&lt;br /&gt;
HEIGHT=$(sips -g pixelHeight &amp;quot;$INPUT&amp;quot; 2&amp;gt;/dev/null | awk &amp;#039;/pixelHeight:/ {print $2}&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
if [[ -z &amp;quot;${WIDTH:-}&amp;quot; || -z &amp;quot;${HEIGHT:-}&amp;quot; ]]; then&lt;br /&gt;
  echo &amp;quot;Error: couldn&amp;#039;t read image dimensions from: $INPUT&amp;quot; &amp;gt;&amp;amp;2&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [[ &amp;quot;$WIDTH&amp;quot; -ne &amp;quot;$HEIGHT&amp;quot; ]]; then&lt;br /&gt;
  echo &amp;quot;Error: input image must be square. Got ${WIDTH}x${HEIGHT}.&amp;quot; &amp;gt;&amp;amp;2&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [[ &amp;quot;$WIDTH&amp;quot; -lt 1024 ]]; then&lt;br /&gt;
  echo &amp;quot;Error: input image must be at least 1024x1024. Got ${WIDTH}x${HEIGHT}.&amp;quot; &amp;gt;&amp;amp;2&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
# Create a 1024x1024 working copy (downscale if larger, keep quality)&lt;br /&gt;
TMPDIR=&amp;quot;$(mktemp -d)&amp;quot;&lt;br /&gt;
trap &amp;#039;rm -rf &amp;quot;$TMPDIR&amp;quot;&amp;#039; EXIT&lt;br /&gt;
WORK_1024=&amp;quot;$TMPDIR/icon_1024.png&amp;quot;&lt;br /&gt;
sips -s format png -z 1024 1024 &amp;quot;$INPUT&amp;quot; --out &amp;quot;$WORK_1024&amp;quot; &amp;gt;/dev/null&lt;br /&gt;
&lt;br /&gt;
# ---- Build iconset ----------------------------------------------------------&lt;br /&gt;
rm -rf &amp;quot;$ICONSET_DIR&amp;quot;&lt;br /&gt;
mkdir -p &amp;quot;$ICONSET_DIR&amp;quot;&lt;br /&gt;
&lt;br /&gt;
sips -z 16 16     &amp;quot;$WORK_1024&amp;quot; --out &amp;quot;$ICONSET_DIR/icon_16x16.png&amp;quot;      &amp;gt;/dev/null&lt;br /&gt;
sips -z 32 32     &amp;quot;$WORK_1024&amp;quot; --out &amp;quot;$ICONSET_DIR/icon_16x16@2x.png&amp;quot;   &amp;gt;/dev/null&lt;br /&gt;
sips -z 32 32     &amp;quot;$WORK_1024&amp;quot; --out &amp;quot;$ICONSET_DIR/icon_32x32.png&amp;quot;      &amp;gt;/dev/null&lt;br /&gt;
sips -z 64 64     &amp;quot;$WORK_1024&amp;quot; --out &amp;quot;$ICONSET_DIR/icon_32x32@2x.png&amp;quot;   &amp;gt;/dev/null&lt;br /&gt;
sips -z 128 128   &amp;quot;$WORK_1024&amp;quot; --out &amp;quot;$ICONSET_DIR/icon_128x128.png&amp;quot;    &amp;gt;/dev/null&lt;br /&gt;
sips -z 256 256   &amp;quot;$WORK_1024&amp;quot; --out &amp;quot;$ICONSET_DIR/icon_128x128@2x.png&amp;quot; &amp;gt;/dev/null&lt;br /&gt;
sips -z 256 256   &amp;quot;$WORK_1024&amp;quot; --out &amp;quot;$ICONSET_DIR/icon_256x256.png&amp;quot;    &amp;gt;/dev/null&lt;br /&gt;
sips -z 512 512   &amp;quot;$WORK_1024&amp;quot; --out &amp;quot;$ICONSET_DIR/icon_256x256@2x.png&amp;quot; &amp;gt;/dev/null&lt;br /&gt;
sips -z 512 512   &amp;quot;$WORK_1024&amp;quot; --out &amp;quot;$ICONSET_DIR/icon_512x512.png&amp;quot;    &amp;gt;/dev/null&lt;br /&gt;
cp &amp;quot;$WORK_1024&amp;quot;         &amp;quot;$ICONSET_DIR/icon_512x512@2x.png&amp;quot;  # 1024x1024&lt;br /&gt;
&lt;br /&gt;
# ---- Create .icns -----------------------------------------------------------&lt;br /&gt;
iconutil -c icns &amp;quot;$ICONSET_DIR&amp;quot; -o &amp;quot;$ICNS_PATH&amp;quot;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;Iconset: $ICONSET_DIR&amp;quot;&lt;br /&gt;
echo &amp;quot;ICNS:    $ICNS_PATH&amp;quot;&lt;br /&gt;
echo&lt;br /&gt;
&lt;br /&gt;
Then, generate the icon set:&lt;br /&gt;
./icons/mkicon.sh ./icons/SysMonitor.png ./assets&lt;br /&gt;
&lt;br /&gt;
Step 5: Build the App Bundle&lt;br /&gt;
cargo bundle --release&lt;br /&gt;
APP=./target/release/bundle/osx/SysMonitor.app&lt;br /&gt;
&lt;br /&gt;
Step 6: Test the Application&lt;br /&gt;
open $APP   &lt;br /&gt;
&lt;br /&gt;
Step 7: Troubleshooting&lt;br /&gt;
&lt;br /&gt;
If you run into issues, here are some quick diagnostics:&lt;br /&gt;
# Quick way to edit/check the plist&lt;br /&gt;
/usr/libexec/PlistBuddy -c &amp;quot;Print&amp;quot; $APP/Contents/Info.plist&lt;br /&gt;
&lt;br /&gt;
# Check that the .icns is inside the bundle&lt;br /&gt;
ls -l $APP/Contents/Resources&lt;br /&gt;
&lt;br /&gt;
# Verify Info.plist points to the icon&lt;br /&gt;
/usr/libexec/PlistBuddy -c &amp;quot;Print :CFBundleIconFile&amp;quot; $APP/Contents/Info.plist&lt;br /&gt;
# If it prints AppIcon.icns, it&amp;#039;s good.&lt;br /&gt;
&lt;br /&gt;
If needed, extract the .icns to inspect it:&lt;br /&gt;
# Validate the .icns itself (most common cause)&lt;br /&gt;
mkdir -p tmp/SM.iconset&lt;br /&gt;
iconutil -c iconset $APP/Contents/Resources/SysMonitor.icns -o tmp/SM.iconset&lt;br /&gt;
ls -1 tmp/SM.iconset&lt;br /&gt;
&lt;br /&gt;
Step 8: Install the App&lt;br /&gt;
You can copy the app bundle to either your system or user Applications folder:&lt;br /&gt;
cp -R target/release/bundle/osx/SysMonitor.app /Applications/&lt;br /&gt;
# or&lt;br /&gt;
cp -R target/release/bundle/osx/SysMonitor.app ~/Applications/&lt;br /&gt;
&lt;br /&gt;
Now it appears in Launchpad, Spotlight search, and you can pin it to the Dock.&lt;br /&gt;
&lt;br /&gt;
Step 9: Adding Resources&lt;br /&gt;
If your app uses additional resources (icons, images, fonts, configuration files, etc.), add them in Cargo.toml:&lt;br /&gt;
[package.metadata.bundle]&lt;br /&gt;
...&lt;br /&gt;
resources = [&amp;quot;assets&amp;quot;, &amp;quot;images/**/*.png&amp;quot;, &amp;quot;data/config_defaults.json&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
These files will be copied into your bundle at SysMonitor.app/Contents/Resources/...&lt;br /&gt;
&lt;br /&gt;
Typical examples include:&lt;br /&gt;
* 		Images, icons, cursors&lt;br /&gt;
* 		Fonts (for custom UI frameworks like Iced)&lt;br /&gt;
* 		Shaders, CSV/JSON, templates, localization files&lt;br /&gt;
* 		Default configuration files&lt;br /&gt;
* 		LICENSE, README, etc.&lt;br /&gt;
&lt;br /&gt;
All resources are read-only inside the bundle.&lt;br /&gt;
Accessing Resources at Runtime (Rust)&lt;br /&gt;
When running from the bundle, resources live under .../Contents/Resources. When running with cargo run, they’re in your project directory.&lt;br /&gt;
&lt;br /&gt;
A small helper function can make this transparent:&lt;br /&gt;
use std::{&lt;br /&gt;
    env,&lt;br /&gt;
    path::{Path, PathBuf},&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#[allow(dead_code)]&lt;br /&gt;
fn resource_path(rel: impl AsRef&amp;lt;Path&amp;gt;) -&amp;gt; PathBuf {&lt;br /&gt;
    // Path to the running executable&lt;br /&gt;
    let exe = env::current_exe().unwrap_or_else(|_| PathBuf::from(&amp;quot;.&amp;quot;));&lt;br /&gt;
    // If we’re inside My.app/Contents/MacOS, hop to Contents/Resources&lt;br /&gt;
    if let Some(macos_dir) = exe.parent() {&lt;br /&gt;
        if macos_dir.ends_with(&amp;quot;MacOS&amp;quot;) {&lt;br /&gt;
            if let Some(contents_dir) = macos_dir.parent() {&lt;br /&gt;
                let res = contents_dir.join(&amp;quot;Resources&amp;quot;);&lt;br /&gt;
                return res.join(rel.as_ref());&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    // Fallback for `cargo run`: use repo-relative path&lt;br /&gt;
    PathBuf::from(rel.as_ref())&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Usage example:&lt;br /&gt;
let icon_path = resource_path(&amp;quot;assets/SysMonitor.icns&amp;quot;);&lt;br /&gt;
let out=format!(&amp;quot;IconPath: {:?}&amp;quot;, icon_path);&lt;br /&gt;
let _ =std::fs::write(&amp;quot;sys_monitor/output.txt&amp;quot;, out);&lt;br /&gt;
&lt;br /&gt;
When you run the bundle:&lt;br /&gt;
open $APP&lt;br /&gt;
# in output.txt:&lt;br /&gt;
# IconPath: &amp;quot;xxxxx/target/release/bundle/osx/SysMonitor.app/Contents/Resources/assets/SysMonitor.icns&amp;quot;&lt;br /&gt;
&lt;br /&gt;
And when you run via Cargo:&lt;br /&gt;
cargo run --release&lt;br /&gt;
# in output.txt:&lt;br /&gt;
# IconPath: &amp;quot;assets/SysMonitor.icns&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Notes on Writable Files&lt;br /&gt;
Only include read-only assets inside the .app bundle. For writable files like logs or user settings, use a per-user directory such as:&lt;br /&gt;
~/Library/Application Support/SysMonitor/&lt;br /&gt;
You can conveniently locate this path via the directories crate.&lt;br /&gt;
&lt;br /&gt;
Summary&lt;br /&gt;
Building a native macOS app in Rust is straightforward with cargo-bundle. Once your application logic is ready, adding a proper icon, bundle metadata, and resource management takes just a few steps — resulting in a polished, fully integrated macOS experience.&lt;/div&gt;</summary>
		<author><name>PC</name></author>
	</entry>
</feed>