This project repository is the result of the simple thought experiment: if John Sundell's Publish static site generator is written in Swift, and Apple's Playgrounds apps for iPad and macOS run Swift code, then surely there should be a way to run Publish in the Playgrounds app.
The generated website itself is for my personal portfolio at a2.io (which probably could have been written more simply in any of several other ways).
As the Swift Playgrounds app (as of this writing, version 3.4.1) only supports Swift 5.3 and not the latest public release of Swift 5.4, certain modifications were made to John Sundell's Publish library as documented in LICENSE.md.
After cloning or otherwise downloading this repository, you can interact with the project in one of a few ways.
- Open Package.swift in Xcode.
- Modify launch arguments as appropriate for your use case in the Product > Scheme > Edit Scheme… menu.
- You may also want to set the
OS_ACTIVITY_MODE=disable
environment variable to reduce system log noise.
On macOS, you can run the following command:
swift run site [--export/-e <dest>] [--serve/-s [--open/-o] [--port/-p <port>]] [--verbose/-v]
Running the site executable will always build the website. What happens next depends on the other flags that are set.
--export/-e <dest>
exports the website from the in-memory file manager to disk at the folder indicated bydest
.--serve/-s
serves the in-memory file system over HTTP.--open/-o
tells the program to open the localhost URL in your configured default browser.--port/-p <port>
changes the port (default: 8000) to theport
argument.
--verbose/-v
tells the program to output additional information to the console, for example incoming server HTTP request logs and generated file information.
- Open the Playground.playgroundbook file in Swift Playgrounds for Mac.
- Copy or share (e.g. via AirDrop) the Playground.playgroundbook file to Swift Playgrounds for iPad.
Then, tap the Run My Code button to compile the project and build the site. A web view connected to the localhost URL (at the default port, 8000) will be assigned to the Playground page's live view
In order for the Playgrounds app to discover local resources, LocalResource
is used as an intermediary to support resources in Xcode and the Playgrounds apps. LocalResource.all
is an array computed property containing all local resources. Local files in the Playground.playgroundbook/Contents/PublicResources directory are named in a GraphQL-esque fashion, where a triple underscore (___
) instead of a sub-directory structure, because Playgrounds only supports a flat directory of resource files.
To add a new App, the App.swift file contains both the App
protocol, the DefaultApp
and StubApp
structs and a computed array property on A2.Website
. All apps are either DefaultApp
or StubApp
values, except for MailApp
and SafariApp
, which live in their own files. The required Sass/CSS lives in HTMLFactory.swift under A2.HTMLFactory.makeStylesCSS(context:)
.
It's inevitable that the Publish library or one of its dependencies will be updated as Swift itself evolves. When that happens, you will want to clone the Publish repository locally to the latest tagged version and check out its registered dependencies. The Swift files for those dependencies lives in Playground.playgroundbook/Contents/UserModules/. Each of the Codextended, Files, Ink, Plot, Publish, and Sweep modules lives in a respective *.playgroundmodule folder. All source files in such a Playground module must be at the top level of that directory. This is important because, as of this writing, the source repositories of the UserModules dependencies have a multi-level directory structure with API and Internal sub-directories.
The sass.dart.js file at Playground.playgroundbook/Contents/PublicResources/sass.dart.js comes from the latest version (as of this writing, 1.25) of the dart-sass Node.js module. If that file is updated, the respective Swift code in Sass.swift will need to be updated as well.
As WebKit framework is available on both iPadOS and macOS, the Sass compiler wrapper uses a WKWebView
(outside of any view hierarchy) to execute the transpiled Dart source. (JavaScriptCore is unavailable in Playgrounds, so WebKit is used for its simpler JavaScript execution captabilities).
On Linux, where this site is compiled, the WebKit framework is unavailable, and thus Foundation.Process
is used to run Node.js.
As a developer for iOS and Apple's other platform technologies, I thought about what it would look like if my website, which showcases my iOS work, itself looked like an iPhone. I drew the above sketch in the IconFactory's Linea app on November 29, 2019. I started implementing what would become this Swift Playgrounds project in 2020, but got distracted by other work and side projects until May 2021 when I got Playgrounds to build Publish sites. That's when I decided to combine my website prototype idea and my Swift Playgrounds work.
Site Playground is available under the MIT license. See the LICENSE.md file for more information as well as third-party code used under license.
Apple and iPhone are trademarks of Apple Inc., registered in the U.S. and other countries and regions.