---
title: "Best practices for the iOS UIViewController and Firebase"
description: "Best practices for the iOS UIViewController and Firebase"
published: "2015-10-15"
last_updated: "2015-10-15"
authors:
  - "David East (Developer Advocate)"
categories:
  - "iOS"
  - "Best Practices"
  - "Swift"
canonical_url: "https://firebase.blog/posts/2015/10/best-practices-for-ios-uiviewcontroller_6"
---

# Best practices for the iOS UIViewController and Firebase

*Published October 15, 2015 by David East (Developer Advocate)*

---
The UIViewController comes with a lifecycle that informs us when important events occur. Events such as [`viewDidLoad`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/#//apple_ref/occ/instm/UIViewController/viewDidLoad),[`viewWillAppear`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/#//apple_ref/occ/instm/UIViewController/viewWillAppear), [`viewDidDisappear`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/#//apple_ref/occ/instm/UIViewController/viewDidDisappear), and the always fun _"[Stop! You're using too much memory!](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/#//apple_ref/occ/instm/UIViewController/didReceiveMemoryWarning)"_ warning.  
The UIViewController's lifecycle is a huge convenience. But it can be confusing to know when to do what. **This article will cover a few best practices to help you develop with confidence.**  
Since we're developers, we'll use a zero-based index for this list.  

### Best Practices

#### 0. Initialize references in `viewDidLoad`

```swift
override func viewDidLoad() {
  super.viewDidLoad()
  ref = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio.com")
}
```

```swift
- (void)viewDidLoad {
  [super viewDidLoad];
  self.ref = [[Firebase alloc] initWithUrl:@"https://<YOUR-FIREBASE-APP>.firebaseio.com"];
}
```

You can't count on a `UIViewController`'s initializer. This is because controllers that come from the Storyboard don't have their initializer called. This leaves us with the `viewDidLoad` method.  
The `viewDidLoad` method is usually the first method we care about in the `UIViewController` lifecycle. Since `viewDidLoad` is called once and only once in the lifecycle, it's a great place for initialization.  
The `viewDidLoad` method is also called whether you use Storyboards or not. Outlets and properties are set at this point as well. This will enable you to do any dynamic creation of a reference's location.  

#### 1. Initialize references with implicitly unwrapped optionals (Swift-only)

```swift
class ViewController : UIViewController {
  var ref: Firebase!

  override func viewDidLoad() {
    super.viewDidLoad()
    ref = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio.com")
  }
}
```

In Swift all properties' values must be set before initialization is complete. And that's a big problem. You can't rely on a `UIViewController`'s initializer to ever be called. So how do you set the value for the `Firebase` reference property? Use an **implicitly unwrapped optional**.  

By unwrapping the property the compiler will assume the value will exist by the time it's called. If the value is `nil` when called, it'll crash the app. That won't happen for a reference property if the value is set in `viewDidLoad`.  

Using an implicitly unwrapped optional is you telling the compiler: _"Chill-out, I know what I'm doing."_  
You might be wondering why you shouldn't inline the value.  

```swift
class ViewController : UIViewController {
  let ref = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio.com")
}
```

There's no problem using an inline value. You're just limited to static values since you can't use other properties or variables.  

```swift
class ViewController : UIViewController {
  // This won't compile :(
  let ref = Firebase(url: "https://my.firebaseio.com/(myCoolProperty)")
}
```

#### 2. Create listeners in `viewWillAppear`, not in `viewDidLoad`

```swift
override func viewWillAppear(animated: Bool) {
  super.viewWillAppear(animated)
  ref.observeEventType(.Value) { (snap: FDataSnapshot!) in print (snap.value) }
}
```

```swift
- (void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];
  [self.ref observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
    NSLog(@"%@", snapshot.value);
  }];
}
```

Your app should be a good citizen of battery life and memory. To preserve battery life and memory usage, you should only synchronize data when the view is visible.  

The `viewWillAppear` method is called each time the view becomes visible. This means if you set your listener here, your data will always be in sync when the view is visible.  

You should avoid creating listeners in `viewDidLoad`. Remember that `viewDidLoad` only gets called once. When the view disappears you should remove the listener. This means the data won't re-sync when the view becomes visible again.  

#### 3. Remove listeners in viewDidDisappear with a FirebaseHandle

```swift
class ViewController : UIViewController {
  var ref: Firebase!
  var handle: UInt!

  override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    handle = ref.observeEventType(.Value) { (snap: FDataSnapshot) in print (snap.value) }
  }
}
```

```swift
@interface ViewController()
@property (nonatomic, strong) Firebase *ref;
@property FirebaseHandle handle;
@end

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  self.ref = [[Firebase alloc] initWithUrl:@"https://<YOUR-FIREBASE-APP>.firebaseio.com"];
}

- (void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];
  self.handle = [self.ref observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
    NSLog(@"%@", snapshot.value);
  }];
}

@end
```

To remove a listener in iOS you need a `FirebaseHandle`. A `FirebaseHandle` is just a `typealias` for a `UInt` that keeps track of a Firebase listener. 

Note that in Swift you need to use an implicitly unwrapped optional since the value can't be set in the initializer. The `handle`'s value is set from the return value of the listener.  
Use this handle to remove the listener in `viewDidDisappear`.  

```swift
override func viewDidDisappear(animated: Bool) {
  super.viewDidDisappear(animated)
  ref.removeObserverWithHandle(handle)
}
```

```swift
-(void)viewDidDisappear:(BOOL)animated {
  [super viewDidDisappear:animated];
  [self.ref removeObserverWithHandle:self.handle];
}
```

If your controller is still syncing data when the view has disappeared, you are wasting bandwidth and memory.  

#### Leaky Listeners
A leaky listener is a listener that is consuming memory to store data that isn't displayed or accessed. This is especially an issue when navigating using a `UINavigationController`, since the root controller isn’t removed from memory when navigating to a detail controller. This means a root controller will continue to synchronize data if the listener isn't removed when navigating away. This action takes up needless bandwidth and memory. 

The thought of removing the listener might sound unappealing. You may think you need to keep your listener open to avoid downloading the same data again, but this is unnecessary.  
Firebase SDKs come baked in with caching and offline data persistence. These features keep the client from having to fetch recently downloaded data.  

#### 4. Enable offline in the `AppDelegate`'s initializer

```swift
class AppDelegate: UIResponder, UIApplicationDelegate {
  var window: UIWindow?

  override init() {
    Firebase.defaultConfig().persistenceEnabled = true
  }
}
```

```swift
@implementation AppDelegate

- (instancetype)init
{
  self = [super init];
  if (self) {
    [[Firebase defaultConfig] setPersistenceEnabled:YES];
  }
  return self;
}

@end
```

Speaking of offline, this tip isn't `UIViewController` specific, but it's important. Offline has to be set before any other piece of Firebase code runs.  

Your first instinct might be to enable offline in the `AppDelegate`'s `application:didFinishLaunchingWithOptions` method. This will work for most situations, but not for all. This can go wrong when you inline a `Firebase` property's value in the root `UIViewController`. The value of the `Firebase` property is set before `application:didFinishLaunchingWithOptions` gets called, which will cause the SDK to throw an exception.  

By setting up the offline config in the `AppDelegate` init we can avoid this issue.    

### Final example
[Check out this gist](https://gist.github.com/davideast/29e2717cef76e63fe0a9) to see the final version of the UIViewController.  

### Takeaways
If you remember anything remember these things:  

*   Initialize references in `viewDidLoad`
*   Synchronize data only when the view is visible
*   Store a handle to simplify removing a reference
*   Remove listeners when the view in not visible
*   Configure offline persistence in the `AppDelegate`'s initializer

_How do you manage Firebase in your `UIViewController`? Let us know if you're using any of these today or if you have any best practices of your own._
