-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathuseful-obscure-foundation-types-in-swift.html
412 lines (365 loc) · 20.3 KB
/
useful-obscure-foundation-types-in-swift.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://use.fontawesome.com/afd448ce82.js"></script>
<!-- Meta Tag -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- SEO -->
<meta name="author" content="Bruno Rocha">
<meta name="keywords" content="Software, Engineering, Blog, Posts, iOS, Xcode, Swift, Articles, Tutorials, OBJ-C, Objective-C, Apple">
<meta name="description" content="The <code>Foundation</code> provides you a lot of the bread-and-butter needed for your daily iOS development, ranging from structures like <code>Data</code> all the way to complete APIs like <code>URLSession</code>. But as it turns out, we only use a fraction of what <code>Foundation</code> offers.">
<meta name="title" content="Useful (and obscure!) Foundation types in Swift">
<meta name="url" content="https://swiftrocks.com/useful-obscure-foundation-types-in-swift">
<meta name="image" content="https://swiftrocks.com/images/thumbs/thumb.jpg?4">
<meta name="copyright" content="Bruno Rocha">
<meta name="robots" content="index,follow">
<meta property="og:title" content="Useful (and obscure!) Foundation types in Swift"/>
<meta property="og:image" content="https://swiftrocks.com/images/thumbs/thumb.jpg?4"/>
<meta property="og:description" content="The <code>Foundation</code> provides you a lot of the bread-and-butter needed for your daily iOS development, ranging from structures like <code>Data</code> all the way to complete APIs like <code>URLSession</code>. But as it turns out, we only use a fraction of what <code>Foundation</code> offers."/>
<meta property="og:type" content="website"/>
<meta property="og:url" content="https://swiftrocks.com/useful-obscure-foundation-types-in-swift"/>
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="https://swiftrocks.com/images/thumbs/thumb.jpg?4"/>
<meta name="twitter:image:alt" content="Page Thumbnail"/>
<meta name="twitter:title" content="Useful (and obscure!) Foundation types in Swift"/>
<meta name="twitter:description" content="The <code>Foundation</code> provides you a lot of the bread-and-butter needed for your daily iOS development, ranging from structures like <code>Data</code> all the way to complete APIs like <code>URLSession</code>. But as it turns out, we only use a fraction of what <code>Foundation</code> offers."/>
<meta name="twitter:site" content="@rockbruno_"/>
<!-- Favicon -->
<link rel="icon" type="image/png" href="images/favicon/iconsmall2.png" sizes="32x32" />
<link rel="apple-touch-icon" href="images/favicon/iconsmall2.png">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Source+Sans+3:ital,wght@0,200..900;1,200..900&display=swap" rel="stylesheet">
<!-- Bootstrap CSS Plugins -->
<link rel="stylesheet" type="text/css" href="css/bootstrap.css">
<!-- Prism CSS Stylesheet -->
<link rel="stylesheet" type="text/css" href="css/prism4.css">
<!-- Main CSS Stylesheet -->
<link rel="stylesheet" type="text/css" href="css/style48.css">
<link rel="stylesheet" type="text/css" href="css/sponsor4.css">
<!-- HTML5 shiv and Respond.js support IE8 or Older for HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://swiftrocks.com/useful-obscure-foundation-types-in-swift"
},
"image": [
"https://swiftrocks.com/images/thumbs/thumb.jpg"
],
"datePublished": "2019-06-26T18:00:00+00:00",
"dateModified": "2020-10-18T18:00:00+02:00",
"author": {
"@type": "Person",
"name": "Bruno Rocha"
},
"publisher": {
"@type": "Organization",
"name": "SwiftRocks",
"logo": {
"@type": "ImageObject",
"url": "https://swiftrocks.com/images/thumbs/thumb.jpg"
}
},
"headline": "Useful (and obscure!) Foundation types in Swift",
"abstract": "The <code>Foundation</code> provides you a lot of the bread-and-butter needed for your daily iOS development, ranging from structures like <code>Data</code> all the way to complete APIs like <code>URLSession</code>. But as it turns out, we only use a fraction of what <code>Foundation</code> offers."
}
</script>
</head>
<body>
<div id="main">
<!-- Blog Header -->
<!-- Blog Post (Right Sidebar) Start -->
<div class="container">
<div class="col-xs-12">
<div class="page-body">
<div class="row">
<div><a href="https://swiftrocks.com">
<img id="logo" class="logo" alt="SwiftRocks" src="images/bg/logo2light.png">
</a>
<div class="menu-large">
<div class="menu-arrow-right"></div>
<div class="menu-header menu-header-large">
<div class="menu-item">
<a href="blog">blog</a>
</div>
<div class="menu-item">
<a href="about">about</a>
</div>
<div class="menu-item">
<a href="talks">talks</a>
</div>
<div class="menu-item">
<a href="projects">projects</a>
</div>
<div class="menu-item">
<a href="software-engineering-book-recommendations">book recs</a>
</div>
<div class="menu-item">
<a href="games">game recs</a>
</div>
<div class="menu-arrow-right-2"></div>
</div>
</div>
<div class="menu-small">
<div class="menu-arrow-right"></div>
<div class="menu-header menu-header-small-1">
<div class="menu-item">
<a href="blog">blog</a>
</div>
<div class="menu-item">
<a href="about">about</a>
</div>
<div class="menu-item">
<a href="talks">talks</a>
</div>
<div class="menu-item">
<a href="projects">projects</a>
</div>
<div class="menu-arrow-right-2"></div>
</div>
<div class="menu-arrow-right"></div>
<div class="menu-header menu-header-small-2">
<div class="menu-item">
<a href="software-engineering-book-recommendations">book recs</a>
</div>
<div class="menu-item">
<a href="games">game recs</a>
</div>
<div class="menu-arrow-right-2"></div>
</div>
</div>
</div>
<div class="content-page" id="WRITEIT_DYNAMIC_CONTENT">
<!--WRITEIT_POST_NAME=Useful (and obscure!) Foundation types in Swift-->
<!--WRITEIT_POST_HTML_NAME=useful-obscure-foundation-types-in-swift-->
<!--WRITEIT_POST_SITEMAP_DATE_LAST_MOD=2020-10-18T18:00:00+02:00-->
<!--WRITEIT_POST_SITEMAP_DATE=2019-06-26T18:00:00+00:00-->
<!--Add here the additional properties that you want each page to possess.-->
<!--These properties can be used to change content in the template page or in the page itself as shown here.-->
<!--Properties must start with 'WRITEIT_POST'.-->
<!--Writeit provides and injects WRITEIT_POST_NAME and WRITEIT_POST_HTML_NAME by default.-->
<!--WRITEIT_POST_SHORT_DESCRIPTION=The <code>Foundation</code> provides you a lot of the bread-and-butter needed for your daily iOS development, ranging from structures like <code>Data</code> all the way to complete APIs like <code>URLSession</code>. But as it turns out, we only use a fraction of what <code>Foundation</code> offers.-->
<title>Useful (and obscure!) Foundation types in Swift</title>
<div class="blog-post">
<div class="post-title-index">
<h1>Useful (and obscure!) Foundation types in Swift</h1>
</div>
<div class="post-info">
<div class="post-info-text">
Published on 26 Jun 2019
</div>
</div>
<p>The <code>Foundation</code> provides you a lot of the bread-and-butter needed for your daily iOS development, ranging from structures like <code>Data</code> all the way to complete APIs like <code>URLSession</code>. But as it turns out, we only use a fraction of what <code>Foundation</code> offers.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>There's a bunch of Foundation types that are <b>so situational</b> 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 <i>already</i> 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!</p>
<h2>NSScanner</h2>
<p><code>NSScanner</code> can progressively extract numbers and strings from a base string, similarly to how <code>scanf</code> works in C:</p>
<pre><code>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]</code></pre>
<p>The scanner API has many variations like <code>scanString</code>, <code>scanDouble</code> and <code>scanHexInt</code>, 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.</p>
<p>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.</p>
<h2>NSCountedSet</h2>
<p>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:</p>
<pre><code>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
}</code></pre>
<p>This is easy to solve with dictionaries with <code>Int</code> values, but we don't need to, because that's exactly what <code>NSCountedSet</code> does.</p>
<pre><code>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
}</code></pre>
<p>Elements can be repeatedly added to the set with <code>countedSet.add(element)</code> and have their counts inspected with <code>countedSet.count(element)</code>. If the count reaches zero, the element is removed from the set. However, just like a regular <code>Set</code> in Swift, you can only add one of each element to the set, which is great to avoid duplication.</p>
<h2>NSCache</h2>
<p><code>NSCache</code> is a collection type that works similarly to a dictionary, but it has two important key differences. First, it <b>does not</b> copy the key objects that are put into it, and second, it <b>automatically removes entries</b> from itself it the system is running out of memory.</p>
<p>The actual policies for removing entries are cryptic, but if you have objects that expensive to create, usage of <code>NSCache</code> can be a very system friendly alternative to regular dictionary caches. Here's how it can be used for caching <code>UIImages</code>:</p>
<pre><code>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)
}
}
}
}</code></pre>
<p>In this case, we can use <code>NSCache</code> as a very simple way of not having to download the same images over and over.</p>
<h2>NSOrderedSet</h2>
<p>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, <code>NSOrderedSet</code> works like a regular <code>Set</code> with the exception that the elements are <b>ordered</b>.</p>
<pre><code>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
}</code></pre>
<p>Because this is an old type you'll notice that there's no generics in it -- all operations are based on the <code>Any</code> type. This isn't very Swifty, so you'll probably want to wrap it in a Swift type:</p>
<pre><code>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)
}
}</code></pre>
<p>Like <code>NSCountedSet</code>, <code>NSOrderedSet</code> is a great tool when you want to have array-like functionality without allowing duplicate elements in it.</p>
<h2>NSByteCountFormatter</h2>
<p>There are tons of obscure formatters in Foundation, but <code>ByteCountFormatter</code> 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.</p>
<pre><code>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</code></pre>
<h2>NSDataDetector</h2>
<p><code>NSDataDetector</code> is similar to <code>NSScanner</code>, with the difference that it extracts <b>contextual</b> data from strings like phone numbers, addresses and links.</p>
<pre><code>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
}
}</code></pre>
<p>The downside is that it relies on Foundation's regex API, once again, because it's old. This is again not very Swifty, but <code>NSDataDetector</code> is still a very solid type if you need to extract such information.</p>
<h2>CFBinaryHeap</h2>
<p>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 <code>CFTree</code> and <code>CFBinaryHeap</code>. Heaps are a very efficient way to implement priority queue structures, and since they're very extensive to code, <code>CFBinaryHeap</code> can save you some time.</p>
<p>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. <code>CFBinaryHeap</code> 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 <a href="https://github.com/matthewcheok/MCBinaryHeap">MCBinaryHeap</a>.</p>
<pre><code>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</code></pre>
<div class="sponsor-article-ad-auto hidden"></div>
<p>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.</p>
<p>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!</p>
<h2>References and Good reads</h2>
<a href="https://developer.apple.com/documentation/foundation/nsscanner">NSScanner</a>
<br>
<a href="https://developer.apple.com/documentation/foundation/nscountedset">NSCountedSet</a>
<br>
<a href="https://developer.apple.com/documentation/foundation/nsorderedset">NSOrderedSet</a>
<br>
<a href="https://developer.apple.com/documentation/foundation/nsbytecountformatter">NSByteCountFormatter</a>
<br>
<a href="https://developer.apple.com/documentation/foundation/nsorderedset">NSDataDetector</a>
<br>
<a href="https://developer.apple.com/documentation/foundation/nscache">NSCache</a>
<br>
<a href="https://developer.apple.com/documentation/corefoundation/cfbinaryheap">CFBinaryHeap</a>
<br>
</div></div>
<div class="blog-post footer-main">
<div class="footer-logos">
<a href="https://swiftrocks.com/rss.xml"><i class="fa fa-rss"></i></a>
<a href="https://twitter.com/rockbruno_"><i class="fa fa-twitter"></i></a>
<a href="https://github.com/rockbruno"><i class="fa fa-github"></i></a>
</div>
<div class="footer-text">
© 2025 Bruno Rocha
</div>
<div class="footer-text">
<p><a href="https://swiftrocks.com">Home</a> / <a href="blog">See all posts</a></p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Blog Post (Right Sidebar) End -->
</div>
</div>
</div>
<!-- All Javascript Plugins -->
<script type="text/javascript" src="js/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<script type="text/javascript" src="js/prism4.js"></script>
<!-- Main Javascript File -->
<script type="text/javascript" src="js/scripts30.js"></script>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-H8KZTWSQ1R"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-H8KZTWSQ1R');
</script>
</body>
</html>