ob-swiftui icon indicating copy to clipboard operation
ob-swiftui copied to clipboard

👉 [[https://github.com/sponsors/xenodium][Support this work via GitHub Sponsors]]

  • ob-swiftui

UPDATE: See [[https://xenodium.com/ob-swiftui-updates/][blog post]] for latest changes.

Evaluate SwiftUI snippets using Emacs [[https://orgmode.org/worg/org-contrib/babel/intro.html][org babel]].

Big thanks to Chris Eidhof, who tweeted a snippet [[https://gist.github.com/chriseidhof/26768f0b63fa3cdf8b46821e099df5ff][to run any SwiftUI view as a macOS app]]. Much of /ob-swiftui/'s Swift code is based on that snippet as well as some additional tweaks he made (like writing to png).

[[file:ob-swiftui.gif]]

  • Install

#+begin_src emacs-lisp (require 'ob-swiftui) (ob-swiftui-setup) #+end_src

  • Header arguments ** :results window Runs SwiftUI in a separate window (default and can be omitted).

Use a generated root view and render in external window (default):

#+begin_src org ,#+begin_src swiftui Rectangle() .fill(Color.yellow) .frame(maxWidth: .infinity, maxHeight: .infinity) ,#+end_src #+end_src

is equivalent to:

#+begin_src org ,#+begin_src swiftui :results window :view none Rectangle() .fill(Color.yellow) .frame(maxWidth: .infinity, maxHeight: .infinity) ,#+end_src #+end_src

** :view FooView If /view:/ is given, use FooView as the root view. Otherwise, generate a root view and embed source block in view body.

#+begin_src org ,#+begin_src swiftui :results window :view FooView struct FooView: View { var body: some View { VStack(spacing: 10) { BarView() BazView() } } }

struct BarView: View {
  var body: some View {
    Rectangle()
      .fill(Color.yellow)
      .frame(maxWidth: .infinity, maxHeight: .infinity)
  }
}

struct BazView: View {
  var body: some View {
    Rectangle()
      .fill(Color.blue)
      .frame(maxWidth: .infinity, maxHeight: .infinity)
  }
}

,#+end_src #+end_src

Note: Defining a view named ContentView, implies =view: ContentView=

** :results file Runs SwiftUI in the background and saves an image snapshot to a file. You can save the image to the location specified by the =:file= header argument, otherwise, it will be saved to a temporary location if =:file= is omitted. Please note that currently, =png= is the only supported image format.

#+begin_src org ,#+begin_src swiftui :results file Rectangle() .fill(Color.yellow) .frame(maxWidth: .infinity, maxHeight: .infinity) ,#+end_src #+end_src

  • Auto refresh :results file image

If executing the block does not refresh the inlined image, try adding /org-redisplay-inline-images/ in a hook. org-babel-after-execute-hook #+begin_src emacs-lisp :lexical no (add-hook 'org-babel-after-execute-hook (lambda () (when org-inline-image-overlays (org-redisplay-inline-images)))) #+end_src

  • Display retina image

For retina (high-resolution) displays, if you found the output image is too large, you can instruct Org to scale the image to 0.5 to display in its native size:

#+begin_src emacs-lisp (advice-add 'org--create-inline-image :filter-return (defun org--create-inline-image/filter-return (x) (setcdr x (plist-put (cdr x) :scale 0.5)) x)) #+end_src

👉 [[https://github.com/sponsors/xenodium][Support my work via GitHub Sponsors]]