Swift: Cocoaphobia

It’s funny how cocoaphobic some people are starting to get. Earlier today, an irc buddy was looking for a string subscripting solution. I offered him this.

extension String {
    subscript (aRange: Range) -> String {
        get {
            var range = max(0, aRange.startIndex)...
                min(countElements(self) - 1, aRange.endIndex)
            return self.bridgeToObjectiveC().substringWithRange(
                NSRange(range))
        }
    }
}

Immediately he recoiled (in text, of course). “I was trying to find an approach that didn’t use the bridge to Obj-C,” he said. After all, he pointed out, what happens when Swift is ported to systems that aren’t Cocoa-based. “I think in terms of long term portability,” he told me. “Swift will be on non-Apple platforms eventually and may or may not bring the baggage of the Apple APIs with them.  I know I’m an old codger when it comes to some of this stuff. I can’t forget the Java problems caused when I was relying on APIs that were sun.* instead of java.*.”

I googled around a bit and found a number of similar solutions that looked more like this:

extension String {
    subscript (aRange: Range) -> String {
        get {
            let start = advance(self.startIndex, aRange.startIndex)
            let end = advance(self.startIndex, aRange.endIndex)
            return self[Range(start: start, end: end)]
        }
    }
}

Lily B explained how this works.  This extension converts from a Range<Int> to a Range<String.Index>, so it calls the pre-existing subscript(Range<String.Index>)->String implementation. Unfortunately, this particular extension produced results that looked like this, which I didn’t think was a massive improvement on Cocoa.

Screen Shot 2014-06-16 at 7.01.51 PM

So, fine. I wrote up this.

extension String {
    subscript (aRange: Range) -> String {
        get {
            // bound the start
            let startValue = min(max(0, aRange.startIndex), countElements(self) - 1)
            var start = advance(self.startIndex, startValue)
            
            // bound the end
            let endValue = min(aRange.endIndex, countElements(self))
            var end = advance(self.startIndex, endValue)

            // return the substring
            return self[Range(start: start, end: end)]
        }
    }
}

After passing that around, I got generally negative feedback. Simon Y told me he’d rather the subscript fail with some loud noticeable complaint  for out of range errors, which he felt was more fitting in a type safe language. As Ken F. adds, “In Cocoa [@”foo” substringWithRange:NSMakeRange(-3,18)] will throw an exception.” So, the code became this, returning an optional type.

extension String {
    subscript (aRange: Range) -> String? {
        get {
            // Check range
            if ((aRange.startIndex < 0) || aRange.endIndex > countElements(self))
            {
                println("Error. Range [\(aRange.startIndex)..\(aRange.endIndex)] is out of bounds in string \"\(self)\".")
                return nil
            }
            
            // Return substring
            var start = advance(self.startIndex, aRange.startIndex)
            var end = advance(self.startIndex, aRange.endIndex)
            return self[Range(start: start, end: end)]
        }
    }
}

But the whole experience really got me thinking. Leaving aside issues of grapheme clusters vs unichars (thank you Ken), why do it all in pure Swift right now other than as an intellectual exercise? Is Foundation suddenly not going to apply in a few months? Should we no longer use UIView? Is there a Swift purity test that I’m missing?

I know this write-up has drifted all over the place but  thought this was both an interesting-enough code story as well as an intriguing set of questions that I’d thought I’d throw them out there for comment. Are you planning to avoid reliance on Foundation where possible or is Swift going to be the new mortar between your Foundation bricks?

5 Comments

  • I think there’s just something nice about a fresh start. Bringing in the bridge just doesn’t feel as refreshing.

    • It isn’t really a bridge though is it? Objective-C objects are native to Swift. Swift was partly built on top of the Objective-C runtime. I’ve hear through friends at WWDC that Apple developers have said that Objective-C will never go away. A lot of low level stuff you want to do will need Objective-C due to its ability to mix in C. To me a bridge suggests a translation between two mismatched languages. But that isn’t the case here since Swift was built specifically for interfacing with Objective-C objects.

  • Hobbling code readability and maintainability for the sake of a theoretic Foundation-free future of Swift is ridiculous.

    One very important point people forget – a framework is just an API from the perspective of your code. You can code to Foundation now and use that as the API to a portable environment in future. I’ve been applying that philosophy since the late 90’s on multiple legacy code porting projects. My most successful and public use of it was pp2mfc where the PowerPlant API was mirrored to allow you to compile portable code that hooked directly into MFC.

    My career spans the entire evolution of OO development and I’ve seen “code re-use” as a justification rise and fall. Being able to understand your and other’s code trumps reuse any day. Without readable code, you can’t tell if it’s doing the right thing.

  • Foundation is open source so there shouldn’t be any system which can’t use Foundation stuff even if Swift is ported to other platforms. Avoiding Cocoa API’s sounds ridiculous since nothing is known about the direction in which Swift APIs will develop. I am inclined to think that Foundation will eventually be ported to Swift with some changes to make it more Swift like. E.g. smarter use of Optionals and utilising multiple returns or enum types for error handling. Other than that I believe API names and use will remain quite similar. Apple will also likely provide tools for upgrading your code automatically, as they have done before. This will likely be a gradual process over many years.

  • A brilliant piece of writing, IMO. I’ve seen the same ridiculous notions flung about on GitHub. My favorite phrasing was “no NSisms”. People are writing in a language made by Apple for long term portability? What an utterly and completely asinine concept. Swift may make its way to non-Apple hardware but it’d be essentially useless without a good API backing it up.