Preventing UIVisualEffectView crash
Tags: swift, uivisualeffectview, crash, ios
Published: 2017-12-20 10:47:24

---
title: Preventing UIVisualEffectView crash
published: true
description: 
tags: swift, uivisualeffectview, crash, ios
---

Original post https://github.com/onmyway133/blog/issues/124

We all know that there's a potential crash with `UIVisualEffectView` on iOS 11. The fix is to not add sub views directly to `UIVisualEffectView`, but to its `contentView`. So we should change

```swift
effectView.addSubview(button)
```

to 

```swift
effectView.contentView.addubView(button)
```

Here we don't need to perform iOS version check, because `effectView.contentView` works for any iOS versions.

### Potential cases for crashes

Here are some cases you can potentially cause the crashes

***Strange namings***

Normally we name our `UIVisualEffectView` as `blurView`, `effectView`. But there's times we name it differently like `navigationView`, `containerView`, `boxView`, ... This way we may completely forget that it's a `UIVisualEffectView` 🙀 

```swift
containerView.addSubview(button)
boxView.insertSubview(label, at: 0)
```

***Custom loadView***

Sometimes it's convenient to have our `UIViewController 's view` as a whole blur view, so that all things inside have a nice blur effect background

```swift
class OverlayController: UIViewController {
  let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
  override func loadView() {
    super.loadView()
    self.view = blurView
  }

  override func viewDidLoad() {
    super.viewDidLoad()

    view.addSubview(button)
  }
}
```

By setting our `blurView` as `view` in `loadView`, we have no idea afterwards that `view` is actually a `UIVisualEffectView` 🙀 

***Inheritance***

What happen if we have another `UIViewController` that inherits from our `OverlayController`, all it knows about `view` is `UIView`, it does not know that it is a disguising `UIVisualEffectView` 🙀 

```swift
class ClocksController: OverlayController {
  override func viewDidLoad() {
    super.viewDidLoad()

    view.addSubview(timeLabel)
  }
}
```

***Superclass type***

Sometimes declare our things but with protocol or superclass types. Consumers of our API have no clue to know that it is `UIVisualEffectView` 🙀 

```swift
let view: UIView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
```

Here it appears to us that `view` is of type `UIView`

***Legacy codebase***

Now imagine you 've handled a legacy codebase to deal with. Perform finding and replacing all those things related to `UIVisualEffectView` is very hard task. Especially since we tend to write less tests for UI

### Making it impossible to crash

I like concept like [Phantom type](https://wiki.haskell.org/Phantom_type) to limit interface. Here we're not using type but a wrapper 

```swift
final class BlurView: UIView {
  private let effectView: UIVisualEffectView

  init(style: UIBlurEffectStyle, backgroundColor: UIColor? = nil) {
    self.effectView = UIVisualEffectView(effect: UIBlurEffect(style: style))
    self.effectView.backgroundColor = backgroundColor
    super.init(frame: .zero)
    insertSubview(effectView, at: 0)
  }

  required init?(coder aDecoder: NSCoder) {
    fatalError()
  }

  override func addSubview(_ view: UIView) {
    effectView.contentView.addSubview(view)
  }

  override func layoutSubviews() {
    super.layoutSubviews()

    effectView.frame = bounds
  }
}
```

Here we override `addSubview` to always add views to `effectView.contentView`. In the `init` method, we need to call `insertSubview` instead because of our overriden `addSubview`

Now `BlurView` has a blur effect thanks to is underlying `UIVisualEffectView`, but expose only `addSubview` because of its `UIView` interface. This way it is impossible to cause crashes 😎 

```swift
let blurView = BlurView(style: .dark)
blurView.addSubview(button(
```