Useful (and obscure!) Foundation types in Swift

Useful (and obscure!) Foundation types in Swift

The Foundation provides you a lot of the bread-and-butter needed for your daily iOS development, ranging from structures like Data all the way to complete APIs like URLSession. But as it turns out, we only use a fraction of what Foundation offers.

There's a bunch of Foundation types that are so situational that people doubt they even existed in the first place! In fact, they are so rarely mentioned as solutions to daily problems that developers may end up coding things that may already exist in these SDKs. But they do exist -- and although some of them are really old, most of these types are still very useful. Let's take a look at some of them!

NSScanner

NSScanner can progressively extract numbers and strings from a base string, similarly to how scanf works in C:

public func extractIntsFrom(string: String) -> [Int] {
    var result: [Int] = []
    let scanner = Scanner(string: string)
    // Jump over everything that isn't a number
    scanner.charactersToBeSkipped = CharacterSet.decimalDigits.inverted
    var pointer: Int = 0
    while scanner.isAtEnd == false {
        if scanner.scanInt(&pointer) {
            result.append(pointer)
        }
    }
    return result
}

let string = "1M very L337 700"
let ints = extractIntsFrom(string: string)
// [1, 337, 700]

The scanner API has many variations like scanString, scanDouble and scanHexInt, and can be configured to scan at specific locations in a string or to take case sensitivity in consideration. This is a more direct and performant solution for searching occurrences of a string when you're looking for specific patterns such as something that resembles a number instead of a concrete match.

You might notice that this API requires pointers which is an unfortunate side-effect of this being an old Obj-C API, but you can always abstract these types under extensions to make it look better for you and your colleagues.

NSCountedSet

There are many problems in programming that requires you to keep track of the quantity of a certain element, like the classic anagram interview problem:

func isAnagram(_ first: String, _ second: String) -> Bool {
    guard first.count == second.count else {
        return false
    }
    var dict = [Character: Int]()
    for character in first {
        firstDict[character, default: 0] += 1
    }
    for character in second {
        dict[character, default: 0] -= 1
        if dict[character] == 0 {
            dict[character] = nil
        }
    }
    return dict.isEmpty
}

This is easy to solve with dictionaries with Int values, but we don't need to, because that's exactly what NSCountedSet does.

func isAnagram(_ first: String, _ second: String) -> Bool {
    guard first.count == second.count else {
        return false
    }
    let countedSet = NSCountedSet(array: Array(first))
    for character in second {
        countedSet.remove(c)
    }
    return countedSet.count == 0
}

Elements can be repeatedly added to the set with countedSet.add(element) and have their counts inspected with countedSet.count(element). If the count reaches zero, the element is removed from the set. However, just like a regular Set in Swift, you can only add one of each element to the set, which is great to avoid duplication.

NSCache

NSCache is a collection type that works similarly to a dictionary, but it has two important key differences. First, it does not copy the key objects that are put into it, and second, it automatically removes entries from itself it the system is running out of memory.

The actual policies for removing entries are cryptic, but if you have objects that expensive to create, usage of NSCache can be a very system friendly alternative to regular dictionary caches. Here's how it can be used for caching UIImages:

final class ImageDownloader {
    let client: HTTPClient
    let cache = NSCache<NSString, NSData>()

    init(client: HTTPClient) {
        self.client = client
    }

    func load(imageUrl: URL, intoImageView imageView: UIImageView) {
       let key = imageUrl.absoluteString as NSString
        func apply(data: NSData) {
            let image = UIImage(data: data as Data)
            imageView.image = image
        }
        if let cachedData = cache.object(forKey: key) {
            apply(data: cachedData)
            return
        } else {
            client.data(from: imageUrl) { data in
                cache.setObject(data as NSData, forKey: key)
                apply(data: data as NSData)
            }
        }
    }
}

In this case, we can use NSCache as a very simple way of not having to download the same images over and over.

NSOrderedSet

Sets are great for keeping track of elements when duplication doesn't matter, but because set access is O(1), there's no guarantee that they will be ordered when you do so. As the name implies, NSOrderedSet works like a regular Set with the exception that the elements are ordered.

let set = NSMutableOrderedSet()
set.add(1)
set.add(4)
set.add(1)
set.add(1)
set.add(1)
set.add(6)
set.add(4)
set.add(6)
for a in set {
    print(a)
    // 1, 4, 6
}

Because this is an old type you'll notice that there's no generics in it -- all operations are based on the Any type. This isn't very Swifty, so you'll probably want to wrap it in a Swift type:

class OrderedSet<T: Hashable>: Sequence {
    private let _set = NSMutableOrderedSet()
    init() {}

    func makeIterator() -> NSFastEnumerationIterator {
        return _set.makeIterator()
    }

    func add(_ element: T) {
        _set.add(element)
    }

    func remove(_ element: T) {
        _set.remove(element)
    }
}

Like NSCountedSet, NSOrderedSet is a great tool when you want to have array-like functionality without allowing duplicate elements in it.

NSByteCountFormatter

There are tons of obscure formatters in Foundation, but ByteCountFormatter is one I specifically like. It formats byte counts into human readable file size formats, which is useful if your app downloads content as you don't need to calculate these values manually.

let bytes = 1024 * 1024
let formatter = ByteCountFormatter()
formatter.allowsNonnumericFormatting = false // Uses '0' instead of 'Zero'
formatter.countStyle = .file
let string = formatter.string(fromByteCount: Int64(bytes))
// 1 MB

NSDataDetector

NSDataDetector is similar to NSScanner, with the difference that it extracts contextual data from strings like phone numbers, addresses and links.

let string = "I write for https://swiftrocks.com and my phone is 555-111-111."
let range = NSRange(0..<string.count)

let types: NSTextCheckingResult.CheckingType = [.link, .phoneNumber]
let dataDetector = try NSDataDetector(types: types.rawValue)
dataDetector.enumerateMatches(in: string, options: [], range: range) { (match, _, _) in
    guard let match = match else { return }
    switch match.resultType {
    case .link:
        print(match.url!)
        // https://swiftrocks.com
    case .phoneNumber:
        print(match.phoneNumber!)
        // "555-111-111"
    default:
        return
    }
}

The downside is that it relies on Foundation's regex API, once again, because it's old. This is again not very Swifty, but NSDataDetector is still a very solid type if you need to extract such information.

CFBinaryHeap

The Swift Standard Library has a notorious lack of data structures (which the new Swift Algorithms package is trying to improve!), but you can find Tree and Heap implementations in Foundation in the shape of CFTree and CFBinaryHeap. Heaps are a very efficient way to implement priority queue structures, and since they're very extensive to code, CFBinaryHeap can save you some time.

I'm adding it to the list because it's obscure and cool, but I have to say that it's unfortunately very hard to use. CFBinaryHeap is a C CoreFoundation class that isn't bridged to Foundation, so you won't be able to use without managing tons of pointers. Your best bet would be to write or use a wrapper like MCBinaryHeap.

let array = [8,3,5,4,1]
let heap = MCBinaryHeap(array: array)
heap?.popMinimumObject() // 1
heap?.popMinimumObject() // 3
heap?.popMinimumObject() // 4
heap?.popMinimumObject() // 5
heap?.popMinimumObject() // 8

Priority queues are popular in graph searching problems and can be used to bring efficiency to anything where you need to keep track of the smallest or largest elements in an unordered array. It might take a while for you to find a use-case for priority queues or graph algorithms in general, but once you do, you'll realize that anything else would have been a giant performance issue. One example where I have personally applied graph algorithms in Swift is to resolve the dependencies of our app's dependency injection system for features.

There are tons of useful types in Foundation and UIKit that we don't hear much of, but knowing them can help you prevent writing unnecessary code. Have fun with these types!

References and Good reads

NSScanner
NSCountedSet
NSOrderedSet
NSByteCountFormatter
NSDataDetector
NSCache
CFBinaryHeap