MacBook Pro M2 Battery Life

Toward the end of last week, I received a new 16” MacBook Pro M2. Prior to this, I was using the last Intel 16” MacBook Pro. I haven’t fully set up this new M2, but I’ve used it enough over the past few days to note one thing quite clearly.

The difference in battery life between the Intel MacBook Pro and this M2 MacBook Pro is drastic. Rediculously so.

Even when new, the Intel (i9 version) rarely lasted more than 2-3 hours on a full battery charge. Granted, I was likely working in Xcode at the time, but the projects were small and quick to build. More recently, most of my time on the Intel was spent surfing the web or watching video on YouTube or other websites. It still struggles to get much beyond 2 hours before reaching zero battery from a full charge.

I’m guesstimating that this M2 MacBook Pro has been in use for about an hour each day for the last 5 days and is sitting happily at 73%, with iStat Menus estimating about 13 hours of use left to go.

This is what a laptop should be like. The only thing I really disliked about the Intel MacBook Pro was the dismal battery life. I’m loving this upgrade for the battery improvement alone. I’m looking forward to getting it fully set up and trying out some other workloads on it (getting back to Xcode included).

Swift Enums in Objective C

Using a Swift Integer enum in Objective C (the only Swift enum type that can be used in Objective C) is problematic. A public Objective C property for the enum can't be created directly. Consider:

// In a Swift file
enum StringStyle: Int {
    case none
    case bold
    case italic
}

// In an Objective C header file
// The Swift header cannot be imported in an Objective C header file...
#import "Project-Swift.h" // Can't do this

@interface MyClass: NSObject {
    // ...so a property of the enum type cannot be declared
    // because the enum type is not visible here without the Swift.h import
    @property (nonatomic, assign) StringStyle style; // Can't do this
}

One work around is to declare an NSInteger in the Objective C header file as a stand in for the enum type instead:

// In an Objective C header file
@property (nonatomic, assign) NSInteger style;

Unfortunately, any function that moves the NSInteger/enum between Swift and Objective C code will require conversion between the Objective C NSInteger and the Swift enum type.

This is a pain.

However, the effect of a public Swift enum property in Objective C code can still be achieved. The conversion between an NSInteger and the Swift enum can be centralized, thereby simplifying all bridging code between Objective C and Swift. This solution works through a Swift extension on the Objective C class that provides the enum property.

Ah, but a new property can't be declared through an extension! This is true, but easily worked around.

A computed property can be declared that creates an associated object as the backing store for the property. It will appear to be a public property of the Objective C class and of the enum type. Client code will not need to perform conversions between NSInteger and Swift enum representations since client code will only see a Swift enum type, as it should. The enum values can be used directly within and passed between both Objective C and Swift code. Here is an example of the computed property in an extension of the Objective C class:

extension MyClass {
    // Create a key to reference the associated object
    private struct Key {
        static var style = "StringStyle"
    }
    
    // This will appear as a public property on instances of the Objective C class
    @objc var style: StringStyle {
    	get {
            guard
                let value = objc_getAssociatedObject(self, &Key.style) as? NSNumber,
                let style = StringStyle(rawValue: value.intValue)
            else {
                // Default value when no value has been set for the property before
                return .none
            }
            
            return style
        }
        
        set {
            objc_setAssociatedObject(self, &Key.style, NSNumber(value: newValue.rawValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}

As shown below, the computed property can be used directly from both Objective C and Swift code.

// In an Objective C implementation file
MyClass *myClass = [[MyClass alloc] init];
myClass.style = StringStyleBold;
NSLog(@"Style: %ld", myClass.style);

// In a Swift file
let myClass = MyClass()
myClass.style = .bold
print("Style: \(myClass.style)")

I've found this technique especially helpful in a mixed Swift/Objective C code base where new functionality is being added in Swift, but Objective C code that can't be converted yet needs to take advantage of new functionality too. Eventually, when all Objective C code is converted, it's a simple task to replace the computed property with a true property since no code outside of the property needs to be changed.

Swift: Optional Equality and Objective C Interoperability

tl;dr

An extension on the Optional type allows us to simplify NSObject equality checks that have the additional complication of supporting optionals.

-----

Objective C provides a standard way to define equality for objects descended from NSObject. When using Swift, especially in mixed Objective C and Swift code bases, it is still necessary to use these standard methods to check the equality of NSObject descendants or to use those objects in Objective C collections. Unfortunately, optionals in Swift throw an additional wrench into equality checks that can lead to repetitive, ugly, hard to read code.

Consider the code for a Search class below. It overrides the NSObjectProtocol methods of isEqual and hash for Objective C compatibility. When one Search object is compared against another with isEqual, there are four cases to consider for each optional property. Using the property keyword from the code as an example:

case 1case 2case 3case 4
"this" keywordnilnilnot nilnot nil
"other" keywordnilnot nilnilnot nil
isEqual outcometruefalsefalsetrue if unwrapped values are equal

The code to check these four cases can be simplified by assuming the properties are not equal until proven otherwise. So, the code can check for equality when both properties have non nil values (case 4 above) and it can check if both values are nil (case 1 above). If neither of those cases applies, then one of the values is nil and the other is not. We don't have to check for those cases because the outcome matches the assumed default outcome.

class Search: NSObject {
    var keyword: NSString?
    var location: NSString?
	
    override func isEqual(_ object: Any?) -> Bool {
        // Object must be of the same class as self
        guard let other = object as? Search else {
            return false
        }

        // No need to check further if other is the same object as self
        if self === other {
            return true
        }
        
        // Check keyword equality
        var equalKeywords = false
        if let thisKeyword = keyword, let otherKeyword = other.keyword {
            equalKeywords = thisKeyword.isEqual(otherKeyword)
        } else if keyword == nil, other.keyword == nil {
            equalKeywords = true
        }
        
        // Check location equality
        var equalLocations = false
        if let thisLocation = location, let otherLocation = other.location {
            equalLocations = thisLocation.isEqual(otherLocation)
        } else if location == nil, other.location == nil {
            equalLocations = true
        }

        return equalKeywords && equalLocations
    }

    override var hash: Int {
        get {
            // Hash of zero has no effect in an XORed set of values, so it's a good default value
            let keywordHash = keyword?.hash ?? 0
            let locationHash = location?.hash ?? 0
            
            return keywordHash ^ locationHash
        }
    }
}

The equality checks shown above are the product of repeated iteration and simplification. I've had much uglier iterations. In this form it is fairly clear, but there is a lot of boiler plate to perform what is conceptually an equality check (like an "==" operation). Since the complexity is caused by the use of optionals, I turned to an extension on the Optional type to simplify further.

extension Optional where Wrapped: NSObjectProtocol {
    // Compare two Optionals
    func isEqual<T: NSObjectProtocol>(_ other: T?) -> Bool {
        switch (self, other) {
        case (.some(let selfValue), .some(let otherValue)):
            return selfValue.isEqual(otherValue)
        case (.none, .none):
            return true
        case (.some, .none): fallthrough
        case (.none, .some):
            return false
        }
    }

    // Compare an Optional and a non-Optional
    func isEqual<T: NSObjectProtocol>(_ other: T) -> Bool {
        switch self {
        case .some(let value):
            return value.isEqual(other)
        case .none:
            return false
        }
    }

    // Convenient unwrap of Optionals to get their hash value
    var hash: Int {
        get {
            switch self {
            case .some(let value):
                return value.hash
            case .none:
                return 0
            }
        }
    }
}

Limiting the extension to Optionals that wrap values adhering to the NSObjectProtocol makes sure we are only targeting objects that already define an isEqual function of their own (or at the very least, depend on the NSObject implementation of that function). There are two versions of the isEqual function in the extension. The first allows comparison of two optionals. The second allows comparison of an optional and a non-optional. The isEqual function provided by NSObject already handles the case of comparing a non-optional and an optional. So, now we can use isEqual with any combination of optionals and non-optionals (for NSObjects only of course).

The other part of the equality story is the hash function. We can simplify the generation of hash values with this extension function that auto unwraps an optional and gets its value's hash.

Applying the extension functions to the Search class, the code is greatly simplified as shown below:

class Search: NSObject {
    var keyword: NSString?
    var location: NSString?

    override func isEqual(_ object: Any?) -> Bool {
        // Object must be of the same class as self
        guard let other = object as? Search else {
            return false
        }

        // No need to check further if other is the same object as self
        if self === other {
            return true
        }

        return keyword.isEqual(other.keyword) && location.isEqual(other.location)
    }

    override var hash: Int {
        get {
            return keyword.hash ^ location.hash
        }
    }
}

Swift: Simplifying Dictionary Processing

Update: A few weeks after this post, I came across information about Swift 4 updates that I hadn't seen before. A new feature for Dictionaries in Swift 4 is similar to what I was aiming to provide with the code shown in this post. It works well for definitively typed dictionaries (e.g. [String: String]), but it doesn't help with mixed type dictionaries (like [String: AnyObject]) because it still requires two type casts to get from the AnyObject in the dictionary to the object type that needs to be pulled out. For example, pulling a String out of a dictionary of type [String: AnyObject] would look like this:

event.location = dictionary["location", default: "TBD" as AnyObject] as! String

So, I still prefer the technique in this post to the Swift 4 provided solution.

tl;dr

// I wanted to make statements like this:
event.location = dictionary["city"] as? String ?? "TBD"
event.recurring = dictionary["recurring"] as? String ?? false

// More readable with something like this:
event.location = dictionary["city", defaultTo: "TBD"]
event.recurring = dictionary["recurring", defaultTo: false]

// And ended up with this:
event.location = dictionary[("city", defaultTo: "TBD")]
event.recurring = dictionary[("recurring", defaultTo: false)]

// And this for setting values for which nil is OK:
event.revenue = dictionary[("revenue", isType: Float.self)]

-----

Pulling apart a dictionary of data to populate a model object often involves some coding gymnastics. There are several outcomes to consider. For a given key, maybe the dictionary:

  • Contains a value of the expected type.
  • Contains a value but not of the expected type.
  • Doesn't contain a value.

When there is an error, I still want to set a valid value in the model object. So, I often ended up with code like this to pull a value out of a dictionary:

// Just making up an imaginary model object called 'event' 
// that will get some property values from a dictionary.
event.location = dictionary["city"] as? String ?? "TBD"

This is perfectly fine. But it's kind of ugly. Imagine a list of statements like this to fill out an "event" model object that has two dozen properties to pull out of the dictionary. Ugh. I also enjoy taking advantage of Swift's type inference machinery anywhere I can to reduce type casting in code.

Initially I extended Dictionary to include a function that was more readable if not slightly longer code. It looked like this:

public func value<T: Any>(for key: Key, defaultsTo: T) -> T {
    if let value = self[key] as? T {
        return value
    }        

    return defaultsTo
}

// At the call site, it looked like this:
event.location = dictionary.value(for: "city", defaultsTo: "TBD")

Again, this is perfectly fine. Any programmer can guess what this is doing without knowing Swift operator syntax for casting and nil-coalescing. But I've traded in harder to read code for boiler plate in the function name and arguments (imagine a dozen lines repeating the ".value(for:" and "defaultsTo:" parts of that function). I really wanted to continue accessing dictionary values using a subscript notation to avoid too much extra boiler plate. Alas, this was not possible... until Swift 4.

Swift 4 adds support for generic subscript operators. This is exactly what I needed to achieve the type inference I desired with subscript notation on a Dictionary.

But, a subscript can only take a single argument. This limitation can be skirted by using a tuple as the single argument passed to the subscript function. Here is an implementation:

extension Dictionary {
    /**
    Allows subscripting of a dictionary with a tuple that includes the
    dictionary key and a default value. If there is not a value for the
    provided key or if the value found is not of the same type as the
    provided default value, then the default value is returned.
    */
    subscript<T: Any>(tuple: (key: Dictionary.Key, defaultTo: T)) -> T {
        if let value = self[tuple.key] as? T {
            return value
        }

        return tuple.defaultTo
    }
}

// At the call site, this yields:
event.location = dictionary[("city", defaultTo: "TBD")]

// If you prefer, since the argument is a tuple, 
// you can even omit all of the interior argument labels.
even.location = dictionary[("city", "TBD")]

This gets really close to the fanciful outcome I was aiming for.

While the above works well for values that are never nil, it isn't useful for setting variable values for which nil is acceptable. A dictionary lookup and conditional type cast can be used for those cases. For the sake of completeness and code consistency, a second subscript function that doesn't use a default value but still provides type checking can be defined and used like this:

extension Dictionary {
    /**
    Allows subscripting of a dictionary with a tuple that includes the
    dictionary key and the expected type of the value.
    If there is not a value for the provided key or if the value found is
    not of the expected type, then nil is returned.
    */
    subscript<T: Any>(tuple: (key: Dictionary.Key, ofType: T.Type)) -> T? {
        return self[tuple.key] as? T
    }
}

// At the call site, this yields:
event.location = dictionary[("city", ofType: String.self)]

// Or, without the argument label:
event.location = dictionary[("city", String.self)]

Swift: Simple Generic UITableView Extensions

If you're looking to make your first steps into Swift generics, here are a couple of very simple yet useful examples. I assume basic knowledge of what generics are, but provide a little more explanation of how these particular functions work with types. The code shown is using Swift 3 syntax.

UITableView has some functions that ask for a cell reuse identifier, which is a string. Specifying strings as arguments is always less desirable than specifying a real type that the compiler has knowledge of, so whenever a string argument can be replaced by a real type, it's a great idea to do so. Some of the benefits include:

  • Compile time checking of the real type instead of a potential run time failure on a mistyped string
  • Code completion hints are given when typing the name of a real type
  • If a faulty string is entered there is no immediate warning or error, but Xcode will give an error immediately if an unknown type is entered

The following extensions rely on the following convention: a UITableViewCell subclass' name, xib file name, and reuse identifier are always the same. Given that, we can use a class name to get the reuse identifier (since it is the same name) and the nib (by loading the nib with the same name). If we can also convert a class type to a class name (as a String), then we can replace the use of Strings with class types, gaining the benefits noted above. After showing the extension functions, I'll discuss a little bit more about how they work.

Here's the first extension:

extension UITableView {
/**
Registers a cell with the table view.
Depends on the assumption that the cell's reuse identifier matches its class name.
If a nib is found in the main app bundle with a filename matching the cell's class name, that nib is registered with the table view. Otherwise, the cell's class is registered with the table view.
- parameters:
   - type: The class type of the cell to register.
*/
    func registerCell<T: UITableViewCell>(ofType type: T.Type) {
        let cellName = String(describing: T.self)
        
        if Bundle.main.path(forResource: cellName, ofType: "nib") != nil {
            let nib = UINib(nibName: cellName, bundle: Bundle.main)
            
            register(nib, forCellReuseIdentifier: cellName)
        } else {
            register(T.self, forCellReuseIdentifier: cellName)
        }
    }
}

As shown below, this extension function slightly simplifies table view setup code and provides the type checking benefits noted at the start of this post.

// Instead of this
let nib = UINib(nibName: "CustomTableViewCell", bundle: Bundle.main)
tableView.register(nib, forCellReuseIdentifier: "CustomTableViewCell")

// We can do this
tableView.registerCell(ofType: CustomTableViewCell.self)

Here's the second extension:

extension UITableView {
/**
Dequeues a cell that has been previously registered for use with the table view.
Depends on the assumption that the cell's class name and reuse identifier are the same.
- parameters:
   - type: The class type of the cell to dequeue.
- returns: A UITableViewCell already typed to match the `type` provided to the function.
*/
    func dequeueCell<T: UITableViewCell>(ofType type: T.Type) -> T     {
       let cellName = String(describing: T.self)
        
       return dequeueReusableCell(withIdentifier: cellName) as! T
    }
}

Again, this extension function simplifies code at the call site and provides type checking, as show below:

// Before having the extension, we might have had code like this to dequeue and populate a cell:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    // This is the line that will change
    let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell") as! CustomTableViewCell

    // Populate the cell here
    
    return cell
}

// Now, the one line changes and we have this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueCell(ofType: CustomTableViewCell.self)

    // Populate the cell here
    
    return cell
}

So, what's happening in these extensions? Lets deconstruct the second extension for a better understanding. Here it is again with more explanation:

/*
The function signature is standard for generics with one exception. The argument is of type `T.Type`. Using `.Type` indicates the function is expecting an actual type, not an instance of the type.
*/
func dequeueCell<T: UITableViewCell>(ofType type: T.Type) -> T {
   /*
   String provides this initializer that gets the name of a type as a String. 
   Note that when passing a type into a function, the type must be suffixed with `.self`.
   */
   let cellName = String(describing: T.self)
        
   /*
   This is the call to the standard UITableView function we are wrapping. It is cast to the type provided to this function. Otherwise the object returned is considered to be of type UITableViewCell by the compiler. Doing the cast here simpilfies code at the call site for this function.
   */
   return dequeueReusableCell(withIdentifier: cellName) as! T
}

Here's the call site code with a little more explanation:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    /*
    Again, when passing a type into a function rather than an instance, use the type name with the suffix `.self`. The standard UITableView function returns a UITableViewCell object that must typically be cast to the subclass type before working with the object. But our extension function performs the cast for us, so the cell will be of type CustomTableViewCell already.
    */
    let cell = tableView.dequeueCell(ofType: CustomTableViewCell.self)

    // Populate the cell here
    
    return cell
}