0
0
iOS Swiftmobile~15 mins

Passing data to destination in iOS Swift - Deep Dive

Choose your learning style9 modes available
Overview - Passing data to destination
What is it?
Passing data to destination means sending information from one screen to another in an app. When you tap a button or select an item, the app often needs to show details on a new screen using data from the first screen. This process helps keep the app interactive and meaningful by sharing the right information between screens.
Why it matters
Without passing data between screens, apps would be static and unable to show personalized or updated information. Imagine opening a contact list but never seeing the details of a selected person. Passing data lets apps feel alive and responsive, improving user experience and making apps useful.
Where it fits
Before learning this, you should understand how to create multiple screens (views) in SwiftUI or UIKit. After this, you can learn about more advanced navigation patterns, data storage, and state management to build complex apps.
Mental Model
Core Idea
Passing data to destination is like handing a note from one friend to another so the second friend knows what to do next.
Think of it like...
Imagine you are at a restaurant and you tell the waiter your order. The waiter carries your order (data) to the kitchen (destination screen) so the chef can prepare your meal. Without passing the order, the kitchen wouldn't know what to cook.
Screen A (Source) ──> [Pass Data] ──> Screen B (Destination)

┌─────────────┐       ┌──────────────┐
│  Screen A   │──────▶│  Screen B    │
│ (Source)    │       │ (Destination)│
│ Data to send│       │ Receives data│
└─────────────┘       └──────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding screens and navigation
🤔
Concept: Learn what screens (views) are and how users move between them.
In iOS apps, each screen is called a View or ViewController. Navigation means moving from one screen to another, like tapping a button to open a new page. This is the first step before sending any data between screens.
Result
You can create multiple screens and move between them in your app.
Knowing how navigation works is essential because data passing only happens when moving between screens.
2
FoundationWhat is data passing in apps?
🤔
Concept: Data passing means sending information from one screen to another during navigation.
When you tap a button on Screen A, you often want Screen B to know something, like which item was selected. This information is called data. Passing data means giving Screen B this information so it can show the right content.
Result
You understand that data passing connects screens with meaningful information.
Realizing data passing is about communication between screens helps you design better app flows.
3
IntermediatePassing data using SwiftUI NavigationLink
🤔Before reading on: Do you think you can pass data directly inside NavigationLink or do you need extra steps? Commit to your answer.
Concept: Use NavigationLink to send data by creating the destination view with data as a parameter.
In SwiftUI, NavigationLink lets you move to a new screen. You pass data by creating the destination view with the data you want to send. Example: struct ItemListView: View { let items = ["Apple", "Banana", "Cherry"] var body: some View { NavigationView { List(items, id: \.self) { item in NavigationLink(destination: DetailView(item: item)) { Text(item) } } } } } struct DetailView: View { let item: String var body: some View { Text("You selected: \(item)") } }
Result
When you tap an item, the app opens DetailView showing the selected item.
Passing data as parameters during navigation is simple and keeps data flow clear.
4
IntermediatePassing data with UIKit segues
🤔Before reading on: Do you think data is passed automatically during segues or do you have to write code? Commit to your answer.
Concept: In UIKit, you pass data by overriding prepare(for:sender:) before the segue happens.
In UIKit, when you use Storyboard segues, you override prepare(for:sender:) in the source view controller. This method lets you set properties on the destination view controller before the screen appears. Example: override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "showDetail" { if let destVC = segue.destination as? DetailViewController, let selectedItem = sender as? String { destVC.item = selectedItem } } }
Result
The destination screen receives the data and can display it.
Understanding prepare(for:sender:) is key to passing data in UIKit navigation.
5
IntermediatePassing data back to previous screen
🤔Before reading on: Can you pass data back by simply setting a property on the previous screen? Commit to your answer.
Concept: Passing data back requires special techniques like delegation or closures because the previous screen is already loaded.
To send data back, you can't just set a property because the previous screen is not recreated. Instead, use delegation or closures: - Delegation: The destination screen has a delegate protocol. When done, it calls a method on the delegate (the previous screen). - Closures: Pass a closure (function) to the destination. When ready, the destination calls the closure with data. Example with closure: struct DetailView: View { var onSave: (String) -> Void // call onSave(data) when done }
Result
Data flows back to the previous screen, enabling updates or actions.
Knowing how to pass data back keeps your app interactive and responsive.
6
AdvancedHandling complex data with models
🤔Before reading on: Do you think passing simple strings is enough for all apps? Commit to your answer.
Concept: Use data models (structs or classes) to pass complex, organized data between screens.
Instead of passing simple values, create data models to hold multiple related pieces of information. Example: struct User { let name: String let age: Int } Pass User instances between screens to keep data organized and scalable.
Result
Your app can handle rich data and grow without messy code.
Using models makes data passing clean and maintainable in real apps.
7
ExpertAvoiding data inconsistency with state management
🤔Before reading on: Do you think passing data once is enough for all app states? Commit to your answer.
Concept: For apps with many screens and dynamic data, use state management tools to keep data consistent across destinations.
Passing data directly works for simple flows, but complex apps need shared state. SwiftUI offers @StateObject, @ObservedObject, and environment objects to share data. Example: class UserData: ObservableObject { @Published var name = "" } Inject UserData into views so all screens see the same data and update automatically.
Result
Your app stays in sync, avoiding bugs from outdated or mismatched data.
Understanding state management prevents subtle bugs and improves user experience in complex apps.
Under the Hood
When you navigate to a new screen, the app creates a new view or view controller instance. Passing data means setting properties or parameters on this new instance before it appears. In SwiftUI, this is done by passing data as parameters to the destination view's initializer. In UIKit, prepare(for:sender:) lets you set properties on the destination view controller. For passing data back, delegation or closures create callbacks that notify the previous screen. State management uses observable objects that notify all views when data changes, keeping UI in sync.
Why designed this way?
This design separates screens as independent units that receive data explicitly, making apps modular and easier to maintain. SwiftUI's declarative style encourages passing data as immutable parameters, reducing bugs. UIKit's prepare(for:sender:) fits with its imperative style and storyboard system. Delegation and closures provide flexible ways to communicate backward without tight coupling. State management evolved to handle complex apps where simple passing is not enough, ensuring data consistency and reactive UI updates.
┌─────────────┐       ┌──────────────┐       ┌───────────────┐
│ Source View │──────▶│ Destination  │       │ Previous View │
│ (creates)   │       │ View/Controller│       │ (waiting)    │
│             │       │ (receives data)│       │               │
└─────────────┘       └──────────────┘       └───────────────┘
       │                     ▲                      ▲
       │                     │                      │
       │          prepare(for:sender:) or          │
       │          init with parameters             │
       │                                            │
       └─────────────── delegation/closure ────────┘
Myth Busters - 4 Common Misconceptions
Quick: Do you think you can pass data back by just setting a property on the previous screen instance? Commit to yes or no.
Common Belief:You can pass data back by directly setting a property on the previous screen before dismissing the current one.
Tap to reveal reality
Reality:The previous screen instance is usually not accessible or updated this way; you need delegation or closures to send data back properly.
Why it matters:Trying to set properties directly causes bugs where the previous screen does not update, confusing users and wasting debugging time.
Quick: Is it okay to pass large data objects directly between screens? Commit to yes or no.
Common Belief:Passing large data objects directly between screens is fine and has no downsides.
Tap to reveal reality
Reality:Passing large data can cause performance issues and memory overhead; better to pass identifiers and fetch data when needed.
Why it matters:Ignoring this leads to slow apps and crashes, harming user experience.
Quick: Does passing data as parameters in SwiftUI create copies or references? Commit to your answer.
Common Belief:Passing data as parameters always creates copies, so changes in destination don't affect source.
Tap to reveal reality
Reality:It depends on data type; structs are copied, classes are referenced. Understanding this affects how data changes propagate.
Why it matters:Misunderstanding this causes unexpected bugs where data changes don't appear or cause side effects.
Quick: Can you rely on prepare(for:sender:) to pass data after the destination view appears? Commit to yes or no.
Common Belief:prepare(for:sender:) can be called anytime, so you can pass data even after the destination screen is visible.
Tap to reveal reality
Reality:prepare(for:sender:) is called before the segue transition; passing data after the screen appears requires other methods.
Why it matters:Relying on this causes timing bugs and crashes when data is missing or inconsistent.
Expert Zone
1
Passing data via environment objects in SwiftUI allows loosely coupled views to share state without explicit parameters, but overusing it can hide data flow and cause debugging challenges.
2
In UIKit, using unwind segues for passing data back is elegant but requires careful storyboard setup and understanding of responder chain, which many developers overlook.
3
When passing data models, immutability helps prevent side effects, but sometimes mutable references are needed for performance; balancing this is a subtle art.
When NOT to use
Passing data directly is not suitable for very large or frequently changing data; instead, use shared databases, network calls, or global state managers like Redux or Combine. Also, avoid passing data back by setting properties directly; use delegation or reactive bindings instead.
Production Patterns
Real-world apps often use MVVM or VIPER patterns where data passing is done via view models or routers, not directly between views. Dependency injection frameworks help manage data flow cleanly. For complex flows, coordinators handle navigation and data passing centrally to avoid tight coupling.
Connections
Observer Pattern
Builds-on
Understanding data passing back with delegation or closures is a practical use of the Observer pattern, where one object listens for changes or events from another.
Functional Programming
Same pattern
Passing data as immutable parameters in SwiftUI reflects functional programming principles, promoting predictable and side-effect-free code.
Human Communication
Analogy
Just like people pass messages clearly to avoid misunderstandings, apps pass data explicitly between screens to keep user experience smooth and error-free.
Common Pitfalls
#1Trying to pass data back by setting a property on the previous screen directly.
Wrong approach:destinationVC.previousVC.data = newData // wrong because previousVC may be nil or not updated
Correct approach:Use delegation: protocol DataDelegate { func didUpdateData(_ data: DataType) } // destinationVC.delegate = previousVC // delegate?.didUpdateData(newData)
Root cause:Misunderstanding that previous screen instances are not directly accessible or updated this way.
#2Passing large data objects directly between screens causing slow performance.
Wrong approach:NavigationLink(destination: DetailView(data: hugeDataObject))
Correct approach:Pass only an identifier: NavigationLink(destination: DetailView(itemID: hugeDataObject.id)) // fetch data inside DetailView
Root cause:Not considering memory and performance implications of passing big data.
#3Assuming prepare(for:sender:) can be used to pass data after the destination appears.
Wrong approach:override func prepare(for segue: UIStoryboardSegue, sender: Any?) { DispatchQueue.main.asyncAfter(deadline: .now() + 1) { segue.destination.data = newData } }
Correct approach:Pass data synchronously before segue transition in prepare(for:sender:). For updates after, use delegation or notifications.
Root cause:Misunderstanding the timing of segue lifecycle.
Key Takeaways
Passing data to destination screens is essential for interactive and meaningful apps.
In SwiftUI, pass data by initializing destination views with parameters; in UIKit, use prepare(for:sender:) to set properties.
Passing data back requires delegation or closures because the previous screen is already loaded.
Use data models to organize complex data and state management tools for consistent updates in complex apps.
Understanding the timing and mechanisms of data passing prevents common bugs and improves app design.