swiftui-coding-guideline — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited swiftui-coding-guideline (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Apply these guidelines when working on SwiftUI code. Prefer minimal, readable, and maintainable changes with explicit, testable state transitions.
Use swift-coding-guideline as the baseline for cross-cutting Swift language/tooling rules (import hygiene, mutation semantics, enum/file rules, naming, and general formatting). This skill defines SwiftUI- and UI-architecture-specific rules on top of that baseline.
1) Minimal changes that solve the problem directly. 2) Readability over cleverness. 3) Maintainability over short-term shortcuts. 4) Clear architecture: each layer owns one responsibility. 5) Pure, explicit logic with minimal side effects. 6) Testability via small, injectable dependencies.
ReadMarkdown/Markdown/View, ReadMarkdown/Directory/ControllerReadMarkdown/Shared/Model and ReadMarkdown/Shared/Service.Enum folder for enums.ReadMarkdown
├─ Directory
│ ├─ Controller
│ │ └─ DirectoryController.swift
│ ├─ Dispatcher
│ │ └─ DirectoryDispatcher.swift
│ ├─ Model
│ │ └─ DirectoryItemModel.swift
│ ├─ View
│ │ └─ DirectoryListView.swift
│ ├─ Store
│ │ └─ DirectoryStore.swift
│ └─ ViewModel
│ └─ DirectoryViewModel.swift
├─ Markdown
│ ├─ Controller
│ ├─ Dispatcher
│ ├─ Model
│ ├─ View
│ ├─ Store
│ └─ ViewModel
├─ Utility
│ ├─ View
│ │ └─ SidebarWidthReaderView.swift
│ └─ File
│ ├─ FileWatcherService.swift
│ └─ FileScannerService.swift
└─ Shared
├─ Enum
│ └─ ScanMode.swift
└─ Service
└─ DirectoryPickerService.swiftView (SwiftUI)
body (e.g., private var foo: some View) must live in its own file as a standalone View type instead of a computed property.ViewModel
final class by default; only use class when subclassing is required.swift-coding-guideline for mutation semantics (private(set), no-op mutator wrappers, and accessor/observer constraints).Controller / Dispatcher
Service
Service suffix for consistency (e.g., FileScannerService, DirectoryPickerService).Store
UserDefaults).Controller
Dispatcher
Model
View
ViewModel
Store
UserDefaults).Service
Example
struct DirectoryController {
func makeDirectoryItems(files: [MarkdownFileModel], rootURL: URL?) -> [DirectoryItemModel] {
// Pure transformation of inputs to outputs.
}
}ObservableObject and @Published require Combine. Import Combine directly in non-view files (view models, stores, services) instead of SwiftUI.final class AppViewModel: ObservableObject {
@Published
private(set) var files: [MarkdownFileModel] = []
private let scanner: FileScannerService
init(scanner: FileScannerService = FileScannerService()) {
self.scanner = scanner
}
func scan(folderURL: URL) {
files = scanner.scan(folderURL: folderURL)
}
}Binding(get:set:) is always a violation.get reads x and set only writes x = newValue, do not use Binding(get:set:).Binding(get:set:) only when set performs intentional behavior (mapping, validation, or calling explicit mutation methods).Example
Toggle("Enabled", isOn: $viewModel.isEnabled)Example (avoid pass-through Binding(get:set:))
Toggle(
"Enabled",
isOn: Binding(
get: { viewModel.isEnabled },
set: { viewModel.isEnabled = $0 }
)
)Example
Toggle("Full scan") {
Binding(
get: { viewModel.scanMode == .full },
set: { viewModel.setScanMode($0 ? .full : .topLevel) }
)
}Example stub
final class StubFileScannerService {
var files: [MarkdownFileModel] = []
func scan(folderURL: URL) -> [MarkdownFileModel] {
files
}
}let declarations inside SwiftUI view-building closures for derived values that are reused or improve readability.View types in their own files over computed properties.@EnvironmentObject on a separate line).@Published, @State, @EnvironmentObject). Treat this as a strict formatting rule.Example
@Published
private(set) var scanMode: ScanMode = .full
private let defaults: UserDefaultsExample (blank line between consecutive property wrappers) Bad
@Published
private(set) var fileAvailability: FileAvailability = .available
@Published
private(set) var missingSelectedFile: MarkdownFile?Good
@Published
private(set) var fileAvailability: FileAvailability = .available
@Published
private(set) var missingSelectedFile: MarkdownFile?Example
final class AppViewModel: ObservableObject {
@Published
private(set) var files: [MarkdownFileModel] = []
}Example
@EnvironmentObject
private var settings: SettingsViewModelExample (prefer computed properties to inline let declarations) Bad
private var contentView: some View {
let bottomInset = showsTerminal ? terminalHeight + terminalPadding * 2 : 0
return MarkdownViewerView(bottomInset: bottomInset)
}Good
private var contentView: some View {
MarkdownViewerView(bottomInset: terminalBottomInset)
}
private var terminalBottomInset: CGFloat {
showsTerminal ? terminalHeight + terminalPadding * 2 : 0
}didSet, global state).~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.