bitwarden/implementing-ios-code
Implement, write code, add a new screen, create a feature, new view, new processor, or wire up a new service in Bitwarden iOS. Use when asked to "implement", "write code", "add screen", "create feature", "new view", "new processor", "add service", or when translating a design doc into actual Swift code.
npx skills add https://github.com/bitwarden/ios --skill implementing-ios-code
Use this skill to implement Bitwarden iOS features following established patterns.
.claude/outputs/plans/<ticket-id>.md. If not, invoke planning-ios-implementation first.Docs/Architecture.md — it is the authoritative source for all patterns. This skill references it, not replaces it.From the plan, identify:
BitwardenShared, AuthenticatorShared, or BitwardenKit?Auth/, Autofill/, Platform/, Tools/, Vault/?See templates.md for file-set skeletons.
Implement from the bottom up:
Data Models (if needed)
Core/<Domain>/Models/Request/ and Response/Core/<Domain>/Models/Enum/switch over that enum (across all layers) rather than appending it — match the surrounding order.Persistence (if needed)
DataStore (add entities to Bitwarden.xcdatamodeld)AppSettingsStore (backed by UserDefaults)KeychainRepositoryStateService. Prefer adding a separate protocol over extending StateService, AppSettingsStore, or KeychainRepository directly, to maintain interface segregation.Services / Repositories
// sourcery: AutoMockableDefault<Name>Service / Default<Name>RepositoryHas<Name> protocoltemplates.md for service skeletonFor new screens, create all required files together (see templates.md):
Services typealiasstruct) holding all view-observable datareceive(_:)perform(_:)StateProcessor subclass, business logic onlystore.binding, store.perform, @ObservedObjectSwiftUI previews:
#Preview macros for new views — the modern default across the codebase. Do not add new PreviewProvider structs. Because the snapshot harness cannot enumerate #Preview macros, such a view's snapshot coverage comes from a test that instantiates the view directly; see the testing-ios-code skill for how to choose between that and iterating PreviewProvider._allPreviews when a view exposes both.New localization keys (BitwardenResources/.../Localizable.strings):
Archive for "Archive", not MoveToArchive or ArchiveTitle.DescriptionLong suffix on a shortened opening phrase. Example: PassphrasesAreOftenEasierToRememberDescriptionLong./* … */ comments describe meaning, placement, or constraints that affect translation — translators are the audience, not internal engineers.After creating a new service/repository:
Has<Name> conformance to ServiceContainer via extensionHas<Name> to the Services typealias of any processor that needs itBefore finishing:
BitwardenSdk for all encryption/decryptionKeychainRepository, not AppSettingsStoreInputValidatorNonLoggableErrorAll new public types and methods require DocC (///) documentation.
Exceptions: protocol property/function implementations (docs live in the protocol), mock classes.
Use pragma marks to organize code. // MARK: - is used to denote different objects in the same file; // MARK: is used to denote different sections within an object.
Run these checks after all code and tests are written, before handing back. Fix every violation found — do not leave warnings for the commit phase.
If new localization strings were added, regenerate first so SwiftGen picks them up:
mint run swiftgen config run --config swiftgen-bwr.yml # BitwardenResources (most common)
# Run the appropriate swiftgen-*.yml if strings landed in a different target
Then run the pre-commit checks:
./Scripts/pre-commit
This runs spell-check, SwiftFormat, and SwiftLint on changed files — the same checks that run at commit time. If violations are reported, fix them and re-run until it passes cleanly.
For SwiftLint violations that require manual fixes, prefer fixing the root cause over suppressing with // swiftlint:disable comments. Suppression is appropriate when the violation is structural and fixing it would require an artificial or non-idiomatic refactor. Common cases:
line_length — wrap long lines using Xcode's ⌃M style (each argument on its own indented line); fix rather than suppressfile_length — if a #if DEBUG preview section pushed a file over the limit, add // swiftlint:disable file_length at the top of the file (this is the established pattern — do NOT move previews to a separate file)type_body_length — split large types using // MARK: extensions in separate files if needed; suppress only if the type is inherently large and splitting would hurt readabilityOnly hand back once ./Scripts/pre-commit passes cleanly for files touched in this session.
// MARK: section, keep members in a single alphabetical order (stored properties, computed properties, methods, and static members share that order), and alphabetize function and initializer parameters the same way (default-valued, variadic, then trailing-closure parameters last). Insert new members and parameters at their alphabetical position rather than appending. Exceptions: UI objects (views, view modifiers) follow visual layout order, not alphabetical; and protocol *conformance* ordering is not enforced.static member on the relevant type or extension over a file-scope global function or property, even a private one.Date, etc.) rather than raw strings, unless deliberately mirroring an external contract (e.g. an SDK model that stores ISO date strings) where typed conversion is intentionally deferred.Take bitwarden/implementing-ios-code from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.