🔥

[Ignite]SwiftUIでWebサイトを開発する

2024/03/25に公開

try! Swift Tokyo 2024に参加しました!
https://tryswift.jp

その中のSwiftで次世代のウェブサイトを構築しようのセッションでIgniteという、
SwiftUIのコードからHTMLやCSSを生成できる静的サイトジェネレーターが紹介されていました。

https://github.com/twostraws/Ignite

今回はIgniteを使用したWebサイトの開発方法を紹介します。

1. プロジェクトの作成

XcodeのFile -> New -> Package -> macOS -> Command-Line Toolで新規プロジェクトを作成します。

今後RyoDeveloperSiteの文字が出てきたときは、自分のプロジェクト名に置き換えて読んでください。

2. Igniteの追加

IgniteはSwift Package Managerを使って追加できます。
Package.swiftを下記のように書き換えます。

Package.swift
import PackageDescription

let package = Package(
    name: "RyoDeveloperSite",
    platforms: [.macOS(.v13)],
    dependencies: [
        .package(url: "https://github.com/twostraws/Ignite.git", branch: "main"),
    ],
    targets: [
        // Targets are the basic building blocks of a package, defining a module or a test suite.
        // Targets can depend on other targets in this package and products from dependencies.
        .executableTarget(
            name: "RyoDeveloperSite",
            dependencies: ["Ignite"]
        ),
    ]
)

BuildするとPackageが追加されます。

3. サイトの生成

Hello, world!を表示してみます。
ファイルはSouresのフォルダー内に追加してください。

Home
import Foundation
import Ignite

struct Home: StaticPage {
    var title = "RyoDeveloper"

    func body(context: PublishingContext) -> [BlockElement] {
        Text("Hello, world!")
            .font(.title1)
    }
}

Themeを追加します。
ThemeではCSSの適応やHeaderやFooterの追加などを行っています。
Header, Footerについては後の章で紹介します。

MainTheme
import Foundation
import Ignite

struct MyTheme: Theme {
    func render(page: Page, context: PublishingContext) -> HTML {
        HTML {
            Head(for: page, in: context)

            Body {
                page.body
            }
        }
    }
}

Siteを追加します。

MySite
import Foundation
import Ignite

struct MySite: Site {
    var name = "RyoDeveloper"
    var url = URL("https://ryodeveloper.com") // Base URL
    var author = "RyoDeveloper"
    var language = Language.japanese

    var homePage = Home() // Root domainのPage
    var theme = MyTheme()

    var pages: [any StaticPage] {
        Home()
    }
}

エントリーポイントを修正します。

RyoDeveloperSite
import Foundation
import Ignite

@main
struct RyoDeveloperSite {
    static func main() {
        let site = MySite()

        do {
            try site.publish()
        } catch {
            print(error.localizedDescription)
        }
    }
}

プロジェクトの配下にAssetsというフォルダーを追加します。

XcodeでBuildをするとAlertが表示されるので許可をクリックします。

プロジェクトの配下にBuildというフォルダーが生成され、その中にHTMLやCSSが出力されます。
ここまでで、サイトの生成が完了しました。

4. サイトの確認

ローカルサーバーを立てて、正しくサイトが動作するか確認してみます。
HomebrewでPythonをインストールします。

brew install python

先ほど生成されたBuildのディレクトリで、サーバーを立ち上げます。

python3 -m http.server 8888

http://localhost:8888にアクセスすることでプレビューを見ることができます。

無事にHello, world!の表示を確認できました。

4. HeaderとFooterの作成

https://ignitesamples.hackingwithswift.com/navigation-examples/

Headerから他のPageに遷移できるようにします。

遷移先のPageを作ります。
今回は、Assetsフォルダーに追加したFujimiIcon.svgを表示してみます。

Fujimi
import Foundation
import Ignite

struct Fujimi: StaticPage {
    var title = "Fujimi"

    func body(context: PublishingContext) -> [BlockElement] {
        Image("/FujimiIcon.svg")
            .resizable()
            .frame(width: 100)
    }
}

MySiteのpagesに今作成したFujimi()を追加します。

MySite
  struct MySite: Site {
      var name = "RyoDeveloper"
      var url = URL("https://ryodeveloper.com") // Base URL
      var author = "RyoDeveloper"
      var language = Language.japanese
  
      var homePage = Home() // Root domainのPage
      var theme = MyTheme()
  
      var pages: [any StaticPage] {
          Home()
+         Fujimi()
      }
  }

Headerを作成します。

Header
import Foundation
import Ignite

struct Header: Component {
    func body(context: PublishingContext) -> [any PageElement] {
        NavigationBar(logo: "RyoDeveloper") {
            Link("Fujimi", target: Fujimi())
        }
        .backgroundColor(.whiteSmoke)
        .position(.fixedTop)
    }
}

MainThemeにHeader()を追加します。

MainTheme
  struct MainTheme: Theme {
      func render(page: Page, context: PublishingContext) -> HTML {
          HTML {
              Head(for: page, in: context)
  
              Body {
+                 Header()
  
                  page.body
              }
+             .padding(.vertical, 80)
          }
      }
  }
Home Fujimi

Footerも同じ要領で自由なデザインを追加できます。
今回は応援の意味も込めて、デフォルトで入っているIgniteFooter()を追加してみます。

/// Displays "Created by Ignite", with a link back to the Ignite project on GitHub.
/// Including this is definitely not required for your site, but it's most appreciated 🙌

MainTheme
struct MainTheme: Theme {
    func render(page: Page, context: PublishingContext) -> HTML {
        HTML {
            Head(for: page, in: context)

            Body {
                Header()

                page.body
                
                IgniteFooter()
            }
            .padding(.vertical, 80)
        }
    }
}

後は、下記のURLを参考に自由にコンポーネントを追加するだけです。
https://ignitesamples.hackingwithswift.com

Discussion