-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathrss.xml
2127 lines (1507 loc) · 205 KB
/
rss.xml
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
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Bruno Rocha | SwiftRocks</title>
<description>SwiftRocks is Bruno Rocha's blog about Swift / iOS and the software engineering industry.</description>
<language>en-us</language>
<copyright>2019 Bruno Rocha</copyright>
<link>https://swiftrocks.com</link>
<atom:link href="https://swiftrocks.com/rss.xml" rel="self" type="application/rss+xml"/><item>
<title>Things that did (and didn't) contribute to Burnout Buddy's success</title>
<link>https://swiftrocks.com/things-that-did-and-didnt-contribute-to-burnout-buddys-success</link>
<guid>https://swiftrocks.com/things-that-did-and-didnt-contribute-to-burnout-buddys-success</guid>
<pubDate>Thu, 23 Jan 2025 13:00:00 GMT+1</pubDate>
<author>Bruno Rocha</author>
<description><![CDATA[
<p>Back in 2022 I launched <a href="https://burnoutbuddy.io/">Burnout Buddy</a>, and today the app has succeeded far beyond my expectations. Netting between $600 and $1000 each month as of writing, BB has been growing 100% organically with little to no effort on my part.</p>
<p>In this post, I'd like to lay out exactly what I've done that I believe contributed (and didn't contribute) to this growth, serving as documentation and inspiration for the indie dev community out there.</p>
<h2>Things that helped</h2>
<h3>Understanding ASO</h3>
<p>I cannot understate the value of having a good grasp of <b>App Store Optimization (ASO)</b>. The case is simple: It doesn't matter how good your app is, if you don't get eyes on it, it will never succeed.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>ASO refers to being strategic about how you assemble your app's store listing (keywords, name, subtitle, description, screenshots, etc) so that it ranks well when people search for keywords related to your app. In many cases what you actually want to do is <b>avoid</b> popular keywords in the beginning, focusing on less popular ones where you have more of a fighting chance until you get "popular" enough that you can try challenging the real ones. How and <i>when</i> you ask for reviews also plays a big role here as reviews also affect your app's rank.</p>
<p>I strongly recommend <a href="https://appfigures.com/">Appfigures</a> for learning and applying ASO for your apps. The owner, Ariel, has posted many videos explaining different strategies you can take, and that's how I got to know about it.</p>
<p>In my case, ASO was only time-intensive in the first few weeks following the app's launch. After it picked up some steam and became no.1 in a couple of important keywords, I was able to leave it alone and enjoy full organic growth ever since.</p>
<h3>I'm my app's primary user</h3>
<p>Most indie apps fail because they are trying to solve problems that don't exist. The devs come up with the solution <i>first</i>, and <i>then</i> try to find users who have a problem that match their solution. This rarely works.</p>
<p>The easiest way to avoid this is to ignore other people and just focus on <b>your own set of problems.</b> If you can manage to build something that would make your own life better, certainly you'll find other people who will also appreciate it.</p>
<p>In my case, I built Burnout Buddy because iOS's default Screen Time feature was too simple for me. I wanted to make more complex scenarios such as schedule or location based conditions, but iOS only allows you to setup simple time limits. You also can't do "strict" conditions where there's no way to disable the block once it goes into effect. I searched for other alternatives, but none of them were good enough for me. So I built my own!</p>
<p>Once my problem was solved, I figured out that most likely there were others out there who could also make use of it. I made the app public with zero expectations, and sure enough, there were tons of other people with the same problem I had.</p>
<p>Being my app's primary user also means that I'm perfectly positioned to know <b>which features the app should and shouldn't have.</b> I don't need things like user interviews, because again, I built this for myself. All I have to do is ask myself what I'd like the app to do, and the result is sure to also be a hit with others with the same problem the app aims to solve.</p>
<p>I attribute Pieter Level's <a href="https://readmake.com/">Make book</a> for helping me understand this concept. It's also a great resource for learning more about indie development and how to create successful products in general!</p>
<h3>No backend, everything happens client-side</h3>
<p>Another decision that I've made that massively simplified things for me is that everything happens on the client. There are no accounts or backend, and I gather zero data from the users.</p>
<p>This means I have no backend to manage, and most importantly, no monthly server costs. As long as Apple doesn't push iOS updates that break the APIs I use <a href="https://x.com/rockbruno_/status/1835791308746789344">(unfortunately happens a lot)</a>, I can trust that everything is working as it should and focus my attention on other things. People seem to really appreciate this too, since many apps nowadays have accounts for no reason other than wanting to hoard data which is really shady.</p>
<h3>The app just works</h3>
<p>After the first couple of releases, I spent a good amount of time building a good suite of tests and architecting the app so that it would be easy to expand and make it even more testable. This means I very rarely have to worry about whether or not I'll push something that will fundamentally break the app. Having no backend-related code also greatly helped here.</p>
<p>This doesn't mean that the app is bug-free (there are a bunch of SwiftUI issues I can't seem to solve, and Apple somehow manages to break their APIs on every iOS release as mentioned above), but when it comes to the core experience of the app, I can trust that everything works as it should. This saved a lot of testing / debugging time on my end and also made sure I almost never had to deal with support e-mails regarding broken features and such.</p>
<h3>I don't extort my users</h3>
<p>Burnout Buddy is a one-time $9.99 bucks purchase. For a long time it used to be $4.99 even.</p>
<p>Why this matters? Because most alternatives are stupidly expensive subscriptions. Most of them also don't have backends and have even less features than BB, why the hell are these apps subscription-based???</p>
<p>Some people justify that subscriptions are necessary even for "simple" apps like BB because of things like recurring support work. While I can see the point, I also think there are other ways to tackle these issues. I for example created a FAQ support page, and that reduced 99.9% of the support requests. I'm not trying to extort my users and I believe this was a strong factor for the app's success.</p>
<h2>Things that didn't help</h2>
<p>It would be naive of me to claim that everything went right. I've made a couple of bad decisions that worked against the app's success, and I wanted to document them as well.</p>
<h3>Thinking I could make it big without marketing</h3>
<p>Like I mentioned in the ASO section, it doesn't matter how good your app is. You need to get the word out, otherwise it will just not work.</p>
<p>There is a saying in tech that goes "if you build something good, people will follow". Whoever said this has absolutely never attempted to sell something. I'm as a tech nerd as it can get and I can safely say that when it comes to building businesses, marketing is a billion times more important than building the actual product!</p>
<p>Unfortunately for me, I hate doing marketing work. I'm fine with putting a sponsorship section on this blog, but reaching out to journalists and hustling on X / LinkedIn is really not my thing. This means that while thankfully I was able to do just enough of it to get some nice results in the beginning, the app is destined to die a slow death as it drops in ranking in the App Store and other similar apps manage to get their word out better than me. Marketing is something you have to do constantly, but unfortunately for me it's something I just don't want to do, so there will always be a hard cap to how far I can go with any given project alone.</p>
<h3>Making the app too cheap</h3>
<p>This will sound weird because I mentioned above that not extorting my users was a positive. But allow me to clarify this.</p>
<p>One thing I've learned the hard way is that you need to avoid <b>cheapskates</b> like the plague. This means people who expect nothing but the highest quality products, but at the same time are not willing to pay anything for it. You know when you see one because they behave like huge assholes and will do everything in their power to extract as much value from you as possible while giving nothing in return, much like the meme of a Karen screaming at the supermarket cashier because of some worthless coupon.</p>
<p>When Burnout Buddy was $4.99, I was constantly having my support e-mail being spammed by such people. They would constantly aggressively complain about different app features and demand refunds, often threatening that they would download a different app if I didn't help them (...why would I care about that?). A lot of these reports didn't even make sense, they were clearly people just searching for excuses to be an asshole and get free stuff. It was such a waste of my time that I even briefly considered abandoning the project entirely / pulling it from the App Store just so I wouldn't have to deal with them anymore.</p>
<p>It was only when I read someone complaining about the <i>exact same problem</i> on HackerNews that I realized what my issue was. It's not that giving support is a thankless job, <b>it's that the app was too cheap.</b> The cheapskates are attracted by free (or in this case, almost free) products. If you raise the price of your product <i>just slightly</i>, you can filter out these people without driving way the good (and kind) users.</p>
<p>After doing just that, these bizarre e-mails <b>completely vanished</b> without resulting in any loss of revenue. While I of course still get support requests every now and then, they are now all very polite and helpful, which makes everything a breeze!</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>In other words, the "fail" here is that I should've made the app cost $9.99 from the get-go to have filtered the cheapskates from the very beginning.</p>
<h3>Not gathering analytics</h3>
<p>This is an interesting one because it's both a good and a bad thing depending on how you look at it.</p>
<p>I mentioned above that having no accounts was a good thing because it made things easier on my side and was appreciated by the users. But it also meant that I had no information regarding how users were using the app. This made things harder for me because 1) I couldn't determine which features were more popular / worth expanding upon (and which ones weren't), and 2) when people reported bugs, I had no easy way to trace their steps in order to quickly reproduce the issue (or to confirm they misunderstood the app / were doing something wrong).</p>
<p>If I could go back, I would probably have gone for a solution that allowed me to gather analytics data for the above reasons.</p>
<h3>Using SwiftUI</h3>
<p>This is mostly out-of-topic for this post, so I'll keep it short. I decided to use SwiftUI for this project as a learning opportunity, and I sort of regret it. As mentioned in my <a href="https://swiftrocks.com/my-experience-with-swiftui ">SwiftUI vs UIKit post</a>, SwiftUI is good for simple apps, but awful for more complex ones. As BB grew and became more intricate, SwiftUI became more and more of an issue. The app today is full of dirty hacks and visual bugs that are impossible to solve (as of writing) because they originate from SwiftUI itself, in ways that are impossible for me to control without dumping the entire framework.</p>
]]></description>
</item>
<item>
<title>My Home Automation setup</title>
<link>https://swiftrocks.com/my-home-automation-setup</link>
<guid>https://swiftrocks.com/my-home-automation-setup</guid>
<pubDate>Sat, 4 Jan 2025 13:00:00 GMT+1</pubDate>
<author>Bruno Rocha</author>
<description><![CDATA[
<p>I recently upgraded my smart home hardware, and I felt like writing a post describing my current setup to serve as inspiration for those wanting to get started or just interested in home automation in general.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<h2>The Software</h2>
<p>I use <a href="https://www.home-assistant.io/">Home Assistant OS</a> like many others. The way I like to describe HA is that it's an Alexa on steroids. With an Alexa, you buy smart devices, link them with the Alexa, and then setup automations to control those devices based on conditions like time, weather, and so on. But the problem with Alexas is that 1) the devices <i>must</i> support Alexas specifically, and 2) the automations themselves are very limited, only allowing you to do simple things.</p>
<p>Home Assistant doesn't have such limitations. HA is <b>open-source</b> and has a thriving community, meaning you can find plugins that enable integrations for pretty much <b>anything</b> you can think of, and if you don't, you can build such plugins yourself assuming you have the programming chops to do it! HA is very extensible, and thus perfect for power users who want to set up complex automations or integrate unusual devices in unusual ways.</p>
<h2>The Hardware</h2>
<p>For a long time, my setup used to be a simple Raspberry Pi 3b and a SD card. The HA community tells you that this is a bad idea (The 3b is weak and SD cards can die if you use them too much), but in my experience this is fine as long as you don't have too many devices / automations / integrations. It's a good starting point, and it did the trick for me for a couple of years until I started wanting to do more complicated integrations.</p>
<p>Nowadays, I've retired the 3b in favor of a <b>Raspberry Pi 5 w/ 8GB RAM, with a 256GB official RPi NVMe SSD and enclosed on the Argon One V3 case.</b> This gives HA enough power and cooling to do everything that I need with ease.</p>
<div class="post-image">
<img src="https://i.imgur.com/CRUuh6Q.png">
</div>
<p>The way I run the server is via WiFi. The community says that this is also a bad idea and that you should use a wired connection to prevent latency issues, but I never had any such issues running HA via WiFi.</p>
<p>For voice control, I use an <b>Alexa</b>. The way this works is that HA has a plugin called <a href="https://www.home-assistant.io/integrations/emulated_hue/">Emulated Hue</a> which allows you to trick an Alexa into thinking your HA server is a Philips Hue hub, allowing you to expose your devices and scripts to the Alexa in order to make use of its voice features. But you can also pay for HA Cloud and enjoy the official "proper" Alexa integration, which I don't because I want to keep everything running on the local network.</p>
<p>I also have a <a href="https://www.amazon.se/-/en/ZBDongle-EFR32MG21-Coordinator-Universal-Assistant/dp/B0BRCWXMSF?th=1">Sonoff ZBDongle-E</a> USB stick plugged into the server in order to drive my Zigbee devices, which I'll mention in more detail further below.</p>
<h2>The Protocol</h2>
<p>Currently, I'm running a combination of WiFi and Zigbee devices, which is an alternate wireless protocol made specifically to be used by IoT devices that uses less energy and lays off a mesh network where the devices communicate with each other (as opposed to WiFi devices where everything goes through the router, thus creating a star network).</p>
<div class="post-image">
<img src="https://i.imgur.com/6sxx3bR.jpeg">
</div>
<p>The reason I run this mix is just because I didn't know about Zigbee in the beginning. If I could go back in time, I would have the entire network consist of Zigbee devices because I think they are just better than WiFi ones overall. It uses significantly less energy (many Zigbee devices can run on those coin cell batteries), the mesh network allows you to have devices very far away from the server, and best of all: they work even when the WiFi is down.</p>
<p>When you buy Zigbee devices, usually the store will say that you need a hub to drive them, which they also sell. It's true that you need a hub, but it doesn't have to be that store's <i>specific</i> hub. When using something like HA, you can use a USB antenna stick like the one mentioned above and that will allow you to control <i>any</i> Zigbee device from any manufacturer via HA.</p>
<h2>The Devices</h2>
<p>Here are the IoT devices that I have around my apartment, excluding things that are "smart" by default like TVs and such.</p>
<h2>Sonoff Basic R2 (WiFi)</h2>
<div class="post-image">
<img src="https://i.imgur.com/kcvnBvS.jpeg">
</div>
<p>This is a DIY WiFi switch that you hook into "dumb" devices in order to be able to make them smart and turn them on and off via WiFi. Given a bit of skill with electronics (stripping / crimping wires), these switches are much cheaper and more durable compared to buying smart lamps, and I have many of these spread around the apartment!</p>
<p>By default, these require you to expose your device to some awful Chinese cloud server. Luckily for us, you can flash these devices with custom firmware like <a href="https://tasmota.github.io/docs/">Tasmota</a>, allowing you to have full control of them. This also requires skills with electronics and some special equipment, so keep that in mind.</p>
<p>As previously mentioned, If I could go back in time, I would have instead bought these switches' Zigbee equivalent to make things easier and better, so I'm in the process of replacing them.</p>
<h2>Sonoff S26 R2 (WiFi)</h2>
<div class="post-image">
<img src="https://i.imgur.com/JsamvLr.jpeg">
</div>
<p>This is the same as above, but as something that you plug directly into the wall socket, thus requiring no messing with wires unless you want to flash the firmware to stop China from spying on you.</p>
<p>If I could back in time, I would have bought <a href="https://www.ikea.com/se/sv/p/inspelning-stickpropp-smart-energimaetare-00569836/">these IKEA wall sockets instead</a>, as they run on Zigbee and can even provide data on your electrical consumption, so these are also something I'll be replacing soon.</p>
<h2>Broadlink RM4C mini</h2>
<div class="post-image">
<img src="https://i.imgur.com/L3Dilv9.jpeg">
</div>
<p>This is a WiFi IR blaster that you can configure IR commands and thus be able to create automations that allow you to control devices that require a remote control, like your TV.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>In my case it turned out that newer Samsung TVs have some sort of API integration where you can control them over the web, but I used these blasters for a long time before I discovered this. This also puts your device on some Chinese cloud though, and in this case I'm not sure if custom firmwares are available.</p>
<h2>Random IKEA IoT utilities</h2>
<p>IKEA has <a href="https://www.ikea.com/se/sv/search/?q=smart">lots of IoT devices</a> like buttons, remote controls, motion detectors, temperature thingies, and more. They are all Zigbee and thus very easy to connect to use. I think only the button and the remote control took a bit more effort because you need to find out exactly how they work in order to build automations against them in HA, but nothing that a simple Google search couldn't solve.</p>
]]></description>
</item>
<item>
<title>What happens when you move a file in git?</title>
<link>https://swiftrocks.com/what-happens-when-you-move-a-file-in-git</link>
<guid>https://swiftrocks.com/what-happens-when-you-move-a-file-in-git</guid>
<pubDate>Mon, 2 Dec 2024 13:00:00 GMT+1</pubDate>
<author>Bruno Rocha</author>
<description><![CDATA[
<p>Recently at work we were considering renaming a folder that contains an enormous amount of files, and we wondered whether or not that would have notable negative consequences for our git repository. Would the repo become considerably larger? Would accessing git history become slower? Or would this be completely fine?</p>
<p>After investigating this, I thought the answer was interesting enough that I felt like writing an article about it.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>To answer this question, we need to briefly explain how git works under the hood. There's also a TL;DR at the bottom if you'd like to skip the entire explanation.</p>
<h2>How does git handle files?</h2>
<p>It's somewhat commonly believed that git's commits are <b>diffs</b>, but this is not true. Commits are <b>snapshots</b> of your repository, meaning that when you make changes to a file, git will store a <b>full copy of that file</b> on your repository <a href="https://codewords.recurse.com/issues/three/unpacking-git-packfiles">(there is an important exception, but let's keep it simple for now)</a>. This is why you can easily switch between commits and branches no matter how old they are; git doesn't need to "replay" thousands of diffs, it just needs to read and apply the snapshot for the commit you're trying to access.</p>
<p>Under the hood, git will store all different versions of your files in the <code>.git/objects</code> folder, and this is something we can play with in order to find out what will happen regarding the main question we're trying to answer.</p>
<p>Let's make a new git repo and add a file called <code>swiftrocks.txt</code> with the <code>Hello World!</code> contents, and commit it:</p>
<pre class="command-line language-bash"><code>git init
echo 'Hello World!' > swiftrocks.txt
git add swiftrocks.txt
git commit -m "Add SwiftRocks"</code></pre>
<p>If you now go to <code>.git/objects</code>, you'll see a bunch of folders with encoded files inside of them. The file we just added is there, but which one?</p>
<p>When you add a file to git, git will do the following things:</p>
<ul>
<li>Compress the file with <code>zlib</code></li>
<li>Calculate a SHA1 hash based on the contents</li>
<li>Place it in .git/objects/(first two hash characters)/(remaining hash characters)</li>
</ul>
<p>We can locate our file in the objects folder by reproducing this process, and luckily for us, we don't have to code anything to achieve this. We can find out what the resulting hash for a given file would be by running <code>git hash-object</code>:</p>
<pre class="command-line language-bash" data-output="2"><code>git hash-object swiftrocks.txt
980a0d5f19a64b4b30a87d4206aade58726b60e3</code></pre>
<p>In my case, the hash of the file was <code>980a0d5f19a64b4b30a87d4206aade58726b60e3</code>, meaning I can find the "stored" version of that file in <code>.git/objects/98/0a0d5f19a64b4b30a87d4206aade58726b60e3</code>. If you do this however, you'll notice that the file is unreadable because it's compressed. Similarly to the previous case, we don't have to code anything to de-compress this file! We just need to run <code>git cat-file -p</code> and git will do so automatically for us:</p>
<pre class="command-line language-bash" data-output="2"><code>git cat-file -p 980a0d5f19a64b4b30a87d4206aade58726b60e3
Hello World!</code></pre>
<p>There it is! Let's now make a change to this file and see what happens:</p>
<pre class="command-line language-bash" data-output="5, 7"><code>echo 'Hello World (changed)!' > swiftrocks.txt
git add swiftrocks.txt
git commit -m "Change swiftrocks.txt"
git hash-object swiftrocks.txt
cf15f0bb6b07a66f78f6de328e3cd6ea2747de6b
git cat-file -p cf15f0bb6b07a66f78f6de328e3cd6ea2747de6b
Hello World (changed)!</code></pre>
<p>Since we've made a change to the file, the SHA1 of the compressed contents changed, leading to a <b>full copy</b> of that file being added to the objects folder. As already mentioned above, <b>this is because git works primarily in terms of snapshots rather than file diffs.</b> You can even see that the "original" file is still there, which is what allows git to quickly switch between commits / branches.</p>
<pre class="command-line language-bash" data-output="2"><code>git cat-file -p 980a0d5f19a64b4b30a87d4206aade58726b60e3
Hello World! # The original file is still there!</code></pre>
<p>Now here's the relevant part: <b>What happens if we change our file back to its original contents?</b></p>
<pre class="command-line language-bash" data-output="5"><code>echo 'Hello World!' > swiftrocks.txt
git add swiftrocks.txt
git commit -m "Change swiftrocks.txt back"
git hash-object swiftrocks.txt
980a0d5f19a64b4b30a87d4206aade58726b60e3</code></pre>
<p><b>The hash is the same as before!</b> Even though this is a new commit making a new change to the file, the hashing process allows git to determine that the file is exactly the same as the one we had in previous commits, <b>meaning that there's no need to create a new copy.</b> This will be the case <b>even if you rename the file</b>, because the hash is calculated based on the contents, not the file's name.</p>
<p>This is a great finding, but it doesn't fully answer the original question. We now know that renaming files will not result in new copies of those files being added to the objects folder, but what about folders? And how are those files and folders attached to actual commits?</p>
<h2>How does git handle folders (and commits)?</h2>
<p>The most useful thing to know right off the bat is that <b>commits are also objects in git.</b> This is why you might have seen other folders / files in <code>.git/objects</code> when first inspecting it; the other files were related to the commits you made when adding the file.</p>
<p>Since commits are also objects, we can read them with <code>git cat-file</code> just like with "regular" files. Let's do it with our latest commit (<code>26d4302</code> in my case):</p>
<pre class="command-line language-bash" data-output="2-7"><code>git cat-file -p 26d4302
tree 350cef2a8054111568f82dc87bbd683ee14bb1a6
parent 2891fe1393c9e1bff116c1b58a30bcf85e0596a8
author Bruno Rocha <email> 1733136171 +0100
committer Bruno Rocha <email> 1733136223 +0100
Change swiftrocks.txt back</code></pre>
<p>As you can see, a "commit" is nothing more than a small text file containing the following bits of information:</p>
<ul>
<li>The author of the commit, and the commit message</li>
<li>The hash of the parent commit</li>
<li>The hash of the commit's "tree", containing information about the file system snapshot for that particular commit</li>
</ul>
<p>In this case, what we're interested in is the last point. Luckily for us, <b>trees are also objects in git.</b> Thus, if we want to see what the file system looks like for that particular commit, we just need to run <code>git cat-file -p</code> against the commit's tree hash:</p>
<pre class="command-line language-bash" data-output="2"><code>git cat-file -p 350cef2a8054111568f82dc87bbd683ee14bb1a6
100644 blob 980a0d5f19a64b4b30a87d4206aade58726b60e3 swiftrocks.txt</code></pre>
<p>Like with commits, tree objects are also very simple text files. In this case, the tree states that there's only one file (a blob) in the repository, which is a file called <code>swiftrocks.txt</code> with the <code>980a0d5f...</code> hash. We've already uncovered that git prevents individual files from being duped, but let's see how this is reflected in the tree object:</p>
<pre class="command-line language-bash" data-output="1-4"><code>(made a commit adding some copies, and did cat-file -p on the new commit / tree)
100644 blob 980a0d5f19a64b4b30a87d4206aade58726b60e3 swiftrocks.txt
100644 blob 980a0d5f19a64b4b30a87d4206aade58726b60e3 swiftrocks2.txt
100644 blob 980a0d5f19a64b4b30a87d4206aade58726b60e3 swiftrocks3.txt</code></pre>
<p>The tree object references the new copies and their different names, but as expected, their hashes all point to the same underlying object under the hood.</p>
<p>If we add folders to our repository, the tree object will include references to <i>other tree objects</i> (related to each of those folders), allowing you to <i>recursively</i> inspect each folder of that commit's snapshot. Here's an example:</p>
<pre class="command-line language-bash" data-output="1-4"><code>100644 blob dd99cb611e0c77b2214392b253ed555fb838d8ee .DS_Store
040000 tree 350cef2a8054111568f82dc87bbd683ee14bb1a6 folder1
040000 tree 11ca8c2fe64b078be34824f071d32a560aba62a7 folder2
100644 blob 980a0d5f19a64b4b30a87d4206aade58726b60e3 swiftrocks.txt</code></pre>
<p>As you can see above, the output directly identifies what each hash is so that you know exactly what you're looking at. (An alternative is to run <code>git cat-file -t</code>, which returns the "type" for a given object hash.)</p>
<h3>So what happens if you rename / move an entire folder?</h3>
<p>The important bit to know here is that <b>tree objects (and commits) are calculated and stored just like regular file (blob) objects</b>, meaning they follow the same rules. This means that if the contents of two folders are exactly the same, <b>git will not create a new tree object</b> for those folders; it will simply reuse the hash it had already computed in the past, just like in the case of files:</p>
<pre class="command-line language-bash" data-output="1-2"><code>040000 tree 350cef2a8054111568f82dc87bbd683ee14bb1a6 folder1
040000 tree 350cef2a8054111568f82dc87bbd683ee14bb1a6 folder1 (copy)</code></pre>
<p><b>However,</b> since tree objects contain references to a folder / file's <b>name</b>, renaming something can result in <b>new tree objects being created for that folder / file's parent tree</b> in order to account for the name change, resulting in new hashes and tree objects <b>recursively</b> all the way up to the root of the repository. This will also be the case when moving files / folders.</p>
<p>The above snippet is one example of this. Even though git was able to avoid duplicating the internal contents of <code>folder1</code>, git still needed to generate a new tree object for its parent in order to account for the fact that a new folder called <code>folder1 (copy)</code> exists. If there are more parents up the chain, they would also require new tree objects.</p>
<p>Whether or not this would be a problem depends on where exactly the change is being made. If the change is too "deep" into the filesystem and / or the affected trees contain a massive number of files then you'd end up with lots of potentially large new tree objects. Still, as you can see, tree objects are quite simple, so you'd need a truly gargantuan repository and / or unfortunate folder setup for this to be an actual problem.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>If you do have a setup that is bad enough for this to be an issue, then the good thing is that there are ways to improve it. By understanding how tree objects are created and which files change / move more often in your repo, it's possible to optimize the structure of your repository to minimize the "blast radius" of any given change. For example, placing files that change very often closer to the root of the repo could reduce the number of trees that would have to be regenerated and their overall size.</p>
<h2>(Bonus) When are commits not snapshots?</h2>
<p>At the beginning of this article, I mentioned that there are cases where commits are <b>not</b> snapshots. While this is not particularly relevant for this article, I wanted to briefly cover this as it's an important aspect of how git works.</p>
<p>We've seen that git will make copies of your files when you change them, but this introduces a massive problem: If a particular file happens to be really big, then duplicating it for every small change could be disastrous.</p>
<p>When this is the case, <b>git will pivot into calculating change deltas</b> instead of making full copies of the file. This feature is called <b>Packfiles</b>, and is something that is automatically managed by git for you. I recommend reading <a href="https://codewords.recurse.com/issues/three/unpacking-git-packfiles">this great write-up by Aditya Mukerjee</a> if you'd like to know more about it.</p>
<h2>TL;DR</h2>
<ul>
<li>Git works in terms of snapshots (for the most part)</li>
<li>Git knows that two files are the same and can avoid duplicating them in its internal storage, even if they have different names</li>
<li>Similarly, Git can also determine if two folders are the same, regardless of where they are or are named</li>
<li>Thus, renaming files or folders will not have any impact on git's internal storage for those files and folders</li>
<li>However, git may end up needing to duplicate information regarding <b>parent folders</b>, recursively, to account for naming changes and / or new files</li>
<li>In theory this can be an issue if the change happens very "deeply" into the file system and / or the parent folders contain massive amounts of files, but you'd need a truly gargantuan repository and / or unfortunate folder setup for this to be an actual problem</li>
<li>Understanding how git objects work under the hood allows you to optimize your repository's folders in ways that can prevent too many unnecessary objects from being created</li>
</ul>
<h2>Sources / References</h2>
<ul>
<li><a href="https://jvns.ca/#git">Julia Evans's many articles on git</a></li>
<li><a href="https://codewords.recurse.com/issues/three/unpacking-git-packfiles">Unpacking git packfiles</a></li>
<li><a href="https://www.youtube.com/watch?v=fCtZWGhQBvo">Git from the inside out</a></li>
]]></description>
</item>
<item>
<title>Focus not on the task, but on the problem behind the task</title>
<link>https://swiftrocks.com/focus-not-on-the-task-but-on-the-problem-behind-the-task</link>
<guid>https://swiftrocks.com/focus-not-on-the-task-but-on-the-problem-behind-the-task</guid>
<pubDate>Mon, 18 Nov 2024 13:00:00 GMT+1</pubDate>
<author>Bruno Rocha</author>
<description><![CDATA[
<p>Consider the following situation: A team is asked by their Product Manager to implement features X, Y, and Z into an existing system.</p>
<p>The team then organizes itself and executes the project. After a certain amount of time, they delivered exactly what was asked of them. But then, one of these things happens (choose at least one):</p>
<ul>
<li>The features work only for the happy path, failing miserably at any sort of edge-case</li>
<li>The features work as expected, but were designed so badly they are immediately considered tech debt</li>
<li>The features work as expected, but are considered useless by the users of the system and end up completely unused (they do not solve those users' problems)</li>
</ul>
<p>What do you think happened here? Is this the Product Manager's fault for giving wrong requests, or the engineering team's fault for not understanding what was asked of them?</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>Some people will say this is the PM's fault, and in some cases it might be true. But the situation I want to cover here is the scenario where this is the <b>engineers' fault</b>, because it's something I've seen countless times throughout my career.</p>
<p>The reason the scenario above happened (when the PM is not the one at fault) is because the engineers focused too much on the <i>immediate</i> task they were given, when what they should've done is <b>focus on the problem behind the task</b>, by asking themselves questions such as:
<ul>
<li>What's the <b>context</b> behind this ask? Is this a larger project that includes other features and tasks?</li>
<li>Who exactly is asking for this? (Does anyone actually need this?)</li>
<li>What <b>problem</b> are these users facing that led to this ask?</li>
<li>Did someone attempt to solve this problem before? What did they try to do?</li>
<li>Who is going to benefit from this ask being executed? How would they use the solution?</li>
</ul>
<p><b>It's only after understanding this context that you can consider yourself ready to come up with a technical solution to it.</b> But what happens a lot is that some engineers tend to <b>immediately</b> jump into problem-solving as soon as they are presented with a task, leading to solutions that despite being "accurate" when evaluating the task in isolation, completely miss the mark when looking at the bigger picture. In short, the issue was that the engineers in that situation had <b>too much tunnel vision.</b></p>
<div class="post-image">
<img src="https://i.imgur.com/BfKDQC5.png" alt="Alt">
</div>
<div class="post-image">
<img src="https://i.imgur.com/IHGr3P5.png" alt="Alt">
</div>
<p>Understanding the context behind tasks allows you to come up with a solution that fits the bigger picture, making it possible not only to solve your users' problems, but also to do so in a way that is clean, scalable, easy to maintain, and that benefits everyone (as opposed to being beneficial to you and your team, but a pain in the ass for everyone else in the company).</p>
<p>In <a href="https://www.oreilly.com/library/view/the-staff-engineers/9781098118723/">The Staff Engineer's Path</a>, Tanya Reilly describes this as the <b>Local vs Global Maxima</b> problem, where the Local maxima means focusing on what's good for you or your team in an individual sense (the tunnel vision situation above), and the Global maxima means focusing on what's good <b>for the company as a whole</b>, regardless of whether or not it would be ideal for you as an individual (the big picture situation). In the book, she presents this idea to argue that this focus and ability to gather context about the bigger picture is a <b>core ability of Staff+ level engineers</b> and a minimum requirement for those aspiring to reach that level.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>Although I agree with her that only Staff+ engineers should be <i>expected</i> to be masters at this, I do also believe that this is something everyone should attempt to do, regardless of level. Doing so not only improves your knowledge of how your company works and is structured, but also teaches you how to better determine what does and doesn't matter when trying to solve a particular problem, enabling you to be more effective both at coding and at providing value for your company.</p>
]]></description>
</item>
<item>
<title>Working at startups vs large companies</title>
<link>https://swiftrocks.com/working-at-startups-vs-large-companies</link>
<guid>https://swiftrocks.com/working-at-startups-vs-large-companies</guid>
<pubDate>Mon, 14 Oct 2024 21:00:00 GMT+2</pubDate>
<author>Bruno Rocha</author>
<description><![CDATA[
<p>Working at a large company like Apple or Google is such a common goal in the tech industry that there's even a market for selling courses and books designed to help people achieve this goal.</p>
<p>But one thing that I've learned in my career is that working at such companies is <b>not for everyone.</b> The experience of working at a large company is extremely different from that of a startup, so if you're not aware of those differences, you can end up having a big (negative) surprise down the road that can make you regret your choices.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>In this article, I'd like to show you the difference between companies of different sizes so that you can <b>determine which one better fits your personal style and interests.</b></p>
<blockquote>Disclaimer: I haven't worked at every company to ever exist in this planet, so this is obviously not a 100% perfect model for every company out there. <b>There are always exceptions</b>, this is just a basic description of the average case.</blockquote>
<h2>Working at a startup</h2>
<p><b>Pros:</b></p>
<ul>
<li>Great to get experience in a wide variety of topics and areas</li>
<li>Growth potential is huge</li>
<li>Little to no bureaucracy or politics</li>
<li>Generally a great community / vibe overall</li>
</ul>
<p><b>Cons:</b></p>
<ul>
<li>Generally crap pay</li>
<li>Highly unstable</li>
<li>Little to no engineering challenges (startups often prioritize speed)</li>
<li>Work is complete chaos</li>
<li>Most likely going to work with a product that nobody cares about</li>
</ul>
<p>Working at a startup is the most fun I've had in my career, but I think it takes a special kind of person to thrive in this environment.</p>
<p>I feel that working at a startup is <b>ideal if you have an entrepreneurship mindset</b>, because you not only get to be constantly exposed to the organizational side of things, you are likely also involved in it. This allows you to build a lot of experience with how companies work under the hood, which I've found to be really handy overall.</p>
<p>Another thing I like about startups is that the vibe is generally very positive. Since there aren't a lot of people in the company, there's basically <b>no bureaucracy</b> and chances are that everyone gets along well. This also makes it so that you can grow quite fast in the company, provided that the company itself is doing well in the first place.</p>
<p>In general, <b>startups are a high-risk high-reward situation.</b> While you can win big fast, you can lose big just as fast because any minor setback can destroy the entire company. This is another reason why I find them best for those with an entrepreneurship itch. The work itself also tends to be very chaotic and thus not something that someone looking for stability would enjoy.</p>
<p>Another important downside to mention is that the <b>engineering side of things tends to be a bit dull.</b> Since startups often prioritize speed, building things tends to be down-prioritized in favor of going for easy out-of-the-box and / or plug-and-play open-source solutions, making a software engineering job feel more like assembling LEGO than anything else. Every work I had as a mobile engineer at a startup was basically 100% building UI, which became really boring to me after a while.</p>
<h2>Working at a mid-level company</h2>
<p><b>Pros:</b></p>
<ul>
<li>Generally good pay and benefits</li>
<li>Better engineering challenges than the startups</li>
<li>Opportunity to work with popular products</li>
<li>Mostly stable</li>
</ul>
<p><b>Cons:</b></p>
<ul>
<li>Growth potential is not as great when compared to a startup</li>
<li>Some bureaucracy / politics</li>
<li>Despite the presence of interesting challenges, there aren't many sufficiently skilled engineers to tackle them / the engineering culture is not strong enough</li>
</ul>
<p>The mid-level company is the company that is big enough to overcome the downsides attributed to startups, but nowhere as big enough to have the pros attributed to large companies. In general, the pros/cons of a mid-level company are essentially the averages of the other two cases in this article.</p>
<p>The primary problem with mid-level companies is that they try to mimic the processes and objectives of large companies, <b>but have nowhere near as many resources as they do.</b> This results for example in the team being tasked to solve <i>massive</i> engineering infrastructure challenges, because that's what large companies do, even though almost no one in the team is skilled enough to pull it off (likely because most who did have such skills ended up getting poached by the large companies). This puts giant pressure on these select few, which on one side can be seen as a great growth opportunity, but on the other side puts the company into a difficult position, as said people are likely to either burn out or leave in favor of an actual large company.</p>
<p>With that said, I find that mid-level companies still offer great growth opportunities. I think they are good choices for people who like the vibe and stability of large companies but can't stand the downsides of working at actual large companies.</p>
<h2>Working at a large company</h2>
<p><b>Pros:</b></p>
<ul>
<li>Life-changing pay and benefits</li>
<li>World-class engineering challenges</li>
<li>Opportunity to work with some of the smartest people on the planet</li>
<li>In some cases, opportunity to pioneer / define tech trends for the entire planet</li>
<li>Opportunity to work with products that are used / loved on the entire planet</li>
<li>Apart from potential layoffs, they are extremely stable / too big to fail</li>
</ul>
<p><b>Cons:</b></p>
<ul>
<li>Unbearable politics</li>
<li>Growth is extremely hard</li>
</ul>
<p>By "large company", we're talking about tech giants like Google, Apple, Meta, and so on. Looking at the pros, it's easy to see why people dream of working at such places. But what a lot of people don't know is that there are strong downsides attached to working at such companies, and <b>being able to tolerate them is critical to succeed there.</b> I've met many folks who couldn't and ended up leaving.</p>
<p>The first and most critical downside is that <b>everything is covered by a thousand layers of bureaucracy and politics.</b> I cannot overstate how unbearable this is, but it's how things at companies of this size.</p>
<p>When you work at a startup, if you want to do something, you just go there and do it. For a mid-level company, it might be slightly more annoying, but still doable. But when you work at a large company, if you want to do something, you're going to have to have a <b>meeting about having a meeting about drafting a document about a meeting about drafting another document</b>, which hopefully will be picked up by the planning season several months later, leading to more meetings and documents until <i>hopefully</i> you get to do some actual work around a year later, unless the company re-orgs sometime during this process, in which case you'll have to drop everything and start from scratch.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>This boundless bureaucracy extends everywhere, <b>including the promotion process.</b> Growing at such companies can be extremely hard as the process involves considerable amounts of bureaucracy and things that are outside of your control, especially for Staff+ positions. Which team you're part of also plays a big role as some teams are bound to have more opportunities to drive impact than others in a company of this size.</p>
<p>I think that thriving at a large company is directly <b>correlated to how much you can tolerate such politics.</b> No one would look at this description and be happy, but if you look at it and feel that you could take it, then working at a large company might be for you.</p>
<h2>Conclusion</h2>
<p>As I mentioned in the beginning, this is just a basic description of the average case. There are thousands of exceptions who surely don't fit into these descriptions. But the idea is just to present that the concept of <b>trade-offs also applies to companies and cultural fit.</b> Just because one company is larger than the other doesn't necessarily mean it's best you; depending on what you value, you might find that smaller companies are a better fit for you.</p>
]]></description>
</item>
<item>
<title>My process for learning new languages</title>
<link>https://swiftrocks.com/my-process-for-learning-new-languages</link>
<guid>https://swiftrocks.com/my-process-for-learning-new-languages</guid>
<pubDate>Sun, 1 Sep 2024 10:00:00 GMT+2</pubDate>
<author>Bruno Rocha</author>
<description><![CDATA[
<p>Throughout my life, I have met several people who struggle to learn a new language, with some of them being unable to communicate at a basic level despite having studied for several years.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>I believe that learning a language is not a matter of talent, but that of dedication and <b>following a good process.</b> In this article, I would like to share the process I used more than once (and am still using) to tackle the challenge of learning a new language with great success. I currently speak three languages (native Portuguese, English, Swedish), and am in the process of learning a fourth (Japanese).</p>
<h2>My process</h2>
<p>For me, learning a new language consists of three major steps:</p>
<ul>
<li>Learning basic grammar</li>
<li>Becoming good at reading and listening</li>
<li>Becoming good at speaking</li>
</ul>
<h3>Learning basic grammar</h3>
<p>My first step towards learning a new language consists of understanding the basics of the language. This includes things like learning how sentences are structured, how words should be pronounced, how to count, and any other language-specific basics that may apply (for example, for Swedish, learning the difference between <i>en / ett</i>, or the concept of <i>soft / hard vowels</i>).</p>
<p>The word "basic" here is <b>very</b> important. I want to have a good understanding of how things work in that language, but I don't want to waste time reasoning about complex grammar rules. Think of the sorts of things a mom would correct a child for; my mom would correct me if I used a word in the wrong place, but she wouldn't lecture me about the theory of participle clauses.</p>
<p>Knowledge of basic grammar massively pays off because later on it will simplify the process of expanding the vocabulary. Although at this point I will not know many words, my knowledge of basic grammar will allow me to more easily figure out how to pronounce any new words that I may encounter in the future, to properly classify them (subject? verb? noun? adjective? present? past tense?) based on their format and position in a sentence, and in some cases even accurately guess their meaning based on this information.</p>
<p>Although in the next section I'm going to complain about traditional language learning books / schools, I think they are one of the best resources for learning basic grammar. It's also usually straightforward and can be mastered in just a couple of months.</p>
<h3>Becoming good at reading and listening</h3>
<p>But this is about as far as those language schools and books will help you out, because in my opinion they massively fuck up pretty much everything beyond this point.</p>
<p>In my experience, after learning basic grammar, schools and books usually follow up by teaching <i>advanced grammar</i>. I think this is a complete waste of time and is why in my opinion many adults struggle with learning a language despite attending classes for multiple years. Learning complex grammar rules will <b>not</b> help you learn the language, even natives don't know this stuff!</p>
<p>What natives know is <b>vocabulary</b>, and this is what I believe is the right focus at this stage. My goal then becomes to expand my vocabulary as much as possible by immersing myself in the language, consuming as much media as I can and as frequently as possible. This is something that is usually referred to as the <b>immersion method</b>.</p>
<p>Here are some examples of things I do in this step:</p>
<ul>
<li>Changing the language of my phone / computer</li>
<li>Watching shows with either sound or subtitles in that language, preferably both if available</li>
<li>Listening to podcasts, sometimes actively and sometimes passively as background noise</li>
<li>Finding internet forums / subreddits / YouTube channels for topics I like</li>
<li>Reading the news</li>
</ul>
<p>You might think that this doesn't make sense because you won't understand anything, but that's exactly the point. Children also don't understand anything at first, yet magically they seem to just "get it" after one point, simply by being exposed to the language. This is because our brains are big pattern-matching machines; the more you expose yourself to a language, the more patterns / words you discover, which leads to further discoveries until you eventually reach a point where everything just clicks. In other words, the purpose of this step is to try to replicate how a child would learn a language at home.</p>
<p>I find watching shows / YouTube videos to be particularly excellent for this because you can usually guess what a word means based on the context of the scene, meaning you don't need to spend as much time translating words as you would when compared to other types of media.</p>
<p>Duolingo can also be a good tool to expand your vocabulary, as long as you don't use it in isolation. This is because although Duolingo is a good way to learn new words, it tends to be quite bad at everything else (e.g grammar), so I think it's important to back it up with the other methods mentioned in this section. It's important to note also that the quality of Duolingo's exercises varies greatly between languages, so looking for reviews before getting started is a must here.</p>
<p>If you tend to quickly forget things like I do, a <b>spaced repetition system</b> can greatly assist you with fixating all of this new knowledge. In my case I quite enjoyed using <a href="https://readwise.io/">Readwise</a> for this, but I know many who have used <a href="https://apps.ankiweb.net/">Anki</a> / traditional flashcards with great success.</p>
<p>This entire process is very painful at first, but gets easier with time as your vocabulary improves. The unfortunate part is that this is a lengthy process; it can take several years of doing this before reaching a point where reading / listening to the language becomes effortless, and I think there's no way around it.</p>
<h3>Becoming good at speaking</h3>
<p>Although the previous step is excellent at making me good at reading and listening, in my experience it doesn't necessarily help me become good at <i>speaking</i>. When I was learning Swedish for example, although I had an easy time understanding what people were saying and knew in theory what to answer back, I still had a very hard time doing so, mostly because I just wasn't used to it. Although I knew the vocabulary in theory, it would still take several seconds for the right words to emerge in my mind when having a conversation with someone.</p>
<p>Unlike the other steps, I don't think there is any special method that one can use to become good at speaking a new language. This is something you just have to keep doing until your brain gets used to it. It's a massive advantage if you actually live in the country in question, but this is doable even if you don't as there are many online services designed around connecting you with native speakers of a particular language. I personally never used them though, so I cannot comment on their efficiency.</p>
<p>Another interesting to mention is that nowadays there are websites that connect LLMs to voice recognition models, allowing you to chat with something like ChatGPT with your voice for the purpose of language learning. I've tried one but personally didn't like the experience as talking to a robot felt completely different from talking to a real person, but if you'd like to try it out, you can easily find them on Google (there are hundreds of websites for this as of writing).</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>I think the most important part here is to resist the urge to switch to another language when you start to struggle, especially if you live in a country like Sweden where the natives are good at English. (In fact I would say that the hardest thing about learning a language like Swedish is not the language itself, but rather that Swedes are so good at English that they automatically switch to it when they see you struggling with Swedish, making it almost impossible for you to improve!)</p>
<h2>Conclusion</h2>
<p>Although we can divide the process of learning a new language in logical steps, we're still talking about a multi-year effort. That's just how it works, there are no shortcuts. If you struggle with learning a new language, I hope this post was able to teach you something new that can help you in your journey!</p>
]]></description>
</item>
<item>
<title>What are mobile release engineering teams and when do you need one? (Runway)</title>
<link>https://www.runway.team/blog/what-are-mobile-release-engineering-teams-and-when-do-you-need-one</link>
<guid>https://www.runway.team/blog/what-are-mobile-release-engineering-teams-and-when-do-you-need-one</guid>
<pubDate>Fri, 28 Jun 2024 09:00:00 GMT+2</pubDate>
<author>Bruno Rocha</author>
<description><![CDATA[
<p>The early (and also not-so-early) days of building a tech startup means hiring and working with people who are capable of wearing a lot of hats. There are a ton of things to do and nowhere near an equal amount of resources to do them. Even if you join as a specialist, chances are you'll find yourself getting deep into other areas.</p>
<p>This is true across the organization, including in mobile engineering. Early engineers don’t just write the code that builds the foundation for the app’s future success (while building up the tech debt that future engineers will pull their hair out over) but also the initial infrastructure both for how teams get things done and how they ship code out the door.</p>
<p>This doesn’t last forever though. As teams grow they make the transition out of generalist, jack-of-all-trades roles and begin focusing on specialization. But one part of the org where there is often a lag in making this transition — and sometimes a very long one — is on the mobile team. Very few mobile engineers will have time to support both their team's app features and the series of Ruby scripts that support their release process, yet the expectation that they do both often lingers far beyond when other teams in the organization have fully specialized.</p>
<p>In this article, we'll look at why this happens and how big tech companies follow-up on this problem.</p>
]]></description>
</item>
<item>
<title>Recommended WWDC24 Sessions</title>
<link>https://swiftrocks.com/recommended-wwdc24-sessions</link>
<guid>https://swiftrocks.com/recommended-wwdc24-sessions</guid>
<pubDate>Mon, 17 Jun 2024 11:20:00 GMT+2</pubDate>
<author>Bruno Rocha</author>
<description><![CDATA[
<p>Every year I watch WWDC and write down some notes. But I've just realized that I've never shared those notes and it felt like something I should do, so I've decided to do just that!</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>My approach is to pick a handful of sessions that are immediately useful / interesting to me and ignore the rest. I find this to be a good strategy because in my experience trying to keep up with things that you don't care about / don't have an immediate need for is a very easy way to burn yourself out, especially because Apple has this awful habit of announcing things and then proceeding to make them completely obsolete the next year. If in the future I happen to start working with something where one of the ignored sessions would be handy, I go back and watch it. Otherwise, it stays unwatched.</p>
<p>I think the Keynote and the State of the Union should always be watched, so I won't cover them here.</p>
<h2><a href="https://developer.apple.com/videos/play/wwdc2024-10179">Meet Swift Testing</a></h2>
<p>I really like this feature. Greatly recommend watching this as it shows not only how it works but also many interesting tricks that you can do with it. To make it better, it's open source and even works on VSCode, although I haven't tried the latter myself.</p>
<h2><a href="https://developer.apple.com/videos/play/wwdc2024-10061">What’s new in StoreKit and In-App Purchase</a></h2>
<p>I watch StoreKit sessions because I have an app that has IAPs, so I'm always eager to see what's new in this regard. StoreKit Views aren't a new concept but I'm glad to see they made them more powerful. They also show how you can test IAPs directly in Xcode, which is pretty neat.</p>
<h2><a href="https://developer.apple.com/videos/play/wwdc2024-10136">What's new in Swift</a></h2>
<p>This session happens every year and it's always a good watch. As someone who has been working with build systems a lot recently, I'm particularly interested in the new explicit modules feature.</p>
<h2><a href="https://developer.apple.com/videos/play/wwdc2024-10135">What's new in Xcode 16</a></h2>
<p>This is a great watch as Xcode 16 changed quite a bit in good ways. The time profiler now has a flame graph, and the new unified backtrace view looks awesome.</p>
<h2><a href="https://developer.apple.com/videos/play/wwdc2024-10118">What's new in UIKit</a></h2>
<p>I haven't worked directly with UI for a very long time, but I enjoy watching these to see what they're improving. I like the improved interop between SwiftUI and UIKit and the new fluid animation type. The new <code>UIUpdateLink</code> type is also very interesting.</p>
<h2><a href="https://developer.apple.com/videos/play/wwdc2024-10144">What's new in SwiftUI</a></h2>
<p>Similarly to "What's new in UIKit", I enjoy watching these to see what's up with the frameworks. Particularly great things this year are custom containers, how things like <code>@Environment</code> and state in <code>Previews</code> have been greatly simplified, and a much better integration with scroll views.</p>
<h2><a href="https://developer.apple.com/videos/play/wwdc2024-10197">Go small with Embedded Swift</a></h2>
<p>This is one of those things that I don't have a use for but watch anyway because it sounded cool, and indeed it was. I think this can also work as a hardcore app size optimization for your apps if you're fine with losing a bunch of Swift's features.</p>
<h2><a href="https://developer.apple.com/videos/play/wwdc2024-10157">Extend your app’s controls across the system</a></h2>
<p>Watching sessions about new iOS features is always a good idea if they relate to something that you can potentially implement in your apps. I can think of many things that I can App Controls for and it seems also really easy to implement since it's all based on the existing Widgets infra.</p>
<h2><a href="https://developer.apple.com/videos/play/wwdc2024-10181">Xcode essentials</a></h2>
<p>This session is great. Xcode has a ton of tools and shortcuts that we're not aware of, and many of them are extremely useful if you can remember that they exist!</p>
<h2><a href="https://developer.apple.com/videos/play/wwdc2024-10171">Demystify explicitly built modules</a></h2>
<p>Apart from talking about the new Swift 6 feature of the same name, this session goes into great detail about how imports work in Swift/Obj-C and how to debug them. I learned a lot from this one.</p>
<h2><a href="https://developer.apple.com/videos/play/wwdc2024-10146">Demystify SwiftUI containers</a></h2>
<p>I am not super interested in SwiftUI improvements, but the new custom container feature is a great addition. I believe this is something that will be used a lot, so it's worth it to check this session that shows how it works and what you can do with it. This session also shares many interesting details about how subviews work in SwiftUI.</p>
<h2><a href="https://developer.apple.com/videos/play/wwdc2024-10170">Consume noncopyable types in Swift</a></h2>
<p>Most people will probably never use this feature, but it's one of those things that are really cool in practice and worth a look. You might also want to check this out because generics involving the new <code>~Copyable</code> type are really complicated, so watching this session will help you be less confused if you end up bumping into it.</p>
<h2><a href="https://developer.apple.com/videos/play/wwdc2024-10198">Run, Break, Inspect: Explore effective debugging in LLDB</a></h2>
<p>Every year has a session on LLDB, and this year's one is especially good. It's hard to summarize this one because they show a ton of different things, so just go there and watch it! I was surprised to find out that you can open crash logs in Xcode (maybe it was always a thing?) and that you can create "manual" breakpoints by calling <code>raise(SIGSTOP)</code>.</p>
<h2><a href="https://developer.apple.com/videos/play/wwdc2024-10217">Explore Swift performance</a></h2>
<p>This was not as actionable as I thought it would be, but I still found it to be interesting because it contains "official" evidence about how structs/protocols can be bad for performance / app size if you misuse them, which is something I've covered in a recent talk and that a lot of people wanted to know more about.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<h2><a href="https://developer.apple.com/videos/play/wwdc2024-10173">Analyze heap memory</a></h2>
<p>This session not only shows interesting examples of how to use the memory debugger and instrument, but also shares a lot of interesting pieces of info about the difference between <code>weak</code> and <code>unowned</code> that I believe weren't documented before, including how to debug their performance! It also now serves as an "official" source for <a href="https://swiftrocks.com/autoreleasepool-in-swift">the autoreleasepool trick I wrote an article about a long time ago</a>, which is pretty neat.</p>
]]></description>
</item>
<item>
<title>Using @_silgen_name to forward declare functions in Swift and improve build times</title>
<link>https://swiftrocks.com/using-silgenname-to-call-private-swift-code</link>
<guid>https://swiftrocks.com/using-silgenname-to-call-private-swift-code</guid>
<pubDate>Thu, 14 Mar 2024 13:00:00 GMT+1</pubDate>
<author>Bruno Rocha</author>
<description><![CDATA[
<blockquote>Disclaimer: The trick I'm going to show here is quite powerful, but like every other underscored attribute in Swift, this is something you should avoid messing with unless you know exactly what you're doing. There are lots of pitfalls attached to these attributes, and the behavior of underscored attributes can change at any time and even stop existing entirely without warning. Don't go around sprinkling this in your projects if you don't fully understand the consequences of doing so!</blockquote>
<p>Swift is regarded for its type safety, meaning the compiler (usually) doesn't allow you to reference or do things that might potentially not exist / be invalid; the complete opposite of languages like Obj-C where the compiler allows you to do pretty much whatever you want in exchange for compile-time safety.</p>
<p>But here's an obscure fact about Swift: The language <i>does</i> support ObjC-like <code>Selectors</code> / forward declarations, it's just that we're not supposed to use it. If you know how a function is going to be named in the compiled binary, you can use the <code>@_silgen_name</code> attribute to craft a <b>direct reference</b> to that function, allowing a module to reference and call it regardless of whether or not it actually has "visibility" of it:</p>
<pre><code>@_silgen_name("somePrivateFunctionSomewhereThatICantSee")
func refToThatFuncIReallyWantToCall()
func foo() {
refToThatFuncIReallyWantToCall() // Just called something I wasn't suposed to be able to!
}</code></pre>
<p>This is used extensively by the Swift standard library to create something akin to the old-school <b>forward declarations</b> in Obj-C / C, allowing it to call functions that live deeper in the Swift Runtime even though it shouldn't be able to. As denoted by the underscore, this is <b>not</b> an official feature of Swift, but rather an internal detail of the compiler that <b>is not meant to be used outside of this specific internal case.</b> Nonetheless, you <i>can</i> use it in your regular Swift apps, so if you know what you're doing and is aware of the consequences / implications, you can do some pretty neat stuff with it.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<h2><code>@_silgen_name</code> and symbol mangling</h2>
<p>Since Swift has namespacing features, the names you give to your Swift functions are not actually what they will be called in the compiled binary. To prevent naming collisions, Swift injects a bunch of context-specific information into a function's symbol that allows it to differentiate it from other functions in the app that might have the same name, in a process referred to as <b>symbol mangling</b>:</p>
<pre><code>// Module name: MyLib
func myFunc() { print("foo") }</code></pre>
<pre class="command-line language-bash" data-output="3-4"><code>swiftc -emit-library
nm libMyLib.dylib
MyLib.myFunc()'s "real" name is:
$s5MyLib6myFuncyyF</code></pre>
<p>What <code>@_silgen_name</code> does under the hood is <b>override a function's mangled symbol</b> with something of your choosing, giving us the ability to reference functions in ways that Swift generally wouldn't allow us to (which I'll show further below).</p>
<p>The attribute can be used in two ways: to override the symbol of a declaration and to override the symbol of a <i>reference</i> to a declaration. When added to a <b>declaration</b>, as in, a function with a body, the attribute overrides that function's mangled name with whatever it is that you passed to the attribute:</p>
<pre><code>@_silgen_name("myCustomMangledName")
func myFunc() { print("foo") }</code></pre>
<pre class="command-line language-bash" data-output="3-4"><code>swiftc -emit-library -module-name MyLib test.swift
nm libMyLib.dylib
MyLib.myFunc()'s name now is:
myCustomMangledName</code></pre>
<p>This is interesting, but what we truly care about here is what happens when you add it to a function that <b>doesn't</b> have a body. This would usually be invalid Swift code, but because we've added <code>@_silgen_name</code> to it, the compiler will treat it as valid code and assume that this function is <i>somehow</i> being declared somewhere else under the name we passed to the attribute, effectively <b>allowing us to build forward declarations in pure Swift:</b></p>
<pre><code>@_silgen_name("$s5MyLib6myFuncyyF")
func referenceToMyFunc()
func foo() {
// Successfully compiles and calls MyLib.myFunc(), even though
// this module doesn't actually import the MyLib module
// that defines myFunc()
referenceToMyFunc()
}</code></pre>
<p>(This only works if the "target" is a free function, so for things like a class's static functions you'll need to first define a function that wraps them.)</p>
<p>Now, it should be noted that knowing a Swift function's mangled name in advance (<code>$s5MyLib6myFuncyyF</code>, in the above example) is not straight-forward as the compiler doesn't expose an easy way of predicting what these values will be, but we can fix this by using <code>@_silgen_name</code> on the declaration itself in order to modify it to something that we know and is under our control, like in the previous example where we replaced it with <code>"myCustomMangledName"</code>. Note that you only need to worry about this when referencing Swift functions; For Obj-C / C, a function's "mangled name" will be the function's actual name as those languages have no namespacing features.</p>
<pre><code>@_silgen_name("myCustomMangledName")
func referenceToFooMyFunc()</code></pre>
<p>It's critical to note that this is extremely <b>unsafe</b> by Swift compiler standards as it sidesteps any and every type safety check that would normally apply here. <b>The compiler will not run any validations here</b>; it will instead completely trust that you know <i>exactly</i> what you're doing, that somehow these functions will exist in runtime even though this doesn't seem to be the case during compilation, that any custom names you're using are unique and not causing any potential conflicts with other parts of the codebase, and that whatever parameters you're passing to the forward-declared functions are correct and managed properly memory-wise (if your target is a C function, you need to do manual memory management with <code>Unmanaged<T></code>).</p>
<p>If everything is done correctly, you just got yourself a nice forward-declared function, but if not, you'll experience undefined behavior. You <i>do</i> get a compile-time linker error though if the functions don't exist at all, which is pretty handy as I've noticed that in addition to all of the above concerns, the compiler also may sometimes accidentally tag these functions as "unused" depending on how you declare them, causing them to be stripped out of the compiled binary when they should not. I am sure that there are way more things that can go wrong here that I'm not aware of.</p>
<h2>Cool, but why?</h2>
<p>Lack of safety aside, there are two situations where I find this attribute useful outside the Swift standard library. The first one is being able to do <b>C interop</b> without having to define annoying headers and imports, similar to how the Swift standard library has been using it. It seems that a lot of people have been doing this, but I'll not cover this here because it's not the use case that led me to use this attribute. I'll just point out that this is something you also need to be very careful about, particularly because <code>@_silgen_name</code> functions use the Swift calling convention, which is incompatible with C (thanks Ole Begemann for pointing that out!).</p>
<h3>Trading safety for better build times</h3>
<p>The second one however, which is what I have been using this for, is that when applied strategically, you can use this attribute to <b>greatly improve your app's incremental build times.</b></p>
<p>Let's assume that we're developers of a large modularized Swift app that has some sort of type safe dependency injection mechanism to pass values around. For this mechanism to work, we might end up with a "central" registry of dependencies that imports every module and configures every possible dependency these modules might request:</p>
<pre><code>import MyDepAImplModule
import MyDepBImplModule
import MyDepCImplModule
...
func setupRegistry() {
myRegistry.register(MyDepA(), forType: MyDepAProtocol.self)
myRegistry.register(MyDepB(
depA: myRegistry.depA,
), forType: MyDepBProtocol.self)
myRegistry.register(MyDepC(
depA: myRegistry.depA,
depB: myRegistry.depB,
), forType: MyDepCProtocol.self)
}</code></pre>
<p>Something like this allows us to have a nice and safe system where features are unable to declare dependencies that don't exist, but it will come at the cost of <b>increased incremental build times.</b> Importing all modules like this will cause this module to be constantly invalidated, and the bigger your project gets, the worse this problem will get. In my personal experience, projects with a setup like this and with several hundred modules can easily end up with a massive <b>10~60 seconds delay</b> to incremental builds, depending on the number of modules and how slow your machine is.</p>
<p>However, by using forward-declared <code>@_silgen_name</code> references to a function that wraps the initializers instead of referencing these initializers directly, we can achieve the same injection behavior <b>without having to import any of the modules that define said initializers!</b></p>
<pre><code>@_silgen_name("myDepAInitializer") func makeMyDepA()
@_silgen_name("myDepBInitializer") func makeMyDepB(_ depA: MyDepAProtocol)
@_silgen_name("myDepCInitializer") func makeMyDepC(_ depA: MyDepAProtocol, _ depB: MyDepBProtocol)</code></pre>
<p>This allows projects like this to completely eliminate these build time bottlenecks, but it comes at the price of losing all type safety around this code. This might sound like a bad trade-off since type safety is the reason why a developer would want to have a dependency injection setup like this in the first place, but if you have other ways of validating those types and dependencies (such as a CLI that scans your app and automatically generates / validates this registry), you can abstract the dangerous bits away from your developers and effectively enjoy all the build time improvements without having to worry about any negatives other than having to be extra careful when making changes to this part of the code.</p>
<h2>Conclusion</h2>
<p>Forward-declaring Swift functions allow you to do all sorts of crazy things, but remember, <b>this is not an official feature of the language</b>. As mentioned in the beginning, my recommendation is that you should avoid messing with internal compiler features unless you're familiar with how Swift works under the hood and know exactly what you're doing.</p>
<p>But putting this aside, one thing that I tend to reflect on when learning about features like this is how the danger involved in using them is not so much about the features themselves, but rather that their behavior might change without warning.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>Although I understand the Core team's vision of making Swift a safe and predictable language, I think there is a real demand for having poweruser-ish / "I know this is dangerous, I don't care" features like this officially supported in Swift, and it would be amazing if <code>@_silgen_name</code> could be recognized as one such feature. I like what you can achieve with it, and I would love to be able to use it without fear that it might change or stop existing in the future.</p>
]]></description>
</item>
<item>
<title>Software engineering book recommendations</title>
<link>https://swiftrocks.com/software-engineering-book-recommendations</link>
<guid>https://swiftrocks.com/software-engineering-book-recommendations</guid>
<pubDate>Sun, 3 Mar 2024 13:00:00 GMT+1</pubDate>
<author>Bruno Rocha</author>
<description><![CDATA[
<p>The following is a list of software engineering books I've read that I felt had a strong and lasting positive impact on my career. It's not a list of <i>everything</i> I enjoyed (that would be impossible to list down), but rather a special list of resources that taught/helped me so much that I still find myself thinking about them years later. They are my top recommendations for other software engineers.</p>
<p>Some are about iOS development specifically, but most relate to general software engineering. If you strive to be a world-class developer, these books and resources will help you get there.</p>
<ul>
<li><b><a href="https://nostarch.com/writegreatcode1_2e">Write Great Code: Understanding The Machine (Randall Hyde)</a></b> - Probably my favorite software engineering book. This book teaches you how modern computers work in a high-level way that is easy to understand and not overly technical. It's not going to teach you new fancy APIs, but it will give you the ability to “understand” code; an ability that has proved to be useful almost daily in my career.</li>
<li><b><a href="https://pages.cs.wisc.edu/~remzi/OSTEP/">Operating Systems: Three Easy Pieces (Remzi Arpaci-Dusseau, Andrea Arpaci-Dusseau)</a></b> - This is similar to Understanding The Machine, but is more technical and focused on operating systems specifically. Amazing resource to learn about core OS fundamentals such as memory, threads / concurrency, file systems, and CPU virtualization. <b>It's also free.</b></li>
<li><b><a href="https://newosxbook.com/home.html">*OS Internals Trilogy (Jonathan Levin)</a></b> - This trilogy about how Apple platforms work internally is an <b>absolute goldmine</b> for us who develop for said platforms, but it's not for everyone. They will teach you everything you could possibly want to know about these platforms, but they're massive and go into extreme detail on things that you're never going to interact with, making this series a very painful read for beginners and those looking for quick / practical knowledge. But if you're reasonably experienced and is interested in things like linkers and kernels, then these books are a godsend. The only problem with these books is that Amazon only delivers them if you live in the US, so getting a copy can be challenging.</li>
<li><b><a href="https://fabiensanglard.net/gebbdoom/index.html">Game Engine Black Book: DOOM (Fabien Sanglard)</a></b> - Fantastic deep-dive on not only the source code, but also the hardware, tooling, and team dynamics that allowed the masterpiece known as DOOM to be conceived. iOS developers will find this book extra interesting because DOOM was developed on NeXT workstations; many of the tools were written in Objective-C, so the book has a lot of interesting information about the language and frameworks like Foundation that is still relevant to this day.</li>
<li><b><a href="https://www.crackingthecodinginterview.com/">Cracking the Coding Interview (Gayle Laakmann McDowell)</a></b> - Even if you don’t care about the interviewing bits (I’m not even sure if companies like Apple still run LeetCode puzzles), this book is still a legendary resource for getting started with computer science theory. If you’re not sure why you’d want to do that, <a href="https://swiftrocks.com/how-necessary-are-the-programming-fundamentals">check out my article about it.</a></li>
<li><b><a href="https://www.amazon.se/-/en/Tanya-Reilly/dp/1098118731">The Staff Engineer's Path (Tanya Reilly)</a></b> - Perfect book for senior engineers looking to get to the next level. This book demystifies the concept of a Staff Engineer and provides a gigantic amount of great advice on how to get there and how to do a good job once you’re there.</li>
<li><b><a href="https://www.amazon.com/Effective-Objective-C-2-0-Specific-Development/dp/0321917014">Effective Objective-C 2.0 (Matt Galloway)</a></b> - Even though we don’t directly write apps in Obj-C anymore, we still have to do so indirectly when using older frameworks like UIKit. This book does a great job of explaining how Obj-C works, which is something you’ll wish you knew when dealing with issues that either originate or pass through Obj-C.</li>
<li><b><a href="https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882">Clean Code (Robert C. Martin)</a></b> - This book gets a lot of flack in the community because some people treat it as some sort of bible that must be followed religiously, but it is nonetheless a great resource for learning how to write code that is easy to read and maintain by humans. <a href="https://swiftrocks.com/there-is-no-right-or-wrong-in-software-engineering">Just don't be like those folks.</a></li>
<li><b><a href="https://www.amazon.com/Hacking-Art-Exploitation-Jon-Erickson/dp/1593271441">Hacking: The Art of Exploitation (Jon Erickson)</a></b> - I didn't actually like the first half of this book as the author spends far too long explaining C / Assembly and C exploits that are probably largely irrelevant nowadays, but the reason I'm adding it to the list is because of the second half which does a pretty good job at explaining <b>how networks work</b>, including some network-based exploitation techniques which I believe are still relevant to this day. This led me to a rabbit hole of wanting to know more about how to reverse engineer online-based apps and games, and thus I'm very grateful for this book even though I wasn't a fan of the first half.</li>
</ul>
<h2>Other Resources</h2>
<p>The following are not “books” in the traditional sense, but are nonetheless great software engineering resources that I think should be present here.</p>
<ul>
<li><b><a href="https://github.com/donnemartin/system-design-primer">System Design Primer</a></b> - A collection of resources for learning how to design scalable backends. Great resource for learning how giant products like Twitter work. For mobile developers, this knowledge can help you make sure the client-side bits are designed properly.</li>
<li><b><a href="https://www.nand2tetris.org/">Nand2Tetris</a></b> - A free online course that teaches you how to build a modern computer and make it run Tetris, from scratch. Another amazing resource for learning how computers work.</li>
<li><b><a href="https://highaltitudehacks.com/2013/06/16/ios-application-security-part-1-setting-up-a-mobile-pentesting-platform.html">Prateek Gianchandani’s articles about iOS security, and DVIA</a></b> - This giant series of 50 articles teaches you how to hack iOS apps and is how I learned everything I know about iOS security. It’s quite old so I’m not sure how much of it still applies, but is an interesting read regardless. Prateek also has a project called <a href="https://github.com/prateek147/DVIA-v2">Damn Vulnerable iOS App (DVIA)</a> that is basically a playground where you can try out everything he mentions in his articles, but unfortunately it seems that the website that explained it doesn’t exist anymore. <a href="https://web.archive.org/web/20220331031231/https://damnvulnerableiosapp.com/">I was able to find it in the Internet Archive</a>, so hopefully that still allows you to try it.</li>
<li><b><a href="https://learngitbranching.js.org/">Learn Git Branching</a></b> - A page that teaches you to use git in the CLI by presenting it as a browser game. This is how I learned how to use git and is a resource that I greatly recommend even if you prefer using GUI apps for your git needs.</li>
<li><b><a href="https://ocw.mit.edu/">MIT OpenCourseWare</a></b> - MIT has a lot of free content on YouTube, and the ones related to software engineering are amazing. <a href="https://www.youtube.com/watch?v=q1OF_0ICt9A&t=3794s">Here’s a link to my favorite one, which is a class about HTTPS/SSL/modern cryptography.</a>
</ul>
]]></description>
</item>
<item>
<title>What the hell are passkeys? (Runway)</title>
<link>https://www.runway.team/blog/an-introduction-to-passkeys</link>
<guid>https://www.runway.team/blog/an-introduction-to-passkeys</guid>
<pubDate>Thu, 22 Feb 2024 13:00:00 GMT+1</pubDate>
<author>Bruno Rocha</author>
<description><![CDATA[
<p><b>Passkeys</b> are an alternative to passwords that allow you to sign in to any website or app that supports this technology <b>without needing to define/remember a password.</b></p>
<p>Over the last couple of years, the tech world has been witnessing many companies updating their products to add support to this new technology, including tech giants like Apple, who introduced several APIs and device features related to passkeys <a href="https://support.apple.com/en-gb/guide/iphone/iphf538ea8d0/ios">in iOS 16</a>, and Google, who has been pushing passkeys hard since announcing passkeys support for Chrome and Gmail <a href="https://blog.google/technology/safety-security/one-step-closer-to-a-passwordless-future/">back in 2022.</a></p>
<p>When I first saw Apple and Google’s announcements, I wasn't sure what I was looking at. The fact that the tech industry has been trying to move away from old-school passwords wasn't news to me, but I asked myself: didn't we already solve this problem when products started allowing users to register a "secondary" authentication requirement, such as receiving an SMS containing a PIN code?</p>
<p>I then decided to look deeper into this topic, and was so pleasantly surprised by what I found that I thought it would be cool to share it with you. In this article, we'll take a look at what passkeys are, why companies like Apple and Google are adding support for them, how they work under the hood, and whether or not you should convert your own accounts to use them and build support for them in your mobile apps!</p>
]]></description>
</item>
<item>
<title>There is no right or wrong in software engineering</title>
<link>https://swiftrocks.com/there-is-no-right-or-wrong-in-software-engineering</link>
<guid>https://swiftrocks.com/there-is-no-right-or-wrong-in-software-engineering</guid>
<pubDate>Thu, 28 Dec 2023 15:00:00 GMT+1</pubDate>
<author>Bruno Rocha</author>
<description><![CDATA[
<p>Have you ever seen "preachings" like these in the wild west of Twitter, Medium, and LinkedIn?</p>
<ul>
<li>"If you're not using X in 2023 you're an idiot!"</li>
<li>"XYZ architecture is the only way to go!"</li>
<li>"Doing X in Swift? Stop immediately!"</li>
</ul>
<p>Humans' obsession with dividing things into groups is not unknown to psychology. The ability to quickly classify information is a core contributor to humanity's evolution and is something we start doing as soon as we're born.</p>
<p>But we need to be careful with the fact that sometimes this classification "feature" goes wrong. Instead of categorizing something as "this or that", we sometimes go for something more in terms of "us vs them", which not only leads to a lot of unnecessary conflict, but is also bad for our lives in general.</p>
<p>As far as I understand, the exact reason why humans do this is not fully understood. You might have heard of the "tribalism" theory which defines that humans are hardcoded to divide themselves in this "us vs them" fashion; this is mentioned a lot in pop culture, but from my understanding, this theory is heavily critized by experts and should not be considered true. But explanations aside, we know that this happens and is something everyone needs to understand and overcome at one point.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>When it comes to software engineering, the reality is <b>most things cannot be cleanly divided into "right or wrong" boxes like that.</b> Yes, some things are concrete and indisputable. If an OS-level API states that you should never call it outside of the main thread, then that's what you should follow.</p>
<p>But most things are the opposite of that. When we talk about general problems and best practices, it's extremely rare for them to have a clear right or wrong way to go. Instead, <b>they depend on what you're trying to achieve, and everyone is trying to achieve something different.</b> There is no right or wrong in these situations, only different approaches.</p>
<p>To add another layer to the problem, <b>a lot of stuff is also largely subjective!</b> Many of our daily choices boil down to personal preference, making it even more senseless to attempt to categorize such things into clean "right" or "wrong" boxes. The industry even has a saying for this: <b>there are no solutions, only trade-offs</b>.</p>
<p>One's ability to understand this concept is the <b>greatest indicator of seniority in my personal opinion.</b> It's so common for intermediate-level engineers to fail to understand the subjectivity of software engineering that I find that you can accurately gauge someone's experience level by simply observing how well they grasp this concept. Those who don't understand it overengineer things and tend to get bogged down on details that are either subjective or outright pointless, while those who <i>do</i> understand it keep things simple and display a much better ability to prioritize important work and ignore less important details.</p>
<p>In this article, I'd like to shine a light on some of the topics that iOS developers tend to be divided on as a way to help developers who still haven't cleared that hurdle understand that these topics are not as straightforward as we might tend to think. Overcoming this barrier is part of the process of becoming a more experienced software engineer, and is something almost everyone goes through in their careers, so it's not something to be anxious about. I also had a period where I thought I had all the answers to the coding universe!</p>
<h2>Tool Wars (e.g SwiftUI vs UIKit, Hybrid vs Native, CLI vs GUI, programming languages)</h2>
<p>When a new framework, tool, or programming language is released, it's not uncommon for developers to divide themselves into groups and argue about which one of them is "better", claiming that theirs is the only option and everything else is a mistake.</p>
<p>The problem with this line of thought is that it assumes that one tool was created to <i>completely</i> replace another, and while sometimes this may very well be the case, in most cases it's not.</p>
<p>As a developer, it's important to understand that <b>different tools solve different problems.</b> While there may be some overlap between them, they were likely designed with different use cases in mind.</p>
<p>The biggest example here as of writing is the SwiftUI vs UIKit discussion. Despite being largely different from each other, social media is full of content about how one is "better" than the other.</p>
<p>Yes, SwiftUI and UIKit are both frameworks for building UI, <b>but they solve different problems.</b> As covered in my earlier <a href="https://swiftrocks.com/my-experience-with-swiftui">"Thoughts on SwiftUI vs UIKit"</a> article, SwiftUI is amazing for simple projects but quickly becomes inferior to UIKit as the project grows in complexity. Neither of these frameworks is better than the other, they are simply different tools for different jobs.</p>
<p>Discussions about Hybrid vs Native development also fall into this category. Hybrid development has a bad reputation because it generally results in apps of very low quality, but it saves companies a lot of time and money. Most companies reject this trade-off as they determine that quality is more important than saving a few bucks, but that doesn't mean that <i>nobody</i> should do so. If you're starting a company but don't have a lot of time or money, hybrid development can be a good way to bootstrap your business. It's not fair to compare these two in a "better/worse" fashion because <b>they don't target the same set of problems.</b></p>
<p>I find that one example of how things can be subjective in this context is how a developer uses <code>git</code>. There is a lot of discussion about whether you should use it via the CLI or as a dedicated GUI app, but there isn't much to be discussed here because this is something that entirely boils down to your personal preference. There are pros and cons to each approach, and you will know which one is the right one for you because you will feel that it better suits your set of preferences. Neither approach is universally right or wrong.</p>
<h2>Best Practices Wars (e.g architecture, general advice)</h2>
<p>Architecture is usually the first thing that an iOS developer fights about. Every year we get a new architecture with some fancy acronym, that architecture gets a bunch of loyal followers, and then the groups start arguing about which architecture has the coolest name and solves the biggest number of problems. The first thing you learn is that MVC is terrible and should be avoided at all costs.</p>
<p>One unfortunate consequence of these fights around architecture is that it leads developers to pick architectures that solve problems that they don't really have (and not solving the problems they actually have), which are guaranteed to make a project harder to maintain in the long run.</p>
<p>It's important to understand that <b>there is no architecture that solves all problems.</b> Just like in the tools example, different architectures are meant to solve different problems, and <b>the right architecture for your project is the one that solves <i>your</i> particular set of problems.</b> MVC for example, which developers love to hate for some reason, can be a great choice for simple projects!</p>
<p>Architecture is not something that you pick once and stick with forever, but rather something that you <b>continuously adjust as your project evolves</b> and you start having to deal with different sets of problems. I have been told that my talk about <a href="https://www.youtube.com/watch?v=sZuI6z8qSmc">how Spotify's iOS app is architected</a> is great at demonstrating this, so I'm mentioning it here in case you want to check it out!</p>
<p>The same applies to general programming advice that you find on the web. We have a lot of content creators in our community, and I find that most of them present their content in the following format: "here's a thing, here's how it works, and here's what you can do with it". This is what I also strive to do when writing content for this blog, and I like this format because it doesn't claim that something is the best way of achieving something, it's simply showing you one possible way and leaving for you to decide whether or not that's the right solution for you.</p>
<p>But every once in a while, the algorithm recommends me content that is more in line with "here's a thing, and here's why you should always use it and abandon everything else". It's not about learning something new, it's about saying that you're wrong about something. There's usually a spike of this type of content in the WWDC week when new APIs are released.</p>
<p>The problem with content like this is that <b>most best practices are highly subjective.</b> Even if the content is referring to a very specific problem, it's hardly the case that the problem in question has one single viable solution. As we've already mentioned a couple of times in this article, personal preference plays a major role in this type of stuff. Something very helpful to you might be terrible for someone else, so they cannot be classified in a universal "right or wrong" fashion.</p>
<h2>Programming Fundamentals War (e.g LeetCode)</h2>
<p>Another common discussion point for iOS developers is whether or not you should learn computer science theory as part of your career. This is usually brought up whenever a company that run old-school programming puzzles (LeetCode) as part of their interview processes is mentioned.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>This topic however is complex enough that it deserves its own article, and convieniently enough, such an article already exists! You can find more information about this in my <a href="https://swiftrocks.com/how-necessary-are-the-programming-fundamentals">"How necessary are the programming fundamentals?"</a> article, but as a quick summary, this is a very complicated topic that has no objective right or wrong.</p>
<h2>Conclusion</h2>
<p>I hope this was able to help you see that some things in software engineering are more complicated than they might seem at first glance. Realizing this is an important step in a software engineer's career, and while this article will certainly not stop those wars from popping up every once in a while, I do believe that as a community we can help others get through this phase faster.</p>
]]></description>
</item>
<item>
<title>What even is code signing in iOS? (Runway)</title>
<link>https://www.runway.team/blog/what-even-is-code-signing-in-ios</link>
<guid>https://www.runway.team/blog/what-even-is-code-signing-in-ios</guid>
<pubDate>Fri, 15 Dec 2023 13:00:00 GMT+1</pubDate>
<author>Bruno Rocha</author>
<description><![CDATA[
<p>If you have been developing for iOS for a while, chances are you had one of two issues involving <b>code signing</b>, the process where Apple/Xcode forces you to "sign" your app with a developer certificate in order to be able to archive it and submit it to the App Store.</p>
<p>I find code signing to be interesting not just because of what it does, but because it's one of those things that iOS developers kinda just take for granted. We know it's there and we know how to deal with it, but we don't really stop to think <b>why</b> it's there or what it's doing under the hood. We just follow Apple's convoluted steps on how to make it work and move on with our lives.</p>
<p>In practice, code signing is an incredibly important safety feature of Apple's ecosystems. Knowing what it is and why it exists makes debugging issues related to it considerably easier, so I've written this article to help you understand it.</p>
]]></description>
</item>
<item>
<title>How I'm using ChatGPT for software engineering</title>
<link>https://swiftrocks.com/how-im-using-chatgpt-for-software-engineering</link>
<guid>https://swiftrocks.com/how-im-using-chatgpt-for-software-engineering</guid>
<pubDate>Mon, 11 Dec 2023 13:00:00 GMT+1</pubDate>
<author>Bruno Rocha</author>
<description><![CDATA[
<p>ChatGPT has become an important part of my daily software engineering work, so I thought it would be interesting to share what exactly I've been using it for in case you're wondering how to use it to improve your productivity.</p>
<h2>Use-case 1: I don't know what I'm looking for</h2>
<p>I find that Google tends to provide good results if you know <b>exactly</b> what you're looking for. But if you don't really know what is it that you're trying to find out, you'll have a hard time with it.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>For example, the other day I was trying to find what the <code>_start</code> function in iOS (the first function called when your app is launched) is, where it's defined, and what it does. But if I go now and search for "_start function iOS" on Google, I will not find a straight answer to this question. Google <i>does</i> return somewhat good results (the second search result contains some interesting information about it if you scroll down far enough), but it cannot give me a direct response because I asked the wrong question. I know today that what I should've done is ask it about details of how C programs are linked, but I didn't know this back then, so I couldn't have done that.</p>
<p>ChatGPT does not have this problem. If you don't know what you're looking for, you can explain your situation to ChatGPT and it will point you in the right direction:</p>
<div class="post-image">
<img src="https://i.imgur.com/KuTBOgc.png" alt="Alt">
</div>
<p>In this example, you can see that ChatGPT immediately pointed out that I asked the wrong question before attempting to explain it.</p>
<p>Although ChatGPT's answers aren't 100% accurate, I find them to be accurate enough to allow me to use Google to find the rest of the information.</p>
<h2>Use-case 2: My question is too specific for Google</h2>
<p>Even if you know exactly what you're looking for, you may have difficulty using Google if your question is too specific. For example, you cannot search Google on "how to implement X thing in Swift in my app that is using XYZ frameworks in X iOS version with dependency injection bridging an Obj-C type after the user logged in on my TODO list app using Apple Sign-in from Italy during a rainy day in October". For cases like this, usually what you need to do is break your problem into multiple, more generic queries, open many tabs that each help you with a specific part of the problem, and then use all of that combined knowledge to come up with the actual answer to your question.</p>
<p>ChatGPT excels at this. You can be as specific as you want and you'll get a relevant answer. Most coding questions fall into this category, although for these specifically I have mostly been using Copilot rather than ChatGPT itself.</p>
<div class="post-image">
<img src="https://i.imgur.com/Rlbf1Pl.png" alt="Alt">
</div>
<p>In this case, I could've probably found the answer I was looking for in Google by making a bunch of generic searches about C++ global constructors and good practices, opening a bunch of tabs, and summarizing everything I found. But by asking ChatGPT, I saved several hours of my time instead.</p>
<h2>Use-case 3: I want FAST answers</h2>
<p>It has been getting harder and harder to get fast answers to your questions with Google. Today, it's very unlikely that the answer to a question will lie at the top of a page you've opened. As SEO optimization became more and more important for survival on the web, the amount of stuff you have to endure before getting to the actual content has increased significantly. There will be a lengthy introduction, a pause for sponsors, ten paragraphs about how the question reminds the author about a personal story of how their dog bodyslammed their grandma on Christmas, a call to action for the author's newsletter, some backstory on the question, and <b>only then</b> you'll get to the actual content.</p>
<p>I find that there are many cases where this fluff is relevant and worth reading. But there are also many cases when I'm in a hurry and would much rather just get a straight answer to my question.</p>
<!-- <div class="sponsor-article-ad-auto hidden"></div> -->
<p>This is also something that I find ChatGPT to be quite good at. It generally doesn't try to educate you on things you didn't ask, it just straight up answers your question.</p>
<div class="post-image">
<img src="https://i.imgur.com/SuUcWBm.png" alt="Alt">
</div>
<p>By asking follow-up questions regarding one or more things it mentioned in its answer, I can get all of the information I need to learn something new considerably faster than if I had used Google instead. Even though I still need to use Google to double-check if ChatGPT didn't hallucinate particular pieces of information, this ability to quickly gather relevant information saves me an absurd amount of time.</p>
]]></description>
</item>
<item>
<title>How async/await works internally in Swift</title>
<link>https://swiftrocks.com/how-async-await-works-internally-in-swift</link>
<guid>https://swiftrocks.com/how-async-await-works-internally-in-swift</guid>
<pubDate>Thu, 28 Sep 2023 14:00:00 GMT+2</pubDate>
<author>Bruno Rocha</author>
<description><![CDATA[
<p>async/await in Swift was introduced with iOS 15, and I would guess that at this point you probably already know how to use it. But have you ever wondered <b>how</b> async/await works internally? Or maybe <b>why</b> it looks and behaves the way it does, or even <b>why was it even introduced in the first place?</b></p>
<p>In typical SwiftRocks fashion, we're going deep into the Swift compiler to answer these and other questions about <b>how async/await works internally in Swift</b>. This is not a tutorial on how to use async/await; we're going to take a deep dive into the feature's history and implementation so that we can understand how it works, <b>why it works</b>, what you can achieve with it, and most importantly, <b>what are the gotchas that you must be aware of when working with it.</b></p>
<blockquote><b>Disclaimer:</b> I never worked at Apple and have nothing to do with the development of async/await. This is a result of my own research and reverse-engineering, so expect some of the information presented here to not be 100% accurate.</blockquote>
<h2>Swift, and the goal of memory safety</h2>
<p>Swift's async/await brought to the language a brand new way of working with asynchronous code. But before trying to understand how it works, we must take a step back to understand why Swift introduced it in the first place.</p>
<p>The concept of <b>undefined behavior</b> in programming languages is something that surely haunts anyone who ever needed to work with the so-called “precursor programming languages” like C++ or Obj-C.</p>
<p>Programming languages historically provided you with 100% freedom. There were no real guardrails in place to prevent you from making horrible mistakes; you could do anything you wanted, and the compilers would always assume that you knew what you were doing.</p>
<p>On one hand, this behavior on behalf of the languages made them extremely powerful, but on the other hand, it essentially made any piece of software written with them a minefield. Consider the following C code where we attempt to read an array's index that doesn't exist:</p>
<pre><code>// arr only has two elements
void readArray(int arr[]) {
int i = arr[20];
printf("%i", i);
}</code></pre>
<p>We know that in Swift this will trigger an exception, but we’re not talking about Swift yet, we’re talking about a precursor language that had complete trust in the developer. What will happen here? Will this crash? Will it work?</p>
<p>The short answer is that we don't know. Sometimes you'll get 0, sometimes you'll get a random number, and sometimes you'll get a crash. It completely depends on the contents of that specific memory address will be at that specific point in time, so in other words, the behavior of that assignment is <b>undefined.</b></p>
<p>But again, this was intentional. The language assumed that you knew what you were doing and allowed you to proceed with it, even though that turned out to almost always be a huge mistake.</p>
<p>Apple was one of the companies at the time that recognised the need for a safer, modern alternative to these languages. While no amount of compiler features can prevent you from introducing logic errors, they believed programming languages should be able to prevent undefined behavior, and this vision eventually led to the birth of Swift: a language that prioritized memory safety.</p>
<p>One of the main focuses of Swift is making undefined behavior impossible, and today this is achieved via a combination of compiler features (like explicit initialization, type-safety, and optional types) and runtime features (like throwing an exception when an array is accessed at an index that doesn’t exist. It’s still a crash, but it’s not undefined behavior anymore because now we know what’s supposed to happen!).</p>
<p>You could argue that this should come with the cost of making Swift an inferior language in terms of power and potential, but one interesting aspect of Swift is that it still allows you to tap into that raw power that came with the precursor languages when necessary. These are usually referred to within the language as “unsafe” operations, and you know when you’re dealing with one because they are literally prefixed with the “unsafe” keyword.</p>
<pre><code>let ptr: UnsafeMutablePointer<Int> = ...</code></pre>
<h2>The problem of concurrency in Swift</h2>
<p>But despite being designed for memory safety, Swift was never truly 100% memory safe because <b>concurrency</b> was still a large source of undefined behavior in the language.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>The primary reason why this was the case is because Grand Central Dispatch (GCD), Apple’s main concurrency solution for iOS apps, was not a feature of the Swift compiler itself, but rather a C library (libdispatch) that was shipped into iOS as part of Foundation. Just as expected of a C library, GCD gave you a lot of freedom in regard to concurrency work, making it challenging for Swift to prevent common concurrency issues like <b>data races, race conditions, deadlocks, priority inversions, and thread explosion.</b></p>
<p>(If you’re not familiar with one or more of the terms above, here’s a quick glossary:)</p>
<ul>
<li>Data Race: Two threads accessing shared data at the same time, leading to unpredictable results</li>
<li>Race Condition: Failing at synchronize the execution of two or more threads, leading to events happening in the wrong order </li>
<li>Deadlock: Two threads waiting on each other, meaning neither is able to proceed</li>
<li>Priority inversion: Low-priority task holding a resource needed by a high-priority task, causing delays in execution</li>
<li>Thread explosion: Excessive number of threads in the program, leading to resource exhaustion and decreased system performance</li>
</ul>
<pre><code>let semaphore = DispatchSemaphore(value: 0)
highPrioQueue.async {
semaphore.wait()
// …
}
lowPrioQueue.async {
semaphore.signal()
// …
}</code></pre>
<p>The above is a classic example of a priority inversion in iOS. Although you as a developer know that the above semaphore will cause one queue to wait on another, GCD would not necessarily agree and fail to properly escalate the lower priority queue’s priority. To be clear, GCD <em>can</em> adjust itself in certain situations, but patterns like the above example were not covered by it.</p>
<p>Because the compiler was unable to assist you with such problems, concurrency <a href="https://swiftrocks.com/thread-safety-in-swift">(and thread safety specifically)</a> historically was one of the hardest things to get right in iOS development, and Apple was well aware of it. In 2017, Chris Lattner, one of the driving forces behind Swift, laid out his vision for making concurrency safe in his <a href="https://gist.github.com/lattner/31ed37682ef1576b16bca1432ea9f782">Swift Concurrency Manifesto</a>, and in 2020, <a href="https://forums.swift.org/t/swift-concurrency-roadmap/41611">a roadmap</a> materialized which envisioned new key features to Swift, which included:</p>
<ul>
<li>The async/await pattern</li>
<li>Task API and the concept of Structured Concurrency</li>
<li>Actors & Actor Isolation</li>
</ul>
<p>But although what the roadmap proposed was new to the language, it was not new to tech itself. The async/await pattern, which was first introduced in 2007 as a feature of F#, has been an industry standard since 2012 (when C# made it mainstream) due to its ability to allow asynchronous code to be written as traditional synchronous ones, making concurrency-related code easier to read.</p>
<p>For example, before you might write:</p>
<pre><code>func loadWebResource(_ path: String, completionBlock: (result: Resource) -> Void) { ... }
func decodeImage(_ r1: Resource, _ r2: Resource, completionBlock: (result: Image) -> Void)
func dewarpAndCleanupImage(_ i : Image, completionBlock: (result: Image) -> Void)
func processImageData1(completionBlock: (result: Image) -> Void) {
loadWebResource("dataprofile.txt") { dataResource in
loadWebResource("imagedata.dat") { imageResource in
decodeImage(dataResource, imageResource) { imageTmp in
dewarpAndCleanupImage(imageTmp) { imageResult in
completionBlock(imageResult)
}
}
}
}
}</code></pre>
<p>whereas now you can write:</p>
<pre><code>func loadWebResource(_ path: String) async -> Resource
func decodeImage(_ r1: Resource, _ r2: Resource) async -> Image
func dewarpAndCleanupImage(_ i : Image) async -> Image
func processImageData1() async -> Image {
let dataResource = await loadWebResource("dataprofile.txt")
let imageResource = await loadWebResource("imagedata.dat")
let imageTmp = await decodeImage(dataResource, imageResource)
let imageResult = await dewarpAndCleanupImage(imageTmp)
return imageResult
}</code></pre>
<p>Introducing this pattern in Swift specifically would not only improve the experience of working with completion handlers but also technically allow the compiler to detect and prevent common concurrency mistakes. It’s easy to see why Chris Lattner made the pattern the central piece of his manifesto, in which he declared, in his own words: “I suggest that we do the obvious thing and support this in Swift.”</p>
<p>Over the years these features were gradually integrated into Swift, culminating in the Swift 5.5 release and the “official” release of async/await in Swift.</p>
<h2>Async/await under the hood</h2>
<p>Now that we understand why async/await became a part of Swift, we’re ready to take a look at how it works under the hood!</p>
<p>But first, I have to set some expectations with you. Because what we refer to as “async/await” is actually multiple different compiler features working in unison, and because each of these features is complicated enough to warrant their own separate article(s), there’s no way I can possibly cover every single detail of how it works in just one article. I would lose my sanity within the first section if I did that.</p>
<p>So instead of doing that, I’ve decided that a good plan would be to cover only what I believe to be async/await’s “core” functionality and to leave the remaining bits for future articles. But although we don’t go into details about those other bits here, I still made sure to mention some of them in the appropriate sections for you to know where they come into play. For simplicity, we’re also not going to cover compatibility modes and Obj-C bridging here.</p>
<p>With that said, let’s get started! When I reverse-engineer something to learn more about it, I always start from the bottom and move my way up. So when I decided that I wanted to understand how async/await works, my first question was: “Who is managing the program’s background threads?”</p>
<h2>The Cooperative Thread Pool</h2>
<p>One of the most important aspects of how async/await works in Swift, and one that we must cover before anything else, is that while async/await technically uses GCD under the hood, <b>it does not use the <code>DispatchQueues</code> that we are familiar with.</b> Instead, what powers async/await is a completely new feature of libdispatch called <b>The Cooperative Thread Pool.</b></p>
<p>Unlike traditional <code>DispatchQueues</code>, which creates and terminates threads dynamically as it deems necessary, the Cooperative Thread Pool manages a <b>fixed number of threads</b> that are constantly helping each other with their tasks.</p>
<p>The “fixed number”, which in this case is equal to the system’s number of CPU cores, is an intentional move that aims to prevent <b>thread explosion</b> and improve system performance in general, <a href="https://tclementdev.com/posts/what_went_wrong_with_the_libdispatch.html">something which DispatchQueues were notoriously not very good at.</a></p>
<p>In other words, the cooperative thread pool is similar to traditional GCD from an interface perspective (it’s a service that receives a job and arranges some thread to run it), but is more efficient and designed to better suit the Swift runtime’s special needs.</p>
<p>We can see exactly how the Cooperative Thread Pool works by exploring <a href="https://github.com/apple-oss-distributions/libdispatch">the open-source libdispatch repo</a>, but I would like to reserve that for a future article. In general, I find that <a href="https://developer.apple.com/videos/play/wwdc2021/10254/">this WWDC session from 2021</a> provides great information about how the pool works internally.</p>
<h3>Gotcha: Starving threads in the pool</h3>
<p>It must be noted that the fact that the pool holds a fixed number of threads has a significant impact on how asynchronous code is supposed to be written in the language. There is now an expectation that threads should always make f<em>orward progress</em>, which means you need to be really careful with when and how you do expensive operations in async/await in order to avoid starving the system's threads. <b>Swift's async/await has many gotchas like this</b>, and we'll uncover some of them as we proceed.</p>
<p>Let’s move up the abstraction layers. How does the compiler “speak” to the pool?</p>
<h2>Keep the jobs flowing: The Executors</h2>
<p>In Swift, you don't interact with the Cooperative Thread Pool directly. This is hidden by several layers of abstractions, and at the lowest of these layers, we can find the <b>executors</b>.</p>
<p>Executors, just like the pool itself, are<b> </b>services that accept jobs and arrange for some thread to run them. The core difference between them is that while the pool is just, well, the pool, executors come in many shapes and forms. They all end up forwarding jobs to the pool, but the way they do so may change depending on which type of executor you’re using. As of writing, executors can be either concurrent (jobs can run in parallel) or serial (one at a time), and the compiler provides built-in implementations for both of them.</p>
<h3>The Built-in Concurrent Executor</h3>
<p>The built-in concurrent executor is referred to internally as the <b>Global Concurrent Executor</b>.</p>
<p>The implementation of this executor in the Swift compiler is for the most part nothing more than an abstraction on top of the cooperative thread pool that we mentioned in the previous section. It starts by creating an instance of the new thread pool, which we can see is done by calling the good ol’ GCD API with a special new flag:</p>
<pre><code>constexpr size_t dispatchQueueCooperativeFlag = 4;
queue = dispatch_get_global_queue((dispatch_qos_class_t)priority,
dispatchQueueCooperativeFlag);</code></pre>
<p>Then, when the executor is asked to run a job, it forwards it to the pool via a special <code>dispatch_async_swift_job</code> API:</p>
<pre><code>JobPriority priority = job->getPriority();
auto queue = getGlobalQueue(priority);
dispatch_async_swift_job(queue, job, (dispatch_qos_class_t)priority,
DISPATCH_QUEUE_GLOBAL_EXECUTOR);</code></pre>
<p>I would like to leave the details of libdispatch and <code>dispatch_async_swift_job</code> for another time, but as mentioned in the previous section, this is supposed to be a special/more efficient variant of the regular <code>dispatch_async</code> API that iOS developers are familiar with that better suits the Swift runtime’s special needs.</p>
<p>Another aspect of this executor worth mentioning is that it's "global", meaning there is only one instance of it for the entire program. The reasoning for this is similar to why a serial <code>DispatchQueue</code> would deep-down forward its jobs to the global ones: while from a systems perspective, it makes sense for the responsibilities to appear to be divided, from a performance perspective it would be a nightmare for each component to have their own dedicated threads. It's sensible then to have a single, global executor that will ultimately schedule most of the work in the system, and have everyone else forward their jobs to it.</p>
<p>The Global Concurrent Executor is Swift’s default executor in general. If your async code is not explicitly requesting that it should go through a specific executor (we will see some examples of that as we continue to explore the abstractions), this is the executor that will handle it.</p>
<p>(Swift uses a different global executor in platforms that don’t support libdispatch, but I will not go into details of that as the primary focus of this article is iOS development.)</p>