title: Method swizzling in Swift
body: <p>There is a little problem with <code>UINavigationBar</code> which I'm trying to overcome. When you hide the status bar using <code>prefersStatusBarHidden()</code> method in a view controller (I don't want to disable the status bar for the entire app), the navigation bar loses 20pt of its height that belongs to the status bar. Basically the navigation bar shrinks.</p>
  
  <p><img src="https://i.stack.imgur.com/IR9tVm.png" alt="enter image description here">
  <img src="https://i.stack.imgur.com/561ttm.png" alt="enter image description here"></p>
  
  <p>I was trying out different workarounds but I found that each one of them had drawbacks. Then I came across this <a href="https://github.com/wicharek/UINavigationBar-FixedHeightWhenStatusBarHidden" rel="nofollow noreferrer">category</a> which using method swizzling, adds a property called <code>fixedHeightWhenStatusBarHidden</code> to the <code>UINavigationBar</code> class which solves this issue. I tested it in Objective-C and it works. Here are the <a href="https://github.com/wicharek/UINavigationBar-FixedHeightWhenStatusBarHidden/blob/master/UINavigationBar%2BFixedHeightWhenStatusBarHidden.h" rel="nofollow noreferrer">header</a> and the <a href="https://github.com/wicharek/UINavigationBar-FixedHeightWhenStatusBarHidden/blob/master/UINavigationBar%2BFixedHeightWhenStatusBarHidden.m" rel="nofollow noreferrer">implementation</a> of the original Objective-C code.</p>
  
  <p>Now since I'm doing my app in Swift, I tried translating it to Swift. </p>
  
  <p>The first problem I faced was Swift extension can't have stored properties. So I had to settle for computed property to declare the <code>fixedHeightWhenStatusBarHidden</code> property that enables me to set the value. But this sparks another problem. Apparently you can't assign values to computed properties. Like so.</p>
  
  <pre><code>self.navigationController?.navigationBar.fixedHeightWhenStatusBarHidden = true
  </code></pre>
  
  <p>I get the error <strong>Cannot assign to the result of this expression</strong>.</p>
  
  <p>Anyway below is my code. It compiles without any errors but it doesn't work.</p>
  
  <pre><code>import Foundation
  import UIKit
  
  extension UINavigationBar {
  
      var fixedHeightWhenStatusBarHidden: Bool {
          return objc_getAssociatedObject(self, "FixedNavigationBarSize").boolValue
      }
  
      func sizeThatFits_FixedHeightWhenStatusBarHidden(size: CGSize) -&gt; CGSize {
  
          if UIApplication.sharedApplication().statusBarHidden &amp;&amp; fixedHeightWhenStatusBarHidden {
              let newSize = CGSizeMake(self.frame.size.width, 64)
              return newSize
          } else {
              return sizeThatFits_FixedHeightWhenStatusBarHidden(size)
          }
  
      }
      /*
      func fixedHeightWhenStatusBarHidden() -&gt; Bool {
          return objc_getAssociatedObject(self, "FixedNavigationBarSize").boolValue
      }
      */
  
      func setFixedHeightWhenStatusBarHidden(fixedHeightWhenStatusBarHidden: Bool) {
          objc_setAssociatedObject(self, "FixedNavigationBarSize", NSNumber(bool: fixedHeightWhenStatusBarHidden), UInt(OBJC_ASSOCIATION_RETAIN))
      }
  
      override public class func load() {
          method_exchangeImplementations(class_getInstanceMethod(self, "sizeThatFits:"), class_getInstanceMethod(self, "sizeThatFits_FixedHeightWhenStatusBarHidden:"))
      }
  
  }
  </code></pre>
  
  <p><code>fixedHeightWhenStatusBarHidden()</code> method in the middle is commented out because leaving it gives me a method redeclaration error.</p>
  
  <p>I haven't done method swizzling in Swift or Objective-C before so I'm not sure of the next step to resolve this issue or even it's possible at all.</p>
  
  <p>Can someone please shed some light on this?</p>
  
  <p>Thank you.</p>
  
  <hr>
  
  <p><strong>UPDATE 1:</strong> Thanks to newacct, my first issue about properties was resolved. But the code doesn't work still. I found that the execution doesn't reach the <code>load()</code> method in the extension. From comments in <a href="https://stackoverflow.com/a/24898473/1077789">this</a> answer, I learned that either your class needs to be descendant of <code>NSObject</code>, which in my case, <code>UINavigationBar</code> isn't directly descended from <code>NSObject</code> but it does implement <code>NSObjectProtocol</code>. So I'm not sure why this still isn't working. The other option is adding <code>@objc</code> but Swift doesn't allow you to add it to extensions. Below is the updated code.</p>
  
  <p>The issue is still open.</p>
  
  <pre><code>import Foundation
  import UIKit
  
  let FixedNavigationBarSize = "FixedNavigationBarSize";
  
  extension UINavigationBar {
  
      var fixedHeightWhenStatusBarHidden: Bool {
          get {
              return objc_getAssociatedObject(self, FixedNavigationBarSize).boolValue
          }
          set(newValue) {
              objc_setAssociatedObject(self, FixedNavigationBarSize, NSNumber(bool: newValue), UInt(OBJC_ASSOCIATION_RETAIN))
          }
      }
  
      func sizeThatFits_FixedHeightWhenStatusBarHidden(size: CGSize) -&gt; CGSize {
  
          if UIApplication.sharedApplication().statusBarHidden &amp;&amp; fixedHeightWhenStatusBarHidden {
              let newSize = CGSizeMake(self.frame.size.width, 64)
              return newSize
          } else {
              return sizeThatFits_FixedHeightWhenStatusBarHidden(size)
          }
  
      }
  
      override public class func load() {
          method_exchangeImplementations(class_getInstanceMethod(self, "sizeThatFits:"), class_getInstanceMethod(self, "sizeThatFits_FixedHeightWhenStatusBarHidden:"))
      }
  
  }
  </code></pre>
  
  <hr>
  
  <p><strong>UPDATE 2:</strong> Jasper helped me to achieve the desired functionality but apparently it comes with a couple of major drawbacks.</p>
  
  <p>Since the <code>load()</code> method in the extension wasn't firing, as Jasper suggested, I moved the following code block to app delegates' <code>didFinishLaunchingWithOptions</code> method.</p>
  
  <pre><code>method_exchangeImplementations(class_getInstanceMethod(UINavigationBar.classForCoder(), "sizeThatFits:"), class_getInstanceMethod(UINavigationBar.classForCoder(), "sizeThatFits_FixedHeightWhenStatusBarHidden:"))
  </code></pre>
  
  <p>And I had to hardcode the return value to 'true' in the getter of <code>fixedHeightWhenStatusBarHidden</code> property because now that the swizzling code executes in the app delegate, you can't set a value from a view controller. This makes the extension redundant when it comes to reusability.</p>
  
  <p>So the question is still open more or less. If anyone has an idea to improve it, please do answer.</p>