15 Prompts for Swift and iOS: SwiftUI, UIKit, Core Data

Why You Need These Prompts

As an iOS developer, you spend hours debugging, refactoring, and writing boilerplate. AI assistants can cut that time by 40–60%, but only if you know how to prompt them correctly. I’ve collected 15 battle-tested prompts from my daily workflow — each one solves a real problem I’ve faced building production apps. No theory, only code.

1. Generate a SwiftUI List with Pull-to-Refresh and Pagination

Prompt:
"Write a SwiftUI view that displays a list of items fetched from an API. Include pull-to-refresh using .refreshable, and infinite scroll using onAppear for pagination. Use @StateObject for the view model. Assume the API returns JSON with id, title, and description. Handle loading and error states."

Example:

struct Item: Codable, Identifiable {
    let id: Int
    let title: String
    let description: String
}

class ItemsViewModel: ObservableObject {
    @Published var items: [Item] = []
    @Published var isLoading = false
    @Published var errorMessage: String?
    private var currentPage = 1

    func fetchItems() async {
        isLoading = true
        errorMessage = nil
        do {
            let url = URL(string: "https://api.example.com/items?page=\(currentPage)")!
            let (data, _) = try await URLSession.shared.data(from: url)
            let decoded = try JSONDecoder().decode([Item].self, from: data)
            await MainActor.run {
                items += decoded
                currentPage += 1
                isLoading = false
            }
        } catch {
            await MainActor.run {
                errorMessage = error.localizedDescription
                isLoading = false
            }
        }
    }
}

struct ContentView: View {
    @StateObject private var viewModel = ItemsViewModel()

    var body: some View {
        NavigationView {
            List {
                ForEach(viewModel.items) { item in
                    VStack(alignment: .leading) {
                        Text(item.title).font(.headline)
                        Text(item.description).font(.subheadline)
                    }
                    .onAppear {
                        if item == viewModel.items.last {
                            Task { await viewModel.fetchItems() }
                        }
                    }
                }
                if viewModel.isLoading {
                    ProgressView()
                }
            }
            .navigationTitle("Items")
            .refreshable {
                viewModel.items.removeAll()
                viewModel.currentPage = 1
                await viewModel.fetchItems()
            }
            .task { await viewModel.fetchItems() }
            .alert("Error", isPresented: .constant(viewModel.errorMessage != nil)) {
                Button("OK") { viewModel.errorMessage = nil }
            } message: {
                Text(viewModel.errorMessage ?? "")
            }
        }
    }
}

Why it works: This prompt covers three common patterns — async loading, pagination, and refresh — in one reusable snippet. Saves ~30 minutes of boilerplate per screen.

2. Convert UIKit View to SwiftUI Using UIViewRepresentable

Prompt:
"Create a SwiftUI wrapper for a UIKit UITextField with custom font, placeholder, and a delegate callback for text changes. Use UIViewRepresentable. Ensure the wrapper updates when SwiftUI state changes."

Example:

struct CustomTextField: UIViewRepresentable {
    @Binding var text: String
    var placeholder: String
    var font: UIFont

    func makeUIView(context: Context) -> UITextField {
        let tf = UITextField()
        tf.placeholder = placeholder
        tf.font = font
        tf.delegate = context.coordinator
        return tf
    }

    func updateUIView(_ uiView: UITextField, context: Context) {
        uiView.text = text
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject, UITextFieldDelegate {
        var parent: CustomTextField
        init(_ parent: CustomTextField) { self.parent = parent }
        func textFieldDidChangeSelection(_ textField: UITextField) {
            parent.text = textField.text ?? ""
        }
    }
}

Why it works: Many legacy UIKit components aren’t available in SwiftUI. This pattern lets you reuse them with minimal code. The prompt forces the AI to handle state synchronisation correctly.

3. Create a Core Data Fetch Request with Predicate and Sorting

Prompt:
"Write a Core Data fetch request for an Event entity with attributes date (Date) and name (String). Filter events where date > today and sort by date ascending. Return as NSFetchedResultsController for a UITableView."

Example:

let fetchRequest: NSFetchRequest<Event> = Event.fetchRequest()
let calendar = Calendar.current
let today = calendar.startOfDay(for: Date())
fetchRequest.predicate = NSPredicate(format: "date > %@", today as NSDate)
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)]

let controller = NSFetchedResultsController(
    fetchRequest: fetchRequest,
    managedObjectContext: persistentContainer.viewContext,
    sectionNameKeyPath: nil,
    cacheName: nil
)

Why it works: Predicates are error-prone. This prompt generates a correct expression that works with both SQLite and in-memory stores.

4. Debug a SwiftUI Layout with Alignment Guides

Prompt:
"I have a VStack with two Text views. The first text is left-aligned, the second is right-aligned. They should share the same baseline. Write code using .alignmentGuide to achieve this."

Example:

struct BaselineExample: View {
    var body: some View {
        HStack(alignment: .top) {
            Text("Left")
                .alignmentGuide(.top) { d in d[.top] }
            Text("Right")
                .alignmentGuide(.top) { d in d[.top] }
        }
        .frame(width: 200)
    }
}

Why it works: Alignment guides are poorly documented. This gives you a working template you can adjust.

5. Implement a Combine Publisher for TextField Debounce

Prompt:
"Create a Combine pipeline that listens to a UITextField text changes, debounces by 0.5 seconds, and then sends a network request. Cancel previous request if a new one starts."

Example:

class SearchViewModel: ObservableObject {
    @Published var query = ""
    private var cancellables = Set<AnyCancellable>()

    init() {
        $query
            .debounce(for: .seconds(0.5), scheduler: RunLoop.main)
            .removeDuplicates()
            .sink { [weak self] value in
                self?.performSearch(value)
            }
            .store(in: &cancellables)
    }

    func performSearch(_ text: String) {
        // Network call here
    }
}

Why it works: Debouncing prevents excessive API calls. The prompt includes cancellation handling implicitly via sink’s lifecycle.

6. Write a Unit Test for a ViewModel with Mock Data

Prompt:
"Write a unit test for a LoginViewModel that validates email format and password length. Mock the authentication service. Use XCTest expectations for async calls."

Example:

func testLoginWithValidCredentials() {
    let mockService = MockAuthService()
    mockService.result = .success(User(id: 1, token: "abc"))
    let viewModel = LoginViewModel(authService: mockService)

    viewModel.email = "test@example.com"
    viewModel.password = "password123"

    let expectation = XCTestExpectation(description: "Login succeeds")
    viewModel.login { success in
        XCTAssertTrue(success)
        expectation.fulfill()
    }
    wait(for: [expectation], timeout: 1.0)
}

Why it works: Many developers skip tests because they take time to write. This prompt gives you a ready-to-run template.

7. Create a Custom Animation with SwiftUI and GeometryReader

Prompt:
"Write a SwiftUI view that animates a card flipping over when tapped. Use GeometryReader to get the card’s frame. The animation should be a 3D rotation around the Y axis."

Example:

struct CardFlip: View {
    @State private var flipped = false

    var body: some View {
        GeometryReader { geo in
            ZStack {
                if !flipped {
                    RoundedRectangle(cornerRadius: 10)
                        .fill(Color.blue)
                        .frame(width: 200, height: 300)
                } else {
                    RoundedRectangle(cornerRadius: 10)
                        .fill(Color.green)
                        .frame(width: 200, height: 300)
                }
            }
            .rotation3DEffect(
                .degrees(flipped ? 180 : 0),
                axis: (x: 0, y: 1, z: 0)
            )
            .onTapGesture {
                withAnimation(.easeInOut(duration: 0.6)) {
                    flipped.toggle()
                }
            }
        }
    }
}

Why it works: 3D animations in SwiftUI require specific modifiers. This snippet is a complete, compilable example.

8. Migrate from UIKit to SwiftUI Using UIHostingController

Prompt:
"Write code to embed a SwiftUI LoginView inside a UIKit UIViewController. Use UIHostingController and pass data back via a delegate."

Example:

class LoginHostingViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let swiftUIView = LoginView { [weak self] in
            self?.dismiss(animated: true)
        }
        let hostingController = UIHostingController(rootView: swiftUIView)
        addChild(hostingController)
        view.addSubview(hostingController.view)
        hostingController.view.frame = view.bounds
        hostingController.didMove(toParent: self)
    }
}

Why it works: Incremental adoption is common in legacy apps. This prompt shows a clean bridge pattern.

9. Optimize a SwiftUI List with LazyVStack and Identifiable

Prompt:
"Rewrite a List with 10,000 items to use LazyVStack inside a ScrollView for better performance. Ensure items are Identifiable and use equatable."

Example:

struct OptimizedListView: View {
    let items: [Item] // Conforms to Identifiable

    var body: some View {
        ScrollView {
            LazyVStack {
                ForEach(items) { item in
                    ItemRow(item: item)
                        .equatable()
                }
            }
        }
    }
}

Why it works: Lists can be slow with huge datasets. LazyVStack renders only visible rows.

10. Handle Errors in Async/Await with Retry Logic

Prompt:
"Write a generic function that retries an async operation up to 3 times with exponential backoff. Use Task.sleep for delays."

Example:

func retry<T>(
    maxAttempts: Int = 3,
    operation: () async throws -> T
) async throws -> T {
    var lastError: Error?
    for attempt in 1...maxAttempts {
        do {
            return try await operation()
        } catch {
            lastError = error
            if attempt < maxAttempts {
                let delay = Double(attempt) * 0.5
                try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
            }
        }
    }
    throw lastError!
}

Why it works: Network failures are inevitable. This reusable function adds resilience without cluttering business logic.

11. Create a SwiftUI Form with Validation

Prompt:
"Build a registration form with fields for email, password, and confirm password. Validate that email is valid format, password is at least 8 characters, and passwords match. Show inline error messages."

Example (partial):

struct RegistrationForm: View {
    @State private var email = ""
    @State private var password = ""
    @State private var confirmPassword = ""

    var isValid: Bool {
        email.contains("@") && password.count >= 8 && password == confirmPassword
    }

    var body: some View {
        Form {
            TextField("Email", text: $email)
            if !email.contains("@") && !email.isEmpty {
                Text("Invalid email").foregroundColor(.red)
            }
            SecureField("Password", text: $password)
            SecureField("Confirm", text: $confirmPassword)
            if password != confirmPassword {
                Text("Passwords don’t match").foregroundColor(.red)
            }
            Button("Submit") { /* handle */ }
                .disabled(!isValid)
        }
    }
}

Why it works: Forms are repetitive. This prompt gives a complete validation flow.

12. Use SwiftUI Previews with Different Data

Prompt:
"Create multiple SwiftUI previews for a ProfileView showing loading, loaded, and error states. Use mock data."

Example:

struct ProfileView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            ProfileView(state: .loading)
            ProfileView(state: .loaded(Profile(name: "Alex", age: 30)))
            ProfileView(state: .error("Network error"))
        }
    }
}

Why it works: Previews speed up UI development. This pattern tests all states in one glance.

13. Implement Drag-and-Drop in UIKit with UIDragInteraction

Prompt:
"Add drag support to a UICollectionView cell. When dragged, the cell should create a drag item with its title string. Use UIDragInteractionDelegate."

Example:

func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
    let item = items[indexPath.row]
    let provider = NSItemProvider(object: item.title as NSString)
    return [UIDragItem(itemProvider: provider)]
}

Why it works: Drag-and-drop is a common UX pattern. The delegate method is the entry point.

14. Use SwiftUI Charts (iOS 16+) to Visualize Data

Prompt:
"Create a bar chart showing monthly sales data using SwiftUI Chart framework. Use Chart, BarMark, and PointMark. Annotate the highest value."

Example:

import Charts

struct SalesChart: View {
    let data: [(month: String, sales: Int)] = [
        ("Jan", 100), ("Feb", 150), ("Mar", 200)
    ]

    var body: some View {
        Chart(data, id: \.month) { item in
            BarMark(x: .value("Month", item.month), y: .value("Sales", item.sales))
                .annotation(position: .top) {
                    Text("\(item.sales)")
                }
        }
    }
}

Why it works: Charts were added in iOS 16. This prompt gives a working template.

15. Write a Playground for Testing Core Data Models

Prompt:
"Create a Swift Playground that sets up an in-memory Core Data stack, inserts a few Person objects, and prints them."

Example:

import CoreData

let container = NSPersistentContainer(name: "Model")
container.persistentStoreDescriptions.first?.url = URL(fileURLWithPath: "/dev/null")
container.loadPersistentStores { _, error in
    if let error = error { fatalError(error.localizedDescription) }
}

let context = container.viewContext
let person = Person(context: context)
person.name = "Alice"
person.age = 25
try context.save()

let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()
let results = try context.fetch(fetchRequest)
results.forEach { print($0.name ?? "") }

Why it works: Testing Core Data in a playground is fast and isolated.

Conclusion

These 15 prompts cover the most common iOS development tasks — from UI layout to data persistence. Copy them, adapt them, and integrate them into your daily workflow. The key is specificity: describe the problem, the desired behaviour, and the constraints. That turns an AI from a toy into a productivity tool.

If you have a favourite prompt that isn’t here, share it in the comments. Let’s build a better library together.

← All posts

Comments