ios-design-patterns/SKILL.md
Implement common design patterns for iOS development including Singleton, Factory, Observer, Strategy, Decorator, and more. Use when choosing patterns, implementing reusable designs, or solving common architectural problems. Triggers on design pattern, Singleton, Factory, Observer, Strategy, Decorator, Builder, pattern, Adapter, Facade, Command.
npx skillsauth add abanoub-ashraf/manus-skills-import ios-design-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
4 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are an expert in design patterns for iOS. When this skill activates, help implement appropriate patterns for the situation.
// Modern Swift Singleton
final class Analytics {
static let shared = Analytics()
private init() {
// Setup
}
func track(_ event: String) {
// Track event
}
}
// Thread-safe actor singleton
actor AnalyticsActor {
static let shared = AnalyticsActor()
private var events: [String] = []
func track(_ event: String) {
events.append(event)
}
}
// Usage
Analytics.shared.track("app_launched")
await AnalyticsActor.shared.track("app_launched")
// Simple Factory
protocol Button {
func render() -> some View
}
struct PrimaryButton: Button {
func render() -> some View {
Text("Primary").padding().background(.blue)
}
}
struct SecondaryButton: Button {
func render() -> some View {
Text("Secondary").padding().background(.gray)
}
}
enum ButtonFactory {
static func create(_ type: ButtonType) -> Button {
switch type {
case .primary: return PrimaryButton()
case .secondary: return SecondaryButton()
}
}
}
// Abstract Factory
protocol UIFactory {
associatedtype ButtonType: Button
associatedtype CardType: Card
func createButton() -> ButtonType
func createCard() -> CardType
}
struct LightThemeFactory: UIFactory {
func createButton() -> LightButton { LightButton() }
func createCard() -> LightCard { LightCard() }
}
struct DarkThemeFactory: UIFactory {
func createButton() -> DarkButton { DarkButton() }
func createCard() -> DarkCard { DarkCard() }
}
// Fluent Builder
class AlertBuilder {
private var title: String = ""
private var message: String = ""
private var actions: [UIAlertAction] = []
private var style: UIAlertController.Style = .alert
func title(_ title: String) -> AlertBuilder {
self.title = title
return self
}
func message(_ message: String) -> AlertBuilder {
self.message = message
return self
}
func addAction(_ title: String, style: UIAlertAction.Style = .default, handler: (() -> Void)? = nil) -> AlertBuilder {
let action = UIAlertAction(title: title, style: style) { _ in handler?() }
actions.append(action)
return self
}
func style(_ style: UIAlertController.Style) -> AlertBuilder {
self.style = style
return self
}
func build() -> UIAlertController {
let alert = UIAlertController(title: title, message: message, preferredStyle: style)
actions.forEach { alert.addAction($0) }
return alert
}
}
// Usage
let alert = AlertBuilder()
.title("Delete?")
.message("This cannot be undone")
.addAction("Cancel", style: .cancel)
.addAction("Delete", style: .destructive) {
deleteItem()
}
.build()
protocol Prototype {
func clone() -> Self
}
struct Document: Prototype {
var title: String
var content: String
var metadata: [String: String]
func clone() -> Document {
Document(title: title, content: content, metadata: metadata)
}
}
// Usage
let template = Document(title: "Template", content: "", metadata: ["author": "System"])
var newDoc = template.clone()
newDoc.title = "New Document"
// Adapt third-party API to your protocol
protocol PaymentProcessor {
func process(amount: Decimal) async throws -> PaymentResult
}
// Third-party SDK
class StripeSDK {
func charge(cents: Int, completion: @escaping (Result<StripeCharge, Error>) -> Void) {
// Stripe implementation
}
}
// Adapter
class StripeAdapter: PaymentProcessor {
private let stripe = StripeSDK()
func process(amount: Decimal) async throws -> PaymentResult {
let cents = Int(truncating: (amount * 100) as NSDecimalNumber)
return try await withCheckedThrowingContinuation { continuation in
stripe.charge(cents: cents) { result in
switch result {
case .success(let charge):
continuation.resume(returning: PaymentResult(
id: charge.id,
status: charge.status == "succeeded" ? .success : .failed
))
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
}
// Protocol
protocol DataSource {
func fetch() async throws -> [Item]
}
// Base implementation
class NetworkDataSource: DataSource {
func fetch() async throws -> [Item] {
// Fetch from network
}
}
// Caching decorator
class CachingDataSource: DataSource {
private let wrapped: DataSource
private var cache: [Item]?
init(wrapping: DataSource) {
self.wrapped = wrapping
}
func fetch() async throws -> [Item] {
if let cached = cache {
return cached
}
let items = try await wrapped.fetch()
cache = items
return items
}
}
// Logging decorator
class LoggingDataSource: DataSource {
private let wrapped: DataSource
private let logger: Logger
init(wrapping: DataSource, logger: Logger) {
self.wrapped = wrapping
self.logger = logger
}
func fetch() async throws -> [Item] {
logger.info("Fetching items...")
let start = Date()
let items = try await wrapped.fetch()
let duration = Date().timeIntervalSince(start)
logger.info("Fetched \(items.count) items in \(duration)s")
return items
}
}
// Usage - compose decorators
let dataSource: DataSource = LoggingDataSource(
wrapping: CachingDataSource(
wrapping: NetworkDataSource()
),
logger: logger
)
// Complex subsystems
class AuthService { func login() async throws -> Token { } }
class UserService { func fetchProfile(token: Token) async throws -> User { } }
class PreferencesService { func load(userId: String) async throws -> Preferences { } }
class AnalyticsService { func identify(user: User) { } }
// Facade simplifies usage
class SessionFacade {
private let auth = AuthService()
private let users = UserService()
private let preferences = PreferencesService()
private let analytics = AnalyticsService()
func startSession(credentials: Credentials) async throws -> Session {
let token = try await auth.login()
let user = try await users.fetchProfile(token: token)
let prefs = try await preferences.load(userId: user.id)
analytics.identify(user: user)
return Session(user: user, preferences: prefs, token: token)
}
}
// Simple usage
let session = try await SessionFacade().startSession(credentials: creds)
protocol FileSystemItem {
var name: String { get }
var size: Int { get }
}
struct File: FileSystemItem {
let name: String
let size: Int
}
class Folder: FileSystemItem {
let name: String
var children: [FileSystemItem] = []
var size: Int {
children.reduce(0) { $0 + $1.size }
}
init(name: String) {
self.name = name
}
func add(_ item: FileSystemItem) {
children.append(item)
}
}
// Usage
let root = Folder(name: "root")
let documents = Folder(name: "Documents")
documents.add(File(name: "resume.pdf", size: 1024))
documents.add(File(name: "photo.jpg", size: 2048))
root.add(documents)
root.add(File(name: "readme.txt", size: 100))
print(root.size) // 3172
// Modern Swift with Combine
import Combine
class UserSession {
@Published var currentUser: User?
@Published var isLoggedIn: Bool = false
}
// SwiftUI observes automatically
struct ProfileView: View {
@StateObject var session = UserSession()
var body: some View {
if session.isLoggedIn {
Text(session.currentUser?.name ?? "")
}
}
}
// Manual observation
class ProfileViewController {
private var cancellables = Set<AnyCancellable>()
private let session: UserSession
func observe() {
session.$currentUser
.receive(on: DispatchQueue.main)
.sink { [weak self] user in
self?.updateUI(with: user)
}
.store(in: &cancellables)
}
}
// Protocol defines strategy
protocol SortingStrategy {
func sort<T: Comparable>(_ items: [T]) -> [T]
}
struct QuickSort: SortingStrategy {
func sort<T: Comparable>(_ items: [T]) -> [T] {
items.sorted() // Using built-in quick sort
}
}
struct ReverseSort: SortingStrategy {
func sort<T: Comparable>(_ items: [T]) -> [T] {
items.sorted(by: >)
}
}
// Context uses strategy
class ListManager<T: Comparable> {
var items: [T]
var sortingStrategy: SortingStrategy
init(items: [T], strategy: SortingStrategy = QuickSort()) {
self.items = items
self.sortingStrategy = strategy
}
func sortedItems() -> [T] {
sortingStrategy.sort(items)
}
}
// Usage
let manager = ListManager(items: [3, 1, 4, 1, 5, 9])
manager.sortingStrategy = ReverseSort()
print(manager.sortedItems()) // [9, 5, 4, 3, 1, 1]
protocol Command {
func execute()
func undo()
}
class AddTextCommand: Command {
private let document: Document
private let text: String
private var previousState: String?
init(document: Document, text: String) {
self.document = document
self.text = text
}
func execute() {
previousState = document.content
document.content += text
}
func undo() {
if let previous = previousState {
document.content = previous
}
}
}
class CommandHistory {
private var commands: [Command] = []
private var undone: [Command] = []
func execute(_ command: Command) {
command.execute()
commands.append(command)
undone.removeAll()
}
func undo() {
guard let command = commands.popLast() else { return }
command.undo()
undone.append(command)
}
func redo() {
guard let command = undone.popLast() else { return }
command.execute()
commands.append(command)
}
}
protocol PlayerState {
func play(_ player: MusicPlayer)
func pause(_ player: MusicPlayer)
func stop(_ player: MusicPlayer)
}
class PlayingState: PlayerState {
func play(_ player: MusicPlayer) {
// Already playing
}
func pause(_ player: MusicPlayer) {
player.setState(PausedState())
player.pausePlayback()
}
func stop(_ player: MusicPlayer) {
player.setState(StoppedState())
player.stopPlayback()
}
}
class PausedState: PlayerState {
func play(_ player: MusicPlayer) {
player.setState(PlayingState())
player.resumePlayback()
}
func pause(_ player: MusicPlayer) {
// Already paused
}
func stop(_ player: MusicPlayer) {
player.setState(StoppedState())
player.stopPlayback()
}
}
class MusicPlayer {
private var state: PlayerState = StoppedState()
func setState(_ state: PlayerState) {
self.state = state
}
func play() { state.play(self) }
func pause() { state.pause(self) }
func stop() { state.stop(self) }
func startPlayback() { /* ... */ }
func pausePlayback() { /* ... */ }
func resumePlayback() { /* ... */ }
func stopPlayback() { /* ... */ }
}
| Problem | Pattern | |---------|---------| | Global shared instance | Singleton (or Actor) | | Create objects without specifying class | Factory | | Complex object construction | Builder | | Adapt incompatible interfaces | Adapter | | Add behavior dynamically | Decorator | | Simplify complex subsystem | Facade | | Tree structures | Composite | | Notify multiple objects of changes | Observer | | Swap algorithms at runtime | Strategy | | Undo/redo operations | Command | | Object behavior depends on state | State |
development
Design principles for building polished, native-feeling SwiftUI apps and widgets. Use this skill when creating or modifying SwiftUI views, iOS widgets (WidgetKit), or any native Apple UI. Ensures proper spacing, typography, colors, and widget implementations that look and feel like quality apps rather than AI-generated slop.
data-ai
Design and implement SwiftUI views, components, and app architecture. Use when creating new SwiftUI views, implementing MVVM/TCA patterns, managing state with @Observable, @State, @Binding, or @Environment, designing navigation flows, or structuring iOS app architecture. Triggers on SwiftUI, view model, state management, navigation, coordinator pattern.
development
Implement, review, or improve SwiftUI animations and transitions. Use when adding implicit or explicit animations with withAnimation, configuring spring animations (.smooth, .snappy, .bouncy), building phase or keyframe animations with PhaseAnimator/KeyframeAnimator, creating hero transitions with matchedGeometryEffect or matchedTransitionSource, adding SF Symbol effects (bounce, pulse, variableColor, breathe, rotate, wiggle), implementing custom Transition or CustomAnimation types, or ensuring animations respect accessibilityReduceMotion.
testing
Audit SwiftUI views for accessibility (iOS + macOS) with patch-ready fixes