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.

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?