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.
Comments