2 Jan 202010 min read

Generating static website

Back in the 2000s when I was still a kid and had no idea about professional programming, website development was something I was passionate about. There was no JavaScript nor even CSS. My first website was created using Microsoft FrontPage and it was mostly based on tables. It was so much fun for me when I was 10 years old. After that I have learned HTML and why would I need anything else? Then the times of Flash websites and other different frameworks come, including JavaScript. I did some web development with the help of PHP but kinda lost interest in that for “some” reason. There was a WordPress anyway so used that for whatever I needed. I always enjoyed HTML and CSS, just too bad it was not efficient to create a website with pure HTML+CSS. Then, out of the blue...

Publish, the static website generator in Swift was announced by John Sundell. I got excited about that and was looking forward to releasing it. I had a year-long break from blogging and I promised myself I will start again once I rewrite my blog using Plot. So here we come...

Article updated

The old version - before the Publish was released is avaialble „here„.

Server

Before we start developing our website with Swift, first of all, we need a http server, thankfully if we are on a macOS it comes with already installed python. All we need it the command below

python -m SimpleHTTPServer 8080

It will start a server on the localhost in the current directory - we will run this command in the directory where all generated files will be saved to.

It would be possible to test everything on a remote server but that would make our lives harder, having to upload all changes to the remote one is quite a time consuming while doing that manually and we rather want to test our changes immediately.


I wonder if it would be possible to wrap the generator with iOS apps (written in SwiftUI) to test it on mobile as well. It’s something I must give it a try someday.


INDIE DEV
MusicHarbor
MusicHarbor

MusicHarbor is an App Store featured app that helps you to track new music releases, music videos, concert dates and news from your favorite artists. Think of it as your personal, chronological feed of every single new release from the artists you follow. It has support for the latest iOS features, and is highly customizable, so you can configure it to work the way you want!

Preparation

Besides the working server, we need to plan the file structure of our project. For my blog, I didn’t want to worry about any missing files when switching between computers while working on my blog so all the resources for the blog are added to the Xcode project and used to generate HTML or copied when needed. It includes article files (markdown), static images (ie. a logo) as well as all other needed files such as styles file and .htaccess.

So below you can see how it looks like

├── Content
│   └── articles
│       ├── configuring-macos-for-ios-development.md
│       ├── creating-custom-siri-shortcuts.md
│       ├── flutter--my-thoughts-and-impressionspart-i.md
│       ├── fluttermy-thoughts-and-impressionspart-ii.md
│       ├── fluttermy-thoughts-and-impressionspart-iii.md
│       ├── fluttermy-thoughts-and-impressionspart-iv.md
│       ├── fluttermy-thoughts-and-impressionspart-v-final.md
│       ├── generating-static-website.md
│       ├── outdated
│       │   └── generating-static-website.md
│       ├── simple-custom-uinavigationcontroller-transitions.md
│       └── swifty-coredata.md
├── Package.resolved
├── Package.swift
├── Resources
│   ├── fonts
│   │   ├── SourceSansPro-Bold.ttf
│   │   ├── SourceSansPro-LightItalic.ttf
│   │   └── SourceSansPro-Regular.ttf
│   ├── images
│   │   ├── about-me.png
│   │   ├── articles
│   │   │   ├── configuring-macos-for-ios-development
│   │   │   ├── creating-custom-siri-shortcuts
│   │   │   │   ├── 1.png
│   │   │   │   ├── 10.gif
│   │   │   │   ├── 2.png
│   │   │   │   ├── 3.png
│   │   │   │   ├── 4.png
│   │   │   │   ├── 5.png
│   │   │   │   ├── 6.png
│   │   │   │   ├── 7.png
│   │   │   │   ├── 8.png
│   │   │   │   └── 9.png
│   │   │   ├── flutter--my-thoughts-and-impressionspart-i
│   │   │   ├── fluttermy-thoughts-and-impressionspart-ii
│   │   │   │   ├── 1.png
│   │   │   │   ├── 2.png
│   │   │   │   └── 3.gif
│   │   │   ├── fluttermy-thoughts-and-impressionspart-iii
│   │   │   ├── fluttermy-thoughts-and-impressionspart-iv
│   │   │   │   ├── 1.png
│   │   │   │   ├── 2.png
│   │   │   │   └── 3.png
│   │   │   ├── fluttermy-thoughts-and-impressionspart-v-final
│   │   │   │   └── 1.png
│   │   │   ├── generating-static-website
│   │   │   ├── simple-custom-uinavigationcontroller-transitions
│   │   │   │   └── 1.gif
│   │   │   └── swifty-coredata
│   │   │       ├── 1.png
│   │   │       ├── 2.png
│   │   │       └── 3.png
│   │   ├── favicon.png
│   │   ├── logo.png
│   │   ├── projects
│   │   │   ├── expenses-pal.png
│   │   │   └── watermaniac.png
│   │   └── social.png
│   └── styles.css
├── Sources
│   └── OrdinaryCoding
│       ├── main.swift
│       ├── models
│       │   └── Project.swift
│       ├── theme
│        │   ├── PlotExtensions.swift
│       │   ├── Theme+OrdinaryCoding.swift
│       │   ├── WebsiteContent.swift
│       │   ├── WebsiteFooter.swift
│       │   ├── WebsiteHeader.swift
│       │   └── WebsitePages.swift
│       └── website
│           ├── OrdinaryCodingWebsite.swift
│           └── QuotesPublishPlugin.swift
└── Output

Since I use Publish framework it is pretty much a default structure that works best with this tool. The generated website is in the Output folder and is ready to upload to the production server. In this directory, I also run my localhost HTTP server.

Articles' file name is the name of the URL path that I want the specific article to have. In Resource directory are static files that are copied (including .htaccess).

Project

I have generated Xcode project using „Publish„ command line tool as described in the README's Quick start section there.

Dependencies that we will be working with are Publish, Plot, Ink, and Splash (or actually it's plugin for Publish).

Publish


Publish, a static site generator built specifically for Swift developers. It enables entire websites to be built using Swift, and supports themes, plugins and tons of other powerful customization options.


Besides Plot, it's the main framework we will be working with. It's used for managing our files - Resources, Content - and the actual website .html files.

Plot


A DSL for writing type-safe HTML, XML and RSS in Swift


The core of the static generator. It’s where the most „magic” comes from.

Ink


A fast and flexible Markdown parser written in Swift


It is somehow optional but quite convenient if you want to keep your articles in markdown (a lightweight markup language). You could just stick to Plot and generate HTML for articles using it but I think it’s way faster to write them using Markdown - ie. right now I am writing this article in Markdown. I am kinda busy so writing articles the other way wouldn’t be efficient for me. I sync markdown files on all my devices so whenever I have a little bit of time I write on either Mac Book, PC with Windows or my phone. And Apple’s Notes are all you need pretty much (If anyone knows any good application for Markdown with syncing let me know, please).

Splash


A fast, lightweight and flexible Swift syntax highlighter for blogs, tools and fun!


if you want to include Swift code in your articles it is a must-have, also works quite good with other languages, and if not, can be customized to make it work. On my blog I post both Swift and Dart code, and as you might have seen it works fine for Dart just fine out of the box. I will try to improve it in the future most likely though.

INDIE DEV
MusicHarbor
MusicHarbor

MusicHarbor is an App Store featured app that helps you to track new music releases, music videos, concert dates and news from your favorite artists. Think of it as your personal, chronological feed of every single new release from the artists you follow. It has support for the latest iOS features, and is highly customizable, so you can configure it to work the way you want!

Generating

First of all, read Plot’s README carefully. It contains so many useful tips that you will regret not knowing if you do not read that. The API is quite big so reading all the code line by line, or looking for a specific function might be challenging.


Please, keep in mind that this little project of mine is still in development are some of the code I show is most likely not the final version.


main.swift is the starting point of the static site generator.

try OrdinaryCoding().publish(using: [
    .installPlugin(.splash(withClassPrefix: "swift-")), // 1
    .installPlugin(.quotes()), // 2

    .copyResources(), // 3
    .addMarkdownFiles(), // 4
    .addDefaultTitles(), // 5

    .addPage(.erro404()), // 6
    .addPage(.contact()), // 7
    .addPage(.projects(with: myProjects)), // 8

    .generateHTML(withTheme: .ordinaryCoding), // 9

    .generateRSSFeed(including: [.articles]), // 10
    .generateSiteMap(excluding: [ "404" ]) // 11
])

It takes 11 steps to generate my blog. Let me explain those: 1. Installs "SplashPublishPlugin" used for syntax highlighting. 2. Installs a small plugin I have written to style quotes in my articles a little bit differently. 3. Copies all resources from the Resource directory. 5. Adds default title to the Articles page. 6. Creates custom page for handling 404 http errors. 7. Creates Contact page. 8. CreatesProjects page that contains my public projects - releases to the store and/or open sourced. 9. Generates all the .html files, including those pages I have added in previous steps. Keep in mind that the order is quite important here, if you call this action before adding pages or doing any other specific work related to website, it won't affect it. 10. Generates RSS Feed with all the files from articles section. 11. Generates site map exluding the page for 404 as it shouldn't be included there.


Let me know on Twitter if you would like to know a little bit more about specific step (or see it's code).


OrdinaryCoding class is a class that conforms to Website protocol from Publish and it contains some basic information about my blog required by Publish's engine to generate the website.

struct OrdinaryCoding: Website {
    enum SectionID: String, WebsiteSectionID {
        case articles
    }

    struct ItemMetadata: WebsiteItemMetadata {
    }

    var url = URL(string: "https://ordinarycoding.com")!
    var name = "Ordinary Coding"
    var description = "Just another blog about programming - iOS, macOS, Flutter"
    var language: Language { .english }
    var imagePath: Path? { "images/social.png" }
    var tagHTMLConfig: TagHTMLConfiguration? { TagHTMLConfiguration(basePath: "topics") }
}

Also I use custom theme (as Foundation one i quite basic 🙂).

What would our blog be without articles? I have all of them in the Content/articles directory as markdown files. Before Publish was released I had a little bit different structure - I prefered to have each article in different folder and be grouped with images related to it but sadly Publish forced on me this - thankfully it's open sourced and maybe some day I will have time to improve it the way I want.

All HTML related code I keep in the extensions to the Node enum from the Plot framework. And I think it's the best way to keep the project clean and share anything that can be shared.

Conclusion

It took me about 2 weeks (around 1-3 hours each day) to create the website from scratch and the most time-consuming task was to rewrite 9 articles to markdown language and to prepare the CSS file. Besides that, you still need to have at least a basic knowledge of how to write HTML and how to style it with CSS (Publish comes with some simple .css file but most of us will rather want to customize it).

It’s also great that while using this DSL there is still room for your own skills to shine or learn new things and there are endless possibilities how this framework can be used. It will be fun to see what people will come with.


Plot is a missing link between Swift and HTML.


And I really hope the static website generators will become more and more popular. I had a lot of fun playing with Publish and Plot, and recommend it to everyone who wants to build such a website.

Final words

I hope you find this article somehow useful while building your website using Plot. If you have any questions feel free to contact me, preferably on Twitter or by e-mail.

Thank you for reading.