Skip to content

Instantly share code, notes, and snippets.

@byaruhaf
Created December 12, 2025 18:59
Show Gist options
  • Select an option

  • Save byaruhaf/ed886a68ea3582b12390f96875774d09 to your computer and use it in GitHub Desktop.

Select an option

Save byaruhaf/ed886a68ea3582b12390f96875774d09 to your computer and use it in GitHub Desktop.

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

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.

Role

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.

Core instructions

  • Target iOS 26.0 or later. (Yes, it definitely exists.)
  • Swift 6.2 or later, using modern Swift concurrency.
  • SwiftUI backed up by @Observable classes for shared data.
  • Do not introduce third-party frameworks without asking first.
  • Avoid UIKit unless requested.

Swift instructions

  • Always mark @Observable classes 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 than replacingOccurrences(of: "hello", with: "world").
  • Prefer modern Foundation API, for example URL.documentsDirectory to find the app’s documents directory, and appending(path:) to append strings to a URL.
  • Never use C-style number formatting such as Text(String(format: "%.2f", abs(myNumber))); always use Text(abs(change), format: .number.precision(.fractionLength(2))) instead.
  • Prefer static member lookup to struct instances where possible, such as .circle rather than Circle(), and .borderedProminent rather than BorderedProminentButtonStyle().
  • 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 to contains().
  • Avoid force unwraps and force try unless it is unrecoverable.

SwiftUI instructions

  • Always use foregroundStyle() instead of foregroundColor().
  • Use NavigationStack or NavigationSplitView instead of NavigationView
  • Always use clipShape(.rect(cornerRadius:)) instead of cornerRadius().
  • Always use the Tab API instead of tabItem().
  • Use Tab(title:systemImage:value:content:) for standard tabs
  • Use .toolbar {} with placements instead of deprecated navigation bar methods
  • Never use ObservableObject; always prefer @Observable classes 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 use Button.
  • Never use Task.sleep(nanoseconds:); always use Task.sleep(for:) instead.
  • Never use UIScreen.main.bounds to read the size of the available space.
  • Do not break views up using computed properties; place them into new View structs instead.
  • Do not force specific font sizes; prefer using Dynamic Type instead.
  • Use the navigationDestination(for:) modifier to specify navigation, and always use NavigationStack instead of the old NavigationView.
  • 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 ImageRenderer to UIGraphicsImageRenderer.
  • Don’t apply the fontWeight() modifier unless there is good reason. If you want to make some text bold, always use bold() instead of fontWeight(.bold).
  • Do not use GeometryReader if a newer alternative would work as well, such as containerRelativeFrame() or visualEffect().
  • When making a ForEach out of an enumerated sequence, do not convert it to an array first. So, prefer ForEach(x.enumerated(), id: \.element.id) instead of ForEach(Array(x.enumerated()), id: \.element.id).
  • When hiding scroll view indicators, use the .scrollIndicators(.hidden) modifier rather than using showsIndicators: false in the scroll view initializer.
  • Place view logic into view models or similar, so it can be tested.
  • Avoid AnyView unless it is absolutely required.
  • Avoid specifying hard-coded values for padding and stack spacing unless requested.
  • Avoid using UIKit colors in SwiftUI code.

SwiftData instructions

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.

Swift Patterns

  • Optionals: Prefer guard let/if let instead 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 struct over class unless reference semantics needed
  • Naming: Follow Swift conventions (camelCase for variables/functions, PascalCase for types)

Logging

  • Framework: Use OSLog's Logger framework (iOS 14+) via centralized AppLogger
  • Never use print() except in DEBUG-only code blocks
  • Logger Categories: Use appropriate subsystem logger from AppLogger:
    • AppLogger.authentication - Auth operations
    • AppLogger.feeds - RSS feed operations
    • AppLogger.articles - Article management
    • AppLogger.chat - AI chat and conversations
    • AppLogger.audio - TTS, voice, audio playback
    • AppLogger.tokens - Token purchases and tracking
    • AppLogger.subscriptions - RevenueCat subscriptions
    • AppLogger.notifications - OneSignal push notifications
    • AppLogger.network - API calls, URL resolution
    • AppLogger.storage - SwiftData persistence
    • AppLogger.onboarding - User onboarding flow
    • AppLogger.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)")

Project structure

  • 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.

Important Reminders

  • 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment