Swift
Published by dralgorhythm in claude-agentic-framework
No known issues56 installs
What this skill does
Write Swift code for iOS/macOS following best practices. Use when developing with SwiftUI, UIKit, or Swift packages. Covers type safety, concurrency, and tooling.
Add Swift to your agent
Review the source and files first. When you are ready, copy the prompt instruction or use the CLI command supported by your environment.
Install with a prompt
Paste this into a compatible coding agent:
add this skill "swift" from https://github.com/dralgorhythm/claude-agentic-frameworkInstall with the CLI
Run this command in a controlled environment after reviewing the repository:
npx skills add https://github.com/dralgorhythm/claude-agentic-framework --skill swiftSkill instructions
Swift / iOS Development
Project Setup
Swift Package Manager
# Initialize new package
swift package init --type executable
# Add dependencies to Package.swift
# swift package update
Package.swift
// swift-tools-version: 5.10
import PackageDescription
let package = Package(
name: "MyApp",
platforms: [.iOS(.v17), .macOS(.v14)],
products: [
.library(name: "MyApp", targets: ["MyApp"]),
],
dependencies: [
// Add dependencies here
],
targets: [
.target(name: "MyApp", dependencies: []),
.testTarget(name: "MyAppTests", dependencies: ["MyApp"]),
]
)
Xcode Project
# Create new Xcode project via Xcode or:
# Use SwiftUI App template for new projects
# Target iOS 17+ for latest APIs
Type Patterns
Optionals
// Safe unwrapping
if let user = optionalUser {
print(user.name)
}
// Guard for early exit
guard let user = optionalUser else {
return
}
// Optional chaining
let name = user?.profile?.displayName ?? "Anonymous"
// Nil coalescing
let displayName = user?.name ?? "Guest"
Result Type
enum NetworkError: Error {
case invalidURL
case noData
case decodingFailed
}
func fetchUser(id: String) async -> Result<User, NetworkError> {
guard let url = URL(string: "https://api.example.com/users/\(id)") else {
return .failure(.invalidURL)
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
let user = try JSONDecoder().decode(User.self, from: data)
return .success(user)
} catch {
return .failure(.decodingFailed)
}
}
// Usage
switch await fetchUser(id: "123") {
case .success(let user):
print("Got user: \(user.name)")
case .failure(let error):
print("Error: \(error)")
}
Protocols & Extensions
protocol Identifiable {
var id: UUID { get }
}
protocol Displayable {
var displayName: String { get }
}
extension User: Identifiable, Displayable {
var displayName: String {
"\(firstName) \(lastName)"
}
}
Error Handling
// Throwing functions
func loadConfig() throws -> Config {
guard let data = FileManager.default.contents(atPath: configPath) else {
throw ConfigError.fileNotFound
}
return try JSONDecoder().decode(Config.self, from: data)
}
// Do-catch
do {
let config = try loadConfig()
print(config)
} catch ConfigError.fileNotFound {
print("Config file missing")
} catch {
print("Unknown error: \(error)")
}
// Try? for optional result
let config = try? loadConfig()
// Try! only when failure is impossible
let bundledConfig = try! loadBundledConfig()
Async/Await Patterns
Basic Async
func fetchUsers() async throws -> [User] {
let (data, _) = try await URLSession.shared.data(from: usersURL)
return try JSONDecoder().decode([User].self, from: data)
}
// Calling async functions
Task {
do {
let users = try await fetchUsers()
await MainActor.run {
self.users = users
}
} catch {
print("Failed: \(error)")
}
}
Structured Concurrency
// Parallel execution
async let users = fetchUsers()
async let posts = fetchPosts()
let (userList, postList) = try await (users, posts)
// Task groups
func fetchAllUserData(ids: [String]) async throws -> [UserData] {
try await withThrowingTaskGroup(of: UserData.self) { group in
for id in ids {
group.addTask {
try await fetchUserData(id: id)
}
}
return try await group.reduce(into: []) { $0.append($1) }
}
}
Actors
actor UserCache {
private var cache: [String: User] = [:]
func get(_ id: String) -> User? {
cache[id]
}
func set(_ user: User) {
cache[user.id] = user
}
}
// Usage
let cache = UserCache()
await cache.set(user)
let cached = await cache.get("123")
SwiftUI Patterns
View with State
struct ContentView: View {
@State private var count = 0
@State private var username = ""
var body: some View {
VStack(spacing: 16) {
Text("Count: \(count)")
.font(.title)
Button("Increment") {
count += 1
}
TextField("Username", text: $username)
.textFieldStyle(.roundedBorder)
}
.padding()
}
}
Observable ViewModel
@Observable
class UserViewModel {
var users: [User] = []
var isLoading = false
var error: Error?
func loadUsers() async {
isLoading = true
defer { isLoading = false }
do {
users = try await userService.fetchAll()
} catch {
self.error = error
}
}
}
struct UserListView: View {
@State private var viewModel = UserViewModel()
var body: some View {
List(viewModel.users) { user in
UserRow(user: user)
}
.task {
await viewModel.loadUsers()
}
.overlay {
if viewModel.isLoading {
ProgressView()
}
}
}
}
Environment & Dependency Injection
// Define environment key
struct UserServiceKey: EnvironmentKey {
static let defaultValue: UserService = .live
}
extension EnvironmentValues {
var userService: UserService {
get { self[UserServiceKey.self] }
set { self[UserServiceKey.self] = newValue }
}
}
// Use in view
struct ProfileView: View {
@Environment(\.userService) private var userService
var body: some View {
// Use userService
}
}
Testing
Swift Testing (Swift 6+)
import Testing
@Suite("UserService Tests")
struct UserServiceTests {
let service = UserService()
@Test("creates user with valid email")
func createUser() async throws {
let user = try await service.create(email: "test@example.com")
#expect(user.email == "test@example.com")
}
@Test("throws on invalid email")
func invalidEmail() async {
await #expect(throws: ValidationError.self) {
try await service.create(email: "invalid")
}
}
@Test("fetches user by ID", arguments: ["user1", "user2", "user3"])
func fetchUser(id: String) async throws {
let user = try await service.fetch(id: id)
#expect(user.id == id)
}
}
XCTest (Legacy)
import XCTest
@testable import MyApp
final class UserServiceTests: XCTestCase {
var service: UserService!
override func setUp() {
super.setUp()
service = UserService()
}
func testCreateUser() async throws {
let user = try await service.create(email: "test@example.com")
XCTAssertEqual(user.email, "test@example.com")
}
func testInvalidEmailThrows() async {
do {
_ = try await service.create(email: "invalid")
XCTFail("Expected error")
} catch {
XCTAssertTrue(error is ValidationError)
}
}
}
Tooling
# SwiftLint (linting)
brew install swiftlint
swiftlint lint
swiftlint lint --fix
# SwiftFormat (formatting)
brew install swiftformat
swiftformat .
swiftformat . --lint
# Run tests
swift test
xcodebuild test -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 15'
# Build
swift build
xcodebuild build -scheme MyApp
# fastlane (CI/CD)
brew install fastlane
fastlane init
fastlane ios test
fastlane ios beta # Deploy to TestFlight
.swiftlint.yml
disabled_rules:
- trailing_whitespace
- line_length
opt_in_rules:
- empty_count
- empty_string
excluded:
- Pods
- .build
.swiftformat
--indent 4
--allman false
--wraparguments before-first
--wrapparameters before-first
--self remove
--importgrouping alphabetized
Files included
- SKILL.md

