At a glance
| Item |
Summary |
| Purpose |
Creates, opens, edits, previews, thumbnails, and restores a custom SceneKit particle document. |
| App architecture |
A document-browser root opens Document values into a compound editor; Quick Look preview/thumbnail targets reuse the same document and particle renderer. |
| Main patterns |
UIDocument model, compound view controller, shared renderer across targets, framework delegates, and extension adapters. |
| Project style |
Main Particles app plus ParticlesPreview and ParticlesThumbnails extension targets. |
Project structure
Particles/
├── DocumentBrowserViewController.swift
├── DocumentViewController.swift
├── Document.swift
├── ParticleViewController.swift
├── EditorViewController.swift
└── AppDelegate.swift
ParticlesPreview/
└── PreviewViewController.swift
ParticlesThumbnails/
└── ThumbnailProvider.swift
Structure observations
Document and particle rendering form the reusable core consumed by three targets.
- The main document screen composes separate renderer and inspector children.
- Quick Look targets adapt the core instead of duplicating file decoding or visual representation.
Overall architecture
flowchart LR
Browser["DocumentBrowserViewController"] --> Document["Document : UIDocument"]
Browser --> Screen["DocumentViewController"]
Screen --> Particle["ParticleViewController"]
Screen --> Editor["EditorViewController"]
Screen --> Document
Particle --> Document
Editor --> Document
Document --> System["SCNParticleSystem"]
Preview["Quick Look Preview extension"] --> Document
Preview --> Particle
Thumbnail["Thumbnail extension"] --> Document
Thumbnail --> Particle
Reference code
Particles/DocumentViewController.swift:77 — the compound screen opens one coordinated document and injects it into both child feature owners.
func setDocument(_ document: Document, completion: @escaping () -> Void) {
self.document = document
loadViewIfNeeded()
document.open { success in
if success {
self.particleViewController.document = self.document
self.editorViewController.document = self.document
}
completion()
}
}
Interpretation
The custom-format boundary is Document, not a view controller. Main app and extensions open that same type and reuse ParticleViewController; only the surrounding lifecycle adapter changes.
Ownership and state
classDiagram
DocumentBrowserViewController *-- UIDocumentBrowserTransitionController : retains current transition
DocumentViewController *-- Document : retains while open
DocumentViewController *-- ParticleViewController : creates
DocumentViewController *-- EditorViewController : creates
Document *-- SCNParticleSystem : decoded model
ParticleViewController o-- Document : receives
EditorViewController o-- Document : receives
PreviewViewController *-- ParticleViewController : creates child
ThumbnailProvider --> ParticleViewController : creates temporarily
Ownership evidence
Particles/DocumentViewController.swift:11 — document replacement is read-only to collaborators, while the compound controller privately owns its children.
class DocumentViewController: UIViewController {
private(set) var document: Document?
private let particleNavigationController = UINavigationController()
private let particleViewController = ParticleViewController()
private let editorViewController = EditorViewController()
}
| Owner |
Object or state |
Relationship |
Mutation authority |
| Document browser |
Transition controller and presented document screen |
Creates/retains during presentation |
Browser delegate/restoration methods |
| Document screen |
Open document and renderer/editor children |
Creates/retains/injects |
setDocument, close action |
Document |
Particle system and last error |
Decodes/retains |
UIDocument lifecycle |
| Preview extension |
Open document and embedded renderer |
Creates for preview lifetime |
Quick Look completion flow |
| Thumbnail provider |
Temporary document/renderer/snapshot |
Creates per request |
Provider callback |
Class and protocol design
| Type or protocol |
Responsibility |
Depends on |
Document / UIDocument |
Coordinated load/save and secure archive of particle system |
NSKeyedArchiver, SceneKit |
| Browser delegates |
Create/import/pick/present documents and preserve bookmark state |
Document browser, transition delegate |
DocumentViewController |
Compose renderer + inspector and own open/close lifecycle |
Child containment, Document |
ParticleViewController |
Render/snapshot one document |
SCNView, SceneKit |
EditorViewController |
Map slider settings to particle-system key paths |
Diffable table source, document picker |
| Preview / thumbnail entry types |
Adapt shared core to Quick Look contracts |
Document and renderer |
The sample uses framework protocols at integration boundaries; it does not introduce an app-defined document-service protocol.
Access control
| Symbol |
Access |
Verified effect |
Likely rationale |
DocumentViewController.document |
private(set) |
Other code may inspect it, but only the compound screen may replace it. |
Preserve open/injection lifecycle. |
| Child controllers/navigation |
private let |
Outside code cannot bypass compound setup or inject different children. |
Keep containment and propagation coherent. |
| Editor settings/data source/derived particle system |
private |
Only inspector logic knows key paths, transformers, and cells. |
Hide KVC/editor implementation. |
| Particle scene view |
private |
Consumers use document/snapshot behavior, not rendering internals. |
Preserve renderer encapsulation. |
| Main types |
implicit internal |
Shared through target membership where configured, not a public API. |
App/extension source reuse. |
Reference code
Particles/EditorViewController.swift:74 — the inspector exposes its document input while keeping table schema and derived model access private.
private lazy var dataSource = makeDataSource()
var document: Document? { didSet { readValues() } }
private var particleSystem: SCNParticleSystem? { document?.particleSystem }
private var settings = [
EditorSetting(keyPath: "birthRate", label: "Birth Rate", min: 0.1, max: 100, transformer: nil)
]
Logic ownership and placement
| Logic |
Owning type or file |
Placement rationale |
| File coordination and serialization |
Document |
Custom format invariant and UIDocument lifecycle. |
| Creation, picking, transition, restoration |
Document browser |
Root has provider URLs and presentation context. |
| Open/close and child injection |
Document screen |
Owns the visible editing session. |
| Particle rendering/snapshot |
Particle controller |
Shared visual capability across targets. |
| Settings-to-model mapping |
Editor controller |
Inspector-specific KVC transformation. |
| Quick Look completion/reply |
Preview/thumbnail adapters |
Framework extension contracts. |
Design patterns
| Pattern |
Source evidence |
Purpose or tradeoff |
| Document model |
Particles/Document.swift:12 |
Centralizes coordinated reads/writes and format decoding. |
| Compound controller |
Particles/DocumentViewController.swift:20 |
Gives renderer and inspector focused roles under one session owner. |
| Shared renderer |
ParticlesPreview/PreviewViewController.swift:29 |
Reuses the app representation in Quick Look. |
| Framework delegate |
Particles/DocumentBrowserViewController.swift:11 |
Adapts provider/document events and custom transitions. |
| Extension adapter |
ParticlesThumbnails/ThumbnailProvider.swift:11 |
Wraps shared core in a per-request Quick Look lifecycle. |
Naming conventions
Document, DocumentBrowserViewController, and DocumentViewController distinguish model, root browser, and open-session UI.
ParticleViewController names the reusable renderer; EditorViewController names mutation UI.
- Extension entry types use framework roles:
PreviewViewController and ThumbnailProvider.
- Commands use lifecycle verbs:
presentDocument, setDocument, open, close, and provideThumbnail.
Architecture takeaways
- Make the UIDocument subclass the custom-format authority shared by app and extensions.
- Reuse a focused renderer across editing, preview, and thumbnail contexts.
- Let the visible document screen own open/close and inject one document into child views.
- Use
private(set) when navigation/restoration needs to inspect the current document but must not replace it.
Source map
| Source file |
Relevant symbols |
Particles/Document.swift |
Custom format and UIDocument lifecycle |
Particles/DocumentBrowserViewController.swift |
Browser, transition, and restoration |
Particles/DocumentViewController.swift |
Compound editor and document ownership |
Particles/ParticleViewController.swift |
Shared SceneKit renderer/snapshot |
Particles/EditorViewController.swift |
Settings inspector |
ParticlesPreview/PreviewViewController.swift |
Quick Look preview adapter |
ParticlesThumbnails/ThumbnailProvider.swift |
Quick Look thumbnail adapter |