iOS App Development for Beginners: Swift and Xcode Complete Guide
Learn iOS app development from scratch with Swift and SwiftUI. Set up Xcode, build your first app, and submit to the App Store — a complete beginners guide for 2026.
Get more content like this on Telegram!
Daily AI tips, notes & resources — free
iOS App Development for Beginners: Swift and Xcode Complete Guide
I submitted my first iOS app to the App Store at 11pm on a Thursday. It was rejected by Friday morning. The reason: "Missing privacy policy." The app collected no user data whatsoever. But Apple's reviewer marked a checkbox in my metadata that indicated it might, and I hadn't included a privacy policy URL.
I fixed it in ten minutes, resubmitted, and it was approved the following Monday. But that experience perfectly illustrated iOS development: the platform is exacting. Apple's standards are high. The tooling is excellent. And there's a whole layer of policy, metadata, and human review between you and your users that doesn't exist on Android.
This guide will walk you through building a real iOS app from scratch, covering the moments that trip up beginners, and get you ready to submit to the App Store.
What You Need Before Starting
- A Mac — Any Mac running macOS Ventura or later works. An M-series chip Mac (M1, M2, M3) is significantly faster for builds and the simulator.
- Xcode — Free on the Mac App Store. It's large (12+ GB), so start the download first.
- An Apple ID — Free, needed to sign in to Xcode and the simulator.
- Apple Developer Program ($99/year) — Required for App Store submission and device testing. Not needed just to run the app in Simulator.
Step 1: Create Your First Xcode Project
Open Xcode → Create New Project → iOS → App.
Configure your project:
- Product Name:
QuickNotes - Team: Your Apple ID (or None if not enrolled)
- Organization Identifier:
com.yourname - Interface: SwiftUI
- Language: Swift
Click Next, choose a folder, Create.
Xcode opens your project. The file tree on the left shows your files. The center is the code editor. The right panel shows the inspector. At the top, your build scheme and device target. Press Cmd+R to build and run — the iOS Simulator launches and shows your app.
Step 2: Understand the SwiftUI Mental Model
SwiftUI is declarative. You describe the state of your UI, and SwiftUI handles rendering. This is different from UIKit where you imperatively told the framework what to do.
Open ContentView.swift:
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
}
.padding()
}
}
#Preview {
ContentView()
}
Every view in SwiftUI is a struct that conforms to the View protocol. The body property returns the view's content. The #Preview macro at the bottom lets you see live previews in the Xcode canvas without running the simulator.
The view hierarchy in SwiftUI consists of containers (VStack, HStack, ZStack, List, ScrollView) and content views (Text, Image, Button). Everything composes.
Step 3: Learn Swift Fundamentals
You don't need to master Swift before building apps, but these concepts are non-negotiable.
Variables and Constants
var mutableValue = "I can change"
let constantValue = "I cannot change" // Prefer let by default
Swift is strongly typed with type inference. The compiler knows mutableValue is a String without you saying so.
Optionals — The Concept That Confuses Everyone
Optionals are Swift's answer to null safety. A variable of type String always has a value. A variable of type String? might be nil.
var name: String = "Alice" // Always has a value
var nickname: String? = nil // Might be nil
// You must handle the nil case
if let actualNickname = nickname {
print("Nickname is \(actualNickname)")
} else {
print("No nickname set")
}
// Or use nil coalescing
let displayName = nickname ?? name // "Alice"
Force-unwrapping with ! compiles but crashes at runtime if the value is nil. Beginners overuse it. nil crashes are the most common iOS crash type in production.
Structs and Classes
// Struct (value type — copied when assigned)
struct Note {
let id: UUID
var title: String
var content: String
var createdAt: Date
init(title: String, content: String) {
self.id = UUID()
self.title = title
self.content = content
self.createdAt = Date()
}
}
// Class (reference type — shared when assigned)
class NoteManager: ObservableObject {
@Published var notes: [Note] = []
}
For data models, use structs. For objects that manage state across the app, use classes that conform to ObservableObject.
Step 4: Build a Real App — QuickNotes
Let's build a notes app. Replace ContentView.swift content:
import SwiftUI
// Data Model
struct Note: Identifiable, Codable {
let id: UUID
var title: String
var content: String
var createdAt: Date
init(title: String = "", content: String = "") {
self.id = UUID()
self.title = title
self.content = content
self.createdAt = Date()
}
}
// State Manager
@MainActor
class NotesViewModel: ObservableObject {
@Published var notes: [Note] = []
private let storageKey = "saved_notes"
init() {
loadNotes()
}
func addNote() {
let note = Note(title: "New Note", content: "")
notes.insert(note, at: 0)
saveNotes()
}
func deleteNote(at offsets: IndexSet) {
notes.remove(atOffsets: offsets)
saveNotes()
}
func updateNote(_ note: Note) {
guard let index = notes.firstIndex(where: { $0.id == note.id }) else { return }
notes[index] = note
saveNotes()
}
private func saveNotes() {
if let encoded = try? JSONEncoder().encode(notes) {
UserDefaults.standard.set(encoded, forKey: storageKey)
}
}
private func loadNotes() {
guard let data = UserDefaults.standard.data(forKey: storageKey),
let decoded = try? JSONDecoder().decode([Note].self, from: data) else { return }
notes = decoded
}
}
// Notes List View
struct ContentView: View {
@StateObject private var viewModel = NotesViewModel()
var body: some View {
NavigationStack {
Group {
if viewModel.notes.isEmpty {
ContentUnavailableView(
"No Notes",
systemImage: "note.text",
description: Text("Tap + to create your first note")
)
} else {
List {
ForEach($viewModel.notes) { $note in
NavigationLink(destination: NoteDetailView(note: $note, onSave: viewModel.updateNote)) {
NoteRowView(note: note)
}
}
.onDelete(perform: viewModel.deleteNote)
}
}
}
.navigationTitle("Quick Notes")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: viewModel.addNote) {
Image(systemName: "square.and.pencil")
}
}
ToolbarItem(placement: .navigationBarLeading) {
EditButton()
}
}
}
}
}
// Note Row
struct NoteRowView: View {
let note: Note
var body: some View {
VStack(alignment: .leading, spacing: 4) {
Text(note.title.isEmpty ? "Untitled" : note.title)
.font(.headline)
.lineLimit(1)
Text(note.content.isEmpty ? "No content" : note.content)
.font(.subheadline)
.foregroundStyle(.secondary)
.lineLimit(2)
Text(note.createdAt, style: .relative)
.font(.caption)
.foregroundStyle(.tertiary)
}
.padding(.vertical, 4)
}
}
// Note Detail/Edit View
struct NoteDetailView: View {
@Binding var note: Note
let onSave: (Note) -> Void
@Environment(\.dismiss) private var dismiss
var body: some View {
VStack(alignment: .leading, spacing: 0) {
TextField("Title", text: $note.title)
.font(.title2.bold())
.padding(.horizontal)
.padding(.top)
Divider().padding(.vertical, 8)
TextEditor(text: $note.content)
.padding(.horizontal, 12)
}
.navigationBarTitleDisplayMode(.inline)
.onDisappear {
onSave(note)
}
}
}
Run this with Cmd+R. You have a functional notes app with list, create, edit, delete, and persistence in under 100 lines of SwiftUI.
Step 5: Key SwiftUI Property Wrappers
These show up everywhere. Understand them early.
| Wrapper | Purpose | Example |
|---|---|---|
@State | Local view state | Toggle, text field text |
@Binding | Two-way connection to parent state | Edit form bound to parent's data |
@StateObject | Own a reference-type object | ViewModel in root view |
@ObservedObject | Observe a reference-type object | ViewModel passed into child view |
@EnvironmentObject | Access shared object deep in view tree | App-wide settings |
@AppStorage | Read/write from UserDefaults | Theme preference, saved strings |
@Environment | Access environment values | Dismiss, color scheme, locale |
The rule of thumb: use @State for simple local values, @StateObject when you own the ViewModel, @ObservedObject when the ViewModel is passed in.
Step 6: Navigation in SwiftUI
Modern SwiftUI uses NavigationStack (iOS 16+):
struct MainView: View {
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
List(items) { item in
NavigationLink(value: item) {
Text(item.name)
}
}
.navigationTitle("Items")
.navigationDestination(for: Item.self) { item in
ItemDetailView(item: item)
}
}
}
}
This programmatic navigation style lets you push and pop views from code, deep link into your app, and manage complex navigation flows cleanly.
Step 7: Async/Await and Networking
Swift uses async/await for asynchronous work:
struct Post: Codable {
let id: Int
let title: String
let body: String
}
class PostsViewModel: ObservableObject {
@Published var posts: [Post] = []
@Published var isLoading = false
@Published var errorMessage: String?
func fetchPosts() async {
isLoading = true
errorMessage = nil
do {
let url = URL(string: "https://jsonplaceholder.typicode.com/posts")!
let (data, _) = try await URLSession.shared.data(from: url)
let decoded = try JSONDecoder().decode([Post].self, from: data)
await MainActor.run {
posts = decoded
isLoading = false
}
} catch {
await MainActor.run {
errorMessage = error.localizedDescription
isLoading = false
}
}
}
}
// In your view:
.task {
await viewModel.fetchPosts()
}
The .task modifier runs when the view appears and cancels automatically when it disappears. Use it instead of onAppear for async work.
Step 8: Preparing for the App Store
App Icons
Xcode requires a single 1024×1024 PNG icon now. Add it to Assets.xcassets → AppIcon → App Store iOS.
Use a tool like Makeappicon.com to generate all required sizes from your 1024px source.
Privacy Manifest
Apple requires a PrivacyInfo.xcprivacy file for apps that use certain APIs. In Xcode: File → New → File → Privacy Manifest. Declare which APIs your app uses and why.
App Store Connect Setup
- Go to appstoreconnect.apple.com
- Create a new app record
- Fill in: App name, bundle ID (must match your Xcode project), primary language, SKU
Screenshots Required (2026):
- 6.9" iPhone (iPhone 16 Pro Max) — required
- 12.9" iPad Pro — if you support iPad
The Simulator can capture screenshots. In Simulator: File → Take Screenshot. Edit them to include device frames using tools like Rottenwood or Apple's own preview tools.
Archive and Upload
- Connect to your real device or select "Any iOS Device" in the scheme selector
- Product → Archive
- Organizer opens → Distribute App → App Store Connect
- Follow the upload wizard
Common Mistakes Beginners Make
Forcing optionals. Every ! in your code is a potential crash waiting for the wrong input. Use if let, guard let, or ?? instead.
Putting logic in views. Views should display data and handle user interaction. Business logic belongs in a ViewModel or model layer.
Not testing on a real device. The Simulator doesn't replicate memory pressure, real network conditions, or actual touch input latency. Test on hardware before submitting.
Ignoring accessibility. Add .accessibilityLabel() modifiers to images and icon buttons. Apple's reviewers check for basic accessibility compliance.
If you want to strengthen your programming fundamentals before diving deep into Swift, the JavaScript complete course will sharpen your logical thinking — the concepts transfer more than you'd expect. For React developers considering the cross-platform route instead, check out how React Native and Flutter compare and the complete beginner's mobile app guide.
💬 DiscussionPowered by GitHub Discussions
Frequently Asked Questions
AiTechWorlds Team
✓ Verified WriterThe AiTechWorlds team is passionate about AI, technology, and education. We create high-quality, research-backed content to help you learn, grow, and succeed in the modern digital world.
Related Articles
Building Your First Android App: Step-by-Step Guide with Kotlin
Build your first Android app with Kotlin and Android Studio — from project setup to Play Store submission. A practical, no-fluff guide for absolute beginners.
Cross-Platform App Development: Best Frameworks Compared 2026
Compare React Native, Flutter, Kotlin Multiplatform, and .NET MAUI for cross-platform app development in 2026. Real performance data, use cases, and honest tradeoffs.
How to Build a Mobile App in 2026: Complete Beginner's Guide
Learn how to build a mobile app in 2026 from scratch — choosing the right tech stack, designing your first screens, and shipping to the App Store or Play Store.
React Native vs Flutter: Which to Choose for Your App in 2026
React Native vs Flutter in 2026 — an honest comparison of performance, DX, hiring, and real-world use cases to help you pick the right framework for your app.