Introduction
Over the past few years, large language models (LLMs) have become indispensable tools for iOS developers. Whether you use ChatGPT, GitHub Copilot, or a specialised code assistant, the quality of the output depends heavily on how you frame your request. A well‑crafted prompt can turn a vague idea into production‑ready code, save hours of debugging, and help you explore frameworks you haven't touched before.
This article collects 10 practical prompts covering SwiftUI, UIKit, Core Data, and Combine. Each prompt includes the exact text to use, a concrete task, and a realistic example result. The examples are based on real‑world scenarios I've encountered building apps for the App Store. I've also added notes on why certain phrasing works better, referencing official Apple documentation and common pitfalls.
To get the most out of these prompts, treat them as a starting point. Modify the context (e.g., your app's data model, deployment target) and always review the generated code – LLMs can still introduce subtle bugs or miss iOS‑specific conventions.
1. SwiftUI: Creating a Reusable Custom View Modifier
Task: You need a view modifier that applies a consistent shadow and corner radius to any view, with configurable colour and intensity.
Prompt:
Generate a SwiftUI ViewModifier called 'CardStyle' that adds a rounded rectangle background, a shadow with configurable color and radius, and optional padding. The modifier should work on any view and expose parameters: backgroundColor (default: white), shadowColor (default: black.opacity(0.1)), shadowRadius (default: 8), and padding (default: 16). Provide an example of using it on a Text and an Image.
Example Result:
import SwiftUI
struct CardStyle: ViewModifier {
var backgroundColor: Color = .white
var shadowColor: Color = .black.opacity(0.1)
var shadowRadius: CGFloat = 8
var padding: CGFloat = 16
func body(content: Content) -> some View {
content
.padding(padding)
.background(
RoundedRectangle(cornerRadius: 12)
.fill(backgroundColor)
.shadow(color: shadowColor, radius: shadowRadius, x: 0, y: 4)
)
}
}
extension View {
func cardStyle(
backgroundColor: Color = .white,
shadowColor: Color = .black.opacity(0.1),
shadowRadius: CGFloat = 8,
padding: CGFloat = 16
) -> some View {
modifier(CardStyle(backgroundColor: backgroundColor,
shadowColor: shadowColor,
shadowRadius: shadowRadius,
padding: padding))
}
}
// Usage
Text("Hello, World!")
.cardStyle()
Image(systemName: "star.fill")
.resizable()
.frame(width: 60, height: 60)
.cardStyle(backgroundColor: .yellow.opacity(0.2), shadowColor: .orange.opacity(0.3))
Why it works: The prompt clearly names the modifier (CardStyle), lists all parameters with defaults, and asks for an extension method (cardStyle) – a pattern Apple itself uses (e.g., .padding()). The example usage with both Text and Image forces the assistant to keep the modifier generic.
2. SwiftUI: Efficient LazyVGrid with Dynamic Data
Task: Display a grid of items fetched from an API, with pull‑to‑refresh, loading state, and error handling.
Prompt:
Write a SwiftUI view that uses LazyVGrid to display a list of 'Product' objects. The grid should have two columns, adaptive item width of 160. Products come from a view model that publishes an enum state: loading, loaded([Product]), error(Error). Show a ProgressView while loading, a properly styled error message with a retry button, and the grid when loaded. Include a .refreshable modifier for pull‑to‑refresh. Assume Product has id, name, price, and imageURL.
Example Result:
import SwiftUI
struct ProductGridView: View {
@StateObject private var viewModel = ProductViewModel()
let columns = [GridItem(.adaptive(minimum: 160), spacing: 16)]
var body: some View {
NavigationView {
Group {
switch viewModel.state {
case .loading:
ProgressView("Loading products…")
case .loaded(let products):
ScrollView {
LazyVGrid(columns: columns, spacing: 16) {
ForEach(products) { product in
ProductCell(product: product)
}
}
.padding()
}
.refreshable {
await viewModel.refresh()
}
case .error(let error):
VStack(spacing: 12) {
Image(systemName: "wifi.slash")
.font(.largeTitle)
.foregroundColor(.secondary)
Text("Something went wrong")
.font(.headline)
Text(error.localizedDescription)
.font(.caption)
.foregroundColor(.gray)
.multilineTextAlignment(.center)
Button("Retry") {
Task { await viewModel.load() }
}
.buttonStyle(.borderedProminent)
}
.padding()
}
}
.navigationTitle("Products")
}
}
}
Note: The prompt explicitly requests an enum state and the .refreshable modifier (iOS 15+). This prevents the AI from falling back to older patterns like @Published strings. Always specify your minimum deployment target in the prompt if you rely on modern APIs.
3. UIKit: Programmatic Auto Layout with NSLayoutAnchor
Task: Create a login screen in UIKit using code (no Storyboard) with a stack view containing email and password fields, a login button, and a forgot‑password label.
Prompt:
Generate a UIViewController subclass called LoginViewController that builds its UI entirely in code. Use UIStackView (vertical axis, spacing 16, centered) containing: a UITextField for email (placeholder "Email", keyboard type .emailAddress), a UITextField for password (placeholder "Password", isSecureTextEntry true), a UIButton (type system, title "Log In"), and a UILabel (text "Forgot password?", textAlignment .center, textColor system blue). Add a method called handleLogin that prints the email and password. All constraints must use NSLayoutAnchor with translatesAutoresizingMaskIntoConstraints = false. Make the stack view centered vertically with 32pt leading/trailing margins.
Example Result:
```swift
import UIKit
class LoginViewController: UIViewController {
private let emailField: UITextField = {
let tf = UITextField()
tf.placeholder = "Email"
tf.keyboardType = .emailAddress
tf.borderStyle = .roundedRect
tf.translatesAutoresizingMaskIntoConstraints = false
return tf
}()
private let passwordField: UITextField = {
let tf = UITextField()
tf.placeholder = "Password"
tf.isSecureTextEntry = true
tf.borderStyle = .roundedRect
tf.translatesAutoresizingMaskIntoConstraints = false
return tf
}()
private let loginButton: UIButton = {
let btn = UIButton(type: .system)
btn.setTitle("Log In", for: .normal)
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
private let forgotLabel: UILabel = {
let lbl = UILabel()
lbl.text = "Forgot password?"
lbl.textAlignment = .center
lbl.textColor = .systemBlue
lbl.translatesAutoresizingMaskIntoConstraints = false
return lbl
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
setupStackView()
loginButton.addTarget(self, action: #selector(handleLogin), for: .touchUpInside)
}
private func setupStackView() {
let stack = UIStackView(arrangedSubviews: [emailField, passwordField, loginButton, forgotLabel])
stack.axis = .vertical
stack.spacing = 16
stack.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stack)
NSLayoutConstraint.activate([
stack.centerYAnchor.constraint(equalTo: view.centerYAnchor),
stack.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32),
stack.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32)
])
}
@objc private func handleLogin() {
print("Email: \\(emailField.text ??
Comments