{
    "version": "https:\/\/jsonfeed.org\/version\/1",
    "title": "\/\/ by kei_sidorov",
    "_rss_description": "Technical noes by Kirill Sidorov",
    "_rss_language": "en",
    "_itunes_email": "",
    "_itunes_categories_xml": "",
    "_itunes_image": "",
    "_itunes_explicit": "",
    "home_page_url": "https:\/\/sidorov.tech\/en\/",
    "feed_url": "https:\/\/sidorov.tech\/en\/json\/",
    "icon": "https:\/\/sidorov.tech\/en\/user\/userpic@2x.jpg?1729685594",
    "author": {
        "name": "Kirill Sidorov",
        "url": "https:\/\/sidorov.tech\/en\/",
        "avatar": "https:\/\/sidorov.tech\/en\/user\/userpic@2x.jpg?1729685594"
    },
    "items": [
        {
            "id": "7",
            "url": "https:\/\/sidorov.tech\/en\/all\/why-task-under-mainactor-hurts-performance\/",
            "title": "Why Task under MainActor can hurts performance",
            "content_html": "<p>Transitioning to Swift 6 and the new concurrency model forces you to gradually adapt old code. Some functions become async, they start updating the UI, and legacy code starts to grow with Task insertions. In many of these cases you need to update the UI, and a simple solution appears: mark the whole task as @MainActor. The errors disappear, the UI updates, and the job feels done.<\/p>\n<p>However, this solution can reduce the app’s performance. Let’s look at an example and see why.<\/p>\n<p>Imagine you had a button that requested some data from a couple of sources and then displayed it in the UI.<\/p>\n<pre class=\"e2-text-code\"><code class=\"swift\">\r\nlet workers = dataProvider.getAllWorkers()\r\nvar result: [Schedule] = []\r\nfor worker in workers {\r\n    let schedule = dataProvider.getSchedule(for: worker)\r\n    result.append(schedule)\r\n}\r\ndisplay(schedule: result)\r\n<\/code>\n<\/pre>\n<p>At some point these were very simple operations from a small local file, but later they became network calls, so obviously they must be async. You will need to wrap them in a Task, and to make sure nothing crashes there, you mark the whole block with <samp>@MainActor<\/samp><\/p>\n<pre class=\"e2-text-code\"><code class=\"swift\">\r\nTask { @MainActor in\r\n    setActivityIndicator(visible: true)\r\n    let workers = await dataProvider.getAllWorkers()\r\n    var result: [Schedule] = []\r\n    for worker in workers {\r\n        let schedule = await  dataProvider.getSchedule(for: worker)\r\n        result.append(schedule)\r\n    }\r\n    setActivityIndicator(visible: false)\r\n    display(schedule: result)\r\n}\r\n<\/code>\n<\/pre>\n<p>This code really works and works correctly. The data provider calls run in the background and do not block the main thread. But inside this construct there is a mechanism that starts overloading the main thread.<\/p>\n<h3>What happens under the hood<\/h3>\n<p>Each await creates a suspension point. After the async function finishes, Swift must return execution to the same context where it started, and in this case it means MainActor.<\/p>\n<p>In the example above, four await calls produce four returns to the main thread.<\/p>\n<p>In simple terms, every time MainActor gets such a return, it puts the continuation into its queue, schedules execution on the main thread, and when the main thread is free, it continues the next step.<\/p>\n<p>This results in 5–10–15 extra returns to MainActor just to load the schedule of the next worker. The Swift compiler cannot merge these hops into one because the actor model forbids such optimization.<\/p>\n<p>When many parts of the app work like this, and each of them has several await calls on MainActor, the total load on the main thread grows. On hot paths (like app startup), this can show up as UI stutters, delayed animations, and reduced responsiveness.<\/p>\n<div class=\"callout\"><div class=\"pin\"><p>🤔<\/p>\n<\/div><p>Does the example look too artificial?<\/p>\n<p>Actually no. In hybrid code that is only starting to move toward the new concurrency model, this pattern is very common. Also there is a strong temptation to ask an LLM to fix such issues automatically, and without careful prompting it will often put @MainActor everywhere to avoid crashes. Be careful!<\/p>\n<\/div><h3>How to write it correctly<\/h3>\n<p>Async work must be done outside MainActor. UI updates should happen in one short block.<\/p>\n<p>A correct approach is to use a detached task and not be lazy about adding MainActor.run:<\/p>\n<pre class=\"e2-text-code\"><code class=\"swift\">\r\nTask.detached {\r\n    await MainActor.run { setActivityIndicator(visible: true) }\r\n    let workers = await dataProvider.getAllWorkers()\r\n    var result: [Schedule] = []\r\n    for worker in workers {\r\n        let schedule = await  dataProvider.getSchedule(for: worker)\r\n        result.append(schedule)\r\n    }\r\n    await MainActor.run {\r\n        setActivityIndicator(visible: false)\r\n        display(schedule: result)\r\n    }\r\n}\r\n<\/code>\n<\/pre>\n<p>If you use agents for refactoring, try adding this paragraph to your rules:<\/p>\n<pre class=\"e2-text-code\"><pre class=\"e2-text-code\"><code class=\"\">Always analyze async\/await code from the perspective of MainActor load: do not mark the whole Task as @MainActor if there is heavy work inside. Remember that each await in a MainActor context creates a hop to the main thread. Do the heavy part in the background (Task or structured concurrency), and move UI updates into short blocks via MainActor.run. Use Task.detached only consciously — it breaks structured concurrency. The main goal is to minimize hops back to the main thread and maximize UI responsiveness.<\/code><\/pre><\/pre>\n<h3>Summary<\/h3>\n<p>Marking the whole Task as MainActor is a simple but often incorrect solution when architecture is not well-defined. With several await calls inside, it triggers multiple returns to the main thread, reduces its throughput, and causes UI lag.<\/p>\n<p>The main thread should update only the UI.<br \/>\nAsync work should run outside MainActor.<br \/>\nOne short UI update at the end is the optimal strategy that scales and keeps the app responsive.<\/p>\n",
            "date_published": "2025-11-18T15:07:19+00:00",
            "date_modified": "2025-11-18T15:16:33+00:00",
            "_date_published_rfc2822": "Tue, 18 Nov 2025 15:07:19 +0000",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "7",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": []
            }
        },
        {
            "id": "6",
            "url": "https:\/\/sidorov.tech\/en\/all\/storekit-trials-and-double-entitlements-a-real-world-edge-case-e\/",
            "title": "StoreKit trials and double entitlements — a real-world edge case explained",
            "content_html": "<p>Recently, we had an interesting case with in-app purchases in our app — so unexpected that we initially thought it was a bug in StoreKit. But then it turned out — everything works exactly as intended. There’s just nowhere this behavior is properly documented.<\/p>\n<p>We’re building an app that helps users in the US prepare for driver’s license exams — for cars, motorcycles, and commercial vehicles. During this process, users go through a series of quizzes and exam marathons, and after successful completion, they receive a certificate required for the DMV. Access to the app is subscription-based, and we recently started testing a 3-day free trial to improve conversion.<\/p>\n<p>During the trial, users get access to the entire app: all tests, marathons, and exams. But the certificate becomes available only after full payment. That’s intentional: it has legal value, and we can’t give it out for free.<\/p>\n<p>About a week after launching the trial, support messages started coming in: “I paid for a subscription, but the certificate is still locked.” We first investigated progress sync issues. Then we were just confused. The classic “my hand is the same but it doesn’t hurt.” We looked into the receipts — the Apple payment looked valid, but the app still showed the user as being on a trial. How was that possible? We started to suspect StoreKit caching issues or bugs. But then more users reported the same. All had real Apple receipts. Strange!<\/p>\n<p>We kept digging and finally figured it out:<\/p>\n<ul>\r\n  <li>Users completed all the tests in just 1–2 days — before the trial ended.<\/li>\r\n  <li>They saw the certificate was locked and didn’t want to wait.<\/li>\r\n  <li>They went to app settings → Manage Subscriptions → picked another subscription from the same group but without a trial — and purchased it.<\/li>\r\n  <li>Both subscriptions shared the same <samp>subscriptionGroupID<\/samp>, and both appeared in <samp>currentEntitlements<\/samp>.<\/li>\r\n<\/ul>\n<p>But one of them had a trial, and the other was a full paid subscription. We didn’t expect that two active transactions could exist at the same time. We assumed that switching to a different subscription in the same group would cancel the trial and leave only one active transaction. In fact, we thought such a combo wasn’t even possible — we had already removed the no-trial option from all our paywalls.<\/p>\n<p>So, our logic looked something like this (simplified):<\/p>\n<pre class=\"e2-text-code\"><code class=\"swift\">\r\nextension Transaction {\r\n    \/\/\/ Is this a trial transaction?\r\n    var isTrial: Bool {\r\n        guard \r\n                let expirationDate = expirationDate, \r\n                Date() < expirationDate \r\n        else { return false }\r\n        return offer?.paymentMode == .freeTrial\r\n    }\r\n\r\n    \/\/\/ Does the user have a trial subscription?\r\n    static var hasTrialSubscription: Bool {\r\n        get async {\r\n            for await result in Transaction.currentEntitlements {\r\n                do {\r\n                    if try result.payloadValue.isTrial {\r\n                        return true\r\n                    }\r\n                } catch { continue }\r\n            }\r\n            return false\r\n        }\r\n    }\r\n}\r\n<\/code>\n<\/pre>\n<p><br \/><\/p>\n<div class=\"callout\"><div class=\"pin\"><p>☝🏼<\/p>\n<\/div><p>When a user switches subscriptions from settings — within the same group — during an active trial, two active transactions appear: one for the trial period, and another for the paid subscription, both sharing the same <samp >originalID<\/samp>.<\/p>\n<\/div><p><br \/><\/p>\n<pre class=\"e2-text-code\"><code class=\"swift\">\r\nextension Transaction {\r\n    \/\/\/ Is this a trial transaction?\r\n    var isTrial: Bool {\r\n        guard let expirationDate = expirationDate, Date() < expirationDate else { return false }\r\n        return offer?.paymentMode == .freeTrial\r\n    }\r\n\r\n    \/\/\/ Does the user have a trial subscription?\r\n    static var hasTrialSubscription: Bool {\r\n        get async {\r\n            for await result in Transaction.currentEntitlements {\r\n                do {\r\n                    if try result.payloadValue.isTrial {\r\n                        return true\r\n                    }\r\n                } catch { continue }\r\n            }\r\n            return false\r\n        }\r\n    }\r\n}\r\n<\/code>\n<\/pre>\n<p><h2>Solution<\/h2><\/p>\n<p>The fix is simple — you need to group transactions by <samp>originalID<\/samp> and check the offer status only on the most recent one:<\/p>\n<pre class=\"e2-text-code\"><code class=\"swift\">\r\nextension Transaction {\r\n    \/\/\/ Does the user have a trial subscription?\r\n    static var hasTrialSubscription: Bool {\r\n        get async {\r\n            var latestTransactions: [UInt64: StoreKit.Transaction] = [:]\r\n            for await result in Transaction.currentEntitlements {\r\n                do {\r\n                    let transaction = try result.payloadValue\r\n                    if\r\n                        let latestTransaction = latestTransactions[transaction.originalID],\r\n                        transaction.purchaseDate < latestTransaction.purchaseDate\r\n                    {  continue }\r\n                    latestTransactions[transaction.originalID] = transaction\r\n                } catch { continue }\r\n            }\r\n            return latestTransactions.values.contains(where: { $0.isTrial })\r\n        }\r\n    }\r\n}\r\n<\/code>\n<\/pre>\n<p><h2>Final note<\/h2><\/p>\n<p>StoreKit doesn’t invalidate the original trial transaction in <samp>currentEntitlements<\/samp> even if the user switches subscriptions within the same group. It’s not a bug — it’s expected behavior. You just need to be aware of it, especially if you restrict functionality during the trial period.<\/p>\n<p>A note to self in the “write it down so you don’t have to dig later” spirit. Hopefully this saves someone a few hours and some support tickets.<\/p>\n",
            "date_published": "2025-06-26T22:46:35+00:00",
            "date_modified": "2025-06-26T19:20:19+00:00",
            "_date_published_rfc2822": "Thu, 26 Jun 2025 22:46:35 +0000",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "6",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": []
            }
        },
        {
            "id": "5",
            "url": "https:\/\/sidorov.tech\/en\/all\/mastering-screen-recording-detection-in-ios-apps\/",
            "title": "Mastering screen recording detection in iOS Apps",
            "content_html": "<p>The other day, I got a bug report from a real user, complete with all the right details—description and a screen recording. But there’s one small issue: no clue which version of the app it’s from. So, I decided to add an overlay showing the app version and build number during screen recording or when taking a screenshot. Sounds simple, right? I remember there are some notifications for that—just subscribe, show\/hide the overlay, and done! Shouldn’t take more than 30 minutes, right? Well, it turned out to be way trickier than I expected!<\/p>\n<p><cut\/><\/p>\n<div class=\"callout\"><div class=\"pin\"><p>🚨<\/p>\n<\/div><p>If you’re eager to try it out, here’s a link to the library and projects for both SwiftUI and UIKit. Just remember—use at your own risk! <a href=\"https:\/\/github.com\/kei-sidorov\/CaptureGuard\">📦 Library and demo project.<\/a><\/p>\n<\/div><p><br \/><\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/iphone-x-and-later-take-screenshot@2x.png\" width=\"350\" height=\"136.5\" alt=\"\" \/>\n<\/div>\n<p>Apple allows tracking screen recordings — no surprises here. Here’s your <a href=\"https:\/\/developer.apple.com\/documentation\/uikit\/uiscreen\/2921652-captureddidchangenotification\">event in Notification Center<\/a>, and here’s your <a href=\"https:\/\/developer.apple.com\/documentation\/uikit\/uitraitcollection\/4292628-scenecapturestate\">flag in UIScreen<\/a>. But things aren’t so great with screenshots: there’s only a <a href=\"https:\/\/developer.apple.com\/documentation\/uikit\/uiapplication\/1622966-userdidtakescreenshotnotificatio\">notification that a screenshot was taken<\/a>, which comes *after* the fact. Meaning, there’s no way to prepare ahead of time. I thought, “What the heck, I’ve definitely seen apps that instantly swap views when a screenshot is taken!” (spoiler: nope, I didn’t, false memory)<\/p>\n<pre class=\"e2-text-code\"><code class=\"swift\">\r\nstruct ContentView: View {\r\n    @State private var isScreenCaptured = false\r\n\r\n    private let center = NotificationCenter.default\r\n    \r\n    var body: some View {\r\n        ZStack {\r\n            Text(\"Main Content\")\r\n            \r\n            if isScreenCaptured {\r\n                Text(\"Screen Recording in Progress\")\r\n                            .foregroundColor(.white)\r\n            }\r\n        }\r\n        .onReceive(center.publisher(for: UIScreen.capturedDidChangeNotification)) { _ in\r\n            \/\/ ⚠️ Deprecated в iOS 18+, it's better to use `sceneCaptureState`\r\n            isScreenCaptured = UIScreen.main.isCaptured \r\n        }\r\n        \/\/\/ You can also subscribe to`UIApplication.userDidTakeScreenshotNotification`\r\n        \/\/\/ but notification is sent after screenshot is taken\r\n        \/\/\/ there's no way to prepare ahead of time\r\n    }\r\n}\r\n<\/code>\n<\/pre>\n<h3>Good artists copy, great artists steal<\/h3>\n<p>I definitely remember Telegram hiding messages in screenshots in secret chats. All I need is to figure out what events they catch and what they do to hide it. Hopefully, the iOS client is open sourced. Let’s go! Git clone, grep ‘screenshot’. About 20 minutes later, I dig up this method called <samp>setLayerDisableScreenshots<\/samp> in <a href=\"https:\/\/github.com\/TelegramMessenger\/Telegram-iOS\/blob\/1d1ea447ad3fdbf9483ce337c532cf0793f3ab3a\/submodules\/UIKitRuntimeUtils\/Source\/UIKitRuntimeUtils\/UIKitUtils.m#L243\">UIKitUtils.m<\/a>, which uses an internal <samp>UITextField<\/samp> view to make any layer hideable on any screen capture, whether it’s a screenshot or a video recording.<\/p>\n<p>Basically, it “buffs” any layer and lets it disappear during recording. But how does it work? Some event must be triggering the layer to hide. To save time on experiments, I decided to open good ol’ Hopper Disassembler and dig into <samp>UIKitCore<\/samp>.<\/p>\n<p><br \/><\/p>\n<div class=\"callout\"><div class=\"pin\"><p>☝🏻<\/p>\n<\/div><p>To quickly find the path to the right framework, just set a breakpoint somewhere in your Swift code and run <samp>po Bundle(for: UIView.self)<\/samp>. Replace `UIView` with whatever class you need.<\/p>\n<\/div><p><br \/><\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/hopper@2x.png\" width=\"1122\" height=\"783\" alt=\"\" \/>\n<\/div>\n<p>So, the digging leads to this conclusion: there really aren’t any events. When you set <samp>isSecureTextEntry<\/samp>, the text field gives the layer of the internal container some special attributes (<samp>disableUpdateMask<\/samp>), which are sent to the render server. Then it decides whether to draw the view or not. Well, makes sense, it’s secure. This way, secure fields won’t be drawn even if the app freezes and the main thread stops responding. Or if the app in background and run loop is suspended.<\/p>\n<p><br \/><\/p>\n<div class=\"callout\"><div class=\"pin\"><p>⚠️<\/p>\n<\/div><p>Even though we can clearly see the flag value 0x12 here, it’s not a good idea to rely on it. On my iOS 18, it works only half the time, so I stuck with the Telegram solution — it works like a charm!<\/p>\n<\/div><p><br \/><\/p>\n<p>Here is swift adaptation of Telegram’s approach:<\/p>\n<pre class=\"e2-text-code\"><code class=\"swift\">\r\nimport UIKit\r\n\r\nprivate let uiKitTextField = UITextField()\r\nprivate var captureSecuredView: UIView?\r\n\r\npublic extension CALayer {\r\n\tfunc makeHiddenOnCapture() {\r\n\t\tlet captureSecuredView: UIView? = captureSecuredView\r\n\t\t\t?? uiKitTextField.subviews\r\n\t\t\t\t.first(where: { NSStringFromClass(type(of: $0)).contains(\"LayoutCanvasView\") })\r\n\t\t\r\n\t\tlet originalLayer = captureSecuredView?.layer\r\n\t\tcaptureSecuredView?.setValue(self, forKey: \"layer\")\r\n\t\tuiKitTextField.isSecureTextEntry = false\r\n\t\tuiKitTextField.isSecureTextEntry = true\r\n\t\tcaptureSecuredView?.setValue(originalLayer, forKey: \"layer\")\r\n\t}\r\n}\r\n<\/code>\n<\/pre>\n<h3>Implementing it in SwiftUI<\/h3>\n<p>Since I needed to solve this in a SwiftUI project, just hiding UIKit layers wasn’t enough. My first idea was to use masks. Like, you take a ZStack, draw a black and white view in there, where the black one is a <samp>UIViewRepresentable<\/samp> with a hideable layer. Let’s code it up:<\/p>\n<pre class=\"e2-text-code\"><code class=\"swift\">\r\nstruct HiddenOnCaptureColorView: UIViewRepresentable {\r\n\t\r\n\tlet color: UIColor\r\n\t\r\n\tfunc makeUIView(context: Context) -> UIView {\r\n\t\tlet view = UIView()\r\n\t\tview.layer.makeHiddenOnCapture()\r\n\t\tupdateViewColor(view: view)\r\n\t\treturn view\r\n\t}\r\n\t\r\n\tfunc updateUIView(_ uiView: UIView, context: Context) {\r\n\t\tupdateViewColor(view: uiView)\r\n\t}\r\n\t\r\n\tfunc updateViewColor(view: UIView) {\r\n\t\tview.backgroundColor = color\r\n\t}\r\n}\r\n\r\nstruct VisibleOnlyOnCaptureModifier: ViewModifier {\t\r\n\tfunc body(content: Content) -> some View {\r\n\t\tcontent.mask {\r\n\t\t\tZStack {\r\n\t\t\t\tColor.black\r\n\t\t\t\tHiddenOnCaptureColorView(color: .white)\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n<\/code>\n<\/pre>\n<p>But here’s the catch — I forgot that <b>masks don’t work with black\/white colors, but with transparent\/opaque pixels<\/b>. What to do? A quick Google search didn’t help, but ChatGPT had my back — there’s this method on View called <samp><a href=\"https:\/\/developer.apple.com\/documentation\/swiftui\/view\/luminancetoalpha()\">luminanceToAlpha()<\/a><\/samp>, which blends the pixels, making black ones transparent and white ones opaque. I couldn’t believe it would work, but it actually did!<\/p>\n<pre class=\"e2-text-code\"><code class=\"swift\">\r\nstruct VisibleOnlyOnCaptureModifier: ViewModifier {\t\r\n\tfunc body(content: Content) -> some View {\r\n\t\tcontent.mask {\r\n\t\t\tZStack {\r\n\t\t\t\tColor.black\r\n\t\t\t\tHiddenOnCaptureColorView(color: .white)\r\n\t\t\t}\r\n\t\t\t.compositingGroup()\r\n\t\t\t.luminanceToAlpha()\r\n\t\t}\r\n\t}\r\n}\r\n<\/code>\n<\/pre>\n<p>Yay! It turned out to be easier than I thought! Just swap the colors, and you get a view that’s either visible in a screenshot or hidden.<\/p>\n<p><br \/><\/p>\n<div class=\"callout\"><div class=\"pin\"><p>⚠️<\/p>\n<\/div><p>This only works on a real device though, you can’t pull this off in a simulator, and UI tests won’t work either. Keep that in mind.<\/p>\n<\/div><p><br \/><\/p>\n<p><br \/><\/p>\n<div class=\"callout\"><div class=\"pin\"><p>⚠️<\/p>\n<\/div><p>Since we’re manipulating masks here, they don’t affect the layout at all. Remember, they work like the alpha modifier — they are factored into the layout calculation but just hidden or shown during screen recording.<\/p>\n<\/div><p><br \/><\/p>\n<h3>Another Half-Day Adventure<\/h3>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/uikit-adventure2@2x.jpg\" width=\"336\" height=\"189\" alt=\"\" \/>\n<\/div>\n<p>By this point, I had already posted on my Telegram channel and decided to write this article. I just needed full support for UIKit. Hiding views there is easy, but showing them…<\/p>\n<p>Again, it seemed simple — subclass the view, apply a mask to the layer, and done! I set up a working experiment — and nothing worked. <b>Once again, I forgot that views need to be transparent\/opaque, not black\/white.<\/b> But how do I get the <samp>luminanceToAlpha<\/samp> effect? UIKit doesn’t have that.<\/p>\n<h3>Going Deeper<\/h3>\n<p>I was about to give up, but my curiosity wouldn’t let it go. How could this be? I managed it in SwiftUI, but not in my good ol’ UIKit!<br \/>\nSo, I decided to check out the View Hierarchy. I launched a minimal SwiftUI app and took a look at what kind of “kit” views it draws, since all SwiftUI views eventually turn into layers that are sent to the render server.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/xcode@2x.png.jpg\" width=\"2560\" height=\"1684\" alt=\"\" \/>\n<\/div>\n<p>I won’t go through my entire view tree traversal, but pretty quickly, I found exactly what I needed. There it was — my view, with a mask, containing two layers and a FILTER with the name luminanceToAlpha! Awesome!<\/p>\n<p>All that was left was a bit of private ObjC magic, and we’re good to go. So, what we need is a layer with two sublayers (one black and one white), where one is prepped to hide during screenshots, and the parent will mix them using the filter. This is a special private <samp>CAFilter<\/samp> (not to be confused with <samp><a href=\"https:\/\/developer.apple.com\/documentation\/coreimage\/cifilter\">CIFilter<\/a><\/samp>), which isn’t easy to access. But, thanks to Objective-C runtime magic — no Hogwarts required — we can pull it off!<\/p>\n<pre class=\"e2-text-code\"><code class=\"swift\">\r\nfunc makeFilter() -> NSObject? {\r\n\tguard let filterClass: AnyClass = NSClassFromString(\"CAFilter\") else { return nil }\r\n\tlet selector = Selector(\"filterWithName:\")\r\n\ttypealias Function = @convention(c) (AnyClass, Selector, String) -> AnyObject?\r\n\t\r\n\tguard let filterWithName = class_getClassMethod(filterClass, selector) else { return nil }\r\n\r\n\tlet implementation = method_getImplementation(filterWithName)\r\n\tlet function = unsafeBitCast(implementation, to: Function.self)\r\n\t\r\n\tguard let filter = function(filterClass, selector, \"luminanceToAlpha\") as? NSObject else { return nil }\r\n\treturn filter\r\n}\r\n<\/code>\n<\/pre>\n<h3>Victory!<\/h3>\n<p>Great, I achieved what I wanted and even more. I wrapped it all up in a <a href=\"https:\/\/github.com\/kei-sidorov\/CaptureGuard\">📦 SPM package<\/a>, which you can use in your project. It has two targets — one for SwiftUI and one for UIKit. The SwiftUI version is safe to use, as there are no private symbols involved. But with the UIKit version, you need to be extra cautious because of the CAFilters. It’s nothing too risky, but it’s better not to use it directly. Instead, you can take a look at the solution and maybe obfuscate the symbols before releasing it to the App Store.<\/p>\n",
            "date_published": "2024-10-23T12:19:36+00:00",
            "date_modified": "2024-10-23T12:18:35+00:00",
            "image": "https:\/\/sidorov.tech\/en\/pictures\/iphone-x-and-later-take-screenshot@2x.png",
            "_date_published_rfc2822": "Wed, 23 Oct 2024 12:19:36 +0000",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "5",
            "_e2_data": {
                "is_favourite": true,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": [
                    "https:\/\/sidorov.tech\/en\/pictures\/iphone-x-and-later-take-screenshot@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/hopper@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/uikit-adventure2@2x.jpg",
                    "https:\/\/sidorov.tech\/en\/pictures\/xcode@2x.png.jpg"
                ]
            }
        },
        {
            "id": "3",
            "url": "https:\/\/sidorov.tech\/en\/all\/classic-concurrency-in-ios\/",
            "title": "Classic concurrency in iOS",
            "content_html": "<p>In 2000, Apple released an open Unix-like operating system <a href=\"https:\/\/en.wikipedia.org\/wiki\/Darwin\">Darwin<\/a>, which would serve as the foundation for the first version of Mac OS X – 10.0 the following year. This, in turn, would become the ancestor of all Apple operating systems, including modern macOS and iOS, and extending to watchOS on watches and audioOS on smart speakers in the future.<\/p>\n<p><cut\/><\/p>\n<p>Darwin is built on <a href=\"https:\/\/en.wikipedia.org\/wiki\/XNU\">XNU<\/a>, a hybrid kernel that includes a microkernel <a href=\"https:\/\/en.wikipedia.org\/wiki\/Mach\">Mach<\/a> and some components from the BSD family of operating systems. In the context of this note, it is important to note that Darwin inherited the Unix process model and <a href=\"https:\/\/en.wikipedia.org\/wiki\/POSIX\">POSIX<\/a> thread model from BSD, and it reimagined processes as tasks from Mach.<\/p>\n<p>For working with threads, OS developers have access to the C library <a href=\"https:\/\/en.wikipedia.org\/wiki\/POSIX_Threads\">pthread<\/a>, which is the first layer of abstraction in our operating systems. Although it is still possible to use pthread in your code to this day, Apple has never recommended using pthread directly. From the early versions of Mac OS, developers had an Apple abstraction layer over pthread called <a href=\"https:\/\/developer.apple.com\/documentation\/foundation\/thread\">NSThread<\/a>.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/Runloop@2x.png\" width=\"765\" height=\"333\" alt=\"\" \/>\n<\/div>\n<p>NSThread is the second layer of abstraction thoughtfully provided by Apple. Besides the more familiar Objective-C syntax for creating and managing threads, the company introduced <a href=\"https:\/\/developer.apple.com\/documentation\/foundation\/runloop\">RunLoop<\/a> – a task and event servicing loop. RunLoop uses Mach microkernel tools (ports, XPC, events, and tasks) to manage POSIX threads, and it can put a thread to sleep when it has nothing to do and wake it up when there is work to be done.<\/p>\n<p>Of course, you can still use NSThread today, but it’s essential to remember that creating a thread is an expensive operation because we must request it from the OS itself. Additionally, thread synchronization and resource access can be somewhat inconvenient for everyday development. That’s why Apple developers started thinking about solving the convenience issues of working with asynchronous code.<\/p>\n<h3>Grand Central Dispatch (GCD)<\/h3>\n<p>With the release of iOS 4, Apple elevated developers to another level of abstraction by introducing <a href=\"https:\/\/developer.apple.com\/documentation\/DISPATCH\">Grand Central Dispatch<\/a> (GCD) and introducing the concept of queues and tasks to organize asynchronous code. GCD is a high-level API that allows you to create custom queues, manage tasks within them, handle synchronization issues, and do so as efficiently as possible.<\/p>\n<p>Since queues are just an abstraction layer, underneath, they still utilize the same system threads, but the mechanism of their creation and usage is optimized. GCD has a pool of pre-created threads and efficiently distributes tasks, maximizing processor utilization when necessary. Developers no longer need to worry about the threads themselves, their creation, or management.<\/p>\n<p>In addition to creating a new queue manually, GCD provides access to the main queue, where UI work is performed, and access to several system (global) queues.<\/p>\n<p>GCD queues come in two types:<\/p>\n<ul>\n<li>Serial queues – tasks are executed sequentially, one after another.<\/li>\n<li>Concurrent queues – tasks are executed simultaneously.<\/li>\n<\/ul>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/Concurent@2x.png\" width=\"809\" height=\"393\" alt=\"\" \/>\n<\/div>\n<p>By default, a queue is created with serial task execution, and to create a concurrent queue, you need to specify it explicitly:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">let queue = DispatchQueue(&quot;com.company.name.app&quot;, attributes: .concurrent)<\/code><\/pre><p>As mentioned earlier, GCD provides pre-created global queues with different priorities:<\/p>\n<ul>\n<li><samp>global(qos: .userInteractive)<\/samp> – For tasks that interact with the user at the moment and take very little time.<\/li>\n<li><samp>global(qos: .userInitiated)<\/samp> – For tasks initiated by the user that require feedback.<\/li>\n<li><samp>global(qos: .utility)<\/samp> – For tasks that require some time to execute and don’t need immediate feedback.<\/li>\n<li><samp>global(qos: .background)<\/samp> – For tasks unrelated to visualization and not time-critical.<\/li>\n<\/ul>\n<p>⚠️ All global queues are queues with concurrent task execution.<\/p>\n<h2>Task Queuing<\/h2>\n<p>Tasks in any queue, whether concurrent or serial, can be enqueued synchronously or asynchronously. When a task is enqueued asynchronously, the code following the task enqueue continues to execute.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/Async@2x.png\" width=\"809\" height=\"478\" alt=\"\" \/>\n<\/div>\n<pre class=\"e2-text-code\"><code class=\"\">...\r\nDispatchQueue.global().async {\r\n   processImage()\r\n}\r\ndoRequest() \/\/ executes immediately, without waiting processImage() to finish<\/code><\/pre><p>In the case of synchronous enqueueing, the code following it will not continue its execution until the task queued has been completed.<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">...\r\nDispatchQueue.global().sync {\r\n   processImage()\r\n}\r\ndoRequest() \/\/ will wait to finish processImage()<\/code><\/pre><h2>DispatchWorkItem<\/h2>\n<p><a href=\"https:\/\/developer.apple.com\/documentation\/dispatch\/dispatchworkitem\">DispatchWorkItem<\/a> is a specialized GCD class that provides a more object-oriented alternative to using closures (blocks) for queuing tasks. Unlike the regular task enqueue, DispatchWorkItem offers the ability to:<\/p>\n<ul>\n<li>Specify the task’s priority.<\/li>\n<li>Receive notification of task completion.<\/li>\n<li>Cancel the task.<\/li>\n<\/ul>\n<div class=\"callout\"><div class=\"pin\"><p>☝️<\/p>\n<\/div><p>It’s important to understand that task cancellation works until the moment the task starts, meaning while it is still in the queue. If GCD has already started executing the code inside the DispatchWorkItem block, cancelling the task will have no effect, and the code will continue to run.<\/p>\n<\/div><h2>NSOperation<\/h2>\n<p>GCD provides a convenient abstraction for writing asynchronous code, both in terms of conceptual ideas and syntax. However, this wasn’t always the case. In Objective-C (and in the early versions of Swift), working with queues and tasks was not as convenient as in modern Swift, and it seemed to go against the very name of the programming language. Apple needed to provide an object-oriented alternative to GCD, and it did so by introducing <a href=\"https:\/\/developer.apple.com\/documentation\/foundation\/operation\">NSOperation<\/a>.<\/p>\n<p>NSOperations are essentially the same as queues, with OperationQueue replacing DispatchQueue, and Operation replacing DispatchWorkItem. However, in addition to the object-oriented syntax, Operations offer two cool advantages:<\/p>\n<ul>\n<li>The ability to specify the maximum number of concurrently executing tasks in a queue.<\/li>\n<li>The ability to specify dependent operations, thus creating a hierarchy of operations. In this case, an operation will only start when all the operations it depends on have completed.<\/li>\n<\/ul>\n<p>The latter point is very convenient for building a chain of requests when one request requires information from several others to execute.<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">class CustomOperation: Operation {\r\n    var outputValue: Int?\r\n\r\n    var inputValue: Int {\r\n        return dependencies\r\n            .filter({ $0 is CustomOperation })\r\n            .first as? CustomOperation\r\n            .outputValue ?? 0\r\n    }\r\n    ...\r\n}\r\n\r\nlet operation1 = CustomOperation()\r\noperation1.start()\r\n\r\nlet operation2 = CustomOperation()\r\noperation2.addDependency(operation1)\r\noperation2.start()<\/code><\/pre><h2>Errors and Issues<\/h2>\n<p>When discussing asynchronous and multithreading programming, it’s crucial to address some of the fundamental mistakes that developers can make when writing code that runs in parallel.<\/p>\n<h2>Race Condition<\/h2>\n<p>Race Condition is a design error in multithreaded systems where access to a resource is not synchronized, and the outcome can depend on the order of code execution. It may be challenging to grasp this concept without an example, so it’s better to understand it based on a specific case.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/Racecodition@2x.png\" width=\"809\" height=\"296\" alt=\"\" \/>\n<\/div>\n<p>Illustration above shows two threads where some code is being incremented. To increment the value of a variable, the processor needs to perform three actions:<\/p>\n<ol start=\"1\">\n<li>Read the value of the variable from shared memory into a register.<\/li>\n<li>Increase the value in the register by 1.<\/li>\n<li>Write the value from the register back to shared memory.<\/li>\n<\/ol>\n<p>As you can see in the illustration, the second thread reads the value of the variable before the first thread has had a chance to write the incremented value. As a result, one increment of the counter is lost, which can lead to both harmless bugs and serious issues, potentially resulting in a crash. This situation is a classic example of a race condition.<\/p>\n<div class=\"callout\"><div class=\"pin\"><p>💡<\/p>\n<\/div><p>The most serious consequence of a race condition is considered the case of the Therac-25, a medical device used for radiation therapy <a href=\"https:\/\/en.wikipedia.org\/wiki\/Therac-25\">Therac-25<\/a>. In this instance, a race condition led to incorrect values in a variable used to determine the operating mode of the radiation mechanism. This resulted in dangerous radiation overdoses to patients and underscores the critical importance of addressing race conditions in software, especially in safety-critical systems.<\/p>\n<\/div><h2>Deadlock<\/h2>\n<p>Deadlock, or mutual deadlock, is a multithreaded software error where multiple threads can indefinitely wait for the release of a particular resource.<\/p>\n<p>Let’s break it down with an example: Suppose task A in thread A locks access to a resource A, let’s say it’s a SettingsStorage. Task A locks access to the storage, reads some values from there, and performs some calculations. Meanwhile, task B starts and locks access to resource B, which could be a database. To perform some calculations, task B also needs access to SettingsStorage, and it starts waiting for task A to release it. At the same time, task A needs access to the database, but it’s already locked by task B. This results in a mutual deadlock: task A is waiting for the database, which is locked by task B, which is waiting for the storage, locked by task A.<\/p>\n<h2>Priority inversion<\/h2>\n<p>Priority inversion is an error that leads to a change in the priorities of threads, which was not intended by the developer.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/PriorityInversion@2x.png\" width=\"809\" height=\"311\" alt=\"\" \/>\n<\/div>\n<p>Let’s say we have only two tasks with different priorities and only one resource, which, once again, is the database. The low-priority task is placed in the queue first. It starts its work and at time T1, it needs the database and locks access to it. Almost immediately after that, the high-priority task starts and preempts the low-priority one. Everything goes as planned until time T3, where the high-priority task tries to acquire the database. Since the resource is locked, the high-priority task is put into a waiting state, while the low-priority task gets CPU time. The time interval between T3 and T4 is called priority inversion. During this interval, there is a logical inconsistency with scheduling rules – the higher-priority task is waiting while the lower-priority task is executing.<\/p>\n",
            "date_published": "2023-03-22T21:55:00+00:00",
            "date_modified": "2024-02-01T21:55:46+00:00",
            "image": "https:\/\/sidorov.tech\/en\/pictures\/Runloop@2x.png",
            "_date_published_rfc2822": "Wed, 22 Mar 2023 21:55:00 +0000",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "3",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": [
                    "https:\/\/sidorov.tech\/en\/pictures\/Runloop@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/Concurent@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/Async@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/Racecodition@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/PriorityInversion@2x.png"
                ]
            }
        },
        {
            "id": "4",
            "url": "https:\/\/sidorov.tech\/en\/all\/xcode-debugging-101-debugger-explained\/",
            "title": "Xcode debugging 101 — debugger explained",
            "content_html": "<p>Every developer, regardless of their skill level or the type of current task, is constantly in a familiar cycle: we write code, run it, and debug. The number of iterations may vary, but we do this multiple times daily.<\/p>\n<p>According to some studies, we spend up to 60% of our time on debugging – and this is an average value, for some, especially for novice developers, it may be even more. This article aims to reduce this time and make the debugging process more efficient and enjoyable.<\/p>\n<h2>Example<\/h2>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/source_code@2x.png\" width=\"720\" height=\"261.5\" alt=\"\" \/>\n<\/div>\n<p>Let’s take a look at the process from the inside. I’ve provided a somewhat unusual example, but it’s closely related to our daily routine and describes most cases quite well. This code calculates the height for a table view cell: the height depends on certain constants and other functions. Let’s assume there’s a bug where the height is calculated incorrectly, and our task is to study this function and fix it.<\/p>\n<p>The first thing that comes to mind is to use `print` for debugging to check the value of the `showTitle` flag at the calculation moment.<\/p>\n<p>We run the project – imagine that it’s big, monolithic, and (here you don’t need your imagination anymore) Xcode is not perfect. Most often, we encounter a familiar situation: we add one line and wait several minutes for compilation. -- Aaand it’s time for Twitter or TikTok (depending on whether you know ObjC or not 😁 ), and a couple of minutes of compilation turn into 20 --. Besides building and running, we also need to reproduce the conditions, reach the desired screen, follow some steps, and only then check the output of the freshly added <samp>print<\/samp>.<\/p>\n<p>And it’s good if the hunch is correct, but as it usually happens, it’s not easy to place prints in useful places right away. Knowing the state of the flag by itself tells us almost nothing, so it was decided to add another <samp>print<\/samp> with information about the element for which the calculation is made. Then we wanted to override some constant values and make an exception for one element. It looks something like this:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/edited_code@2x.png\" width=\"720\" height=\"318.5\" alt=\"\" \/>\n<\/div>\n<p>And again, we’ll face with building, reproducing conditions, and analyzing. Disclaimer: debugging via prints is not inherently bad. Sometimes it’s the only way to debug something complex, for example, when a bug only occurs in a release build with some optimizations enabled. But today, we’re talking about simple, routine scenarios that most often happen during development.<\/p>\n<h2>Breakpoints<\/h2>\n<p>We’ve already established that debugging through prints is not very effective, and we need a tool that will make our lives easier. Breakpoints are such a tool! This mechanism is present in almost all development environments, on many platforms and languages. It’s implemented differently in different places, but Apple has provided us with a powerful and flexible breakpoint system. However, while working with different developers, I noticed that in most cases, people use breakpoints just to pause the program – simply to make sure that its execution is going according to the planned scenario. Sometimes people use the debug console and the `po` command, but only when they need to check the state of a variable once. I suggest exploring additional capabilities of the debugger built into our IDE and providing examples of situations where they can be useful.<\/p>\n<h2>Conditional breakpoints<\/h2>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/bp_edit@2x.png\" width=\"720\" height=\"224\" alt=\"\" \/>\n<\/div>\n<p>Let’s start with the obvious: conditional breakpoints. Strangely enough, the line that allows you to specify conditions for a breakpoint is always right in front of us, but for some reason, people are surprised by this possibility. For those surprised by the mere presence of such a dialog – you can see it by double-clicking on the breakpoint itself. In the Condition field, you can write any expression that can return a Boolean value, whether it’s a comparison of a variable from the current scope or even the value of a singleton. But be careful – a slow-to-calculate expression can significantly reduce your program’s performance.<\/p>\n<h2>Skipping<\/h2>\n<p>The next feature, which is also often overlooked by developers, is the ability to ignore a certain number of hits. This feature can come in handy, for example, in recursive functions to see what’s happening at the N-th depth or to check the result of a function for the N-th element of an array. In the example with an array, this approach may be preferable to setting a condition, as it doesn’t require evaluating an expression.<\/p>\n<h2>Actions<\/h2>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/bp_actions@2x.png\" width=\"720\" height=\"245.5\" alt=\"\" \/>\n<\/div>\n<p>But the most interesting part for us is behind the “Add Action” button. This button allows you to add an additional action that will be triggered when the breakpoint is hit. As you can see, there are 6 types of actions you can use to enhance a breakpoint:<\/p>\n<ol start=\"1\">\n<li><b>Apple script.<\/b> Allows you to run a script in the AppleScript language.<\/li>\n<li><b>Capture GPU Frame.<\/b> This may be required for debugging applications using the Metal engine.<\/li>\n<li><b>Debugger command.<\/b> Allows you to execute a debugger command. We will talk about this later.<\/li>\n<li><b>Log message.<\/b> Allows you to print a text message to the log.<\/li>\n<li><b>Shell command.<\/b> Lets you execute an arbitrary command in the default shell environment, like sh\/bash\/zsh.<\/li>\n<li><b>Sound.<\/b> Allows you to play a sound through the computer’s speakers where Xcode is running.<\/li>\n<\/ol>\n<p>I won’t go into detail about the first two types as they are too specific and are unlikely to be useful for most developers. I’ll also skip the last point because there’s not much to say about it. However, it’s worth remembering – it can come in handy when you need to quickly perform an action in your application following a trigger, which could be the sound from a breakpoint set in the right place.<\/p>\n<h2>Log messages<\/h2>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/bp_log_actions@2x.png\" width=\"720\" height=\"361.5\" alt=\"\" \/>\n<\/div>\n<p>Let’s take a closer look at the “Log message” type of additional action. When you select it, you’ll have an input field for the message format at your disposal. Please note that in this input field, you can use helpful placeholders, two of which allow you to include information about the breakpoint, and one, the most useful one, allows you to insert the result of evaluating an arbitrary expression. This expression can be a variable or any other construct of the programming language you are using. However, this won’t make much sense unless you check the “Automatically continue after evaluating actions” checkbox. It’s this checkbox, when combined with any of the actions, that allows us to save time during debugging. You no longer need to write <samp>print()<\/samp>, rebuild the project, and wait for ages. At any moment, without restarting the project, you have access to the debug console’s output of any information about the program’s execution. And for those who are adept at debugging, Apple has provided the ability to play back expressions using the built-in speech synthesizer.<\/p>\n<h2>Shell command<\/h2>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/bp_shell_commands@2x.png\" width=\"720\" height=\"405.5\" alt=\"\" \/>\n<\/div>\n<p>It’s not hard to guess that this action allows you to run an arbitrary command in the standard terminal shell of the OS. Like the “Log message” action, it allows you to evaluate the result of an expression in the current context and use it as arguments for the command call. What could this be useful for? There are plenty of examples. In real life: throttling requests through Charles. I needed to slow down requests from a specific point, while keeping the connection fully functional the rest of the time. I didn’t have time to manually enable and disable throttling and perform actions in the simulator. This trick with a breakpoint and the “Shell command” came in very handy. Another time, I needed to modify server information simultaneously with a request to catch a rather strange bug. This type of breakpoint was also useful in that case. Some enthusiasts might even build a setup with an Arduino and an electric shocker to shock themselves whenever unwanted code is triggered. Just kidding. Don’t attempt this in real life.<\/p>\n<h2>Debugger command<\/h2>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/bp_lldb_cmd@2x.png\" width=\"720\" height=\"286.5\" alt=\"\" \/>\n<\/div>\n<p>One of the most interesting types of actions, in my opinion, is the “Debugger command.” This action allows you to have a truly unlimited impact on the debugging program. Debugger command is a set of LLDB debugger commands, and LLDB is a debugger used by Apple and Xcode to build programs. The LLDB debugger allows you to connect to a process, interrupt the execution of a program, and manipulate its memory. To do this, the debugger has a multitude of commands, some of which will become the heroes of today’s story. It is thanks to the LLDB debugger that we have the wonderful opportunity to debug a program, including setting breakpoints.<\/p>\n<p>Let’s start with the most well-known command – <samp>po<\/samp>. Most of you have probably used this command for debugging many times, but for me, it was a discovery at some point, even though I already had some experience in iOS development at that time. <samp>po<\/samp> stands for “print object.” This command allows you to evaluate the expression on the right side of the command and print the result in the console. In this case, the object will be asked for its <samp>debugDescription<\/samp> if it’s defined, or simply <samp>description<\/samp> if it’s not. There is a parent command for <samp>po<\/samp> called <samp>print<\/samp> or <samp>p<\/samp>, which will similarly evaluate the expression and print the result, but in this case, you will get raw information about the object or scalar type. Both of these commands will compile the entered expression in the current context, which will inevitably slow down the code execution when a breakpoint is hit. Fortunately, in Xcode 10.2, Apple added another debugger command – <samp>v<\/samp>, which works significantly faster. It allows you to output the value of a variable from the current scope to the console. However, unlike <samp>p<\/samp> and <samp>po<\/samp>, it does not compile the expression. The natural limitation imposed by this feature is that console output is only possible for stored properties.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/lldb_v_po@2x.png\" width=\"720\" height=\"303.5\" alt=\"\" \/>\n<\/div>\n<h2>Altering execution flow<\/h2>\n<p>Such a combination (breakpoint + debugger command po + automatic continuation) will replace the previously described Log message for us. What else can we do with this combination? For instance, with the help of the debugger, we can skip the execution of several lines of code as if they were commented out. You don’t need to rebuild the program and reproduce the conditions for this. To do this, simply enter<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">thread jump --by 1<\/code><\/pre><p>to jump forward by one line or<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">thread jump --line 44<\/code><\/pre><p>to jump, as you might have guessed, to line 44.<\/p>\n<div class=\"callout\"><div class=\"pin\"><p>☝️<\/p>\n<\/div><p><b>But be careful — you cannot jump between lines completely safely.<\/b> The reason is that you may jump over the initialization of certain variables, which can cause a crash. It’s further complicated by the fact that Swift is “lazy” in nature, and initialization may occur not where you think. Plus, the compiler inserts additional instructions when building your program, such as memory management, and skipping them can lead to memory leaks at best, and crashes at worst.<\/p>\n<\/div><h2>Interacting with debugger<\/h2>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/bp_leo@2x.png\" width=\"720\" height=\"286.5\" alt=\"\" \/>\n<\/div>\n<p>Apart from influencing your program, you can also influence the debugger itself with the help of the debugger. For example, you can set a breakpoint within a breakpoint. You may wonder why this is necessary? There are general-purpose methods that trigger under various conditions. For instance, a function for sending analytics data may be called hundreds of times per second, and you want to catch the specific call triggered by a button press. In this case, you can set a breakpoint on the button press method and add a command to set a breakpoint on an arbitrary line of code in any file. The command <samp>bp s -o -f Calc.swift -l 44<\/samp> is decoded as <b>b<\/b>reak<b>p<\/b>oint <b>s<\/b>et <b>o<\/b>ne-shot on <b>f<\/b>ile Calc.swift at line 44. The <samp>-o<\/samp> or <samp>--one-shot<\/samp> modifier creates a special type of breakpoint that exists only until it’s hit, and then it disappears. Using this straightforward method, we can create interesting breakpoint-setting algorithms for debugging non-trivial bugs.<\/p>\n<h2>Other breakpoints types<\/h2>\n<p>Are there any other types of breakpoints that we may not know about? Certainly, there are. Xcode allows you to add certain types of breakpoints that are not tied to a specific file and line. In Xcode, there is a Breakpoint Navigator tab that lets you manage existing breakpoints across all project files and create new ones. At the bottom of our IDE window, there’s a plus sign button.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/dbg_new@2x.png\" width=\"500\" height=\"328\" alt=\"\" \/>\n<\/div>\n<p>This allows you to use 6 additional types of breakpoints:<\/p>\n<ul>\n<li><b>Swift Exception breakpoint<\/b> – a breakpoint that stops the program when an unhandled throw occurs in Swift code.<\/li>\n<li><b>Exception breakpoint<\/b> – the same as above but for the ObjC world. It may seem like an outdated breakpoint in the modern world, but it’s still relevant. Keep in mind that we still need UIKit, written in ObjC, and we can catch its errors with this type of breakpoint.<\/li>\n<li><b>Symbolic breakpoint<\/b> – allows you to halt the program execution when code associated with a specific identifier, which Apple calls a symbol, is executed. I will explain more about symbols shortly.<\/li>\n<li><b>OpenGL ES Error breakpoint<\/b> – a breakpoint that stops the program when an OpenGL error occurs during the development of relevant applications.<\/li>\n<li><b>Constraint Error breakpoint<\/b> – obviously, it will pause your program when an auto-layout error occurs.<\/li>\n<li><b>Test Failure breakpoint<\/b> – can be helpful in debugging tests.<\/li>\n<\/ul>\n<p>Since it’s not possible to cover all types of breakpoints in this session, I will focus on the most commonly used ones. In my experience, I always use the <b>Exception breakpoint<\/b>. Quite often during program development, I encounter caught system exceptions that can be challenging to debug due to the uninformative call stack. I believe you’ve encountered such an error at least once:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/excep_crash@2x.png\" width=\"720\" height=\"496.5\" alt=\"\" \/>\n<\/div>\n<h2>Exception breakpoint<\/h2>\n<p>To make the call stack more informative, you can add an Exception breakpoint. It allows you to stop the program right at the moment an exception is thrown and track the chain of events that led to such a result. By default, an unhandled exception will cause a crash in the application, and in the call stack, you won’t see anything useful because the exception will propagate up the call stack, losing all information about where it was thrown. The Exception breakpoint allows you to halt the program when the exception is thrown, and using familiar methods, you can gather much more information about the issue by inspecting the call stack and variable values, if necessary. I consider this type of breakpoint very useful and use it as a default on all projects.<\/p>\n<p>In Xcode, there is a convenient mechanism that allows you to specify the breakpoint’s level and store it at three levels:<\/p>\n<ol start=\"1\">\n<li>Project.<\/li>\n<li>Workspace.<\/li>\n<li>User.<\/li>\n<\/ol>\n<p>Simply right-click on the breakpoint and select “Move breakpoint.” When moved to the user level, the breakpoint will be available in all projects you open in your Xcode.<\/p>\n<h2>Symbolic Breakpoint<\/h2>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/symbolic_bp@2x.png\" width=\"720\" height=\"316\" alt=\"\" \/>\n<\/div>\n<p>The second most commonly used type of breakpoints is the Symbolic Breakpoint. As mentioned earlier, this breakpoint allows you to halt the program’s execution when code associated with a symbol is executed, and I promised to explain more about symbols. Symbols are human-readable identifiers associated with specific memory addresses. LLDB can map known symbols to function addresses and vice versa. During each project build, the system creates a special bundle of files in the dSYM format, which stands for Debug Symbols. These files contain something like a table containing method addresses, identifiers including method signatures, file names, offsets, and line numbers. It’s thanks to these files that we can set breakpoints on a file’s line, get a readable call stack, or decode an application’s crash log from the AppStore.<\/p>\n<p>This mechanism allows us to set breakpoints on any class method, knowing only its name. We don’t need to know for sure where the method is declared or whether the source files are available. Let’s look at a real example. You’ve been assigned to a new project, and the first task is to fix strange behavior in the credit card data input form when the focus suddenly jumps to the name input field in the middle of typing. Initially, nothing is clear; there is a lot of code, but the symptoms are evident.<\/p>\n<p>To investigate, you need to understand who and why initiates the focus change. You could spend a long time reading the code, looking for logic in non-obvious class extensions, and when you get tired, create a UITextField subclass, override the <samp>becomeFirstResponder()<\/samp> method there, change the implementations, and set a breakpoint there. Or you can create a symbolic breakpoint <samp>-[UITextField becomeFirstResponder]<\/samp> in 10 seconds, and the program will stop when the focus changes. By tracing the backtrace, we can easily reconstruct the sequence of events leading to undesired results.<\/p>\n<p>For those who are new to using this type of breakpoint, you might wonder, what is <samp>-[UITextField becomeFirstResponder]<\/samp>? This is the Objective-C method signature for setting text in a label. The use of Objective-C is due to the fact that UIKit is written in this language.<\/p>\n<p>A few words for those with little experience in Objective-C. The minus sign denotes that we are interested in an instance method, not a class method. Then, in square brackets, the class name is written, followed by the method name, and a colon indicates that this method accepts a parameter.<\/p>\n<p>You may argue that the example is far-fetched. I agree—good code wouldn’t have ten places for setting the label text, but my goal is to show how it can work. Let’s consider a more realistic example. Suppose, for debugging purposes, we need to print the sequence of view controllers appearing. We add a breakpoint with the symbol <samp>-[UIViewController viewDidAppear:]<\/samp>, specify an additional action <samp>po NSStringFromClass([instance class])<\/samp>, and, of course, don’t forget to check “Automatically continue after evaluating actions.”<\/p>\n<p>Once again, we have to use Objective-C, even in the additional command, because we are in its context. As for Swift, symbols are written as <samp>ClassName.methodName(param:)<\/samp>. Specifying parameters is not mandatory; LLDB will try to resolve ambiguity if there are methods with the same name but different parameters.<\/p>\n<h2>Search for symbols<\/h2>\n<p>When talking about symbolic breakpoints, I can’t skip mentioning the ability to search for symbols. By stopping the program in any way, whether through a breakpoint or simply by clicking the pause button, you can use the command<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">image lookup -r -n<\/code><\/pre><p>to find the symbols you’re interested in within your program and all loaded libraries. This truly empowers you to become a debugging deity, as you have the ability to search for symbols everywhere, such as in UIKit, search for private methods, halt and explore the inner workings of system libraries. I hope I’ve convinced you of the power of this method, as it can help you save a lot of time.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/sym_search@2x.png\" width=\"720\" height=\"264\" alt=\"\" \/>\n<\/div>\n<p>Here, variables in the current frame that are available for debugging will be listed. In large projects, calculating the available variables and their types may take some time, so sometimes you need to wait a few (dozens?) of seconds before the variables become available for manipulation. A pleasant bonus is the ability to “look inside” Objective-C objects: the Variables View functionality allows you to see the private variables of these objects. Right-clicking on a variable doesn’t offer too many options – you can change the values of scalar variables and, of course, add watchpoints.<\/p>\n<p>Of course, you can also set a watchpoint using the LLDB command: <samp>watchpoint set variable variable_name<\/samp>, or using the command abbreviation feature in LLDB, simply: <samp>w s v variable_name<\/samp>. However, remember that the variable must be visible to the debugger, meaning it should be in the current frame. In addition to setting a watchpoint on a variable, you can also set a watchpoint on a memory address:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">watchpoint set expression — 0x0d78ab5ea8<\/code><\/pre><p>. In both cases, the program will break when the contents of the monitored memory address change. You can view the set watchpoints with the command<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">watchpoint list<\/code><\/pre><p>or in the Debugger navigator. Since all watchpoints ultimately monitor a memory address, they become irrelevant after a restart and are not saved between application restarts. Even if you set a watchpoint on a variable change, underneath, the LLDB mechanism calculates its address and sets a watchpoint on that address.<\/p>\n<h2>Affecting app state<\/h2>\n<p>We are about to wrap it up. The last thing I wanted to cover in this article is influencing the application’s state from LLDB. Up to this point, I’ve only talked about changing the state of a system object when stopping at a breakpoint. But what if we need to pause the program at an arbitrary moment? Clicking the pause icon pauses the program, but instead of the familiar code, we see assembly code. So, how do we access an arbitrary object and perform clever manipulations with it?<\/p>\n<h2>Memory graph<\/h2>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/mem_graph@2x.png\" width=\"1012\" height=\"159\" alt=\"\" \/>\n<\/div>\n<p>Most iOS developers start using this tool from their first months of work. For those who have never used it, let me explain. The Memory Graph allows you to create a memory dump of the program and display all instances of objects currently in memory in the form of a list and a graph. Often, this tool is used to detect object leaks and analyze the relationships that led to such results. But today, we only need this tool for the ability to pause the program at any time, find the required object, and discover its address. But what can we do with this seemingly useless information?<\/p>\n<p>In fact, anything you want. Here, the power of ObjC comes to our rescue. We can write<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">[0x7fafffa54a5 setValue:[UIColor redColor] forKey:@&quot;switchedOffColor&quot;]<\/code><\/pre><ul>\n<li>and we have already changed the color of the turned-off lamp to red using the standard NSObject methods available out of the box. But what if these methods are not enough, and we need to “pull” our own levers? It’s simple – we can use casting:<\/li>\n<\/ul>\n<pre class=\"e2-text-code\"><code class=\"\">[(MyLamp *)0x7fafffa54a5 powerOff]<\/code><\/pre><p>. Using techniques like this, you can influence any services, managers, and view models of your application at any time.<\/p>\n<p>We can save the value of this address in a variable for convenience:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">(MyLamp *)$lamp = 0x7fafffa54a5<\/code><\/pre><p>. It’s important that the variable name starts with a dollar sign. This variable will exist until the program is completely stopped, meaning you can use it not only in the current debugging session but also in the next interruption of the program within a single run.<\/p>\n<p>Objective-C provides truly wide possibilities to hack the current state and bypass many limitations. But what about classes that are only available in Swift? Of course, when attempting to cast a Swift class in an ObjC context, nothing will happen. Fortunately, Swift has a similar mechanism. More precisely, a function with the name <samp>unsafeBitCast(_:to:)<\/samp>. We are allowed to use it with an address:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">unsafeBitCast(0x7fafffa54a5, to: MySwiftLamp.self)<\/code><\/pre><p>and obtain an instance of the MySwiftLamp class by the address. Remember, its use is unsafe, as implied by its name, and it should be used very carefully in the application’s code. However, when you consciously need to use this function, you will be experienced enough to handle such warnings.<\/p>\n<h2>View Hierarchy<\/h2>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/view_hierarchy@2x.png\" width=\"1012\" height=\"152\" alt=\"\" \/>\n<\/div>\n<p>Next to the Debug Memory Graph tool, there is another equally useful tool called View Hierarchy. It allows you to quickly find the necessary view, view its properties and layout, and inspect active and inactive constraints. Starting with iOS 11, this tool has learned to display ViewControllers in the hierarchy, making it easier to find the desired view. An important feature here is the ability to filter by name and toggle the display of views hidden off-screen. I also noticed that not many people use the control panel at the bottom of the visual View display window.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/view_h_tools@2x.png\" width=\"1012\" height=\"152\" alt=\"\" \/>\n<\/div>\n<p>Furthermore, apart from the ability to adjust the depth of the hierarchy view, it allows specifying “enable displaying clipped content” and “enable displaying constraints.” Be sure to experiment with all these tools; I’m sure you’ll find some of them useful for your purposes.<\/p>\n<p>However, within the scope of this story, we only need the ability to locate the desired View and find its address. Then, we proceed as follows:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">po unsafeBitCast(0x7fafffa54a5, to: UIView.self)<\/code><\/pre><p>. But in this case, we’ll encounter an error because we are currently in the context of ObjectiveC and cannot use <samp>po<\/samp> with Swift code. We are forced to use the “expression” command or simply <samp>e<\/samp> with the specified language:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">e -l Swift -- unsafeBitCast(0x7fafffa54a5, to: UIView.self)<\/code><\/pre><p>. But even here, our attempts will not be successful, and we will receive an error <i>error: <EXPR>:3:35: error: use of unresolved identifier ‘UIView’<\/i>.<\/p>\n<p>This happens because of Swift’s modular nature. To successfully execute the operation, we need to import the UIKit module:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">e -l Swift -- import UIKit<\/code><\/pre><p>, and after that, we will finally achieve the result:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">e -l Swift -- unsafeBitCast(0x7fafffa54a5, to: UIView.self)<\/code><\/pre><p>. Hooray! We have obtained the description in the console. Now let’s try to change, for example, the background color. First, let’s save the View in a variable to make it easier to access. Just like in the case of ObjectiveC, when creating a variable in the LLDB context, its name should start with a dollar sign:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">e -l Swift -- let $view = unsafeBitCast(0x7fafffa54a5, to: UIView.self)<\/code><\/pre><p>. Then we can apply the necessary changes:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">e -l Swift -- $view.backgroundColor = .red<\/code><\/pre><p>. To see the changes, you need to continue the program execution. But there is a way to see the changes without that, while in the “pause” mode.<\/p>\n<p>The thing is, we don’t see the changes not because the application is paused, but because all UIView changes accumulate in a CALayer transaction and are applied only at the end of the current RunLoop “rotation” using the <samp>CATrasaction.flush()<\/samp> call. When the application is paused for debugging, the operating system is still alive, and you can switch to another application. The operating system still queries the UI state of your application and redraws your application several dozen times per second, only the RunLoop is paused, CATrasaction.flush is not called, and the changes are not applied.<\/p>\n<p>So, it’s sufficient to manually make the call<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">e -l Swift -- CATrasaction.flush()<\/code><\/pre><p>, and we will see the changes.<\/p>\n<p>It’s time to wrap this up. I hope these examples will make life easier for someone, save time and nerves. Bookmark this and the next time you spend more than 15 minutes searching and debugging a bug, take a look at this article – maybe one of the tricks will be useful to you.<\/p>\n",
            "date_published": "2022-04-28T22:26:00+00:00",
            "date_modified": "2024-02-01T22:34:19+00:00",
            "image": "https:\/\/sidorov.tech\/en\/pictures\/source_code@2x.png",
            "_date_published_rfc2822": "Thu, 28 Apr 2022 22:26:00 +0000",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "4",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": [
                    "https:\/\/sidorov.tech\/en\/pictures\/source_code@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/edited_code@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/bp_edit@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/bp_actions@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/bp_log_actions@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/bp_shell_commands@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/bp_lldb_cmd@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/lldb_v_po@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/bp_leo@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/dbg_new@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/excep_crash@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/symbolic_bp@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/sym_search@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/mem_graph@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/view_hierarchy@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/view_h_tools@2x.png"
                ]
            }
        },
        {
            "id": "2",
            "url": "https:\/\/sidorov.tech\/en\/all\/corebluetooth-101-ultimate-walkthrough\/",
            "title": "CoreBluetooth 101 — your ultimate walkthrough",
            "content_html": "<p>One of my favorite areas in mobile development is creating mobile applications that interact with objects outside their infrastructure, including various intriguing gadgets, sensors, and devices from the world of the Internet of Things. Today, let’s talk about this particular field, more specifically, about Bluetooth.<\/p>\n<p><cut\/><\/p>\n<p><a href=\"https:\/\/github.com\/kei-sidorov\/ble-ios-example\">📦 Demo project with simple BLE receiver<\/a><\/p>\n<p>Probably none of you need an explanation of what Bluetooth is and what it’s used for. This technology for wireless data transmission is currently at version 5.3 and has made significant leaps in its development. I bought my first iPhone in 2009, and even then, I had to endure tons of criticism about the imperfections of this device. One of the most frequent and irritating points for me was the constant reproach for the lack of file transfer via Bluetooth. And, in general, the inadequacy of Bluetooth on iOS and its limitation to just connecting wireless headphones. An amusing fact: just three years later, iOS had the most powerful framework among mobile platforms, which competitors took a long time to catch up with (in my opinion, they still haven’t). Indeed, most of the things I will talk about today were introduced in 2012 (and some even in 2011) and have changed little since then.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/history@2x.png\" width=\"1257\" height=\"477\" alt=\"\" \/>\n<\/div>\n<p>So, Bluetooth. I won’t provide formal explanations or delve deeply into its historical overview. Let’s keep it brief. For us, as developers, it’s an interface for interacting with external devices. Its development history began back in 1998, but it gained more widespread adoption in the mid-2000s with the rapid growth of the smartphone and communicator market. In the early days of iOS, up to version 4, developers had no means to interact with devices via Bluetooth at all, but everything changed in 2011 with the release of iOS 5. A key factor in this rapid growth was the adoption of the Bluetooth 4.0 standard in 2010, which met market requirements in terms of speed and energy consumption. Energy efficiency played a crucial role. It was then that the standard, or more precisely, the specification, BLE – <a href=\"https:\/\/en.wikipedia.org\/wiki\/Bluetooth_Low_Energy\">Bluetooth Low Energy<\/a>, was introduced, significantly expanding the applications of this wireless technology.<\/p>\n<p>What are the special features of BLE? Well, as the name suggests, its main highlight is its extremely (certainly for 2010) low energy consumption. The specification allows the development of devices that minimally drain their batteries and can operate for up to one year on a single charge of a small battery. This specification was developed by the Bluetooth Special Interest Group (SIG), and these guys got as intricate as, it seems to me, was even possible. To prevent developers’ imaginations from running wild and to avoid creating a multitude of competing standards, SIG developed a specification for all possible types of devices, from wearable pulse sensors to stationary door locks. It is through Bluetooth LE that interaction is available to us through the built-in CoreBluetooth framework. Apple also did a tremendous job, hiding all the details of working with low-level stuff and providing a very simple API that even a novice iOS developer can understand!<\/p>\n<h3>Bluetooth Low Energy in a nutshell<\/h3>\n<p>The foundation of BLE is the GATT profile – General Attribute Profile, which provides us with the abstractions of services and characteristics. Roughly speaking, a characteristic is a data cell, and a service is a logical combination of these cells. This might sound confusing, but I’ll give an example and everything will fall into place. The most classic and actually common example is a thermometer. It can have a temperature measurement service with two characteristics: the unit of measurement (℃, ℉) and the actual temperature. Additionally, it might measure humidity, for which there would be a separate service with just one characteristic – the humidity percentage.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/term@2x.png\" width=\"415\" height=\"467\" alt=\"\" \/>\n<\/div>\n<p>All interaction in BLE is based on the classic client-server model: here too, there are clients and servers, as well as requests. From this point on, I will smoothly transition to the CoreBluetooth framework itself and will explain new terms closer to the subject area, as most of the terms coincide with BLE terms, but with a CB prefix — CoreBluetooth.<\/p>\n<h3>CoreBluetooth<\/h3>\n<p>So, CoreBluetooth. As mentioned earlier, it appeared in iOS 5, was significantly revised in iOS 6, and has existed ever since, without undergoing any major changes. As I’ve already said, all interaction is based on the classic client-server model, and in CB, we have the server – Peripheral, and the client – Central. This naming might be slightly confusing at first, as we are accustomed to the server being some kind of center, but CB turns everything upside down. However, from another perspective, it all makes sense: a Peripheral device is an external device relative to our central one. A Peripheral device could be a fitness tracker, a smart light bulb, a lock, or an entire smart home system, and it could also be another mobile device or computer.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/ble-model-1@2x.png\" width=\"1082\" height=\"449\" alt=\"\" \/>\n<\/div>\n<h3>Discovering<\/h3>\n<p>Although the process is client-server, unlike HTTP, we don’t know the exact address or alias of the device, so to start interacting with it, we need to find it in the air. The process of discovering a device is called scanning or discovering. It is performed by the Central device, the side that wants to connect to something. The process involves scanning the Bluetooth ether for so-called Advertisement packets, which the peripheral device should send. An Advertisement packet is a tiny packet regularly sent by the peripheral device when it wants to be found. It usually contains basic information necessary to understand the class of the device: a thermometer, a lock, a scooter, or another iPhone. Depending on the operating mode of the device and its energy consumption settings, packets can be sent at varying intervals, from once every few microseconds to once every tens of seconds, so the duration of scanning directly depends on the operating mode of the peripheral device.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/advertisement@2x.png\" width=\"706\" height=\"486\" alt=\"\" \/>\n<\/div>\n<p>As soon as the desired device is found, we can connect to it and inquire about the services it offers and what characteristics are available in these services. Let’s move closer to the code.<\/p>\n<p>So, we want to find a thermometer, which in our case is a peripheral device. This means we will be the client, or the Central Device. The object representing the client in the CoreBluetooth framework is of type <samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbcentralmanager\">CBCentralManager<\/a><\/samp>. As you might guess, all interaction with an external device is expected to be asynchronous. And it’s even easier to guess that Apple preferred the delegate pattern to facilitate asynchronous operations. The delegate we need is of type <samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbcentralmanagerdelegate\">CBCentralManagerDelegate<\/a><\/samp>, and the object implementing this protocol will receive notifications about events related to discovery and connection.<\/p>\n<p>As soon as we are ready to scan the ether, we need to call the method <samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbcentralmanager\/1518986-scanforperipherals\">scanForPeripherals<\/a><\/samp> on the <samp>CBCentralManager<\/samp> manager. As soon as our device detects a new advertisement packet, the delegate’s <samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbcentralmanagerdelegate\/1518937-centralmanagerdidDiscoverPeripheral\">didDiscoverPeripheral<\/a><\/samp> method will be called with a <samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbperipheral\">CBPeripheral<\/a><\/samp> object in the method parameters. When we find the desired device or devices, we need to stop scanning with the <samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbcentralmanager\/1518984-stopscan\">stopScan<\/a><\/samp> method and call <samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbcentralmanager\/1518766-connect\">connect<\/a><\/samp> , specifying an instance of the <samp>CBPeripheral<\/samp> class.<\/p>\n<p><br \/><br \/><\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/central-mgr@2x.png\" width=\"984\" height=\"491\" alt=\"\" \/>\n<div class=\"e2-text-caption\"><br \/><br \/><\/div>\n<\/div>\n<h3>Discovering of services<\/h3>\n<p>Once the connection is established, we can begin querying the device. From this point, we are working with the <samp>CBPeripheral<\/samp> object and its delegate, <samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbperipheraldelegate\">CBPeripheralDelegate<\/a><\/samp>, which represent the external device we have connected to. To read a value (characteristic), we first need to find the service that contains this characteristic. Let’s initiate a search for services. We can either specify the service or services we are interested in, or not specify them at all and discover all that the device implements.<\/p>\n<p><br \/><\/p>\n<div class=\"callout\"><div class=\"pin\"><p>☝🏻<\/p>\n<\/div><p>Here, it’s important to take a moment to explain how we can distinguish one service from another. The identifier for services and their characteristics is the UUID, a unique identifier that comes in two types: 16-bit and 128-bit. We can generate and use 128-bit identifiers at our discretion when writing our own client-server applications. However, 16-bit identifiers are reserved by SIG. Remember, I mentioned that these guys went to great lengths and described almost everything that interacts or can interact via BLE? Well, they <a href=\"https:\/\/www.bluetooth.com\/specifications\/assigned-numbers\/\">numbered<\/a> all these services and characteristics and assigned them unique 16-bit UUIDs. If you ever want to develop a hardware device that can work with more than just your application, you should study their specs and follow these recommendations. But if you’re developing an application for a certain class of devices, you can use this spec to find the identifier of the service you need. Since we’re looking for a thermometer, we can confidently specify the UUID <samp>0x1809<\/samp> and ignore the rest (if there are any)<\/p>\n<\/div><p><br \/><\/p>\n<h3>Discovering of characteristics<\/h3>\n<p>Okay, we’ve found the services, and we’ll be notified about this in the delegate. Now, in a similar way, we can find the characteristics within them by calling <samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbperipheral\/1518797-discovercharacteristics\">discoverCharacteristics(for:<\/a>)<\/samp> for each service. Just as with services, we can either specify the UUIDs of the characteristics we’re interested in or discover all of them, but the latter will be slower. When the process of exploring the characteristics is complete, the delegate will be notified with a call to the method <samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbperipheraldelegate\/1518821-peripheral\">didDiscoverCharacteristicsForService<\/a><\/samp>, and the characteristics themselves will be available in the corresponding property.<\/p>\n<p><br \/><br \/><\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/peripheral@2x.png\" width=\"1083\" height=\"491\" alt=\"\" \/>\n<div class=\"e2-text-caption\"><br \/><br \/><\/div>\n<\/div>\n<h3>Characteristics<\/h3>\n<p>At this stage, we’ve finally reached the most “exciting” part – the data, or more precisely, the characteristics. In most straightforward cases, they can be divided by the types of operations they support: read, write, notify. In reality, there are a few more options (more accurately, properties), but for the scope of today’s discussion, we’ll only look at these.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/term-characteristics@2x.png\" width=\"430\" height=\"512\" alt=\"\" \/>\n<\/div>\n<p>A characteristic must support at least one of these operations, but it can also support all of them. The notify type is particularly interesting. As you might guess, instead of directly reading, we have the option to receive a notification when the device decides to send us data, for example, when they change. Returning to the thermometer example, the characteristic for the unit of measurement will have the flags read and write, while the actual temperature will have read and notify. It’s sufficient to read the unit of measurement upon connection, but constantly reading the temperature via a timer is not the best solution. We’re trying to be Low Energy — constantly querying the device can be costly both for us and for the peripheral device.<\/p>\n<p>To work with characteristics, three main methods are used:<\/p>\n<ul>\n<li><samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbperipheral\/1518759-readvalue\">readValue(for:<\/a>)<\/samp><\/li>\n<li><samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbperipheral\/1518747-writevalue\">writeValue(_, for:, type:<\/a>)<\/samp><\/li>\n<li><samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbperipheral\/1518949-setnotifyvalue\">setNotifyValue(_, for: <\/a>)<\/samp><\/li>\n<\/ul>\n<p>Reading data is straightforward: trigger the <samp>readValue<\/samp> method, and a request will be sent to the device. The response will be received in the <samp>CBPeripheralDelegate<\/samp> delegate when the data arrives. The same thing happens if the data gets updated and we are subscribed to it using <samp>setNotifyValue<\/samp>.<\/p>\n<p>Writing data comes in two types — <samp>withResponse<\/samp> and <samp>withoutResponse<\/samp>. Without going into too much detail, these are write operations with and without acknowledgment. Naturally, <samp>withResponse<\/samp> is slower, as each write request will generate a response. This should be particularly considered when transmitting relatively large volumes of data — in this case, writing <samp>withoutResponse<\/samp> would be preferable.<\/p>\n<h3>Limits<\/h3>\n<p>Indeed, what about the limits? We must not forget that LE in the name stands for Low Energy, meaning it was designed to be highly economical in all aspects, including packet sizes. BLE is not meant for streaming or large volumes of data. According to Wikipedia, the speed for the 4.0 standard is only 0.27 Mbit\/sec.<\/p>\n<p>By default, the packet size for the payload is <b>23 bytes<\/b>, of which 3 are reserved by the system. This leaves <b>20 bytes<\/b> for the actual payload. When working with BLE, you’ll have to deal with this limitation and figure out how to manually divide your data into packets, as 20 bytes often prove insufficient. In some cases, when communicating with a modern smartphone, this limit can be increased up to 512 bytes. CoreBluetooth takes responsibility for negotiating the maximum MTU, as well as for automatic packet segmentation if supported by the device. If you need different speeds or volumes, you might have to use the L2CAP channel and work at a lower level, bypassing GATT. Fortunately, this option has been available since iOS 11 (but only for Apple devices).<\/p>\n<h3>“Server” side (Peripheral)<\/h3>\n<p>At this stage, you’ve been introduced to all the key classes and concepts of CoreBluetooth and are likely able to write your first application that connects to a BLE device. But what if that device is another iPhone? Let’s briefly look at the other side of the equation, the server side, or in our case, the Peripheral.<\/p>\n<p>Analogous to <samp>CBCentralManager<\/samp>, for creating a server we have <samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbperipheralmanager\">CBPeripheralManager<\/a><\/samp> and its <samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbperipheralmanagerdelegate\">CBPeripheralManagerDelegate<\/a><\/samp>. To create our service, we first need to create the service and its characteristics. Let’s have just one service with a single read-only characteristic. The counterpart of <samp>CBService<\/samp> on the server side is <samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbmutableservice\">CBMutableService<\/a><\/samp>. Yes, the CoreBluetooth API isn’t “swiftified” yet, so we have the prefix Mutable for mutable versions of classes. When creating it, we need to specify a UUID, which can be generated using the command <samp>uuidgen<\/samp> in the console, and indicate whether it is the primary service of our device.<\/p>\n<p>After creating the service, it needs to be filled with characteristics, represented by the class <samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbmutablecharacteristic\">CBMutableCharacteristic<\/a><\/samp>. When creating it, the following are specified:<\/p>\n<ul>\n<li>UUID<\/li>\n<li>Properties<\/li>\n<li>Initial value (for static characteristics)<\/li>\n<li>Access rights<\/li>\n<\/ul>\n<p>We assemble our tiny tree with one leaf, add it to the manager, and can announce ourselves by broadcasting advertisement packets. For this, <samp>CBPeripheralManager<\/samp> has a method <samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbperipheralmanager\/1393252-startadvertising\/\">startAdvertising<\/a><\/samp>. It takes a dictionary with broadcasting parameters as input. I don’t want to delve into the details of the structure of the advertisement packet right now, but there’s something I’d like to highlight.<\/p>\n<p><br \/><\/p>\n<div class=\"callout\"><div class=\"pin\"><p>☝🏻<\/p>\n<\/div><p>At the beginning of the discussion about <samp>CBCentral<\/samp>, I intentionally omitted the fact that when scanning the ether, you can specify the UUIDs of the services you’re interested in. It would be impractical and time-consuming to scan the ether and then connect to all found devices just to determine if they have the desired service. Instead, the server can include the UUIDs of its main services in the advertisement packet, allowing clients to filter out devices that are not of interest to them during scanning, without connecting and reading the list of services. This can be done by specifying the corresponding key in the dictionary mentioned earlier at the start of advertising.<\/p>\n<\/div><p><br \/><\/p>\n<p>As soon as the broadcasting of packets begins, we’ll receive a corresponding notification in the delegate. And when a client connects to us... nothing will happen. The manager’s delegate doesn’t have a corresponding method for this event. Instead, we have methods to react to events of reading, writing, and subscribing to characteristics. Using these methods, we can decide whether we are ready to provide or write a value, validate it, and so on.<\/p>\n<h3>States<\/h3>\n<p>So far, I haven’t mentioned situations when Bluetooth is unavailable or turned off. Monitoring the manager’s state is crucial, and operations should only be performed in permissible states to avoid crashes. The manager, whether Central or Peripheral, can be in the following states:<\/p>\n<ul>\n<li>unknown<\/li>\n<li>resetting — BLE stack is rebooting<\/li>\n<li>unsupported — BLE is not supported<\/li>\n<li>unauthorized — User denied permission<\/li>\n<li>poweredOff — Bluetooth is turned off<\/li>\n<li>poweredOn — Bluetooth is on, ready to operate<\/li>\n<\/ul>\n<p>Beginning device discovery, broadcasting advertisement packets, and even adding services to the manager can only be done in the <samp>poweredOn<\/samp> state. Therefore, paying attention to this delegate method is unavoidable, as the manager delegate protocol itself implies — the method for tracking the state is the only one marked as mandatory.<\/p>\n<h3>Security<\/h3>\n<p>As mentioned several times, energy efficiency has always been a priority in BLE. Therefore, by default, all traffic between devices is not encrypted, saving both CPU resources and bytes in packets. Such traffic can be easily intercepted or even tampered with by an attacker. For most practical tasks engineers solve with BLE, this level of security is sufficient. After all, it’s not a significant risk if someone intercepts the temperature from your thermometer or the battery charge level. However, there are cases where it’s crucial to protect private data from malicious interception or to ensure that writing is from a trusted source.<\/p>\n<p>In cases where additional security is needed, appropriate rights can be set for a characteristic. Attempting to read or write such a characteristic, the server will reject the process, informing that the communication needs to be encrypted, responding with an Insufficient Authentication error. To proceed, the client needs to establish a secure connection, which we more commonly refer to as pairing, and less often as bonding.<\/p>\n<p>The pairing process can vary for different devices; some require the input of constant numbers, some display numbers on the screens of both devices, or simply ask to confirm the match. As a result of pairing, keys are generated on both sides of the process, and the communication is encrypted.<\/p>\n<p>After successful pairing, you need to reconnect to the device and re-read the characteristic. Fortunately, CoreBluetooth takes this entire process upon itself, hiding all these complex actions under the hood. That means if you try to read or write a protected characteristic, CoreBluetooth will perform all these actions, and you will eventually get a response in the delegate, just a bit later than usual.<\/p>\n<p><br \/><br \/><\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/pairing@2x.png\" width=\"1107\" height=\"448\" alt=\"\" \/>\n<div class=\"e2-text-caption\"><br \/><br \/><\/div>\n<\/div>\n<h4>Permissions<\/h4>\n<p>Speaking of security, it’s worth mentioning that Apple recently requires the inclusion of a text in the Info.plist file explaining to the end-user why your application needs access to Bluetooth. At the first attempt to scan or start broadcasting advertisement packets, the system will ask the user whether they allow the use of Bluetooth, just as it usually happens when requesting access to the photo gallery or push notifications. On the Android platform, the system also requires permission for low-precision location access because potentially, scanning can be used to determine the user’s location. It’s possible that similar rules might be implemented in iOS in the future.<\/p>\n<h3>Reconnection<\/h3>\n<p>When working with Bluetooth, as with any other communication technology, connection interruptions may occur. A disconnection can be initiated by either side or due to a loss of signal. CoreBluetooth is designed to continuously maintain a connection until the <samp>cancelConnection(from:)<\/samp> method is called. Therefore, if the connection is lost due to an unstable connection or when the remote device is turned off, CoreBluetooth will attempt to restore the connection in the background as long as your application is running. It will autonomously scan the ether and reconnect as soon as possible. This might seem like a simple feature, but believe me – you wouldn’t want to write this algorithm from scratch. Colleagues on other platforms don’t have this mechanism out-of-the-box, saving the iOS team several days of discussions and implementation.<\/p>\n<p>In the example above, we considered a case where the disconnect was unexpected. However, it’s also worth considering reconnection as a natural part of the life cycle. Suppose, after working with a device for a few minutes, we’ve obtained all the information we currently need and don’t want to waste resources maintaining the connection. In this case, we can save the unique UUID of the peripheral device and call the method <samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbcentralmanager\/1518952-cancelperipheralconnection\/\">cancelPeripheralConnection(<\/a>)<\/samp>. Later, when we need data from the device again, we can use the saved UUID to reconnect. It’s important to note that the `connect` method takes a CBPeripheral instance as a parameter, which doesn’t have a public constructor. To obtain an instance, we can use the <samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbcentralmanager\/1519127-retrieveperipherals\/\">retrievePeripherals(withIdentifiers:<\/a>)<\/samp> method of <samp>CBCentralManager<\/samp>, which can take an array of UUIDs.<\/p>\n<p><br \/><\/p>\n<div class=\"callout\"><div class=\"pin\"><p>⚠️<\/p>\n<\/div><p>It’s important to note that the method now works with UUIDs from the Foundation library, not CBUUID, because this is a platform implementation and does not reference the CoreBluetooth specification. After mapping the UUIDs to an array of CBPeripheral, you can connect to them.<\/p>\n<\/div><p><br \/><\/p>\n<p>There may be a question of what to do if the device is not within Bluetooth range or has gone to sleep for a while. In this case... nothing will happen. We won’t receive a timeout error or any other kind. CoreBluetooth will keep trying to connect to the device indefinitely, until you call <samp>cancelPeripheralConnection(:)<\/samp>. This logic might seem odd; indeed, why would I want to connect endlessly to a thermometer if it’s not in range? But let’s not forget that there are many usage scenarios, and we are working with the LE version of the technology. This means that some devices by design may only broadcast at infrequent intervals to save battery life. Then such behavior makes sense. Ultimately, adding a timeout logic is much easier than writing a reconnection chain, should the timeout be systemic.<\/p>\n<h3>Working in the background<\/h3>\n<p>Like all resource-intensive operations in iOS, working with devices in the background is not available by default. When the application is minimized, whether your app is a server or a client, it will lose connection and, like all others, will stop serving all queues. But upon returning to the foreground, CoreBluetooth will restore the connection.<\/p>\n<p>Automatic reconnection is good, but clearly not enough. You can fully control the interaction process with devices in the background by setting the corresponding background mode in the Info.plist.<\/p>\n<p>For each role, server and client, there’s a separate checkbox. You can mark both. With the background mode activated, the application can interact with devices, read and write characteristics, scan the network, etc. However, of course, this process is not eternal and, like almost any application, iOS may eventually unload the application from memory. Nothing can be done about this, and we have to live with it. The good news is that upon returning to the application, CoreBluetooth can restore the state, with all connected devices, found characteristics, etc. All of the above applies to cases where the application was unloaded at the system’s request, not closed by the user from the task manager.<\/p>\n<p>I would like to remind you again that Apple is serious about energy consumption, so background processes will be slower. For example, scanning will occur discretely and less intensively.<\/p>\n<p>It’s important to note that when implementing a connection between two iPhones, iPads, or Macs without the corresponding background mode, the server part going into the background will be accompanied by the deactivation of registered services, rather than the termination of the Bluetooth connection. That is, the remote side (client) will not receive a disconnection event, but will receive a <samp>didModifyServices<\/samp> notification that some services of the Peripheral device have disappeared. Remember, in this case, you are connected to one physical device — another phone, tablet, or computer, and not to a program as in the case of HTTP interaction.<\/p>\n<h3>L2CAP connection<\/h3>\n<p>In 2017, Apple introduced the capability for developers to communicate with Apple devices not only through the GATT protocol but also by opening their own low-level connection through the underlying L2CAP protocol. This allows for the creation of custom implementations for radio channel interactions, bypassing the limitations of GATT characteristics. While this is more complex to implement than reading and writing characteristics, it is still relatively straightforward.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/l2cap@2x.png\" width=\"406\" height=\"427\" alt=\"\" \/>\n<\/div>\n<p>From the server side, we still need to scan the airwaves, find, and connect to the device. If the device supports this type of connection, it will have a special service with a characteristic containing a channel ID. To initiate information exchange, you need to read this ID and request the CBPeripheral to open the channel using the <samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbperipheral\/2880151-openl2capchannel\/\">openL2CAPChannel(<\/a>)<\/samp> method.<\/p>\n<p>If you are implementing the server-side, you need to declare that you support L2CAP. To do this, in the relevant manager, there is a method <samp><a href=\"https:\/\/developer.apple.com\/documentation\/corebluetooth\/cbperipheralmanager\/2880160-publishl2capchannelwithencryptio\/\">publishL2CAPChannelWithEncryption(<\/a>)<\/samp> that will prepare the channel, service, and characteristic, and automatically publish the necessary data.<\/p>\n<p>After a successful connection, delegates on both sides of the process will receive a notification <samp>didOpenL2CAPChannel<\/samp> specifying the channel. The channel itself has two properties, <samp>inputStream<\/samp> and <samp>outputStream<\/samp>, which allow you to implement both reading and writing data through the channel using the standard iOS\/Mac stream abstraction.<\/p>\n<h3>Hidden Hurdles<\/h3>\n<p>We are very close to completing my narrative. In the end, I would like to briefly highlight the stumbling blocks that had to be overcome when working with CoreBluetooth.<\/p>\n<h4>Caching<\/h4>\n<p>When developing and debugging an application with BLE, always remember the caching that you have no control over. CoreBluetooth will attempt to cache both services and characteristics for your device. In addition to this, meta-information is also cached, such as the device name.<\/p>\n<p>On one of the projects, it was necessary to create a simulator for a hardware device so that QA could test interactions in various edge cases that couldn’t be reproduced with a real device or where the reproduction was too time-consuming. When developing the server-side, we can change the name of our device in the advertising packet and hope that we will see the changed name. If we were the only Bluetooth users on the device, everything would be fine: we specify the name in the real packet, and that’s what the client sees. However, Bluetooth on the device is also used by the OS, often sending advertising packets with the device name from system settings. For example, for quick device discovery with services like AirDrop. If the device where you plan to test your client-side has already captured at least one advertising packet, the name will be cached. When attempting to read the name of the CBPeripheral, we will see the cached name rather than the one specified in the advertising packet settings.<\/p>\n<p>The same can apply to the set of services and characteristics for paired devices. If you add a service after establishing a connection, it is quite possible that the client may not see it.<\/p>\n<h4>Versioning<\/h4>\n<p>A very brief note where I urge you to consider incorporating a software version characteristic. If you are working with a standalone device, such a privilege may not be necessary. However, if you are implementing client-server communication between Apple devices, do not hesitate to do so. This will reduce the need for code workarounds when guessing the device or software version based on the presence or absence of certain services or characteristics. This is often done to determine supported functions on a peripheral device.<\/p>\n<h4>Hardware Issues<\/h4>\n<p>Be prepared for things to not go as planned. Hardware developers are also developers who encounter bugs and crashes, but their release cycles are usually much longer, especially when you have limited communication with them. Be prepared for someone to deviate from standards, such as not including information about services in the advertising packet.<\/p>\n<p>For example, at the very beginning of my journey with a BLE project, I faced such a problem. I wrote and debugged the client side according to the documentation, tested it with a simulator I had created, and everything worked perfectly. When the real device arrived a month later, it was impossible to find it. I could see that it was in the air, but I couldn’t see any notifications in the delegate. After several days of correspondence and by pure chance, I realized that if I turned off the UUID service filtering, everything worked. I had to filter by name, and the name sometimes changed, which led me back to the caching issue. In short, it was quite an adventure.<\/p>\n<p>As I mentioned earlier, the device can crash and hang. Fortunately, on a physical hardware device, there is almost always a watchdog that will reboot it, but understanding what is going wrong can be quite challenging. As I mentioned, there are developers working on it, and in such a low-level system, it’s easy to encounter some race conditions. I experienced unexpected device reboots several times when reading characteristics, either when there was no data for them or when they were supposed to be read in a different order.<\/p>\n",
            "date_published": "2022-04-28T10:47:00+00:00",
            "date_modified": "2024-02-01T08:01:38+00:00",
            "image": "https:\/\/sidorov.tech\/en\/pictures\/history@2x.png",
            "_date_published_rfc2822": "Thu, 28 Apr 2022 10:47:00 +0000",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "2",
            "_e2_data": {
                "is_favourite": true,
                "links_required": [],
                "og_images": [
                    "https:\/\/sidorov.tech\/en\/pictures\/history@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/term@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/ble-model-1@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/advertisement@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/central-mgr@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/peripheral@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/term-characteristics@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/pairing@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/l2cap@2x.png"
                ]
            }
        },
        {
            "id": "1",
            "url": "https:\/\/sidorov.tech\/en\/all\/digging-into-springboard\/",
            "title": "Digging into SpringBoard",
            "content_html": "<p>Like many iOS and macOS developers, every year I wait for WWDC to see new APIs, new tools and improvements to existing ones. But besides everything related to development, I’m waiting for the iOS itself – I want to see what has changed for me as a regular iOS user.<\/p>\n<p>This year there were two big changes related to the home screen that have been discussed a lot. Apple introduced widgets and Application Library. Right on the first day beta released I removed all the junk carefully placed in folders “Other” and “Utilities” away from the home screen, leaving only one screen with the stuff I use every day. This was a neat improvement for me: I have not been looking for the app icon I need with my eyes for a long time, but I used the Spotlight each time I need to find anything. Now I able to search it in App Library, that is beauty and fast way to find the app.<\/p>\n<p>In besides with the convenience come with App Library, I paid attention to the top bar with the search field. There is an effect I had never seen before in iOS. This is a gradient blur with a radius that increases from 0 to some value. I thought I’m the only who wondered about it before I had listened to StackTrace podcast, where Guilherme Rambo have also shared his impressions of this UI element. His words challenged me: I could make it at my own?<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/all-library-gif.gif\" width=\"717\" height=\"468\" alt=\"\" \/>\n<\/div>\n<h2>Something about blur<\/h2>\n<p>Let’s get a little talk about blurs. Although Apple provides a rich API allows developer to make a blur and other effects for the image, the ability to do live blur with UI is severely limited. Of course, it is  possible to render a layer into image, apply effects and filters on it and show in ImageView, but obviously the performance of such solution is poor. All that we can do is to use UIVisualEffectView, which provides an opportunity to make a frosted glass effect using blur and get some fun with vibrancy effect.<\/p>\n<div class=\"callout\"><div class=\"pin\"><p>☝️<\/p>\n<\/div><p>Although Apple provides a rich API allows developer to make a blur and other effects for the image, the ability to do live blur with UI is severely limited.<\/p>\n<\/div><h2>Dive in!<\/h2>\n<p>So we realized that there is no solution to implement it out of the box. What to do, where to start?<\/p>\n<p>Without a jailbreak we can’t connect to arbitrary process to reveal the view hierarchy and find a view we need. We have to figure it out of the dark.  Hopefully we have a simulator and the ability to reveal its file system.<\/p>\n<p>The first step is to figure out what we are looking for. The App Library is located on the home screen, so we have to find something related to the SpringBoard process. SpringBoard is an application that responsible for the displaying home screen and icons on it, launching applications and everything related to it. We will try to find it:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/search_springboard@2x.png\" width=\"874\" height=\"391\" alt=\"\" \/>\n<\/div>\n<p>Lucky we! Let’s load a binary in Hopper Disassembler to see what’s inside. Hopper Disassembler is a grate application to research application without a source code. Most likely there is no something interesting inside, as the binary is only 140 Kbytes. The disassembler meets us with a window askig us to choose the architecture, because <a href=\"https:\/\/en.wikipedia.org\/wiki\/Fat_binary\">the binary is fat<\/a>.<\/p>\n<div class=\"callout\"><div class=\"pin\"><p>☝️<\/p>\n<\/div><p>So starting with Xcode 12, Apple supplies libraries compiled for the classic x86_64 and the new ARM (Apple Silicon) architectures. Now it is clear why Xcode 12 takes so much disk space!<\/p>\n<\/div><div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/hopper_sb@2x.png\" width=\"1000\" height=\"654\" alt=\"\" \/>\n<\/div>\n<p>As expected, there is nothing anything useful inside an executable file. Only a few procedures responsible for the launch. So, interesting to us is hiding somewhere else in system frameworks. Let’s see what the binary is linked to.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/otool@2x.png\" width=\"1146\" height=\"355\" alt=\"\" \/>\n<\/div>\n<h2>Private frameworks<\/h2>\n<p>There is not much to analyze: SpringBoard’s “guts” obviously are in a private framework with the same name. We have to go back up the file hierarchy to iOS.simruntime and look for it relative to this directory. <samp>find . -name ‘SpringBoard.framework’ -print<\/samp> is easily get us to the folder with the private libraries and the framework we need! Load it into the Hopper!<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/search_sym@2x.png\" width=\"1000\" height=\"654\" alt=\"\" \/>\n<\/div>\n<p>Let’s try to find any symbols related to AppLibrary. Something is there, but not much. Let’s play around with the search and try to find the View Controller of this library. We have a lead! By the string <samp>LibraryViewController<\/samp> we can find setters and getters of related VC in the root (I guess) <samp>SBIconController<\/samp>. But it wont help us. If it is not here, then it is somewhere else in another framework. Let’s try again a trick with <samp>otool<\/samp>.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/sb_otool@2x.png.jpg\" width=\"2560\" height=\"1453\" alt=\"\" \/>\n<\/div>\n<p>Oh, no! This framework uses a lot (170!) of external dependencies! Going through all frameworks is a searching needle in haystack. We have to use another way, and there is such one! Mach-O file contain External Symbols segment, that will help us to find source of the original symbol. Let’s return to Hopper and look for something related with class and <samp>LibraryViewController<\/samp> keyword.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/objc_symb_ext_ref@2x.png\" width=\"1000\" height=\"654\" alt=\"\" \/>\n<\/div>\n<p>Here is the symbol <samp>_OBJC_CLASS_$_SBHLibraryViewController<\/samp>, and by clicking on it we can see it is declared in external SpringBoardHome framework which located nearly. Load it into Hopper! Now let’s find and explore the class <samp>SBHLibraryViewController<\/samp>.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/blur_init@2x.png\" width=\"1000\" height=\"654\" alt=\"\" \/>\n<\/div>\n<p>Complaining that your MVC is a MassiveViewController? Take a look at the header of this class: 147 methods and it is normal for Apple. Best practices in best companies, isn’t it? :)<\/p>\n<p>Okay, let’s take a look at the  <samp>init<\/samp> method. We will use a switching to pseudo-code mode — the Hopper is so good that it can try to make a human-readable code from a machine code.<\/p>\n<h2>The first clue<\/h2>\n<p>The init method calls the <samp>initWithCategoryMapProvider<\/samp>: which initializes the parent <samp>SBNestingViewController<\/samp> via <samp>initWithNib:bundle:<\/samp> and creates a special queue for some events. In <samp>initWithNib:bundle:<\/samp> two nils are passed, that tell us that initialization of the view occurs in the code. Remember the controller’s lifecycle — to load a view we usually use a <samp>loadView<\/samp> method, let’s see what it is.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/load_view@2x.png\" width=\"683\" height=\"548\" alt=\"\" \/>\n<\/div>\n<p>Ok, we can see the method is overridden and <samp>_setupIconTableViewController<samp> is called.<\/p>\n<p>Let’s see what’s inside. The method is massive, we don’t want to spent a lot of time to analyze it. Let’s see what kind of views or View Controllers are created there — just search <samp>alloc<\/samp> keyword.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/init_w_frame@2x.png\" width=\"697\" height=\"658\" alt=\"\" \/>\n<\/div>\n<p>And why didn’t I just search the Blur line before?! Ok, it’s not time to blame myself, let’s see what’s inside. The first thing we see is <samp>initWithFrame<\/samp>, where <samp>SBHFeatherBlurView<\/samp> is created using the <samp>initWithRecipe:<\/samp> method with the parameter <samp>0x2<\/samp>.<\/p>\n<p>We are done with Hopper for now, it is time to experiment. Let’s create a new project in Xcode and choose the ObjectiveC as a primary language. Then create <samp>UIScrollView<\/samp> and put some content for the experiment. We won’t link the framework directly, we will load it at runtime.<\/p>\n<p>After it, let’s take benefits of ObjectiveC’s runtime to create an instance of class that we didn’t have in our code. Add it to our view and check if our guess is correct. Especially for this article, I made an example similar to the original screen to compare it with. Well, let’s run it on a simulator.<\/p>\n<pre class=\"e2-text-code\"><code class=\"objc\">\r\n#import \"FeatherBlurView.h\"\r\n#import <dlfcn.h>\r\n\r\n#define SBH_PATH @\"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/PrivateFrameworks\/SpringBoardHome.framework\/SpringBoardHome\"\r\n\r\n\/\/ Объявим интерфейс класса из SpribngBoardHome для удобной инициализации\r\n@interface SBHFeatherBlurView : UIView\r\n- (instancetype) initWithRecipe:(int)arg1;\r\n@end\r\n\r\n@implementation FeatherBlurView\r\n\r\n+ (void)load {\r\n    const char *path = [SBH_PATH cStringUsingEncoding:NSUTF8StringEncoding];\r\n    dlopen(path, RTLD_NOW);\r\n}\r\n\r\n- (instancetype) init {\r\n    self = [super init];\r\n    if (self) {\r\n        SBHFeatherBlurView *view = [NSClassFromString(@\"SBHFeatherBlurView\") alloc];\r\n        view = [view initWithRecipe:0x2];\r\n        view.translatesAutoresizingMaskIntoConstraints = NO;\r\n        [self addSubview:view];\r\n        [[view.topAnchor constraintEqualToAnchor:self.topAnchor] setActive:YES];\r\n        [[view.bottomAnchor constraintEqualToAnchor:self.bottomAnchor] setActive:YES];\r\n        [[view.leftAnchor constraintEqualToAnchor:self.leftAnchor] setActive:YES];\r\n        [[view.rightAnchor constraintEqualToAnchor:self.rightAnchor] setActive:YES];\r\n    }\r\n    return self;\r\n}\r\n\r\n@end\r\n<\/code>\n<\/pre>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/sidorov.tech\/en\/pictures\/Result@2x.png\" width=\"600\" height=\"582\" alt=\"\" \/>\n<\/div>\n<p>Yeah! Everything turned out and works as I expected. You can <a href=\"https:\/\/github.com\/kei-sidorov\/feather-blur-sample\">download sample project<\/a> and play with it.<\/p>\n<div class=\"callout\"><div class=\"pin\"><p>🖖<\/p>\n<\/div><p>Time to wrap up and highlight interesting facts:<\/p>\n<ul>\n<li>Looking under the hood of the OS is not so difficult and very interesting<\/li>\n<li>Objective-C and controller life cycle knowledge can be useful in 2k21.<\/li>\n<li>All frameworks and utilities inside Xcode come in bold binary compiled for ARM and x86_64, this partly explains the almost doubled Xcode size.<\/li>\n<li>Despite the 2020 year and the current Swift version 5.4, Apple actively uses Objective-C in system frameworks. Even the code for managing widgets (which, for a second SwiftUI-only) is written in Objective-C.<\/li>\n<li>Apple’s engineers aren’t afraid of Massive View Controller and deep inheritance<\/li>\n<\/ul>\n<\/div><p><br \/><\/p>\n<p>But is it really that simple and anyone can use it in his own projects?! Unfortunately, no. We cant use private libraries in our code, not because it prohibited by Apple, but because of the fact we have no possibility to use external frameworks while app is running in the sandbox.<\/p>\n<p>That is, for now we can’t run it on a device. But this is not a reason to give up! Right in the next article we will go deeper and research how does this view works and try to implement it our code in a way that it will run on a device! Stay tuned!<\/p>\n<h2>iOS 15 update<\/h2>\n<p><samp>SBHFeatherBlurView<\/samp> has been moved to new private framework <samp>SpringBoardFoundation<\/samp> and renamed to <samp>SBFFeatherBlurView<\/samp>.<\/p>\n",
            "date_published": "2022-04-05T10:47:13+00:00",
            "date_modified": "2022-04-18T05:48:30+00:00",
            "image": "https:\/\/sidorov.tech\/en\/pictures\/all-library-gif.gif",
            "_date_published_rfc2822": "Tue, 05 Apr 2022 10:47:13 +0000",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "1",
            "_e2_data": {
                "is_favourite": true,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": [
                    "https:\/\/sidorov.tech\/en\/pictures\/all-library-gif.gif",
                    "https:\/\/sidorov.tech\/en\/pictures\/search_springboard@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/hopper_sb@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/otool@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/search_sym@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/sb_otool@2x.png.jpg",
                    "https:\/\/sidorov.tech\/en\/pictures\/objc_symb_ext_ref@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/blur_init@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/load_view@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/init_w_frame@2x.png",
                    "https:\/\/sidorov.tech\/en\/pictures\/Result@2x.png"
                ]
            }
        }
    ],
    "_e2_version": 3576,
    "_e2_ua_string": "E2 (v3576; Aegea)"
}