Dan Rosenstark, author of MIDI Designer, on Tech & Music

Dan Rosenstark

Dan Rosenstark, Author & CEO of MIDI Designer, muses about all things tech. Particularly: Notes on software development in Swift, Objective-C, and many non-Apple languages. Also: lots of random technology notes on OS X and iOS.

Showing all posts tagged "Swift3"

Just Because It Compiles: More Notes from Swift 3 Upgrade

This is the method signature for the superclass:

public func sendRequest<T: Request>(request: T, handler: (Result<T.Response, Error>) -> Void) -> NSURLSessionDataTask? {

and this is in the subclass

override func sendRequest<T : RequestType>(request: T, handler: (Result<T.Response, PayLib.Error>) -> Void) -> NSURLSessionDataTask? {

and in Swift 3 this got translated to be the same, except for some minor changes:

open func sendRequest<T: Request>(_ request: T, handler: @escaping (Result<T.Response, Error>) -> Void) -> URLSessionDataTask? {

Subclass:

override func sendRequest<T : RequestType>(_ request: T, handler: @escaping (Result<T.Response, PayLib.Error>) -> Void) -> URLSessionDataTask? ```

Now that was good enough for Swift 2. In Swift 3, a call to this method resulted in a crash with no interesting information.

The reason was the mismatch between RequestType and Request (RequestType is the protocol which the Request Protocol extends). So the fix was merely to have the signatures match exactly.

override func sendRequest<T : Request>(_ request: T, handler: @escaping (Result<T.Response, PayLib.Error>) -> Void) -> URLSessionDataTask? {

I'm not sure that there's a point here, except that: Just because Xcode likes it, it compiles and it works in Swift 2 doesn't mean that it won't cause a runtime crash in Swift 3. Or something like that: check everything by hand/eye!

Optional Block Parameters and Fix-Its

Consider this fix-it:



Now if this happens to you -- as it happened to me -- as part of a massive code conversion to Swift 3, you might be tempted to follow the fix it. Then your code would read like this:

var blockWithOptionalParam: ((NSString?)->())?

func assignBlockWithNonOptionalParameter() {
let tempBlock: (NSString)->() = {
print("count of string \($0.appending("appendMe"))")
}
blockWithOptionalParam = tempBlock as! ((NSString?) -> ())
}

This might be fine, but would result in a crash if you ever passed a nil to your blockWithOptionalParam. The only safe thing to do, really, is to ignore the fix it and handle the optional properly:

func assignBlockWithNonOptionalParameter() {
let tempBlock: (NSString?)->() = {
guard let text = $0 else { return }
print("count of string \(text.appending("appendMe"))")
}
blockWithOptionalParam = tempBlock
}


So What?
For some other cases -- and I'm not sure what the difference is, yet -- Xcode always suggests casting the block rather than fixing the optional in the block. I'm unable to show this example in the lab, but it does show up in the codebase I'm working on currently.



Lazy Array in Swift

Obviously if you google "lazy array in Swift," you'll find out the right way to do this, which uses NSPointerArray and whatever else. As you should.

But sometimes you want to make your own to see how far you can get in half an hour with generics. Here's my naive implementation.

import Foundation

private class WeakWrapperAnyObject> {
weak var thing: T?
init(_ thing:T) {
self.thing = thing
}
}

struct WeakArrayAnyObject> : Sequence {
private var array: [WeakWrapper<T>] = []
var count: Int { return objects.count }
init(_ objects: [T]) {
append(objects)
}
var objects: [T] {
get {
let retVal: [T?] = array.map { $0.thing != nil ? $0.thing : nil }
return retVal.flatMap { $0 }
}
}
mutating func append(_ object: T) {
array.append(WeakWrapper(object))
}
mutating func append(_ objects: [T]) {
array.append(contentsOf: objects.map { WeakWrapper($0) })
}
mutating func compress() {
array = array.filter{ $0.thing != nil }
}
// MARK: - Sequence Protocol methods
typealias Iterator = IndexingIterator<Array<T>>
func makeIterator() -> Iterator {
return objects.makeIterator()
}
}

let firstObject = NSString(string: "one")
var things = WeakArray([firstObject, NSString(string: "two"), NSString(string: "three")])
things.count
for thing in things {
print("Just one \(thing)")
}
things.compress()

Of course, I did have to look some stuff up, particularly to understand how easy Sequence was to implement (since I already have an array).