Bug Archeology: Solving a decade-old Swift/C++ mystery with LLMs
Bug Archeology: Solving a decade-old Swift/C++ mystery with LLMs<br>13 May 2026
10 min read
bug tech
“When you eliminate the impossible, whatever remains, however improbable, must be the truth.”
Sir Arthur Conan Doyle, The Sign of Four.
Many years ago I worked on a very cool project, Juke.<br>It was one of the pioneering music apps from EU and boasted a catalogue of 57 million songs. The app was rewritten in a very unique tech stack pioneered by our maverick CTO Guillaume.
Here’s a screenshot I found in my archives.
A quick detour to the architecture.
The app used a handcrafted toolchain using Djinni framework1 from Dropbox. The idea was wild: Use the MVVM architecture, write the UI layers in Java/Objective-C and the common business logic in C++. The ViewModels and all the core business logic like networking, audio, storage, encryption etc was written in C++. The app used platform components like networking or CoreData using a thin wrapper over the native components.
I led the efforts to port the iOS part to Swift from Objective-C. While I was adept at writing ObjC, writing the ViewControllers in Swift was a breeze compared to ObjC. C++/Java devs found it easier to write Swift code too. I also helped setup the build system and CI to incorporate Swift into it. In the end the buildchain consisted of a diverse set of tools like Carthage, Gradle, Conan, Cucumber and each CI builds ran for 45 minutes on a good day. Writing and debugging cross platform C++ was a tedious job with lots of footguns. At one point I was so frustrated with C++ that I even explored Swift on Android2 and Rust3 as an alternative.
The feature
Apart from crisp clear audio quality, one of the most popular features of the app was the feature to store songs offline . The screenshot above shows the functionality. Android users could even save it on their SD cards. Designing this feature in a cross-platform way was an interesting challenge. The details are bit hazy but I remember we used a salted hash and a nonce to encrypt the songs before storing them locally. The UI layers would call the core C++ encrypt/decrypt functions to get the job done.
The bug
In September 2017 Apple released iOS 11. One Friday morning we got a bug report from an angry user who couldn’t play their ~1GB offline songs after upgrading to iOS 11.0.1. After debugging for some time with no success on iOS 11, it became clear to me that something in the toolchain broke during the OS upgrade. I immediately saved a song offline on an iOS 10 device and upgraded it to iOS 11. As expected the song refused to play after the upgrade. I had found the smoking gun!
Now that I have found a way to reproduce the bug, I was staring at the daunting task ahead of me. I had to manually debug through each step, in XCode, along a trail that went from Swift -> Objective-C -> Objective-C++ -> C++ and back. The bug could be anywhere in this chain.
The second problem was running out of time. Not only it was Friday evening but Apple’s release cadence made the deadline even tighter for the bugfix. Apple publishes only 2 .ipsw4 updates, current and previous version. They had already released an update for iOS 11.0.1 and they would stop signing iOS 10.3.3 (the last iOS 10 release) anytime. I had no option but to go to the office over the weekend and hope Apple wouldn’t release a new .ipsw that weekend5.
Thus began my epic marathon race to debug and fix this error.
Downgrade to iOS 10.3.3
Download a song and see if it plays.
Meticulously debug step by step and note down every tiny detail.
Upgrade to iOS 11.0.1
See if the downloaded song plays
Repeat step 3.
This is how the Xcode debugging window looked like by the way. Imagine the scroll blindness with these symbols. Picture from the Internet.
You might be wondering why I took this expensive path to debug this issue. That’s because I had no idea where in the chain the bug was. The logs were not helpful and only brute force approach helped me reproduce the bug. Nevertheless it was not pleasant. Downgrading and upgrading was time consuming and I couldn’t afford to miss any step. The worst part was I wasn’t even sure which of the 4 languages caused the bug. Finding the needle in the Swift/ObjC/ObjC++/C++ haystack was a nightmare. I threw my hands up after few attempts and went for a walk along the Spree6.
During flâneuring I figured out I was on the wrong track by brute forcing it to the max. I was stepping through each step, logging the output, inspecting the variables and noting down anything worth noting in my diary. Instead of debugging every step starting from the ViewController, I could simply start with the encryption and decryption and see where it fails. I ran back to the office and started the journey and added copious breakpoints and logs around these areas. Soon, which means few upgrades/downgrades later, I hit the jackpot.
The meticulous note taking and logs...