Create macOS apps with Swift packages instead of Xcode projects

Overview

Swift Bundler

A Swift Package Manager wrapper that allows the creation of MacOS apps with Swift packages instead of Xcode projects. My motivation is that I think Xcode projects are a lot more restrictive than Swift packages, and Swift packages are less convoluted. My end goal is to be able to create Swift apps for Windows, Linux and MacOS with a single codebase.

Installation

git clone https://github.com/stackotter/swift-bundler
cd swift-bundler
sh ./install.sh

Usage

Init

# Create a new swift package and set it up for bundling, by default the packge is created in a new directory with a name matching the package.
swift bundler init [name]

Currently it is not possible to automatically setup swift bundler for an existing package. However it is not too difficult to do manually. First make sure your package contains a main.swift file and make sure macOS platform version in Package.swift is at least 11.0 (earlier versions will likely work as well, but they are not tested). Then create a file called Bundle.json containing configuration in the format specified in the following section. You should now be good to go. If your Package.swift explicitly specifies an executable product, the name of the product must match the name of the package.

Configuration

Running swift bundler init creates a Bundle.json file which contains all the configuration for the app. Below is an example configuration;

{
  "target": "AppTargetName",
  "buildNumber" : 1,
  "bundleIdentifier" : "com.example.bundler-hello-world",
  "category" : "public.app-category.games",
  "minOSVersion" : "11.0",
  "versionString" : "0.1.0"
}

Remember to change this configuration to match your project.

Xcode support

If you want to use xcode as your ide, run this in the package directory. Make sure you've run init first. This command only needs to be run once (or each time you clone if you gitignore the .swiftpm directory).

# Creates the files necessary to get xcode to run the package as an app
swift bundler generate-xcode-support

Build

# Build the app and output the .app to the specified dir
# If no directory is specified the default output dir is .build/bundler
swift bundler build -o [dir]

Build and run

# Builds and runs the app
swift bundler run

Bundle

# Bundles an executable into a .app using config from the given package dir
swift bundler bundle -d [package dir] -e [executable file] -o [output dir]

# OR
# Converts the executable and bundles in a products dir into a .app
# If doing it this way and your app contains bundles, you may need to add the --dont-fix-bundles flag if the build was performed as a universal build or using xcode because both of those produce correct bundles
swift bundler bundle -d [package dir] -P [products dir] -o [output dir]

Custom build scripts

Both prebuild and postbuild scripts are supported. Just create the prebuild.sh and/or postbuild.sh files in the root directory of your project and they will automatically be run with the next build. No extra configuration required.

App Icons

There are two ways to add custom app icons to a bundle project.

  1. The simplest way is to add an Icon1024x1024.png file in the root directory of your project and the bundler will automatically convert it to all the required sizes and create the AppIcon.icns in the app's resources. The png file must have an alpha channel and should be 1024x1024 but this isn't checked when building.
  2. If you want to have different versions of your icon for different file sizes you can create a custom AppIcon.icns and add it to the root directory. You can even generate it from an IconSet in a custom prebuild script! (just see createIcns in Utils.swift for how this is done).

If both are present, AppIcon.icns is used.

Help

If you want to see all available options just add --help to the end, (e.g. swift bundler run --help).

Troubleshooting

If you are having an issue to do with bundle resources, try doing a universal build. Universal builds output correct bundles whereas for regular single architecture builds the bundler has to compile metal shaders and structure the bundles correctly. I don't know why only universal builds output correct bundles.

Comments
  • Command Not Found via Xcode

    Command Not Found via Xcode

    Hi there đź‘‹

    Running into a little bit of an issue with using Xcode where it can't seem to find the Swift Bundler executable.

    I installed the tool using mint (mint install stackotter/swift-bundler), created a new project following the commands on the README, and generated the Xcode support files.

    If I run swift bundler run in Terminal it works as expected, and just for sanity, so does swift-bundler run.

    However, if I build my project in Xcode, it compiles correctly ("Build Succeeded") but then throws an error.

    Could not launch “HelloWorld” The executable is missing. Please check your project settings and ensure that they produce a valid product.

    If I check the logs, then it's clear the it's failing to run Swift Bundler:

    /var/folders/54/7cl_0jq16sl8bg5xvn20skwm0000gn/T/SchemeScriptAction-SR1x7H.sh: line 2: swift-bundler: command not found
    

    I've tried the standard lark of restarting Xcode and all that jazz to no avail.

    Using Xcode 13.3.1 (latest non-beta), and bundler version 2.0.2 (latest).

    Looking forward to giving this tool a try out. Thanks 🙂

    opened by Sherlouk 9
  • iOS build support

    iOS build support

    This shouldn't be too difficult. Just need to target the build for iOS and then create an ipa instead of a .app (the structure seems pretty similar in some ways.

    enhancement 
    opened by stackotter 3
  • Init Naming Conflicts

    Init Naming Conflicts

    Using the init command uses the directory naming for its content. I initialized under a directory "bundler-testing" which created a compile error with the character -. This exception can be handled with a character substitute such as _ or the init command could include a name and create the associated directory which seems more consistent with existing cli's.

    opened by JacksonUtsch 3
  • Change docs target

    Change docs target

    As discussed, this

    • removes the extra package manifest in Documentation
    • flattens the directory structure in Documentation
    • adds the SwiftBundler documentation target to the main manifest
    • updates update-docs.yml to the new directory structure

    I don't think there's a way to test update-docs.yml prior to merging into main, is there?

    opened by finestructure 2
  • Support sandboxing and code signing

    Support sandboxing and code signing

    Currently all Swift Bundler apps are non-sandboxed and aren't code signed. As Swift Bundler gets more users it would be useful to support such distribution features because code signing and sandboxing are important parts of shipping production-grade applications.

    enhancement 
    opened by stackotter 1
  • Fix log glitches when building

    Fix log glitches when building

    It seems like when Swift Bundler runs swift build, swift doesn't get given access to the interactive terminal, and its interactive output gets printed as separate lines and sometimes the lines get joined together for some reason.

    opened by stackotter 1
  • Create string descriptions for all errors

    Create string descriptions for all errors

    The error enums themselves are quite descriptive, but it would improve developer experience if those errors could be converted into more human readable strings. This would also involve ensuring that ArgumentParser displays the string descriptions of errors.

    enhancement 
    opened by stackotter 1
  • Support Asset Catalogs

    Support Asset Catalogs

    Developers migrating from Xcode will want Asset Catalogs to behave as they would expect when using Xcode, this is currently not the case. I don't actually know how Asset Catalogs work so I don't know what is involved in implementing this.

    enhancement 
    opened by stackotter 1
  • Support for bundling XPC Services

    Support for bundling XPC Services

    I would like to use swift-bundler to create an app that includes an XPC Service. For that the resulting .app directory needs to contain a directory Contents/XPXServices which in turn contains .xpc bundles.

    A first step to make this possible would be to support dictionaries dictionaries as additional plist entries:

    [apps.ServiceProvider]
    product = "ServiceProvider"
    version = "0.0.1" # The apps can specify separate versions
    [apps.ServiceProvider.extra_plist_entries]
    CFBundleIdentifier = "com.my.ServiceProviderXPC"
    CFBundlePackageType = "XPC!"
    XPCService = { ServiceType = "Application" }
    

    results in

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>CFBundleIdentifier</key>
        <string>com.my.ServiceProviderXPC</string>
        <key>CFBundlePackageType</key>
        <string>XPC!</string>
        <key>XPCService</key>
        <dict>
            <key>ServiceType</key>
            <string>Application</string>
        </dict>
    </dict>
    </plist>
    

    Given this I could already add a manual step that adds my ServiceProvider to the already created bundle.

    opened by bo0ts 3
  • Provide JSON Schema for Configuration

    Provide JSON Schema for Configuration

    Discussed this very briefly on Discord, but I think it would be really handy if we could provide a JSON schema document detailing every value supported in the configuration TOML file.

    This would allow it to be used with plugins such as this one for VS Code which would provide code completion and validation of configurations improving the developer experience and hopefully reducing the number of issues developers face.

    Taplo have stated they're happy for new schemas to be provided for native support, once the API is stabilised some, but even without pushing it upstream users can still utilise it if we host it on the documentation website 🙂

    opened by Sherlouk 0
  • Improve configuration deserialisation error messages

    Improve configuration deserialisation error messages

    Currently the debug description of the deserialisation is just included in the error message for ConfigurationError.failedToDeserializeConfiguration. This is ok for now, but it takes quite a bit of reading to figure out what is wrong with your configuration. Configuration errors are common and they should be as easy to diagnose as possible.

    It would be preferable to have some sort of custom error message for each command codable error. This could probably be done by adding LocalizedError conformance to whichever Codable related errors can be thrown. It could also be improved by adding nice error descriptions to all of the errors in TOMLKit.

    TOMLKit also doesn't seem to provide a correct coding path (it's always empty from what I've seen).

    enhancement good first issue 
    opened by stackotter 2
  • Add option to remove log noise when using Xcode

    Add option to remove log noise when using Xcode

    Xcode is notorious for having a lot of unnecessary log noise which isn't at all useful. To fix this, OS_ACTIVITY_MODE=disable can be added to the Xcode scheme's environment variables. Given that Swift Bundler can generate Xcode schemes, it would be useful to be able to automatically add this environment variable.

    enhancement 
    opened by stackotter 0
Releases(v2.0.4)
  • v2.0.4(May 10, 2022)

  • v2.0.3(Apr 24, 2022)

    What's Changed

    • Change docs target by @finestructure in https://github.com/stackotter/swift-bundler/pull/20
    • Fix Xcode not finding swift-bundler on the path by @stackotter in https://github.com/stackotter/swift-bundler/pull/23

    Full Changelog: https://github.com/stackotter/swift-bundler/compare/v2.0.2...v2.0.3

    Source code(tar.gz)
    Source code(zip)
    swift-bundler(11.63 MB)
  • v2.0.2(Apr 9, 2022)

  • v2.0.1(Apr 2, 2022)

    This release removes reliance on Swift Bundler being installed at /opt/swift-bundler and updates the installation instructions accordingly. The recommended installation method is now via mint.

    If you have previously installed Swift Bundler via the installation script method, you must delete the /opt/swift-bundler directory.

    Full Changelog: https://github.com/stackotter/swift-bundler/compare/v2.0.0...v2.0.1

    Source code(tar.gz)
    Source code(zip)
  • v2.0.0(Apr 2, 2022)

    After a month of work and over 120 commits, I have finished rewriting and improving the entirety of Swift Bundler 🎉

    For a nice overview of the changes go and read my blog post.

    Improvements

    • Better configuration format
    • Help messages are more useful
    • Error messages are more descriptive
    • Commands are more intuitive
    • Command line output is colourful!
    • Improved consistency between SwiftPM and Xcode builds
    • Automatic migration from old configuration format (Bundle.json) to new configuration format (Bundler.toml)
    • Swift Bundler is now released under the more permissive Apache 2.0
    • Swift Bundler is less opinionated about the layout of your project and the only requirement is that Bundler.toml is in the root directory
    • The chance of fatal errors occurring has been significantly reduced by avoiding all force unwraps (and similar), and removing many other sources of fatal errors

    New features

    • Package templates
    • Commands give tips on what to do next
    • Multi-app packages
    • Plist entries can contain variables (e.g. {COMMIT_HASH} can be used to insert the current commit hash into the Info.plist at build time)
    • Option to set indentation style when creating a new package
    • Stylish and helpful command-line output
    • Documentation site

    Internal changes

    • Swift Bundler is now written in a functional style for maximum reusability and maintainability
    • Result-based error handling is now used instead of throws to ensure that error messages are always descriptive and always have user-friendly error messages
    • All errors implement errorDescription
    • The monolithic Bundler type has been split into several smaller self-contained utilities
    • All child processes are terminated when the swift-bundler process is killed
    • Errors are now propagated instead of just terminating the process when the error occurs, allowing for centralised error handling
    Source code(tar.gz)
    Source code(zip)
  • v1.4.8(Feb 18, 2022)

  • v1.4.7(Feb 3, 2022)

  • v1.4.6(Jan 12, 2022)

  • v1.4.5(Dec 19, 2021)

  • v1.4.4(Dec 1, 2021)

  • v1.4.3(Oct 31, 2021)

  • v1.4.2(Oct 30, 2021)

  • v1.4.1(Oct 29, 2021)

    Fixes an issue regarding dynamic linking. The bundler used to use DynamicLibraries inside an app's contents as the location for dylibs. However the default location is lib inside an app's contents and using a custom rpath lead to some issues.

    Source code(tar.gz)
    Source code(zip)
    swift-bundler(2.33 MB)
  • v1.4.0(Oct 29, 2021)

    Swift Bundler can now automatically detect dynamic libraries and copy them into the app bundle. The dynamic libraries are located at /path/to/YourApp.app/Contents/DynamicLibraries. Framework resources are not supported and if a framework contains resources, swift-bundler will let you know by throwing an error and exiting.

    Source code(tar.gz)
    Source code(zip)
    swift-bundler(2.33 MB)
  • v1.3.0(Oct 18, 2021)

    Allows the executable target to be selected in Bundle.json and removes a few janky bits of code. Someone was having an issue with swift-bundler crashing cause zsh could not be found so now sh is used for running shell commands.

    Source code(tar.gz)
    Source code(zip)
    swift-bundler(2.28 MB)
  • v1.2.0(Sep 28, 2021)

    Fixes a bug with generate-xcode-support and adds remove-file-headers which removes all file headers of source files. File headers are the autogenerated comments Xcode puts at the tops of files and some people don't like them. Add the remove-file-headers command to a prebuild script to automatically remove new ones whenever you build.

    Source code(tar.gz)
    Source code(zip)
    swift-bundler(2.25 MB)
  • v1.1.0(Sep 28, 2021)

  • v1.0.1(Sep 26, 2021)

  • v1.0.0(Sep 26, 2021)

Owner
Creator of Delta Client. Loves Swift and Rust.
null
Easily use UIKit views in your SwiftUI applications. Create Xcode Previews for UIView elements

SwiftUIKitView Easily use UIKit views in SwiftUI. Convert UIView to SwiftUI View Create Xcode Previews from UIView elements SwiftUI functional updatin

Antoine van der Lee 682 Dec 29, 2022
UIPheonix is a super easy, flexible, dynamic and highly scalable UI framework + concept for building reusable component/control-driven apps for macOS, iOS and tvOS

UIPheonix is a super easy, flexible, dynamic and highly scalable UI framework + concept for building reusable component/control-driven apps for macOS, iOS and tvOS

Mohsan Khan 29 Sep 9, 2022
Full configurable spreadsheet view user interfaces for iOS applications. With this framework, you can easily create complex layouts like schedule, gantt chart or timetable as if you are using Excel.

kishikawakatsumi/SpreadsheetView has moved! It is being actively maintained at bannzai/SpreadsheetView. This fork was created when the project was mov

Kishikawa Katsumi 34 Sep 26, 2022
Create SwiftUI Views with any data

Create SwiftUI Views with any data

Zach Eriksen 20 Jun 27, 2022
Create descriptive UIKit screens, faster!

Columbina's DeclarativeUIKit Create descriptive UIKit screens, faster! Get rid of constraints manipulation and use declarative language to create your

Columbina 2 Dec 12, 2021
Confetti View lets you create a magnificent confetti view in your app

ConfettiView Confetti View lets you create a magnificent confetti view in your app. This was inspired by House Party app's login screen. Written in Sw

Or Ron 234 Nov 22, 2022
A UIControl subclass that makes it easy to create empty states.

AZEmptyState Making empty state simple. Screenshots Installation Cocoa Pods: pod 'AZEmptyState' Manual: Simply drag and drop the Sources folder to you

Antonio Zaitoun 88 Oct 2, 2022
Create view hierarchies declaratively.

Create view hierarchies declaratively. Quick Look view.pd.add( imageView.pd.image(logoImage), label.pd.text("Logo").textColor(.red).font(size:

Javier Zhang 69 Oct 19, 2022
NotSwiftUI is designed to help you create UI components quickly and efficiently with code!

NotSwiftUI NotSwiftUI is designed to help you create UI components quickly and efficiently with code! Capitalizing on the idea that most of the UI ele

Jonathan G. 50 Dec 20, 2022
A simple, customizable view for efficiently collecting country information in iOS apps.

CountryPickerView CountryPickerView is a simple, customizable view for selecting countries in iOS apps. You can clone/download the repository and run

Kizito Nwose 459 Dec 27, 2022
Beautiful flag icons for usage in apps and on the web.

FlagKit Beautiful flag icons for usage in apps and on the web. All flags are provided as stand-alone PNG and SVG files. FlagKit also provides an Asset

Bowtie 2.9k Dec 29, 2022
Progress and Activity Indicators for iOS apps

Progress Indicators and Activity Views for iOS Apps Features Storyboard compatible, configure apprearance with the property inspector. fully animated,

Alexander Kasimir 101 Nov 13, 2022
Custom emojis are a fun way to bring more life and customizability to your apps.

Custom emojis are a fun way to bring more life and customizability to your apps. They're available in some of the most popular apps, such as Slack, Di

Stream 244 Dec 11, 2022
Work in progress gallery of controls available to Catalyst apps using Optimized for Mac

Catalyst Controls Gallery Very simple work-in-progress demonstration of many common controls available to Mac Catalyst as of macOS 11. Provided moreso

Steven Troughton-Smith 163 Sep 18, 2022
It provides UI components such as popover, popup, dialog supporting iOS apps

Overview LCUIComponents is an on-going project, which supports creating transient views appearing above other content onscreen when a control is selec

Linh Chu 7 Apr 8, 2020
Oovium's AetherView and other basic UI elements necessary for embedding Oovium in other apps

Oovium's AetherView and other basic UI elements necessary for embedding Oovium in other apps

Joe Charlier 0 Aug 24, 2022
Super awesome Swift minion for Core Data (iOS, macOS, tvOS)

⚠️ Since this repository is going to be archived soon, I suggest migrating to NSPersistentContainer instead (available since iOS 10). For other conven

Marko Tadić 306 Sep 23, 2022
High performance Swift treemap layout engine for iOS and macOS.

Synopsis YMTreeMap is a high performance treemap layout engine for iOS and macOS, written in Swift. The input to YMTreeMap is a list of arbitrary numb

Yahoo 118 Jan 3, 2023
Circular progress indicator for your macOS app

CircularProgress Circular progress indicator for your macOS app This package is used in production by apps like Gifski and HEIC Converter. Requirement

Sindre Sorhus 520 Jan 3, 2023