I’ve built a macOS screensaver that uses orbital calculations to plot the current position of the international space station. It’s currently available for download on github.

Preview of the Screensaver

How it works

To make this work, I built a small ffi wrapper around my ported rust library. From there, I imported/linked it to a screensaver Objective-C project, and drew the computed trajectories using Bezier paths.

In order to fetch the parameters for the orbit calculations, I set up a CloudFront distribution to cache celestrak’s two-line element sets, and wrote a small swift module to fetch them from there.

For the world map, I loaded GeoJSON using swift, directly into a data-structure using Swift 4’s Codables. Next, I wrote a small Objective-C portion which reads the data structure and creates Bezier paths for each of the map strokes.

Download it

the source is available on github, but may not be able to be compiled yet (need to release a few of the updated dependencies to github first).

It’s probably easiest to: download latest release.

Dev. Notes

Components

  1. Objective-C Component - used for Cocoa View Logic (main entrypoint).
  2. The Rust Component - compiled into “cdylib”, referenced from objective-c.
  3. Swift - used for easy deserialization of JSON and data fetch.

Linking

In order to properly linking to an external dylib in a screen saver, you must add a few build steps. By default, clicking “Add Other” XCode’s linked frameworks and libraries section doesn’t use relative paths (or embed the library in your bundle). To make the binaries work on other machines, you must configure the build like so:

  1. Select your build target.
  2. Add .dylib under “General” / “Linked Frameworks and Libraries”
  3. Add copy files stage under “Build Phases”, after the “Link Binary with Libraries” Stage.
    • Set the destination to “Frameworks”
    • Choose the .dylib file as the file to copy. Ensure code-sign is checked.
  4. Add Run Script Action to alter the path (After Compile Sources stage) adapted steps found from here.

     # Change to name of your dylib.
     export DYLIB=librust_satellite_wrapper.dylib
    
     # Our dylib is under the "rust/target/release/deps" folder.
     # It's copied into the Frameworks/ Directory in the contents.
     # use @rpath to load it.
    
     # syntax: install_name_tool -change [from] [to] [target binary]
     install_name_tool -change \
     "$PROJECT_DIR/rust/target/release/deps/$DYLIB" \
     "@rpath/$DYLIB" \
     "$TARGET_BUILD_DIR/$PRODUCT_NAME.saver/Contents/MacOS/$PRODUCT_NAME"
    
  5. Profit 🚀