Skip to content

Instantly share code, notes, and snippets.

Last active September 16, 2021 13:58
Show Gist options
  • Save xenodium/79154033bc26e733b8c43af228cbce5b to your computer and use it in GitHub Desktop.
Save xenodium/79154033bc26e733b8c43af228cbce5b to your computer and use it in GitHub Desktop.
Preview SwiftUI layouts using Emacs org blocks
(use-package org
:hook ((org-mode . org-display-inline-images))
(use-package ob
:bind (:map org-mode-map
("C-c C-c" . org-ctrl-c-ctrl-c))
(use-package ob-swift
:ensure t
(org-babel-do-load-languages 'org-babel-load-languages
(append org-babel-load-languages
'((swift . t))))
(defun ar/org-refresh-inline-images ()
(when org-inline-image-overlays
;; Automatically refresh inline images.
(add-hook 'org-babel-after-execute-hook 'ar/org-refresh-inline-images)
(defun adviced:org-babel-execute:swift (f &rest args)
"Advice `adviced:org-babel-execute:swift' enabling swiftui header param."
(let* ((body (nth 0 args))
(params (nth 1 args))
(swiftui (cdr (assoc :swiftui params)))
(when swiftui
(assert (or (string-equal swiftui "preview")
(string-equal swiftui "interactive"))
nil ":swiftui must be either preview or interactive")
(setq body (format
import Cocoa
import SwiftUI
import Foundation
let screenshotURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent(ProcessInfo.processInfo.globallyUniqueString + \".png\")
let preview = %s {
extension NSApplication {
public func run<V: View>(@ViewBuilder view: () -> V) {
let appDelegate = AppDelegate(view())
mainMenu = customMenu
delegate = appDelegate
extension NSApplication {
var customMenu: NSMenu {
let appMenu = NSMenuItem()
appMenu.submenu = NSMenu()
let quitItem = NSMenuItem(
title: \"Quit \(ProcessInfo.processInfo.processName)\",
action: #selector(NSApplication.terminate(_:)), keyEquivalent: \"q\")
quitItem.keyEquivalentModifierMask = []
let mainMenu = NSMenu(title: \"Main Menu\")
return mainMenu
class AppDelegate<V: View>: NSObject, NSApplicationDelegate, NSWindowDelegate {
var window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 414 * 0.2, height: 896 * 0.2),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
var contentView: V
init(_ contentView: V) {
self.contentView = contentView
func applicationDidFinishLaunching(_ notification: Notification) {
window.delegate = self
window.contentView = NSHostingView(rootView: contentView)
if preview {
screenshot(view: window.contentView!, saveTo: screenshotURL)
// Write path (without newline) so org babel can parse it.
print(screenshotURL.path, terminator: \"\")
window.setFrameAutosaveName(\"Main Window\")
NSApp.activate(ignoringOtherApps: true)
func screenshot(view: NSView, saveTo fileURL: URL) {
let rep = view.bitmapImageRepForCachingDisplay(in: view.bounds)!
view.cacheDisplay(in: view.bounds, to: rep)
let pngData = rep.representation(using: .png, properties: [:])
try! pngData?.write(to: fileURL)
(if (string-equal swiftui "preview")
(setq args (list body params)))
(setq output (apply f args))
(when org-inline-image-overlays
(advice-add #'org-babel-execute:swift
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment