Skip to content

Instantly share code, notes, and snippets.

@DevAndArtist
Last active December 16, 2023 16:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DevAndArtist/eb7e8aa5e7134610c20b1a7aca358604 to your computer and use it in GitHub Desktop.
Save DevAndArtist/eb7e8aa5e7134610c20b1a7aca358604 to your computer and use it in GitHub Desktop.
import SwiftUI
import UniformTypeIdentifiers
// DOCUMENT EXAMPLE
extension UTType {
static var exampleText: UTType {
UTType(importedAs: "com.example.plain-text")
}
}
final class MyDocument: ReferenceFileDocument {
// We add `Published` for automatic SwiftUI updates as
// `ReferenceFileDocument` refines `ObservableObject`.
@Published
var number: Int
static var readableContentTypes: [UTType] { [.exampleText] }
init(number: Int = 42) {
self.number = number
}
init(configuration: ReadConfiguration) throws {
guard
let data = configuration.file.regularFileContents,
let string = String(data: data, encoding: .utf8),
let number = Int(string)
else {
throw CocoaError(.fileReadCorruptFile)
}
self.number = number
}
func snapshot(contentType: UTType) throws -> String {
"\(number)"
}
func fileWrapper(
snapshot: String,
configuration: WriteConfiguration
) throws -> FileWrapper {
// For the sake of the example this force unwrapping is considered as safe.
let data = snapshot.data(using: .utf8)!
return FileWrapper(regularFileWithContents: data)
}
}
// APP EXAMPLE FOR MACOS
@main
struct MyApp: App {
var body: some Scene {
DocumentGroup(
newDocument: {
MyDocument()
},
editor: { file in
ContentView(document: file.document)
.frame(width: 400, height: 400)
}
)
}
}
struct DocumentProxy {
let documentController: NSDocumentController = .shared
let undoManager: UndoManager?
func registerUndo<Target>(
withTarget target: Target,
handler: @escaping (Target) -> Void
) where Target : AnyObject {
if let undoManager = undoManager {
undoManager.registerUndo(withTarget: target, handler: handler)
}
}
func updateDocument() {
// FIXME: `currentDocument` could return `nil` in certain situations.
// Make this logic more robust.
if let document = documentController.currentDocument {
document.updateChangeCount(.changeDone)
}
}
}
struct DocumentReader<Content>: View where Content: View {
@Environment(\.undoManager)
var _undoManager: UndoManager?
let content: (DocumentProxy) -> Content
init(@ViewBuilder content: @escaping (DocumentProxy) -> Content) {
self.content = content
}
var body: some View {
content(DocumentProxy(undoManager: _undoManager))
}
}
struct ContentView: View {
@ObservedObject
var document: MyDocument
var body: some View {
DocumentReader { proxy in
VStack {
Text(String("\(document.number)"))
Button("randomize") {
let currentNumber = document.number
proxy.registerUndo(withTarget: document) { document in
document.number = currentNumber
}
document.number = Int.random(in: 0 ... 100)
}
Button("randomize without undo") {
document.number = Int.random(in: 0 ... 100)
proxy.updateDocument()
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment