TourKit is a Swift Package that provides a SwiftUI slideshow view for feature walkthroughs and onboarding flows, plus a ready-to-use floating window controller on macOS.
tourkit-demo.mp4
- Drop-in SwiftUI view. Embed
TourSlideshowViewinside any sheet, window, or custom layout you already manage. - Ready-to-use floating window on macOS.
TourKitWindowControllerpresents a borderless, draggable, card-sized window — perfect for onboarding on app launch. - Polished dark UI. Gradient blending between artwork and the text panel, animated page indicator, and subtle glass-style icon buttons (with liquid-glass material on newer OS versions).
- Fully localizable text.
title,description, and button labels areLocalizedStringKeys, with optional per-pagetableNameandstringsBundleoverrides for custom.strings/.xcstringstables. - Flexible image loading. Load images from your asset catalog, a specific module bundle (e.g.
.module), or on-disk resource files (png,jpg,heic,tiff,gif,webp, …). - Smooth transitions. Animated page changes and a keyboard shortcut (
Return) on the primary action. - Zero dependencies. Pure SwiftUI + AppKit.
- iOS support. Full feature parity with the macOS experience, including a UIKit-friendly presenter.
- Richer slide content. Go beyond a title and description — custom SwiftUI views, videos, and interactive elements per slide.
- macOS 13+
Add TourKit as a Swift Package dependency in Xcode, or in your Package.swift:
.package(url: "https://github.com/your-org/TourKit.git", from: "1.0.0")Then add "TourKit" to your target's dependencies.
Each slide is a TourPage with:
imageName— image in your app's asset catalog (or a resource inimageBundle)imageBundle— optional bundle to load the image from (defaults to the host app bundle)title— headline shown on the slidedescription— supporting copy shown under the titletableName/stringsBundle— optional localization overrides fortitleanddescription
There are two ways to use TourKit:
- Embed
TourSlideshowViewdirectly inside any SwiftUI view, sheet, or window you already manage. - Present a standalone floating window on macOS using
TourKitWindowController.
Use this when you want the tour to live inside your own layout, sheet, or window.
import SwiftUI
import TourKit
struct WelcomeTourView: View {
var body: some View {
TourSlideshowView(
pages: [
TourPage(
imageName: "tour-welcome",
title: "Welcome to MyApp",
description: "Get started quickly with a clean new interface."
),
TourPage(
imageName: "tour-search",
title: "All New Spotlight Search",
description: "Find what you need instantly with intuitive search."
),
TourPage(
imageName: "tour-automation",
title: "Smarter Automation",
description: "Save time by automating repetitive actions."
)
],
continueButtonTitle: "Continue",
finishButtonTitle: "Get Started",
onFinish: {
// Called when the user taps the finish button on the last slide.
},
onClose: {
// Called when the user taps the checkmark to dismiss the tour.
}
)
.frame(width: 660)
.padding()
}
}On macOS you can present the tour as a borderless, draggable, floating window — ideal for onboarding on app launch. Keep a reference to the controller so the window isn't deallocated while it's on screen.
import SwiftUI
import AppKit
import TourKit
@main
struct MyApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
var body: some Scene {
Settings { EmptyView() }
}
}
@MainActor
final class AppDelegate: NSObject, NSApplicationDelegate {
private let tour = TourKitWindowController()
func applicationDidFinishLaunching(_ notification: Notification) {
NSApp.setActivationPolicy(.regular)
tour.present(
pages: [
TourPage(
imageName: "tour-welcome",
title: "Welcome to MyApp",
description: "Get started quickly with a clean new interface."
),
TourPage(
imageName: "tour-search",
title: "All New Spotlight Search",
description: "Find what you need instantly with intuitive search."
)
],
width: 660,
continueButtonTitle: "Continue",
finishButtonTitle: "Get Started",
onFinish: {
// Called on the last slide's finish button.
},
onClose: {
// Called when the user closes the window (checkmark or finish).
}
)
}
}You can also dismiss the window programmatically:
tour.close()Calling present(...) again while a window is already visible will simply bring it to the front.
Note: For best results, use images with a 16:10 aspect ratio. The slide layout blends the top of the image into the dark bottom panel, so 16:10 assets keep headlines and subtitles from feeling cramped and avoid awkward letterboxing.
The package includes a runnable macOS sample target named TourKitSampleApp that demonstrates both usage styles with bundled screenshots.
- Open this package in Xcode.
- Select the
TourKitSampleAppscheme. - Run (
Cmd + R).
swift run TourKitSampleAppimageBundleinTourPageis optional. By default it isnil, so images are loaded from the host app bundle. If you ship images inside another bundle (e.g. a package module), pass that bundle explicitly usingimageBundle: .module.- Localized strings for
titleanddescriptionare looked up usingtableNameandstringsBundlewhen provided; otherwise the defaultLocalizabletable andimageBundleare used. - For best results, use images with a 16:10 aspect ratio.
Apps using TourKit. Click an icon to visit the app.
Shipping an app that uses TourKit? We'd love to feature it. Open a pull request with:
- Icon file. Add your app icon to
Documentation/Images/WOF/following the existing format:- File name: lowercase app name, e.g.
myapp.png. - Format:
.pngwith a transparent background. - Recommended source size: 512×512 (or any square size ≥ 128×128).
- File name: lowercase app name, e.g.
- README entry. Add a new
<a><img></a>pair to the Wall of Fame list above, matching the existing entries. Link the icon to your app's website or App Store page. - PR description. Briefly describe your app, how it uses TourKit, and attach a screenshot of TourKit running inside it.
TourKit is released under the MIT License. You are free to use it in personal and commercial projects, provided that the copyright notice and permission notice are preserved. The software is provided "as is", without warranty of any kind.



