Why Prompt Engineering Matters for iOS Development
As an iOS developer, you likely use AI daily to speed up coding, debug tricky issues, or generate boilerplate. But generic prompts often produce mediocre results. The key is specificity: providing context, constraints, and examples. This collection brings together 10 battle-tested prompts I use in my own Swift workflow — covering SwiftUI, UIKit, Core Data, and Combine. Each prompt is accompanied by a real usage example and code snippet.
1. SwiftUI Form with Validation
Prompt: "Generate a SwiftUI form for user registration with fields: email, password, confirm password. Include inline validation that shows error messages under each field when the user taps 'Submit'. Use @State for form data and a computed property to check if the form is valid."
Example usage: I needed a quick sign-up screen for a prototype. The prompt produced a clean RegistrationView with .alert for submission success and red error text for validation failures.
struct RegistrationView: View {
@State private var email = ""
@State private var password = ""
@State private var confirmPassword = ""
@State private var showAlert = false
var isValid: Bool {
!email.isEmpty && email.contains("@") &&
password.count >= 8 &&
password == confirmPassword
}
var body: some View {
Form {
TextField("Email", text: $email)
if !email.isEmpty && !email.contains("@") {
Text("Enter a valid email").foregroundColor(.red)
}
SecureField("Password", text: $password)
SecureField("Confirm Password", text: $confirmPassword)
if password != confirmPassword {
Text("Passwords do not match").foregroundColor(.red)
}
Button("Submit") { showAlert = true }
.disabled(!isValid)
}
.alert("Success", isPresented: $showAlert) {
Button("OK") { }
}
}
}
2. UIKit CollectionView with DiffableDataSource
Prompt: "Write a UIKit UICollectionViewController subclass that uses UICollectionViewDiffableDataSource and NSDiffableDataSourceSnapshot. The cell should display a label from a string array. Use UICollectionLayoutListConfiguration for a modern list layout."
Example usage: I migrated an old UITableView-based settings screen to a modern collection view. The prompt gave me a complete controller with swipe-to-delete support.
class SettingsViewController: UICollectionViewController {
private var dataSource: UICollectionViewDiffableDataSource<Section, String>!
enum Section { case main }
override func viewDidLoad() {
super.viewDidLoad()
let config = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
collectionView.collectionViewLayout = UICollectionViewCompositionalLayout.list(using: config)
cellRegistration = UICollectionView.CellRegistration { cell, indexPath, item in
var content = cell.defaultContentConfiguration()
content.text = item
cell.contentConfiguration = content
}
dataSource = UICollectionViewDiffableDataSource<Section, String>(
collectionView: collectionView,
cellProvider: { collectionView, indexPath, item in
return collectionView.dequeueConfiguredReusableCell(using: self.cellRegistration, for: indexPath, item: item)
}
)
var snapshot = NSDiffableDataSourceSnapshot<Section, String>()
snapshot.appendSections([.main])
snapshot.appendItems(["Wi-Fi", "Bluetooth", "Airplane Mode"])
dataSource.apply(snapshot, animatingDifferences: false)
}
}
3. Core Data Fetch Request with Predicate
Prompt: "Create a NSFetchRequest for a Core Data entity called Task with attributes title (String) and isCompleted (Bool). Fetch only incomplete tasks, sorted by title ascending. Return the request as a property wrapper or helper function."
Example usage: In a to-do app, I needed to display only active tasks. The prompt generated a reusable fetch helper.
extension Task {
static func incompleteTasksRequest() -> NSFetchRequest<Task> {
let request = Task.fetchRequest()
request.predicate = NSPredicate(format: "isCompleted == NO")
request.sortDescriptors = [NSSortDescriptor(key: "title", ascending: true)]
return request
}
}
// Usage in a ViewModel
let context = PersistenceController.shared.container.viewContext
let request = Task.incompleteTasksRequest()
let tasks = try context.fetch(request)
4. Combine Publisher for TextField Debounce
Prompt: "Write a Combine pipeline that observes a UITextField text changes, debounces by 300ms, removes duplicates, and updates a label with the search results count. Use NotificationCenter for text changes."
Example usage: I built a live search bar that queries a local database after the user stops typing.
import Combine
class SearchViewModel {
@Published var searchText = ""
@Published var resultCount = 0
private var cancellables = Set<AnyCancellable>()
func bind(textField: UITextField) {
NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: textField)
.compactMap { ($0.object as? UITextField)?.text }
.debounce(for: .milliseconds(300), scheduler: RunLoop.main)
.removeDuplicates()
.sink { [weak self] text in
self?.searchText = text
self?.resultCount = text.isEmpty ? 0 : 42 // example count
}
.store(in: &cancellables)
}
}
5. SwiftUI Custom TabView with Badge
Prompt: "Create a custom SwiftUI TabView with four tabs: Home, Search, Notifications, Profile. The Notifications tab should display a red badge with a count from a @Published property. Use .badge modifier for iOS 15+."
Example usage: For an e-commerce app, I needed a tab bar showing unread notification count.
struct MainTabView: View {
@StateObject private var notificationVM = NotificationViewModel()
var body: some View {
TabView {
HomeView().tabItem { Label("Home", systemImage: "house") }
SearchView().tabItem { Label("Search", systemImage: "magnifyingglass") }
NotificationsView()
.tabItem { Label("Notifications", systemImage: "bell") }
.badge(notificationVM.unreadCount)
ProfileView().tabItem { Label("Profile", systemImage: "person") }
}
}
}
6. UIKit Auto Layout with Anchors
Prompt: "Write a UIView subclass that adds a child view with constraints: centered horizontally, 50 points from top, width 200, height 100. Use Auto Layout anchors (no storyboard)."
Example usage: I programmatically added a banner to the top of a view.
class BannerView: UIView {
private let banner: UIView = {
let v = UIView()
v.backgroundColor = .systemBlue
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(banner)
NSLayoutConstraint.activate([
banner.centerXAnchor.constraint(equalTo: centerXAnchor),
banner.topAnchor.constraint(equalTo: topAnchor, constant: 50),
banner.widthAnchor.constraint(equalToConstant: 200),
banner.heightAnchor.constraint(equalToConstant: 100)
])
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
}
7. SwiftUI LazyVGrid with Dynamic Data
Prompt: "Generate a SwiftUI view that displays a grid of photos using LazyVGrid. The grid should have 3 flexible columns. Each cell shows a system image and a label. Data comes from an array of (name: String, icon: String)."
Example usage: For a dashboard, I displayed feature icons in a responsive grid.
struct GridView: View {
let items = [
(name: "Camera", icon: "camera"),
(name: "Music", icon: "music.note"),
(name: "Map", icon: "map"),
(name: "Settings", icon: "gearshape")
]
let columns = [GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 20) {
ForEach(items, id: \.name) { item in
VStack {
Image(systemName: item.icon)
.font(.largeTitle)
Text(item.name)
.font(.caption)
}
.padding()
.background(Color.gray.opacity(0.2))
.cornerRadius(8)
}
}
.padding()
}
}
}
8. Core Data Batch Delete
Prompt: "Write a function that deletes all Core Data entities of type LogEntry created before a given date, using NSBatchDeleteRequest for performance. Return the number of deleted objects."
Example usage: I implemented a cleanup routine for old analytics logs.
func deleteOldLogs(before date: Date, in context: NSManagedObjectContext) -> Int {
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = LogEntry.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "createdAt < %@", date as NSDate)
let batchDelete = NSBatchDeleteRequest(fetchRequest: fetchRequest)
batchDelete.resultType = .resultTypeCount
do {
let result = try context.execute(batchDelete) as? NSBatchDeleteResult
context.reset() // refresh objects in memory
return result?.result as? Int ?? 0
} catch {
print("Delete failed: \(error)")
return 0
}
}
9. Combine with Core Data (View Model)
Prompt: "Create a SwiftUI ViewModel that uses Combine to observe a Core Data fetch request via @FetchRequest or NSFetchedResultsController. The ViewModel should expose a @Published array of items and a method to add a new item."
Example usage: For a note-taking app, I needed real-time updates from Core Data.
class NotesViewModel: ObservableObject {
@Published var notes: [Note] = []
private let context = PersistenceController.shared.container.viewContext
private var cancellable: AnyCancellable?
init() {
let fetchRequest = Note.fetchRequest()
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)]
// Use NSFetchedResultsController publisher (simplified)
cancellable = NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave, object: context)
.sink { [weak self] _ in
self?.fetchNotes()
}
fetchNotes()
}
func fetchNotes() {
let request = Note.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)]
notes = (try? context.fetch(request)) ?? []
}
func addNote(title: String) {
let note = Note(context: context)
note.title = title
note.date = Date()
try? context.save()
}
}
10. SwiftUI Animation with GeometryReader
Prompt: "Create a SwiftUI view that animates a circle moving from left to right when a button is tapped. Use GeometryReader to get the container width. Animate with .spring()."
Example usage: I built a onboarding carousel with animated indicators.
struct AnimatedCircleView: View {
@State private var offsetX: CGFloat = 0
var body: some View {
GeometryReader { geometry in
VStack {
Circle()
.fill(.blue)
.frame(width: 50, height: 50)
.offset(x: offsetX)
.animation(.spring(), value: offsetX)
Button("Move Right") {
withAnimation {
offsetX = geometry.size.width - 50
}
}
.padding(.top, 50)
Button("Reset") {
withAnimation {
offsetX = 0
}
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
}
Conclusion
These 10 prompts cover daily tasks in SwiftUI, UIKit, Core Data, and Combine. The key is to be specific: include entity names, attribute types, and framework versions. Bookmark this list, adapt the prompts to your projects, and you'll save hours each week. For more iOS development tips, explore the Asibiont blog (asibiont.com/blog) — we cover everything from performance optimization to architecture patterns.
Comments