First entry into the “Swift 2 to 3” challenge comes from Cristian Filipov, who brings me a routine to extract words from phone numbers.
There are several points of interest in this port, which I’ll get to in just a second, but since his code didn’t arrive with the vital isWord(string:)
function, I had to build one myself.
Building isWord
It’s in Cocoa because I find that writing OS X Command Line apps are the most congenial way to build small Swift 3 apps. At this point, playgrounds (as you see, his code was originally intended for playground use with the in-line markup) do not support Swift 3. That should change within the next two weeks but I didn’t want to wait for WWDC to start showing tricks of the transition trade.
So here’s the function in question:
import Cocoa func isWord(string: String) -> Bool { // If it's a number, nope if let _ = Int(string) { return false } // Use NSSpellChecker to find misspelling // if it fails, the string is proper let range = NSSpellChecker.shared() .checkSpelling(of: string, startingAt: 0) return range.location == NSNotFound }
Currying
Christian’s code heavily depends on currying, which is one of the easiest points of transition in Swift 2 to Swift 3 code. Here’s an example:
func transform<T: Hashable, V>(dict: [T:V])(element: T) -> V? { return dict[element] }
Establishing the dictionary is curried from looking up an element in the dictionary. How to fix?
- Place an arrow between each curried element
- Use “
return { argument in...}
” closures for each curry
Here’s the fixed example
func transform<T: Hashable, V>(dict: [T:V]) -> (element: T) -> V? { return { element in return dict[element] } }
Mandated Function Parens
This is one of the Swift 3 changes I was really excited to see adopted. Prior to Swift 3, you could specify types of T -> U
. Now it’s uniformly (T) -> U
. So this example:
func not<T>(pred: T -> Bool)(e: T) -> Bool { return !pred(e) }
becomes the following (with some bonus curry update):
func not<T>(pred: (T) -> Bool) -> (e: T) -> Bool { return { e in return !pred(e) } }
Taking Charge of Parameter Labels
With Swift 3’s new mandated first parameter labels, it’s time to toss away duplicated names and add in first parameter labels that normally defaulted away. Starting here:
func phoneWords(minLength minLength: Int)(number: String) -> [String] { if number.characters .filter(("2"..."9").contains) .count >= minLength { return phoneWords(number) } else { return [number] } }
Ending up here:
func phoneWords(minLength: Int) -> (number: String) -> [String] { return { number in if number.characters .filter(("2"..."9").contains) .count >= minLength { return phoneWords(number: number) } else { return [number] } } }
Other tweaks
I had to fix the isSeparator
closure (needed a closure and a first parameter), and add in one or two more parentheses around parameter types but other than the mystifying spellchecker that insists that “eloydqs” is a word just like “flowers”, everything seems to work beautifully
You can find the diffed/forked gist here.
Got a short standalone Swift 2.2 code sample that needs updating to new syntax and concepts? Send it along and I may do a post on it. Please no requirements outside the sample, and keep the code short. Code that introduces new kinds of issues (for example, currying is pretty much already covered now) will be preferred.
9 Comments
Wow, don’t know how I missed that. Updated the gist to include the isWord function I forgot to include: https://gist.github.com/cfilipov/1ece2ca2973a4fa4c712
You can also use UIKit’s text checking, would simplify
Yeah, I like your method better, just felt the need to correct the accidental omission.
By the way, I thought this was no longer supposed to be possible in Swift 3:
.map(String.init)
Doesn’t that essentially make use of currying?
“Curried function syntax has been deprecated, and is slated to be removed in Swift 3.0.” Not all currying functionality and not this particular use, just the declaration functionality syntax.
I swear I saw mention of this specific use being deprecated too. But I can’t find it now so I must be mistaken.
Erica, how can I filter a string in Swift 3.0 for only English Words and names? Any points in the right direction?
Depends if it is single words, multiple words, case insensitive, dictionary membership, etc. Cocoa and Cocoa Touch have a number of classes that can help including NSLinguisticTagger and NSSpellChecker
Thanks Erica, will check those out!