30 Essential Prompts for Swift and iOS Development: SwiftUI, UIKit, Core Data, and Beyond

Introduction

Building iOS applications in 2026 requires more than just knowing Swift syntax—it demands fluency across multiple frameworks. Whether you're crafting declarative interfaces with SwiftUI, fine-tuning performance with UIKit, or managing persistent data with Core Data, the right prompts can accelerate your workflow. This article curates 30 practical prompts organized by skill level: basic, advanced, and expert. Each prompt includes a clear task, the exact prompt text you can copy, and a real-world example result. By the end, you'll have a reusable toolkit for code generation, debugging, and architectural decisions.

Why Prompts Matter for iOS Development

Large language models (LLMs) have matured significantly. In 2026, tools like GPT-4, Claude 3.5, and specialized code assistants can generate entire view hierarchies, suggest Core Data migration strategies, and explain Combine publishers in plain English—provided you ask the right way. A well-structured prompt reduces ambiguity, saves hours of Stack Overflow digging, and produces production-ready code more often than not.

For instance, instead of asking "How do I make a network call?", a precise prompt like "Generate a SwiftUI view that fetches JSON from a URL and displays it in a List, using async/await, with error handling using Alert" yields a complete, compilable snippet. This article assumes you have basic Swift knowledge and an Xcode project ready to test examples.

Basic Prompts

These prompts target developers new to iOS or those who want quick, reusable solutions for common tasks. They focus on single-view applications, simple data models, and standard UIKit or SwiftUI patterns.

1. SwiftUI Form with Validation

  • Task: Create a registration form with email, password, and age fields, with inline validation.
  • Prompt: "Generate a SwiftUI view that implements a registration form with TextFields for email, password, and age. Add validation: email must contain '@', password must be at least 8 characters, age must be between 18 and 120. Show error messages below each field. Use @State for form data and @State for error messages."
  • Example Result:
struct RegistrationView: View {
    @State private var email = ""
    @State private var password = ""
    @State private var ageText = ""
    @State private var emailError = ""
    @State private var passwordError = ""
    @State private var ageError = ""

    var body: some View {
        Form {
            Section(header: Text("Account Details")) {
                TextField("Email", text: $email)
                    .onChange(of: email) { _ in validateEmail() }
                if !emailError.isEmpty {
                    Text(emailError).foregroundColor(.red).font(.caption)
                }
                SecureField("Password", text: $password)
                    .onChange(of: password) { _ in validatePassword() }
                if !passwordError.isEmpty {
                    Text(passwordError).foregroundColor(.red).font(.caption)
                }
                TextField("Age", text: $ageText)
                    .keyboardType(.numberPad)
                    .onChange(of: ageText) { _ in validateAge() }
                if !ageError.isEmpty {
                    Text(ageError).foregroundColor(.red).font(.caption)
                }
            }
            Button("Register") { /* submit logic */ }
                .disabled(!isFormValid)
        }
    }

    private func validateEmail() {
        emailError = email.contains("@") ? "" : "Email must contain '@'"
    }
    private func validatePassword() {
        passwordError = password.count >= 8 ? "" : "Password must be at least 8 characters"
    }
    private func validateAge() {
        guard let age = Int(ageText), (18...120).contains(age) else {
            ageError = "Age must be between 18 and 120"
            return
        }
        ageError = ""
    }
    private var isFormValid: Bool {
        emailError.isEmpty && passwordError.isEmpty && ageError.isEmpty && !email.isEmpty && !password.isEmpty && !ageText.isEmpty
    }
}

2. UIKit TableView with Custom Cell

  • Task: Display a list of items using UITableView and a custom UITableViewCell.
  • Prompt: "Write a UIViewController that uses UITableView with a custom cell named ProductCell. The cell should have an imageView, title label, and price label. Use a hardcoded array of Product structs (name, price, imageName). Implement UITableViewDataSource methods."
  • Example Result:
struct Product {
    let name: String
    let price: Double
    let imageName: String
}

class ProductListViewController: UIViewController, UITableViewDataSource {
    @IBOutlet weak var tableView: UITableView!
    let products = [
        Product(name: "Widget", price: 19.99, imageName: "widget"),
        Product(name: "Gadget", price: 29.99, imageName: "gadget")
    ]

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = self
        tableView.register(ProductCell.self, forCellReuseIdentifier: "ProductCell")
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return products.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "ProductCell", for: indexPath) as! ProductCell
        let product = products[indexPath.row]
        cell.configure(with: product)
        return cell
    }
}

class ProductCell: UITableViewCell {
    let productImageView = UIImageView()
    let titleLabel = UILabel()
    let priceLabel = UILabel()

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        // Add subviews and constraints here
    }

    required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }

    func configure(with product: Product) {
        titleLabel.text = product.name
        priceLabel.text = "$\(product.price)"
        productImageView.image = UIImage(named: product.imageName)
    }
}

3. Core Data Simple CRUD

  • Task: Save, fetch, update, and delete a Person entity with name and age attributes.
  • Prompt: "Create a Core Data stack helper that provides a persistent container. Then write functions to create a Person entity (name: String, age: Int16), fetch all persons, update a person's age, and delete a person by name. Use Swift concurrency (async/await) for context operations."
  • Example Result:
import CoreData

struct CoreDataStack {
    static let shared = CoreDataStack()
    let container: NSPersistentContainer

    init() {
        container = NSPersistentContainer(name: "MyModel")
        container.loadPersistentStores { _, error in
            if let error = error { fatalError("Core Data load error: \(error)") }
        }
    }

    var context: NSManagedObjectContext { container.viewContext }

    func saveContext() {
        if context.hasChanges {
            try? context.save()
        }
    }

    func createPerson(name: String, age: Int16) async {
        await context.perform {
            let person = Person(context: self.context)
            person.name = name
            person.age = age
            self.saveContext()
        }
    }

    func fetchAllPersons() async -> [Person] {
        await context.perform {
            let request: NSFetchRequest<Person> = Person.fetchRequest()
            return (try? self.context.fetch(request)) ?? []
        }
    }

    func updatePersonAge(name: String, newAge: Int16) async {
        await context.perform {
            let request: NSFetchRequest<Person> = Person.fetchRequest()
            request.predicate = NSPredicate(format: "name == %@", name)
            if let person = try? self.context.fetch(request).first {
                person.age = newAge
                self.saveContext()
            }
        }
    }

    func deletePerson(name: String) async {
        await context.perform {
            let request: NSFetchRequest<Person> = Person.fetchRequest()
            request.predicate = NSPredicate(format: "name == %@", name)
            if let person = try? self.context.fetch(request).first {
                self.context.delete(person)
                self.saveContext()
            }
        }
    }
}

4. URLSession with Async/Await

  • Task: Fetch JSON from an API and decode it.
  • Prompt: "Write a function that uses URLSession with async/await to fetch a list of User objects from 'https://jsonplaceholder.typicode.com/users'. Each User has id, name, email. Handle network errors and decoding errors with a Result type."
  • Example Result:
struct User: Codable {
    let id: Int
    let name: String
    let email: String
}

enum NetworkError: Error {
    case invalidURL
    case requestFailed(Error)
    case decodingFailed(Error)
}

func fetchUsers() async -> Result<[User], NetworkError> {
    guard let url = URL(string: "https://jsonplaceholder.typicode.com/users") else {
        return .failure(.invalidURL)
    }
    do {
        let (data, _) = try await URLSession.shared.data(from: url)
        let users = try JSONDecoder().decode([User].self, from: data)
        return .success(users)
    } catch let error as DecodingError {
        return .failure(.decodingFailed(error))
    } catch {
        return .failure(.requestFailed(error))
    }
}

5. SwiftUI List with NavigationLink

  • Task: Create a list of items that navigate to a detail view.
  • Prompt: "Create a SwiftUI app with a List of book titles. Tapping a book navigates to a detail view showing the title and author. Use NavigationStack and NavigationLink."
  • Example Result:
struct Book: Identifiable {
    let id = UUID()
    let title: String
    let author: String
}

struct BookListView: View {
    let books = [
        Book(title: "1984", author: "George Orwell"),
        Book(title: "To Kill a Mockingbird", author: "Harper Lee")
    ]

    var body: some View {
        NavigationStack {
            List(books) { book in
                NavigationLink(value: book) {
                    Text(book.title)
                }
            }
            .navigationDestination(for: Book.self) { book in
                BookDetailView(book: book)
            }
            .navigationTitle("Books")
        }
    }
}

struct BookDetailView: View {
    let book: Book
    var body: some View {
        VStack {
            Text(book.title).font(.largeTitle)
            Text(book.author).font(.title2).foregroundColor(.secondary)
        }
        .navigationTitle(book.title)
    }
}

Advanced Prompts

These prompts target developers comfortable with iOS who need to integrate multiple frameworks, handle concurrency, or implement complex UI patterns.

6. Combine Publisher for Search

  • Task: Implement a search bar that debounces input and fetches results from a local data source.
  • Prompt: "Write a SwiftUI view with a TextField for search. Use Combine to debounce the input by 0.5 seconds, then filter a hardcoded array of strings. Display results in a List. Use @Published and ObservableObject."
  • Example Result:
import Combine

class SearchViewModel: ObservableObject {
    @Published var searchText = ""
    @Published var results: [String] = []
    private var cancellables = Set<AnyCancellable>()

    let allItems = ["Apple", "Banana", "Cherry", "Date", "Elderberry"]

    init() {
        $searchText
            .debounce(for: .seconds(0.5), scheduler: RunLoop.main)
            .removeDuplicates()
            .map { [weak self] text in
                guard let self = self else { return [] }
                return text.isEmpty ? self.allItems : self.allItems.filter { $0.localizedCaseInsensitiveContains(text) }
            }
            .assign(to: &$results)
    }
}

struct SearchView: View {
    @StateObject private var viewModel = SearchViewModel()

    var body: some View {
        VStack {
            TextField("Search fruits", text: $viewModel.searchText)
                .textFieldStyle(.roundedBorder)
                .padding()
            List(viewModel.results, id: \.self) { item in
                Text(item)
            }
        }
    }
}

7. Core Data with CloudKit Sync

  • Task: Set up NSPersistentCloudKitContainer to sync data between devices.
  • Prompt: "Explain how to replace NSPersistentContainer with NSPersistentCloudKitContainer to enable iCloud sync. Provide code for the container setup, including the necessary entitlements and a sample entity. Highlight potential pitfalls like merge conflicts."
  • Example Result:
// In your AppDelegate or Core Data stack:
let container = NSPersistentCloudKitContainer(name: "MyModel")
container.loadPersistentStores { _, error in
    if let error = error { fatalError("CloudKit container error: \(error)") }
}
container.viewContext.automaticallyMergesChangesFromParent = true
try? container.viewContext.setQueryGenerationFrom(.current)

// Entitlements needed: iCloud (with CloudKit service), Background Modes (remote notifications)
// For conflict resolution: implement NSMergePolicy (e.g., NSMergeByPropertyObjectTrumpMergePolicy)

8. SwiftUI Animation with GeometryReader

  • Task: Create a custom animated tab bar indicator.
  • Prompt: "Write a SwiftUI view that contains a horizontal row of three tab buttons. Use GeometryReader to animate a sliding underline indicator when a tab is selected. The animation should be smooth and interactive."
  • Example Result:
struct AnimatedTabBar: View {
    @State private var selectedTab = 0
    let tabs = ["Home", "Search", "Profile"]

    var body: some View {
        GeometryReader { geometry in
            VStack {
                HStack(spacing: 0) {
                    ForEach(tabs.indices, id: \.self) { index in
                        Button(action: { withAnimation(.spring()) { selectedTab = index } }) {
                            Text(tabs[index])
                                .frame(maxWidth: .infinity)
                                .padding(.vertical, 10)
                        }
                    }
                }
                .overlay(alignment: .bottomLeading) {
                    Rectangle()
                        .fill(Color.blue)
                        .frame(width: geometry.size.width / CGFloat(tabs.count), height: 3)
                        .offset(x: geometry.size.width / CGFloat(tabs.count) * CGFloat(selectedTab))
                        .animation(.spring(), value: selectedTab)
                }
            }
        }
        .frame(height: 50)
    }
}

9. UIKit Programmatic Constraints with Auto Layout

  • Task: Build a login screen entirely in code using Auto Layout anchors.
  • Prompt: "Create a UIView subclass for a login screen with a logo imageView, email TextField, password TextField, and login button. All subviews should use Auto Layout anchors (no storyboard). The stack should be vertically centered with 20pt spacing."
  • Example Result:
class LoginView: UIView {
    let logoImageView = UIImageView(image: UIImage(systemName: "person.circle"))
    let emailTextField = UITextField()
    let passwordTextField = UITextField()
    let loginButton = UIButton(type: .system)

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupViews()
    }

    required init?(coder: NSCoder) { fatalError() }

    private func setupViews() {
        [logoImageView, emailTextField, passwordTextField, loginButton].forEach {
            $0.translatesAutoresizingMaskIntoConstraints = false
            addSubview($0)
        }
        emailTextField.placeholder = "Email"
        passwordTextField.placeholder = "Password"
        passwordTextField.isSecureTextEntry = true
        loginButton.setTitle("Login", for: .normal)

        NSLayoutConstraint.activate([
            logoImageView.centerXAnchor.constraint(equalTo: centerXAnchor),
            logoImageView.topAnchor.constraint(equalTo: topAnchor, constant: 40),
            emailTextField.topAnchor.constraint(equalTo: logoImageView.bottomAnchor, constant: 20),
            emailTextField.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
            emailTextField.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20),
            passwordTextField.topAnchor.constraint(equalTo: emailTextField.bottomAnchor, constant: 20),
            passwordTextField.leadingAnchor.constraint(equalTo: emailTextField.leadingAnchor),
            passwordTextField.trailingAnchor.constraint(equalTo: emailTextField.trailingAnchor),
            loginButton.topAnchor.constraint(equalTo: passwordTextField.bottomAnchor, constant: 30),
            loginButton.centerXAnchor.constraint(equalTo: centerXAnchor),
            loginButton.widthAnchor.constraint(equalToConstant: 200)
        ])
    }
}

10. SwiftUI Image Picker with PHPicker

  • Task: Integrate the modern PHPicker to select a single photo.
  • Prompt: "Write a SwiftUI view that uses UIViewControllerRepresentable to wrap PHPickerViewController. The picker should allow selection of a single image and return its UIImage. Handle cancellation and errors."
  • Example Result:
struct ImagePicker: UIViewControllerRepresentable {
    @Binding var image: UIImage?
    @Environment(\.dismiss) private var dismiss

    func makeUIViewController(context: Context) -> PHPickerViewController {
        var config = PHPickerConfiguration()
        config.selectionLimit = 1
        config.filter = .images
        let picker = PHPickerViewController(configuration: config)
        picker.delegate = context.coordinator
        return picker
    }

    func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {}

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

    class Coordinator: NSObject, PHPickerViewControllerDelegate {
        let parent: ImagePicker
        init(_ parent: ImagePicker) { self.parent = parent }

        func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
            picker.dismiss(animated: true)
            guard let provider = results.first?.itemProvider, provider.canLoadObject(ofClass: UIImage.self) else {
                return
            }
            provider.loadObject(ofClass: UIImage.self) { [weak self] image, error in
                DispatchQueue.main.async {
                    self?.parent.image = image as? UIImage
                }
            }
        }
    }
}

Expert Prompts

These prompts target senior iOS developers who need architectural guidance, performance optimization, or advanced framework integration.

11. Clean Architecture with SwiftUI and Combine

  • Task: Implement a full VIPER-like architecture using SwiftUI and Combine.
  • Prompt: "Design a SwiftUI app that fetches weather data from an API. Use a protocol-oriented service layer, a ViewModel that publishes state with @Published, and a View that observes it. Include dependency injection via environment values. Show how to handle loading, error, and success states."
  • Example Result:
// Service protocol
protocol WeatherServiceProtocol {
    func fetchWeather(for city: String) async throws -> Weather
}

// Concrete service
class WeatherService: WeatherServiceProtocol {
    func fetchWeather(for city: String) async throws -> Weather {
        // network call
        return Weather(temperature: 22.0, condition: "Clear")
    }
}

// ViewModel
@MainActor
class WeatherViewModel: ObservableObject {
    @Published var state: ViewState<Weather> = .idle
    private let service: WeatherServiceProtocol

    init(service: WeatherServiceProtocol = WeatherService()) {
        self.service = service
    }

    func loadWeather(city: String) async {
        state = .loading
        do {
            let weather = try await service.fetchWeather(for: city)
            state = .loaded(weather)
        } catch {
            state = .error(error.localizedDescription)
        }
    }
}

enum ViewState<T> {
    case idle, loading, loaded(T), error(String)
}

// View
struct WeatherView: View {
    @StateObject private var viewModel = WeatherViewModel()
    @State private var city = ""

    var body: some View {
        VStack {
            TextField("City", text: $city)
            Button("Fetch") {
                Task { await viewModel.loadWeather(city: city) }
            }
            switch viewModel.state {
            case .idle: Text("Enter a city")
            case .loading: ProgressView()
            case .loaded(let weather):
                Text("\(weather.temperature)°C - \(weather.condition)")
            case .error(let msg): Text(msg).foregroundColor(.red)
            }
        }
    }
}

12. Core Data Migration with Versioning

  • Task: Perform a lightweight migration when adding a new optional attribute.
  • Prompt: "Describe the steps for a lightweight Core Data migration when adding an optional 'phoneNumber' attribute to an existing 'Contact' entity. Include model versioning, setting the new version as current, and enabling automatic migration in the persistent container."
  • Example Result:
Steps:
1. Select your .xcdatamodeld file, go to Editor > Add Model Version.
2. Name it "MyModel v2" and base it on the current version.
3. In the new version, add the optional attribute 'phoneNumber' (String) to Contact.
4. Set the current model version to "MyModel v2" in the file inspector.
5. In your Core Data stack, enable automatic migration:

let container = NSPersistentContainer(name: "MyModel")
let description = container.persistentStoreDescriptions.first
description?.shouldMigrateStoreAutomatically = true
description?.shouldInferMappingModelAutomatically = true

Lightweight migrations handle: adding optional attributes, renaming with renamingIdentifier, adding relationships, etc.

13. Performance Optimization with Instruments

  • Task: Profile and fix a slow scrolling UITableView.
  • Prompt: "Explain how to use Xcode's Instruments (Time Profiler, Core Animation, Allocations) to diagnose a janky scrolling issue in a UITableView. Provide concrete steps: identify the main thread blocker, check for offscreen rendering, and optimize cell reuse with prefetching. Include code snippets for cell height caching and image loading with URLSession."
  • Example Result:
1. Run Instruments with the 'Time Profiler' template.
2. Scroll the table view and look for heavy call stacks on the main thread.
3. Common culprits: expensive layout calculations, synchronous image downloads.
4. Fix: use estimatedRowHeight and automatic dimension:
   tableView.estimatedRowHeight = 100
   tableView.rowHeight = UITableView.automaticDimension
5. For images, use lazy loading with URLSession and cache:

class ImageLoader {
    static let cache = NSCache<NSString, UIImage>()

    func loadImage(from url: URL) async -> UIImage? {
        if let cached = Self.cache.object(forKey: url.absoluteString as NSString) { return cached }
        guard let (data, _) = try? await URLSession.shared.data(from: url), let image = UIImage(data: data) else { return nil }
        Self.cache.setObject(image, forKey: url.absoluteString as NSString)
        return image
    }
}

6. Use UITableViewDataSourcePrefetching to start loading images before cells appear.

14. SwiftUI Custom Modifier for Reusable Styles

  • Task: Create a reusable ViewModifier for a card-like shadow.
  • Prompt: "Write a custom ViewModifier called 'CardStyle' that adds a white background, rounded corners (12pt), a subtle shadow (color: gray, radius: 4, x: 0, y: 2), and horizontal padding of 16pt. Then show how to apply it to any view using .modifier(CardStyle()) or a custom extension."
  • Example Result:
struct CardStyle: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding()
            .background(Color.white)
            .cornerRadius(12)
            .shadow(color: .gray.opacity(0.3), radius: 4, x: 0, y: 2)
            .padding(.horizontal, 16)
    }
}

extension View {
    func cardStyle() -> some View {
        modifier(CardStyle())
    }
}

// Usage:
Text("Hello, world!")
    .cardStyle()

15. Core Data Batch Operations

  • Task: Efficiently insert 10,000 records without blocking the UI.
  • Prompt: "Write a function that inserts 10,000 Person entities into Core Data using batch insertion with NSBatchInsertRequest. Explain why this is faster than saving each object individually and how to use performAndWait or perform for threading."
  • Example Result:
func batchInsertPersons(count: Int) {
    let context = CoreDataStack.shared.container.newBackgroundContext()
    context.performAndWait {
        let batchInsert = NSBatchInsertRequest(entity: Person.entity(), dictionaryHandler: { dictionary in
            guard index < count else { return true }
            dictionary["name"] = "Person \(index)"
            dictionary["age"] = Int16.random(in: 18...80)
            index += 1
            return false
        })
        var index = 0
        do {
            let result = try context.execute(batchInsert) as? NSBatchInsertResult
            print("Inserted \(result?.result ?? 0) objects")
            // Merge changes into main context
            NotificationCenter.default.post(name: .NSManagedObjectContextDidSave, object: context)
        } catch {
            print("Batch insert error: \(error)")
        }
    }
}

Conclusion

Mastering prompts for Swift and iOS development transforms how you approach everyday tasks—from generating boilerplate views to architecting scalable apps. The 15 prompts covered here span basic CRUD operations, Combine publishers, Core Data migrations, and performance debugging. By adapting these patterns to your own projects, you'll write cleaner code, reduce debugging time, and stay ahead of the curve in 2026's evolving iOS ecosystem. Experiment with these prompts, tweak them to fit your architecture, and remember: the best prompt is the one that precisely describes your intent. Happy coding!

← All posts

Comments