Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/miles990/claude-software-skills --skill swift- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads swift automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
Swift
Overview
Swift programming patterns including protocols, generics, async/await, and SwiftUI.
Swift Fundamentals
Structs and Classes
import Foundation
// Struct (value type, preferred for most cases)
struct User: Identifiable, Codable {
let id: UUID
var email: String
var name: String
var createdAt: Date
// Memberwise initializer provided automatically
// Custom initializer
init(email: String, name: String) {
self.id = UUID()
self.email = email
self.name = name
self.createdAt = Date()
}
// Computed property
var displayName: String {
"\(name) <\(email)>"
}
// Mutating method (for structs)
mutating func updateEmail(_ newEmail: String) {
email = newEmail
}
}
// Class (reference type)
class UserManager {
static let shared = UserManager() // Singleton
private var users: [UUID: User] = [:]
private init() {}
func add(_ user: User) {
users[user.id] = user
}
func find(id: UUID) -> User? {
users[id]
}
}
// Actor (thread-safe reference type)
actor UserStore {
private var users: [UUID: User] = [:]
func add(_ user: User) {
users[user.id] = user
}
func find(id: UUID) -> User? {
users[id]
}
func count() -> Int {
users.count
}
}
Enums and Pattern Matching
// Enum with associated values
enum Result<Success, Failure: Error> {
case success(Success)
case failure(Failure)
var isSuccess: Bool {
if case .success = self { return true }
return false
}
func map<NewSuccess>(_ transform: (Success) -> NewSuccess) -> Result<NewSuccess, Failure> {
switch self {
case .success(let value):
return .success(transform(value))
case .failure(let error):
return .failure(error)
}
}
}
// Enum with raw values
enum Status: String, Codable, CaseIterable {
case pending = "pending"
case active = "active"
case inactive = "inactive"
var displayName: String {
switch self {
case .pending: return "Pending Review"
case .active: return "Active"
case .inactive: return "Inactive"
}
}
}
// Pattern matching
func process(_ result: Result<User, Error>) {
switch result {
case .success(let user) where user.email.contains("@admin"):
print("Admin user: \(user.name)")
case .success(let user):
print("Regular user: \(user.name)")
case .failure(let error):
print("Error: \(error.localizedDescription)")
}
}
// If-case pattern
if case .success(let user) = result {
print(user.name)
}
// Guard-case pattern
func handleSuccess(_ result: Result<User, Error>) -> User? {
guard case .success(let user) = result else {
return nil
}
return user
}
Optionals
// Optional declaration
var name: String? = nil
var age: Int? = 25
// Optional binding
if let name = name {
print("Name: \(name)")
}
// Multiple bindings
if let name = name, let age = age, age > 18 {
print("\(name) is \(age) years old")
}
// Guard let (early exit)
func processUser(_ user: User?) -> String {
guard let user = user else {
return "No user"
}
return user.displayName
}
// Nil coalescing
let displayName = name ?? "Anonymous"
// Optional chaining
let uppercased = name?.uppercased()
// Map and flatMap
let nameLength = name.map { $0.count }
let parsed: Int? = "42".flatMap { Int($0) }
// Implicitly unwrapped (use sparingly)
var apiKey: String!
Protocols and Generics
Protocols
// Protocol definition
protocol Repository {
associatedtype Entity: Identifiable
func find(id: Entity.ID) async throws -> Entity?
func findAll() async throws -> [Entity]
func save(_ entity: Entity) async throws
func delete(id: Entity.ID) async throws
}
// Protocol with default implementation
extension Repository {
func findAll() async throws -> [Entity] {
// Default implementation
[]
}
}
// Protocol composition
protocol Named {
var name: String { get }
}
protocol Aged {
var age: Int { get }
}
typealias Person = Named & Aged
func greet(_ person: some Person) {
print("Hello, \(person.name)!")
}
// Protocol with Self requirement
protocol Copyable {
func copy() -> Self
}
// Conforming to protocols
struct UserRepository: Repository {
typealias Entity = User
func find(id: UUID) async throws -> User? {
// Implementation
nil
}
func save(_ entity: User) async throws {
// Implementation
}
func delete(id: UUID) async throws {
// Implementation
}
}
Generics
// Generic function
func swap<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
// Generic type
struct Stack<Element> {
private var items: [Element] = []
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element? {
items.popLast()
}
var top: Element? {
items.last
}
var isEmpty: Bool {
items.isEmpty
}
}
// Generic constraints
func findIndex<T: Equatable>(of value: T, in array: [T]) -> Int? {
for (index, item) in array.enumerated() {
if item == value {
return index
}
}
return nil
}
// Where clause
func allItemsMatch<C1: Container, C2: Container>(
_ c1: C1,
_ c2: C2
) -> Bool where C1.Item == C2.Item, C1.Item: Equatable {
guard c1.count == c2.count else { return false }
for i in 0..<c1.count {
if c1[i] != c2[i] { return false }
}
return true
}
// Opaque types (some)
func makeCollection() -> some Collection {
[1, 2, 3]
}
// Primary associated types (Swift 5.7+)
func process<C: Collection<String>>(_ collection: C) {
for item in collection {
print(item)
}
}
Async/Await (Swift 5.5+)
// Async function
func fetchUser(id: String) async throws -> User {
let url = URL(string: "https://api.example.com/users/\(id)")!
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode(User.self, from: data)
}
// Concurrent execution
func fetchAllUsers(ids: [String]) async throws -> [User] {
try await withThrowingTaskGroup(of: User.self) { group in
for id in ids {
group.addTask {
try await fetchUser(id: id)
}
}
var users: [User] = []
for try await user in group {
users.append(user)
}
return users
}
}
// Async sequences
struct NumberSequence: AsyncSequence {
typealias Element = Int
let start: Int
let end: Int
struct AsyncIterator: AsyncIteratorProtocol {
var current: Int
let end: Int
mutating func next() async -> Int? {
guard current <= end else { return nil }
defer { current += 1 }
try? await Task.sleep(nanoseconds: 100_000_000)
return current
}
}
func makeAsyncIterator() -> AsyncIterator {
AsyncIterator(current: start, end: end)
}
}
// Using async for-in
func processNumbers() async {
for await number in NumberSequence(start: 1, end: 10) {
print(number)
}
}
// Task groups
func processItems(_ items: [Item]) async throws -> [Result] {
try await withThrowingTaskGroup(of: Result.self) { group in
for item in items {
group.addTask {
try await process(item)
}
}
return try await group.reduce(into: []) { $0.append($1) }
}
}
// Actors for thread-safe state
actor Counter {
private var value = 0
func increment() {
value += 1
}
func get() -> Int {
value
}
}
// @MainActor for UI updates
@MainActor
class ViewModel: ObservableObject {
@Published var users: [User] = []
func loadUsers() async {
do {
let fetched = try await fetchAllUsers(ids: ["1", "2", "3"])
users = fetched
} catch {
print(error)
}
}
}
SwiftUI Patterns
import SwiftUI
// View composition
struct UserListView: View {
@StateObject private var viewModel = UserListViewModel()
var body: some View {
NavigationStack {
List(viewModel.users) { user in
NavigationLink(value: user) {
UserRow(user: user)
}
}
.navigationTitle("Users")
.navigationDestination(for: User.self) { user in
UserDetailView(user: user)
}
.refreshable {
await viewModel.refresh()
}
.task {
await viewModel.loadUsers()
}
}
}
}
struct UserRow: View {
let user: User
var body: some View {
HStack {
AsyncImage(url: user.avatarURL) { image in
image
.resizable()
.aspectRatio(contentMode: .fill)
} placeholder: {
ProgressView()
}
.frame(width: 44, height: 44)
.clipShape(Circle())
VStack(alignment: .leading) {
Text(user.name)
.font(.headline)
Text(user.email)
.font(.subheadline)
.foregroundColor(.secondary)
}
}
}
}
// View model with Combine
@MainActor
class UserListViewModel: ObservableObject {
@Published var users: [User] = []
@Published var isLoading = false
@Published var error: Error?
private let userService: UserService
init(userService: UserService = .shared) {
self.userService = userService
}
func loadUsers() async {
isLoading = true
defer { isLoading = false }
do {
users = try await userService.fetchUsers()
} catch {
self.error = error
}
}
func refresh() async {
await loadUsers()
}
}
// Custom view modifier
struct CardModifier: ViewModifier {
func body(content: Content) -> some View {
content
.padding()
.background(Color(.systemBackground))
.cornerRadius(12)
.shadow(radius: 4)
}
}
extension View {
func card() -> some View {
modifier(CardModifier())
}
}
// Environment values
private struct UserServiceKey: EnvironmentKey {
static let defaultValue = UserService.shared
}
extension EnvironmentValues {
var userService: UserService {
get { self[UserServiceKey.self] }
set { self[UserServiceKey.self] = newValue }
}
}
Error Handling
// Define errors
enum NetworkError: Error, LocalizedError {
case invalidURL
case noData
case decodingFailed(Error)
case serverError(statusCode: Int)
var errorDescription: String? {
switch self {
case .invalidURL:
return "Invalid URL"
case .noData:
return "No data received"
case .decodingFailed(let error):
return "Decoding failed: \(error.localizedDescription)"
case .serverError(let code):
return "Server error: \(code)"
}
}
}
// Throwing functions
func fetchData(from urlString: String) async throws -> Data {
guard let url = URL(string: urlString) else {
throw NetworkError.invalidURL
}
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse else {
throw NetworkError.noData
}
guard (200...299).contains(httpResponse.statusCode) else {
throw NetworkError.serverError(statusCode: httpResponse.statusCode)
}
return data
}
// Result type
func fetchUser(id: String) async -> Result<User, NetworkError> {
do {
let data = try await fetchData(from: "https://api.example.com/users/\(id)")
let user = try JSONDecoder().decode(User.self, from: data)
return .success(user)
} catch let error as NetworkError {
return .failure(error)
} catch {
return .failure(.decodingFailed(error))
}
}
// Do-catch
do {
let user = try await fetchData(from: "...")
print(user)
} catch NetworkError.invalidURL {
print("Bad URL")
} catch {
print("Other error: \(error)")
}
// try? and try!
let user = try? await fetchUser(id: "1") // Returns optional
let definiteUser = try! loadFromCache() // Force unwrap (crashes on error)
Related Skills
- [[mobile]] - iOS/macOS development
- [[frontend]] - SwiftUI patterns
- [[testing]] - XCTest, Quick/Nimble
FAQ
What does the swift skill do?
Swift programming patterns for iOS and macOS
How do I install swift?
Run npx skills add https://github.com/miles990/claude-software-skills --skill swift in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/swift/ (personal) or .claude/skills/swift/ (project).
Which AI agents can use swift?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is swift safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains swift?
It is published by miles990 in miles990/claude-software-skills. It has 54 recorded installs.
More from miles990/claude-software-skills
code-quality
miles990/claude-software-skills
Clean code principles, SOLID, and code review practices
devops-cicd
miles990/claude-software-skills
CI/CD pipelines, infrastructure as code, and deployment strategies
game-development
miles990/claude-software-skills
Game development patterns, architectures, and best practices
project-management
miles990/claude-software-skills
Open the skill to read its SKILL.md instructions.
e-commerce
miles990/claude-software-skills
E-commerce platforms, payment processing, and shopping cart patterns
saas-platforms
miles990/claude-software-skills
Open the skill to read its SKILL.md instructions.
Popular mobile skills
design-mobile-apps
designed-by-ai/skills
Design mobile app screens with Sleek, edit Sleek projects, and implement their designs in React Native or HTML.
ios-design
uizze.sh
Design and review iOS UI/UX for iPhone apps in SwiftUI, UIKit, or React Native. Use for mobile app design, screen redesigns, navigation flows, typography, forms, keyboard and safe-area layout, and accessibility checks. Apply Apple Human Interface Guidelines and consult real product references through the optional Uizze connection.
vercel-react-native-skills
vercel-labs/agent-skills
React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.
animate-expo
emilkowalski/skills
Build animations in React Native and Expo, making the decisions in the order that determines whether they feel right — should it animate, which thread it runs on, which properties, spring or timing, how the gesture hands off, how it degrades. Writes the implementation with Reanimated, Gesture Handler, Expo Router and expo-haptics. Use when animating anything in an Expo app, adding gestures, sheets, screen transitions, press feedback or haptics, or fixing motion that stutters on device. For web animation use animate.
xcode-project-setup
firebase/agent-skills
Safely modifies Xcode projects (.pbxproj) to add Swift Packages and link files. Use this skill whenever an iOS project needs dependencies installed (e.g. Firebase, Alamofire).
firebase-remote-config-basics
firebase/agent-skills
Manages Firebase Remote Config templates, feature flags, loading strategies, and SDKs (Android, iOS). Use when downloading/deploying remoteconfig JSON templates, managing version history/feature flags, setting in-app defaults, fetchAndActivate(), real-time listeners, or SDK setup. Don't use for Firebase Hosting, Auth, Firestore, Data Connect, Crashlytics, or App Hosting.
