DEV Community

Cover image for Support Copy/Paste keyboard shortcuts with Swift UI for Mac OS X
Gboyega Dada
Gboyega Dada

Posted on

Support Copy/Paste keyboard shortcuts with Swift UI for Mac OS X

Thought this might be a great opportunity for my first post! πŸŽ‰


Objectives:

  • Add a paste command to your app menu (window commands) and a Cmd+V keyboard shortcut
  • Make a copy button that you can add to your window commands

TL;DR:
Add the PasteButton system button in your window commands.
Find snippet for copy button action at the end.


Setup

Create an ObservableObject to store and publish your clipboard content to the rest of the app.

// AppClipboard.swift

class AppClipboard: ObservableObject {
    @Published var content: URL?

    init(content: URL? = nil) {
        self.content = content
    }
}
Enter fullscreen mode Exit fullscreen mode

Attach an instance in your app entry point:

// MyApp.swift

import SwiftUI

@main
struct MyApp: App {
    @StateObject var clipboard = AppClipboard() // <---- 1. //

    var body: some Scene {

        WindowGroup("My App Name") {
            ContentView()
                .environmentObject(clipboard) // <---- 2. //

        }

    }
}
Enter fullscreen mode Exit fullscreen mode

And now you can access the object in child views:

import SwiftUI

struct MyView: View {
    @EnvironmentObject var clipboard: AppClipboard // <---- 1. //

    var body: some View {
      Text(clipboard.content) // <----- 2. //
    }
}
Enter fullscreen mode Exit fullscreen mode

Next, create a FocusBinding by extending the FocusedValues struct and adding a key for the clipboard. This makes the ObservableObject we created earlier accessible from the window menu commands we will create later.

// FocusedValuesExtension.swift

private struct ClipboardContentKey: FocusedValueKey {
    typealias Value = Binding<String?>
}

extension FocusedValues {
    var clipboardContent: Binding<String?>? {
        get { self[ClipboardContentKey.self] }
        set { self[ClipboardContentKey.self] = newValue }
    }
}
Enter fullscreen mode Exit fullscreen mode

Now in your window menu commands add the clipboard FocusedBinding like so:

// Commands.swift

import SwiftUI

struct MyWindowCommands: Commands {
    @FocusedBinding(\.clipboardContent) var clipboardContent // <--- 1. //

    var body: some Commands {
        // Option 1: Replace system pasteboard menu...
        CommandGroup(replacing: CommandGroupPlacement.pasteboard) {
            PasteButton(payloadType: String.self) { values in
                 guard let value = values.first else { return }
                 // make sure we updating on the main thread (or we get a warning)
                 DispatchQueue.main.async{
                     clipboardContent = value // <---- 2. //
                 }
                 return
            }
            .keyboardShortcut("V", modifiers: [.command]) // <--- shortcut 3. // 
        }

        // Option 2: Create custom menu
        CommandMenu("My Custom Menu") {
            PasteButton(payloadType: String.self) { values in
                 guard let value = values.first else { return }
                 DispatchQueue.main.async{
                     clipboardContent = value 
                 }
                 return
            }
            .keyboardShortcut("V", modifiers: [.command])  
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

If you build your app in XCode, you should now see a working paste under the Edit menu or your custom menu.

Making a copy button

This is a little trickier because it depends on how you plan to use the copy action in your app. But I will include here a simple button action snippet that lets you put content in the system clipboard.

Button(action: {
    NSPasteboard.general.declareTypes([.string], owner: nil)

    let pasteboard = NSPasteboard.general // <----- This is important! Do not convert to a one liner!
    pasteboard.setString(webURL.absoluteString, forType: .string)
}, label: {
    Image(systemName: "square.on.square")
})
Enter fullscreen mode Exit fullscreen mode

You can incorporate this into your window commands like the paste button.

Hope you found this useful!

Resources

SwiftUI Mac Menus


Photo by JΓΈrgen HΓ₯land

Top comments (0)