NSCoding class chaining with Swift

Published on Aug 24th, 2015

While I was trying to get some Swift classes to conform to NSCoding so that I could serialize them, I found out that it can be a little annoying to get the init(coder:) calls to chain through your class hierarchy. All of the resources I found online suggested that my initializers need to be declared as convenience, however if I declared them as such, they would complain that you need to call self’s designated initializer. I could remove the convenience, but this is quite annoying because it forced me to do all of my initialization with a coder. After a little research, I discovered that I needed to remove the convenience AND make sure I also override the default init. I believe the issue is that the default init is marked as designated and forces your subclasses to call init(), if you override without required, you can call a different initializer from a subclass. Here is a short example and a Playground

class Person: NSObject, NSCoding {

    var name: String = "Bob"

    override init() {
        super.init()
    }

    required init(coder aDecoder: NSCoder) {
        self.name = aDecoder.decodeObjectForKey("name") as! String
    }

    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(self.name, forKey: "name")
    }
}

class Employee: Person {

    var position: String = "Manager"

    override init() {
        super.init()
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.position = aDecoder.decodeObjectForKey("position") as! String
    }

    override func encodeWithCoder(aCoder: NSCoder) {
        super.encodeWithCoder(aCoder)
        aCoder.encodeObject(self.position, forKey: "position") 
    }
}

var person = Employee()
person.name = "Bill"
person.position = "Worker"

print(person.name)
print(person.position)

let data = NSKeyedArchiver.archivedDataWithRootObject(person)
let newPerson = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? Employee

print(person.name)
print(person.position)

This has been tested with Swift 1.2

Remove that shadow!

Published on Jan 14th, 2015

UIKit can be so frustrating sometimes. I was trying to remove a shadow from UINavigationBar, and it turns out there is a property for doing this…

navigationBar.shadow = <something>

Perfect! or not… Apparently, it only works if you have a custom image as your bar background

The default value is nil, which corresponds to the default shadow image. When non-nil, this property represents a custom shadow image to show instead of the default. For a custom shadow image to be shown, a custom background image must also be set with the setBackgroundImage:forBarMetrics: method. If the default background image is used, then the default shadow image will be used regardless of the value of this property.

So, I cannot use a solid colored background without a shadow… after looking around online, I found bunch of extensions on UIImage that returns an image of a given size and color, but I felt that custom drawing was a little overkill here. My solution is as follows:

extension UINavigationBar {
    
    func removeShadow() {
        if let view = removeShadowFromView(self) {
            view.hidden = true
        }
    }
    
    func removeShadowFromView(view: UIView) -> UIImageView? {
        if (view.isKindOfClass(UIImageView) && view.bounds.size.height <= 1) {
            return view as? UIImageView
        }
        for subView in view.subviews {
            if let imageView = removeShadowFromView(subView as UIView) {
                return imageView
            }
        }
        return nil
    }   
}

This way, I simply call

navigationController?.navigationBar.removeShadow()

No Shadow

and all is right with the world. Feel free to use this extension in your own work, it’s nothing particularly genius, but it sure helps me out. This is based on some Objective-C code I found here

Update for iOS 10: I changed the above code so it “hides” the shadow, rather than removing it. This is confirmed working on both iOS 9 and 10. The removeFromSuperview() method doesn’t work on iOS 10.

Concerning Swift

Published on Aug 19th, 2014

swift 1-point-no

I came across an excellent article by Erica Sadun talking about her experiences with the Xcode Betas and her expectations of Swift 1.0, and I think it was a nice dose of reality. (…And not only because I have a domain that will be obsolete in a post Objective-C world.)

It’s nice to pretend that we will all be writing Swift apps by the end of the year, but if this git repo is any indication, we will have a little while to go before the language is stable enough for prime time.

I feel sorry for all the people who decided that Swift was their excuse to start learning iOS development, because of all the strange quirks with Objective-C compatibility, it won’t be as easy as picking up a Swift book and writing an app. There will be challenges and changes for at least the next year and possibly longer. As it is now, you will have to update your Swift apps very often to keep up with what will certainly be many revisions of the language.

I have a friend who I am trying to teach iOS development, and even though it will be available fairly soon, I have urged him to pretend that Swift doesn’t exist for the time being, and I urge the same to anyone else just starting out.

I intend to practice what I preach, while I will undoubtably tinker with Swift over the next several months (I am not completely nuts), I will not do any serious writing in it until they at least start updating the Cocoa API’s to be a bit more Swift-like.

How I Finally Understood Swift Optionals

Published on Aug 4th, 2014

I had a bit of trouble understanding Swift Optionals. When should I use a question mark, and when should I use an exclamation point? The iBook was a bit confusing on this point for me. Until I was sitting at IndyCocoaHeads last night and @AndyDuff was giving a talk about Swift. When he got to the part about Optionals, something finally clicked for me and I was able to finally understand them.

Optionals are like Schrödinger’s Cat!

Think of it like this, you can have an integer, and we all know that integers are whole numbers (-1, 0, 1 , 2, 3…) within a certain range depending on what type of system you are on. In Swift you would declare it as such:

var n: Int = 23

The compiler does not need the type here, but I included it for clarity. This is telling the compiler that while, n “could” be any integer, here, it’s 23. Now lets look at an Optional:

var n: Int? = 23

In this line we are telling the compiler something else. Currently n is 23, but n “could” be any integer OR nil. If you are familiar with Objective-C, this is quite familiar with NS objects. NSString can equal @”Hello, World” but you can also set it to nil, which is not a string, or any type for that matter. This is why I like to say it’s like Schrödinger’s Cat If you are not familiar with the concept, I believe this link is a fun place to start. Essentially the variable can contain a value, or nil (the cat is either alive or dead), but we don’t know until we check.

if (n != nil) {
    println("n = \(n!)." // Note the ! after n.
}

Once you are 100% sure that n does have a value, then you can force it to behave like a normal integer by using “!”, if you use ! and n is a nil value, then your app will crash.

var n: Int? // Optionals default their initial value to nil.
println("n = \(n)." // This will crash because it's trying to print nil.

Once you get the hang of it, it becomes natural and feels like things we have done in Objective-C for quite some time now just we now have access to this feature on “Primitive” types.

Format specifiers for NSInteger and NSUInteger (Without Warnings)

Published on Jul 24th, 2014
%zd = NSInteger
%tu = NSUInteger
%tx = Hex

I have been bothered by the fact that if I use NSInteger NSUInteger in [NSString stringWithFormat:], I will get warnings depending on what architecture I am building for.

Does this look familiar?

Does this look familiar?

You can change the format to %ld and the cast to (long), but that felt really awkward. After googling around for a while and even Apple’s Documentation is not helping much, I decided to pose the question in #iphonedev. I got a response from someone named “Jer” with the following link Gred Parker describes the %zd (NSInteger), %tu (NSUInteger) and %tx (Hex) format specifiers which will suppress the warnings when switching architectures.

I immediately double-checked the docs to make sure I hadn’t overlooked something, and I had not. So I filed a bug report with Apple and hopefully this will get cleared up soonish. In the mean time I decided to make this blog post, to help Google, help others.