Have you found yourself stuck trying to make sense of a framework or library and wished you could see the source code? Apple does not share the source for UIKit
but if the method you are struggling to understand is part of the Swift standard library you are in luck.
Accessing the GitHub repository
Apple publishes the source code for the Swift programming language, including the standard library, in a public GitHub repository:
You may find browsing the repository on GitHub is enough for a quick look but I like to clone and download a local copy:
$ mkdir swift-source
$ cd swift-source/
$ git clone https://github.com/apple/swift.git
Cloning into 'swift'...
remote: Enumerating objects: 915646, done.
remote: Total 915646 (delta 0), reused 0 (delta 0), pack-reused 915646
Receiving objects: 100% (915646/915646), 415.74 MiB | 6.42 MiB/s, done.
Resolving deltas: 100% (742664/742664), done.
Checking out files: 100% (16802/16802), done.
You can drop the swift
folder into an Xcode project to browse, but that leaves my Mac with a spinning beach ball for several minutes. I prefer to open the folder with BBEdit, which is both faster and has better multi-file search tools.
You’ll find the source for the standard library mostly in the core
sub-directory:
$ cd swift/stdlib/public/core
It’s worth taking the time to browse the source code. I find it well documented and informative. Let’s see an example where the source code helps our understanding.
A question about String
I recently wrote about testing for empty strings in Swift. In that post, I claim you should use isEmpty
rather than count
to avoid iterating over the entire string. A reader asked me if that was true? My claim came from the String
documentation:
To check whether a string is empty, use its isEmpty property instead of comparing the length of one of the views to 0. Unlike with isEmpty, calculating a view’s count property requires iterating through the elements of the string.
Can we do better than trust the documentation? Can we use the standard library source code to see how isEmpty
and count
work for strings?
StringCharacterView
It’s not always obvious where to look in the standard library source code. Searching the core
directory will eventually lead you to the right place but can also take you down some rabbit holes. There is a String.swift
file but it doesn’t contain either isEmpty
or count
.
Looking back at my Swift String cheat sheet you might remember that Swift 4 made the default view of a string a collection of characters. There is a StringCharacterView.swift
file in the core
directory which extends String
to adopt the BidirectionalCollection
protocol:
// StringCharacterView.swift
extension String: BidirectionalCollection {
The StringCharacterView
doesn’t define isEmpty
but we do find count
:
public var count: Int {
return distance(from: startIndex, to: endIndex)
}
The distance
method calls through to an internal method which we still need to find, but it’s a start:
public func distance(from start: Index, to end: Index) -> IndexDistance {
return _distance(from: start, to: end)
}
BidirectionalCollection
A bidirectional collection extends a collection to add backward traversal over the collection:
// BidirectionalCollection.swift
public protocol BidirectionalCollection: Collection
where SubSequence: BidirectionalCollection, Indices:
BidirectionalCollection {
This is where we find the implementation of _distance
and can see it does iterate (forward or backward) over the collection:
internal func _distance(from start: Index, to end: Index) -> Int {
var start = start
var count = 0
if start < end {
while start != end {
count += 1
formIndex(after: &start)
}
}
else if start > end {
while start != end {
count -= 1
formIndex(before: &start)
}
}
return count
}
Let’s keep going and see if we can find isEmpty
.
Collection
The Collection
protocol builds on Sequence
and adds the startIndex
and endIndex
properties together with our friends isEmpty
and count
:
// Collection.swift (details omitted)
public protocol Collection: Sequence {
var startIndex: Index { get }
var endIndex: Index { get }
var isEmpty: Bool { get }
var count: Int { get }
The comments in the source code give us some more hints:
For collections that don’t conform to
RandomAccessCollection
, accessing thecount
property iterates through the elements of the collection.
and for count
there is this warning:
Complexity: O(1) if the collection conforms to
RandomAccessCollection
; otherwise, O(n), where n is the length of the collection.
The comments confirm what we understood about String
but also tell us why this is the case. What matters is whether the collection conforms to RandomAccessCollection
. A type like Array
allows for random access, but String
does not. See RandomAccessCollection for the full list.
Further down the Collection.swift
file we find a protocol extension which finally gives us the default implementation of isEmpty
:
public var isEmpty: Bool {
return startIndex == endIndex
}
So for a String
, we are better off using isEmpty
rather than count
.
Try it yourself
I hope you were able to follow along. Digging into the source code has helped my understanding of how the basic types and protocols fit together. Give it a try the next time you have a question about the standard library.
Learn More
I recommend you watch the talk on Understanding the Standard Library by Paul Hudson. He has lots of helpful hints on navigating and understanding the source code: