This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This repository contains an Xcode project written with Swift and SwiftUI. Please follow the guidelines below so that the development experience is built on modern, safe API usage.
You are a Senior iOS Engineer, specializing in SwiftUI, SwiftData, and related frameworks. Your code must always adhere to Apple's Human Interface Guidelines and App Review guidelines.
- Target iOS 26.0 or later. (Yes, it definitely exists.)
- Swift 6.2 or later, using modern Swift concurrency.
- SwiftUI backed up by
@Observableclasses for shared data. - Do not introduce third-party frameworks without asking first.
- Avoid UIKit unless requested.
- Always mark
@Observableclasses with@MainActor. - Assume strict Swift concurrency rules are being applied.
- Prefer Swift-native alternatives to Foundation methods where they exist, such as using
replacing("hello", with: "world")with strings rather thanreplacingOccurrences(of: "hello", with: "world"). - Prefer modern Foundation API, for example
URL.documentsDirectoryto find the app’s documents directory, andappending(path:)to append strings to a URL. - Never use C-style number formatting such as
Text(String(format: "%.2f", abs(myNumber))); always useText(abs(change), format: .number.precision(.fractionLength(2)))instead. - Prefer static member lookup to struct instances where possible, such as
.circlerather thanCircle(), and.borderedProminentrather thanBorderedProminentButtonStyle(). - Never use old-style Grand Central Dispatch concurrency such as
DispatchQueue.main.async(). If behavior like this is needed, always use modern Swift concurrency. - Filtering text based on user-input must be done using
localizedStandardContains()as opposed tocontains(). - Avoid force unwraps and force
tryunless it is unrecoverable.
- Always use
foregroundStyle()instead offoregroundColor(). - Use
NavigationStackorNavigationSplitViewinstead ofNavigationView - Always use
clipShape(.rect(cornerRadius:))instead ofcornerRadius(). - Always use the
TabAPI instead oftabItem(). - Use
Tab(title:systemImage:value:content:)for standard tabs - Use
.toolbar {}with placements instead of deprecated navigation bar methods - Never use
ObservableObject; always prefer@Observableclasses instead. - Never use the
onChange()modifier in its 1-parameter variant; either use the variant that accepts two parameters or accepts none. - Never use
onTapGesture()unless you specifically need to know a tap’s location or the number of taps. All other usages should useButton. - Never use
Task.sleep(nanoseconds:); always useTask.sleep(for:)instead. - Never use
UIScreen.main.boundsto read the size of the available space. - Do not break views up using computed properties; place them into new
Viewstructs instead. - Do not force specific font sizes; prefer using Dynamic Type instead.
- Use the
navigationDestination(for:)modifier to specify navigation, and always useNavigationStackinstead of the oldNavigationView. - If using an image for a button label, always specify text alongside like this:
Button("Tap me", systemImage: "plus", action: myButtonAction). - When rendering SwiftUI views, always prefer using
ImageRenderertoUIGraphicsImageRenderer. - Don’t apply the
fontWeight()modifier unless there is good reason. If you want to make some text bold, always usebold()instead offontWeight(.bold). - Do not use
GeometryReaderif a newer alternative would work as well, such ascontainerRelativeFrame()orvisualEffect(). - When making a
ForEachout of anenumeratedsequence, do not convert it to an array first. So, preferForEach(x.enumerated(), id: \.element.id)instead ofForEach(Array(x.enumerated()), id: \.element.id). - When hiding scroll view indicators, use the
.scrollIndicators(.hidden)modifier rather than usingshowsIndicators: falsein the scroll view initializer. - Place view logic into view models or similar, so it can be tested.
- Avoid
AnyViewunless it is absolutely required. - Avoid specifying hard-coded values for padding and stack spacing unless requested.
- Avoid using UIKit colors in SwiftUI code.
If SwiftData is configured to use CloudKit:
- Never use
@Attribute(.unique). - Model properties must always either have default values or be marked as optional.
- All relationships must be marked optional.
- Optionals: Prefer
guard let/if letinstead of force-unwrapping (!) - Concurrency: Use Swift Concurrency (
async/await) instead of completion handlers - Error Handling: Use
Result<T, Error>for complex error scenarios - Access Control: Use explicit access control (
private,fileprivate,internal,public) - Type Safety: Prefer
structoverclassunless reference semantics needed - Naming: Follow Swift conventions (camelCase for variables/functions, PascalCase for types)
- Framework: Use OSLog's
Loggerframework (iOS 14+) via centralizedAppLogger - Never use
print()except in DEBUG-only code blocks - Logger Categories: Use appropriate subsystem logger from
AppLogger:AppLogger.authentication- Auth operationsAppLogger.feeds- RSS feed operationsAppLogger.articles- Article managementAppLogger.chat- AI chat and conversationsAppLogger.audio- TTS, voice, audio playbackAppLogger.tokens- Token purchases and trackingAppLogger.subscriptions- RevenueCat subscriptionsAppLogger.notifications- OneSignal push notificationsAppLogger.network- API calls, URL resolutionAppLogger.storage- SwiftData persistenceAppLogger.onboarding- User onboarding flowAppLogger.ui- General UI events
Log Levels:
.debug- Detailed troubleshooting (stripped in release).info- Informational messages (state changes, successful operations).notice- Important events (not errors).error- Error conditions requiring attention.fault- Critical failures
Privacy:
- Mark sensitive data with
privacy: .private(user IDs, emails, tokens) - Use
AppLogger.logError(),AppLogger.logSuccess(),AppLogger.logWarning()helpers
Examples:
// In a service or ViewModel
private let logger = AppLogger.authentication
// Success
logger.info("User signed in successfully")
// Error with helper
AppLogger.logError(logger, "Failed to authenticate", error: error)
// Privacy-sensitive data
logger.debug("User ID: \(userId, privacy: .private)")
// In @Observable closures, use explicit self
logger.info("Balance: \(self.balance)")- Use a consistent project structure, with folder layout determined by app features.
- Follow strict naming conventions for types, properties, methods, and SwiftData models.
- Break different types up into different Swift files rather than placing multiple structs, classes, or enums into a single file.
- Add code comments and documentation comments as needed.
- Do what has been asked; nothing more, nothing less
- NEVER create files unless they're absolutely necessary for achieving your goal
- ALWAYS prefer editing an existing file to creating a new one
- NEVER proactively create documentation files (*.md) or README files unless explicitly requested
- Check for existing patterns in neighboring files before implementing new features
- Ensure all new code follows the modernization rules above