# Understanding View Lifecycle in UIKit - iOS

## What is a View Lifecycle?

In iOS development with UIKit, the **view lifecycle** refers to the complete sequence of method calls that occur as a view controller goes through different stages of its existence - from initialization to destruction. Understanding this lifecycle is crucial for iOS developers because it determines when and how you should perform specific tasks like setting up UI elements, loading data, responding to user interactions, and cleaning up resources.

Think of the view lifecycle as the "birth-to-death" journey of a view controller. Just like how a person goes through different life stages (birth, childhood, adulthood, etc.), a view controller goes through predictable stages where the system calls specific methods at each phase.

The view lifecycle is important because:
- It ensures your app's UI appears and behaves correctly
- It helps manage memory efficiently  
- It provides the right timing for data loading and UI updates
- It maintains proper app state during navigation and background transitions
- It prevents premature view loading that can hurt performance

## The Complete View Lifecycle Sequence

The UIViewController lifecycle involves multiple phases that UIKit manages automatically.

### Phase 1: View Creation

#### 1. loadView()

```swift
override func loadView() {
    // Don't call super when overriding!
    let customView = UIView()
    customView.backgroundColor = .systemBackground
    view = customView
}
```

**When it's called:**
- Only once during the entire lifecycle when the view controller's view property is nil and needs to be created
- Automatically when using Storyboards/XIBs
- When you first access the `view` property

**What to do here:**
- **Only override when creating views programmatically** (no Storyboard/XIB)
- Create your custom view hierarchy
- Assign the root view to the `view` property
- **Never call `super.loadView()`** when overriding
- **Never call this method directly**

**Important Notes:**
- If you are working with storyboards or nib files you do not have to do anything with this method and you can ignore it
- This is where subclasses should create their custom view hierarchy if they aren't using a nib

#### 2. viewDidLoad()

```swift
override func viewDidLoad() {
    super.viewDidLoad() // Always call this first!
    
    // One-time setup code here
    setupUI()
    configureNavigationBar()
    setupNotificationObservers()
}
```

**When it's called:**
- Only once when the view controller's view hierarchy is loaded into memory
- After loadView() completes its execution
- Before the view appears on screen

**What to do here:**
- One-time setup tasks
- Configure UI elements (outlets are connected)
- Set up delegates and data sources
- Initialize objects used by the view controller
- Add/remove subviews (when using Storyboards/XIBs)

**Important Notes:**
- The view bounds are not yet established, so avoid layout-dependent code
- All IBOutlets are connected and available

### Phase 2: Appearance Cycle (Can happen multiple times)

#### 3. viewWillAppear(_:)

```swift
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated) // Required!
    
    // Refresh data that might have changed
    refreshUserData()
    updateNavigationBar()
}
```

**When it's called:**
- Every time the view is about to be added to the view hierarchy
- Before any appearance animations start
- View has bounds but orientation is not set yet

**What to do here:**
- Refresh data that might have changed while away
- Update UI based on current state
- Prepare for the view to become visible
- Start services that should be active when visible

#### 4. viewIsAppearing(_:) - **New in iOS 17** (Back-deployable to iOS 13)

```swift
override func viewIsAppearing(_ animated: Bool) {
    super.viewIsAppearing(animated)
    
    // Perfect place for layout-dependent setup
    configureViewsBasedOnTraits()
    setupLayoutConstraints()
}
```

**When it's called:**
- Called after viewWillAppear but before viewDidAppear
- After the view has been added to the hierarchy but is not yet onscreen
- The view controller's view has been laid out so you can rely on its size and traits

**What to do here:**
- Update UI based on final layout and traits
- Configure constraints that depend on view size
- Perfect timing for size-dependent configurations

#### 5. viewWillLayoutSubviews()

```swift
override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
    // Called before Auto Layout runs
}
```

**When it's called:**
- Every time the frame changes (rotation, keyboard appearance, etc.)
- Before Auto Layout processes constraints

#### 6. viewDidLayoutSubviews()

```swift
override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    // Called after Auto Layout runs
    
    // Now view bounds are final
    configureLayoutDependentElements()
}
```

**When it's called:**
- After Auto Layout runs and view bounds are final
- Multiple times during the view's lifetime

**What to do here:**
- Layout-dependent calculations
- Update frames for views not using Auto Layout
- Configure elements based on final bounds

#### 7. viewDidAppear(_:)

```swift
override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    
    // Start active processes
    startLocationServices()
    beginVideoPlayback()
    startTimers()
}
```

**When it's called:**
- Every time after the view has appeared on screen and is fully visible
- After appearance animations complete

**What to do here:**
- Start animations, and intensive processing
- Begin location services or other sensors
- Start video/audio playback
- Register for notifications that should only be active when visible

### Phase 3: Disappearance Cycle

#### 8. viewWillDisappear(_:)

```swift
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    
    // Prepare to leave
    saveUserInput()
    pauseVideoPlayback()
}
```

**When it's called:**
- Every time before the view is about to disappear
- Before disappearance animations start

**What to do here:**
- Save user input or current state
- Validate and persist data
- Pause animations
- Prepare for view to become inactive

#### 9. viewDidDisappear(_:)

```swift
override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    
    // Clean up active processes
    stopLocationServices()
    invalidateTimers()
}
```

**When it's called:**
- Every time after the view has disappeared from screen
- After disappearance animations complete

**What to do here:**
- Stop animations, and intensive processing
- Pause video/audio playback
- Clean up temporary resources
- Unregister from notifications

### Phase 4: Memory Management

#### 10. deinit

```swift
deinit {
    // Final cleanup before object is deallocated
    NotificationCenter.default.removeObserver(self)
    // Release any remaining resources
}
```

**When it's called:**
- When the view controller is being deallocated from memory
- After all strong references are removed

**What to do here:**
- Remove observers
- Release resources not handled by ARC
- Final cleanup tasks

## Complete Lifecycle Sequence Summary

**Creation & First Appearance:**
1. `loadView()` - View creation  
2. `viewDidLoad()` - View loaded (once only)
3. `viewWillAppear(_:)` - About to appear
4. `viewIsAppearing(_:)` - Added to hierarchy, layout complete
5. `viewWillLayoutSubviews()` - Before layout
6. `viewDidLayoutSubviews()` - After layout  
7. `viewDidAppear(_:)` - Fully visible

**Subsequent Appearances:**
- Steps 3-7 repeat each time the view appears

**Disappearance:**
8. `viewWillDisappear(_:)` - About to disappear
9. `viewDidDisappear(_:)` - No longer visible

**Destruction:**
10. `deinit` - Object deallocation



## Common Pitfalls and Best Practices

### 1. Always Call Super

```swift
override func viewDidLoad() {
    super.viewDidLoad() // Always call this first!
    // Your code here
}
```

### 2. Never Access self.view in init()

```swift
// WRONG - This will trigger premature view loading!
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    view.backgroundColor = .red // This triggers loadView() and viewDidLoad() too early!
}

// CORRECT - Initialize non-view properties only
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    // Only initialize properties that don't require the view
}
```

### 3. Use the Right Method for Layout-Dependent Code

```swift
// WRONG - Bounds not yet established
override func viewDidLoad() {
    super.viewDidLoad()
    let center = view.center // This might be incorrect
}

// CORRECT - Use viewIsAppearing or viewDidLayoutSubviews
override func viewIsAppearing(_ animated: Bool) {
    super.viewIsAppearing(animated)
    let center = view.center // This is correct
}
```

### 4. Don't Override loadView When Using Storyboards

```swift
// WRONG - Don't override when using Storyboards/XIBs
override func loadView() {
    super.loadView() // This breaks the loading process
    // Additional setup
}

// CORRECT - Use viewDidLoad for Storyboard-based setup
override func viewDidLoad() {
    super.viewDidLoad()
    // Setup code here
}
```

### 5. Proper Memory Management

```swift
override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    
    // Clean up to prevent memory leaks and save battery
    refreshTimer?.invalidate()
    refreshTimer = nil
    
    // Stop expensive operations
    locationManager.stopUpdatingLocation()
    videoPlayer.pause()
}
```

## Key Takeaways

1. **Complete Sequence:** `loadView()` → `viewDidLoad()` → `viewWillAppear(_:)` → `viewIsAppearing(_:)` → layout methods → `viewDidAppear(_:)` → disappearance cycle → `deinit`

2. **One-Time Methods:** `loadView()`, `viewDidLoad()`, and `deinit` are called only once

3. **Repeating Methods:** Appearance/disappearance methods are called every time the view appears/disappears

4. **Critical Rule:** Never access `self.view` in `init()` - this causes premature view loading

5. **New in iOS 17:** `viewIsAppearing(_:)` provides the perfect timing for layout-dependent setup

6. **Memory Safety:** Always clean up resources in `viewDidDisappear(_:)` and `deinit`

7. **Call Super:** Always call the superclass implementation in lifecycle methods

Understanding and properly implementing the view lifecycle ensures your iOS apps are performant, responsive, and provide excellent user experiences.
