{
    "version": "https://jsonfeed.org/version/1.1",
    "title": "Jesse Squires: Recently Updated",
    "home_page_url": "https://www.jessesquires.com",
    "feed_url": "https://www.jessesquires.com/feeds/updates.json",
    "description": "Turing complete with a stack of 0xdeadbeef",
    "icon": "https://www.jessesquires.com/img/logo.png",
    "favicon": "https://www.jessesquires.com/favicon.ico",
    "expired": false,
    "language": "en-US",
    "authors": [
        {
            "name": "Jesse Squires",
            "url": "https://www.jessesquires.com",
            "avatar": "https://www.jessesquires.com/img/me.jpg"
        }
    ],
    "items": [
    

    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2021/03/30/apple-cooperation-with-authoritarian-governments/#updated-24-october-2025",
            "url": "https://www.jessesquires.com/blog/2021/03/30/apple-cooperation-with-authoritarian-governments/",
            "title": "[Updated] Apple's cooperation with authoritarian governments",
            "date_published": "2025-10-24T12:29:50-07:00",
            "date_modified": "2025-10-24T12:29:50-07:00",
            "summary": "<p>Over the past few years, Apple seems increasingly willing to cooperate with authoritarian governments, uninterested in protecting its own users, and unwilling to actually standup for human rights in broad terms, as often <a href=\"https://s2.q4cdn.com/470004039/files/doc_downloads/gov_docs/2020/Apple-Human-Rights-Policy.pdf\">portrayed by its marketing department</a> or <a href=\"https://www.npr.org/sections/alltechconsidered/2015/10/01/445026470/apple-ceo-tim-cook-privacy-is-a-fundamental-human-right\">direct statements from CEO Tim Cook</a>.</p>\n\n",
            "tags": [
                "tech","politics","capitalism","apple",
                "essays"
            ],
            "content_html": "<p>Over the past few years, Apple seems increasingly willing to cooperate with authoritarian governments, uninterested in protecting its own users, and unwilling to actually standup for human rights in broad terms, as often <a href=\"https://s2.q4cdn.com/470004039/files/doc_downloads/gov_docs/2020/Apple-Human-Rights-Policy.pdf\">portrayed by its marketing department</a> or <a href=\"https://www.npr.org/sections/alltechconsidered/2015/10/01/445026470/apple-ceo-tim-cook-privacy-is-a-fundamental-human-right\">direct statements from CEO Tim Cook</a>.</p>\n\n<!--excerpt-->\n\n<p>The company is quick to position itself as a <a href=\"https://www.macrumors.com/2020/09/04/apple-publishes-human-rights-policy/\">prominent human rights advocate</a> in the corporate world, especially regarding issues like user privacy and security. Although, <a href=\"https://oleb.net/2020/icloud-end-to-end-encryption/\">as Ole Begemann has aptly pointed out</a>, this is increasingly disingenuous to the point of deliberately deceiving its customers and the general public. There are even (unconfirmed) reports that the lack of end-to-end encryption <a href=\"https://oleb.net/2020/icloud-end-to-end-encryption/\">that Ole criticizes</a> is actually due to <a href=\"https://www.reuters.com/article/us-apple-fbi-icloud-exclusive/exclusive-apple-dropped-plan-for-encrypting-backups-after-fbi-complained-sources-idUSKBN1ZK1CT\">willful coordination and cooperation with the FBI</a>. And like most companies in the industry, Apple employs a <a href=\"https://www.washingtonpost.com/technology/2020/12/29/lens-technology-apple-uighur/\">highly problematic</a> <a href=\"https://www.theinformation.com/articles/apple-took-three-years-to-cut-ties-with-supplier-that-used-underage-labor\">supply chain</a>, which makes its human rights crusade seem even less authentic.</p>\n\n<p>Most recently, there was a <a href=\"https://www.macrumors.com/2021/03/23/protonvpn-app-store-dispute-myanmar/\">dispute with ProtonVPN</a> (the company that also makes <a href=\"https://protonmail.com\">ProtonMail</a>) over an update for its app in the App Store. <a href=\"https://protonvpn.com/blog/apple-blocks-app-updates/\">Proton Technologies claimed</a> that Apple was intentionally blocking the update amid the ongoing <a href=\"https://www.bbc.com/news/world-asia-56546920\">crackdown in Myanmar</a>. I agree <a href=\"https://daringfireball.net/2021/03/apple_protonvpn\">with Gruber</a> that there is little direct evidence to support this <em>exact claim</em>. While I am willing to give Apple the benefit of the doubt and consider this an inconvenient coincidence, I would <strong>not</strong> be surprised if this <em>were</em> a deliberate move. After all, <a href=\"https://www.nytimes.com/2017/07/29/technology/china-apple-censorhip.html\">Apple has pulled VPN apps from the App Store</a> before. For now, we can assume (as Gruber highlights) that this is yet another issue with Apple’s poorly executed app review process where its so-called <a href=\"https://mjtsai.com/blog/2021/03/23/protonvpn-security-updates-rejected-due-to-previously-approved-app-description/\">rules are applied arbitrarily</a>.</p>\n\n<p>However, there is still reason to be concerned, because Apple does not have a laudable record when it comes to cooperating with authoritarian governments. Below is a brief history of events that I have been tracking so far. If you know of others, get in touch.</p>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<h5 id=\"january-2017--apple-removed-nyt-from-the-china-app-store\">January 2017 — Apple removed NYT from the China App Store</h5>\n\n<p><a href=\"https://www.nytimes.com/2017/01/04/business/media/new-york-times-apps-apple-china.html\">Katie Benner and Sui-Lee Wee, reporting for the New York Times</a>:</p>\n\n<blockquote>\n  <p>Apple, complying with what it said was a request from Chinese authorities, removed news apps created by The New York Times from its app store in China late last month.</p>\n</blockquote>\n\n<h5 id=\"july-2017--apple-removes-vpn-apps-from-china-app-store\">July 2017 — Apple removes VPN apps from China App Store</h5>\n\n<p><a href=\"https://www.nytimes.com/2017/07/29/technology/china-apple-censorhip.html\">Paul Mozur, reporting for the New York Times</a>:</p>\n\n<blockquote>\n  <p>China appears to have received help on Saturday from an unlikely source in its fight against tools that help users evade its Great Firewall of internet censorship: Apple.</p>\n\n  <p>Software made by foreign companies to help users skirt the country’s system of internet filters has vanished from Apple’s app store on the mainland.</p>\n</blockquote>\n\n<p><a href=\"https://www.expressvpn.com/blog/china-ios-app-store-removes-vpns/\">ExpressVPN</a>:</p>\n\n<blockquote>\n  <p>We received notification from Apple today, July 29, 2017 — at roughly 04:00 GMT, that the ExpressVPN iOS app was removed from the China App Store. Our preliminary research indicates that all major VPN apps for iOS have been removed.</p>\n</blockquote>\n\n<h5 id=\"november-2017--apple-removes-skype-from-china-app-store\">November 2017 — Apple removes Skype from China App Store</h5>\n\n<p><a href=\"https://www.nytimes.com/2017/11/21/business/skype-app-china.html\">Paul Mozur, reporting for the New York Times</a>:</p>\n\n<blockquote>\n  <p>“We have been notified by the Ministry of Public Security that a number of voice over internet protocol apps do not comply with local law. Therefore these apps have been removed from the app store in China,” an Apple spokeswoman said Tuesday in an emailed statement responding to questions about Skype’s disappearance from the app store.</p>\n</blockquote>\n\n<h5 id=\"september-2019--apple-adopts-a-sim-canary\">September 2019 — Apple adopts a “SIM canary”</h5>\n\n<p>If you insert a Chinese carrier SIM, apps like TikTok &amp; Apple News no longer function.</p>\n\n<p><a href=\"https://twitter.com/thisboyuan/status/1169413062736007168\">Wang Boyuan on Twitter</a> (via <a href=\"https://twitter.com/_DanielSinclair/status/1169420981179428864\">Daniel Sinclair</a>):</p>\n\n<blockquote>\n  <p>TikTok for iOS has an unique region (i.e. China) lock. Before that it was Apple News.\nNews detects iPhone’s SIM region, you can’t use the app only when ALL sims are from China.\nTikTok also checks SIM region, the diff is you can’t use it if ONE of the sims is from China.</p>\n\n  <p>Having two sims inserted, one local and one roaming from China, TikTok can’t load a single video. Also failed on airplane mode. It works only after I take the China sim out.</p>\n</blockquote>\n\n<p><a href=\"https://twitter.com/_DanielSinclair/status/1169420981179428864\">Daniel Sinclair on Twitter</a>:</p>\n\n<blockquote>\n  <p>I had no idea China-bound iPhones were shipping with physical dual SIM support. Clever tactic to region users.</p>\n\n  <p>iPhone XS Max and iPhone XR. iPhone XS only supports dual SIMs with an eSIM.</p>\n\n  <p>This is a pretty intense tactic, and I think it will be increasingly more common as the Chinese tech ecosystem diverges. It’s pretty concerning that Apple itself adopts it. The Great Firewall is moving to the metal; the VPNs won’t work in that future.</p>\n</blockquote>\n\n<h5 id=\"october-2019--apple-removes-taiwan-flag-emoji-in-ios-13\">October 2019 — Apple removes Taiwan flag emoji in iOS 13</h5>\n\n<p><a href=\"https://hongkongfp.com/2019/10/05/taiwan-flag-emoji-disappears-latest-apple-iphone-keyboard/\">Kris Cheng, reporting for the Hong Kong Free Press</a>:</p>\n\n<blockquote>\n  <p>The Republic of China flag emoji has disappeared from Apple iPhone’s keyboard for Hong Kong and Macau users. The change happened for users who updated their phones to the latest operating system.</p>\n\n  <p>Updating iPhones to iOS 13.1.1 or above caused the flag emoji to disappear from the emoji keyboard. The flag, commonly used by users to denote Taiwan, can still be displayed by typing “Taiwan” in English, and choosing the flag in prediction candidates.</p>\n</blockquote>\n\n<p>This unfortunately wasn’t a bug, as <a href=\"https://twitter.com/_danielsinclair/status/1180603673967153153?s=21\">the same behavior shipped in iOS 13.2</a>.</p>\n\n<p>See also: reporting by <a href=\"https://www.theverge.com/2019/10/7/20903613/apple-hiding-taiwan-flag-emoji-hong-kong-macau-china\">The Verge</a>.</p>\n\n<h5 id=\"october-2019--apple-removes-hkmaplive-from-the-app-store\">October 2019 — Apple Removes HKmap.live from the App Store</h5>\n\n<p><a href=\"https://www.nytimes.com/2019/10/09/technology/apple-hong-kong-app.html\">Jack Nicas, writing for The New York Times</a>:</p>\n\n<blockquote>\n  <p>Apple removed an app late Wednesday that enabled protesters in Hong Kong to track the police, a day after facing intense criticism from Chinese state media for it, plunging the technology giant deeper into the complicated politics of a country that is fundamental to its business.</p>\n</blockquote>\n\n<p>See also: reporting by <a href=\"https://www.theverge.com/2019/10/10/20908498/apple-ceo-tim-cook-hong-kong-protest-app-removed-store-email-employees-hkmaplive\">The Verge</a> and <a href=\"https://www.macrumors.com/2019/10/18/us-lawmakers-condemn-apple-decision-hkmap-app/\">MacRumors</a>.</p>\n\n<p>These decisions have real consequences for movements. In January 2021 — <a href=\"https://www.nytimes.com/2021/01/05/world/asia/hong-kong-arrests-national-security-law.html\">the Hong Kong police arrested 53 elected pro-democracy officials and activists</a>, advancing Beijing’s initiative to quash dissent.</p>\n\n<h5 id=\"november-2019--apple-changes-crimea-map-to-meet-russian-demands\">November 2019 — Apple changes Crimea map to meet Russian demands</h5>\n\n<p><a href=\"https://www.bbc.com/news/technology-50573069\">The BBC</a>:</p>\n\n<blockquote>\n  <p>Apple has complied with Russian demands to show the annexed Crimean peninsula as part of Russian territory on its apps.</p>\n\n  <p>Russian forces annexed Crimea from Ukraine in March 2014, drawing international condemnation.</p>\n\n  <p>The region, which has a Russian-speaking majority, is now shown as Russian territory on Apple Maps and its Weather app, when viewed from Russia.</p>\n</blockquote>\n\n<h5 id=\"november-2019--trump-re-election-campaign-ad-shot-in-apples-mac-pro-plant-in-austin\">November 2019 — Trump re-election campaign ad shot in Apple’s Mac Pro plant in Austin</h5>\n\n<p><a href=\"https://daringfireball.net/2019/11/cook_trump_campaign_ad\">Gruber, at Daring Fireball</a>:</p>\n\n<blockquote>\n  <p>This wasn’t a promotion for the Mac Pro or its assembly plant. It was a promotion for Trump. This video makes it look like Trump’s trade policies have been good for Apple and that Tim Cook supports Trump.</p>\n\n  <p>[…]</p>\n\n  <p>This is how Apple chose to unveil the packaging for the Mac Pro — in a poorly-shot overexposed propaganda video by the White House, scored with bombastic music that sounds like it came from an SNL parody of a Michael Bay film.</p>\n</blockquote>\n\n<p>See also: <a href=\"https://www.nytimes.com/2019/11/20/us/politics/trump-texas-apple-factory.html\">No, That Mac Factory in Texas Is Not New</a> by Jack Nicas at The New York Times.</p>\n\n<p>Admittedly, this event is a bit of an outlier in relation to the others I have listed so far. But it is important to mention. This ad revealed how much effort Tim Cook is willing to exert to debase not only himself, but his entire company, to avoid tariffs and please whoever wields political power.</p>\n\n<p>I find it difficult to reconcile. How can you claim to stand up for human rights while voluntarily participating in a re-election campaign ad for The Human-Rights-Violator-in-Chief? And to top it off, <a href=\"https://www.macrumors.com/2021/01/20/tim-cook-donald-trump-mac-pro-gift/\">Tim Cook gifted Trump the ‘first’ 2019 Mac Pro</a>. Gruber is adamant that Cook does not support Trump and that Cook personally engaging with Trump does not imply support. However, I fail to see how that is relevant, when Cook is clearly willingly to play the game, regardless of his personal views.</p>\n\n<p>This campaign ad made obvious the tacit acknowledgment that Tim Cook is merely a capitalist — not a human rights activist. Apple is ultimately beholden to shareholders and investors, which means the pursuit of profit takes priority over anything else — including and especially human rights violations.</p>\n\n<h5 id=\"february-2020--apple-removed-plague-inc-from-chinese-app-store\">February 2020 — Apple removed Plague Inc. from Chinese App Store</h5>\n\n<p><a href=\"https://technode.com/2020/02/27/plague-inc-removed-from-chinese-app-stores-amid-outbreak/\">Eliza Gkritsi, writing for TechNode</a>:</p>\n\n<blockquote>\n  <p>Popular infection simulation game Plague Inc. has been removed from Chinese app stores, Apple and Xiaomi users noticed today, after enjoying renewed popularity during the Covid-19 outbreak.</p>\n\n  <p>Why it matters: The removal shows just how serious the country’s authorities are in managing the public perception of the virus.</p>\n\n  <p>Chinese authorities have been known to ban adult content and games with politically sensitive hidden messages. Plague Inc. has been praised for its educational value and scientific approach.</p>\n</blockquote>\n\n<h5 id=\"october-2020--apple-forced-telegram-to-close-channels-run-by-belarus-protestors\">October 2020 — Apple forced Telegram to close channels run by Belarus protestors</h5>\n\n<p><a href=\"https://decrypt.co/44339/telegram-forced-to-close-channels-run-by-belarus-protestors\">Scott Chipolina, writing for Decrypt</a>:</p>\n\n<blockquote>\n  <p>Apple is requesting that Telegram shut down three channels used in Belarus to expose the identities of individuals belonging to the Belarusian authoritarian regime that may be oppressing civilians.</p>\n</blockquote>\n\n<p>See also: <a href=\"https://mjtsai.com/blog/2020/10/09/apple-forces-telegram-to-close-channels-run-by-belarus-protestors/\">Michael Tsai’s round up</a>, and Daring Fireball’s <a href=\"https://daringfireball.net/2020/10/telegram_apple_belarus\">Telegram, Apple, Belarus, and Conflating ‘Irrelevance’ With ‘Inconvenience’</a></p>\n\n<h5 id=\"september-2020--apple-helps-track-down-blm-protester\">September 2020 — Apple helps track down BLM protester</h5>\n\n<p><a href=\"https://www.forbes.com/sites/thomasbrewster/2020/09/16/apple-helps-fbi-track-down-george-floyd-protester-accused-of-firebombing-cop-cars/\">Thomas Brewster, reporting for Forbes</a>:</p>\n\n<blockquote>\n  <p>Anyone who believes Apple and the FBI are at an impasse over investigations into the iPhone maker’s criminal customers should think again. In Seattle, Apple has given the feds vital evidence from one of its iCloud users who was arrested for firebombing cop cars during the George Floyd protests in late May.</p>\n\n  <p>The case shows how Apple is willing to help even where the context of the crime is controversial, namely the Black Lives Matter protests.</p>\n</blockquote>\n\n<p>(Note: you may be wondering why I have included the US in a list of authoritarian governments. Let me remind you of the <a href=\"https://www.theverge.com/2020/5/31/21276044/police-violence-protest-george-floyd\">outrageous</a> <a href=\"https://slate.com/news-and-politics/2020/05/george-floyd-protests-police-violence.html\">police brutality</a> <a href=\"https://www.huffpost.com/entry/police-medics-protests-george-floyd_n_5eda95b1c5b656c8b5c7c83a\">that we saw</a> <a href=\"https://www.bbc.com/news/world-us-canada-52880970\">last summer</a>. And let’s not forget the orange elephant in the room.)</p>\n\n<p>Related, <a href=\"https://www.reuters.com/article/us-apple-fbi-icloud-exclusive/exclusive-apple-dropped-plan-for-encrypting-backups-after-fbi-complained-sources-idUSKBN1ZK1CT\">Apple has reportedly dropped plans</a> to implement full end-to-end encryption for all of iCloud. <a href=\"https://www.reuters.com/article/us-apple-fbi-icloud-exclusive/exclusive-apple-dropped-plan-for-encrypting-backups-after-fbi-complained-sources-idUSKBN1ZK1CT\">Joseph Menn, reporting for Reuters</a>:</p>\n\n<blockquote>\n  <p>Apple Inc dropped plans to let iPhone users fully encrypt backups of their devices in the company’s iCloud service after the FBI complained that the move would harm investigations, six sources familiar with the matter told Reuters.</p>\n</blockquote>\n\n<p>While this report is not confirmed, it does seem plausible. After years of incrementally advancing the security of iOS and touting its tough stance on privacy, why has Apple not delivered on end-to-end encryption for iCloud? I understand there are many technical challenges, but surely it should be a high priority.</p>\n\n<h5 id=\"december-2020--pham-v-apple\">December 2020 — Pham v. Apple</h5>\n\n<p>via <a href=\"https://mjtsai.com/blog/2020/12/31/fired-app-reviewer-sues-apple/\">Michael Tsai</a>:</p>\n\n<blockquote>\n  <p>At this meeting, defendant Apple supervisors stated that the Guo Media App is critical of the Chinese government and, therefore, should be removed from the App Store. Plaintiff Pham responded stating the Guo Media App publishes valid claims of corruption against the Chinese government and Chinese Communist Party and, therefore, should not be taken down. Plaintiff Pham further told his supervisors that the Guo Media App does not contain violent content or incite violence; does not violate any of defendant Apple’s policies and procedures regarding Apps; and, therefore, it should remain on the App Store as a matter of free speech.</p>\n\n  <p>Defendant Apple became aware of plaintiff Pham’s criticism and defendant Apple’s managers responded by retaliating against plaintiff Pham and ultimately terminating plaintiff Pham.</p>\n</blockquote>\n\n<h5 id=\"february-2021--apple-removes-apps-for-pakistani-government\">February 2021 — Apple Removes Apps for Pakistani Government</h5>\n\n<p><a href=\"https://www.buzzfeednews.com/article/meghara/pakistan-forced-down-ahmadiyya-apps\">Megha Rajagopalan, reporting for BuzzFeed</a>:</p>\n\n<blockquote>\n  <p>Over the last two years, the government of Pakistan has forced Google and Apple to take down apps in the country created by developers based in other nations who are part of a repressed religious minority.</p>\n\n  <p>[…]</p>\n\n  <p>At issue are seven religious apps created by the Ahmadi community in the United States, published under the name “Ahmadiyya Muslim Community.”</p>\n</blockquote>\n\n<h5 id=\"march-2021--apple-reportedly-agrees-to-preinstall-russian-software\">March 2021 — Apple reportedly agrees to preinstall Russian software</h5>\n\n<p><a href=\"https://www.rferl.org/a/apple-russia-iphone-software/31153385.html\">RadioFreeEurope</a>:</p>\n\n<blockquote>\n  <p>Russian media are reporting that Apple has agreed to sell its gadgets in Russia with preinstalled Russian-made software to comply with a law that comes into force on April 1.</p>\n\n  <p>[…]</p>\n\n  <p>The list of Russian government-approved programs for mandatory preinstallation on smartphones and tablets includes the search engine Yandex, Mail.ru mail and news, ICQ messenger, social network VKontakte, payment system MirPay, and antivirus Kaspersky Lab, among others.</p>\n</blockquote>\n\n<p>See also: reporting by <a href=\"https://arstechnica.com/gadgets/2021/03/apple-bent-its-rules-for-russia-and-other-countries-will-take-note/\">Ars Technica</a>.</p>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-24-october-2025\">\n        <a href=\"#updated-24-october-2025\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"24 Oct 2025 12:29:50 PM \">\n            <i>24 October 2025</i>\n        </small>\n    </h5>\n    \n<p>The entries below were added after this piece was first published. I will continue to keep this post up-to-date as new events occur. The date of this notice reflects the last time an update was published. Updates are in chronological order. Scroll to the bottom for the most recent update.</p>\n\n</div>\n\n<h5 id=\"may-2021--seven-apple-suppliers-accused-of-using-forced-labor-from-xinjiang\">May 2021 — Seven Apple Suppliers Accused of Using Forced Labor From Xinjiang</h5>\n\n<p><a href=\"https://www.theinformation.com/articles/seven-apple-suppliers-accused-of-using-forced-labor-from-xinjiang\">Wayne Ma, reporting for The Information</a>:</p>\n\n<blockquote>\n  <p>The industrial park is surrounded by walls and fences with only one way in or out.</p>\n\n  <p>And next to the park was a large compound identified by a satellite imagery researcher as a detention center where the factory workers lived. The researcher, Nathan Ruser, from an Australian think tank, said “almost no other factories in Xinjiang have these characteristics except for industrial parks where there is detainee labor.</p>\n\n  <p>The Information and human rights groups have found seven companies supplying device components, coatings and assembly services to Apple that are linked to alleged forced labor involving Uyghurs and other oppressed minorities in China. At least five of those companies received thousands of Uyghur and other minority workers at specific factory sites or subsidiaries that did work for Apple, the investigation found.</p>\n\n  <p>The revelation stands in contrast to Apple’s assertions over the past year that it hasn’t found evidence of forced labor in its supply chain.</p>\n</blockquote>\n\n<h5 id=\"may-2021--censorship-surveillance-and-profits-a-hard-bargain-for-apple-in-china\">May 2021 — Censorship, Surveillance and Profits: A Hard Bargain for Apple in China</h5>\n\n<p><a href=\"https://www.nytimes.com/2021/05/17/technology/apple-china-censorship-data.html\">Jack Nicas, Raymond Zhong, and Daisuke Wakabayashi writing for The New York Times</a>:</p>\n\n<blockquote>\n  <p>Tim Cook, Apple’s chief executive, has said the data is safe. But at the data center in Guiyang, which Apple hoped would be completed by next month, and another in the Inner Mongolia region, Apple has largely ceded control to the Chinese government.</p>\n\n  <p>Chinese state employees physically manage the computers. Apple abandoned the encryption technology it used elsewhere after China would not allow it. And the digital keys that unlock information on those computers are stored in the data centers they’re meant to secure.</p>\n\n  <p>Internal Apple documents reviewed by The New York Times, interviews with 17 current and former Apple employees and four security experts, and new filings made in a court case in the United States last week provide rare insight into the compromises Mr. Cook has made to do business in China. They offer an extensive inside look — many aspects of which have never been reported before — at how Apple has given in to escalating demands from the Chinese authorities.</p>\n\n  <p>[…]</p>\n\n  <p>Mr. Cook often talks about Apple’s commitment to civil liberties and privacy. But to stay on the right side of Chinese regulators, his company has put the data of its Chinese customers at risk and has aided government censorship in the Chinese version of its App Store. After Chinese employees complained, it even dropped the “Designed by Apple in California” slogan from the backs of iPhones.</p>\n</blockquote>\n\n<p>See also: <a href=\"https://www.macrumors.com/2021/05/17/apple-security-compromises-china-icloud/\">MacRumors</a>, <a href=\"https://daringfireball.net/2021/05/nyt_apple_china_icloud\">Daring Fireball</a>, and <a href=\"https://mjtsai.com/blog/2021/05/19/a-hard-bargain-for-apple-in-china/\">Michael Tsai</a>.</p>\n\n<h5 id=\"june-2021--apples-new-private-relay-feature-will-not-be-available-in-multiple-countries\">June 2021 — Apple’s new ‘private relay’ feature will not be available in multiple countries</h5>\n\n<p><a href=\"https://www.reuters.com/world/china/apples-new-private-relay-feature-will-not-be-available-china-2021-06-07/\">Stephen Nellis and Paresh Dave writing for Reuters</a>:</p>\n\n<blockquote>\n  <p>Apple Inc on Monday said a new “private relay” feature designed to obscure a user’s web browsing behavior from internet service providers and advertisers will not be available in China for regulatory reasons.</p>\n\n  <p>Apple said it also will not offer “private relay” in Belarus, Colombia, Egypt, Kazakhstan, Saudi Arabia, South Africa, Turkmenistan, Uganda and the Philippines.</p>\n</blockquote>\n\n<h5 id=\"august-2021--engrave-danger-an-analysis-of-apple-engraving-censorship-across-six-regions\">August 2021 — Engrave Danger: An Analysis of Apple Engraving Censorship across Six Regions</h5>\n\n<p><a href=\"https://citizenlab.ca/2021/08/engrave-danger-an-analysis-of-apple-engraving-censorship-across-six-regions/\">Jeffrey Knockel and Lotus Ruan at The Citizen Lab</a>:</p>\n\n<blockquote>\n  <p>We analyzed Apple’s filtering of product engravings in six regions, discovering 1,105 keyword filtering rules used to moderate their content.</p>\n\n  <p>Across all six regions we analyzed, we found that Apple’s content moderation practices pertaining to derogatory, racist, or sexual content are inconsistently applied and that Apple’s public-facing documents failed to explain how it derives their keyword lists.</p>\n\n  <p>Within mainland China, we found that Apple censors political content including broad references to Chinese leadership and China’s political system, names of dissidents and independent news organizations, and general terms relating to religions, democracy, and human rights.</p>\n\n  <p>We found that part of Apple’s mainland China political censorship bleeds into both Hong Kong and Taiwan. Much of this censorship exceeds Apple’s legal obligations in Hong Kong, and we are aware of no legal justification for the political censorship of content in Taiwan.</p>\n\n  <p>We present evidence that Apple does not fully understand what content they censor and that, rather than each censored keyword being born of careful consideration, many seem to have been thoughtlessly reappropriated from other sources.</p>\n</blockquote>\n\n<h5 id=\"september-2021--apple-removes-navalny-app-from-stores-before-russian-elections\">September 2021 — Apple removes Navalny app from stores before Russian elections</h5>\n\n<p><a href=\"https://www.wired.com/story/opinion-in-russia-apple-and-google-staff-get-muscled-up-by-the-state/\">Justin Sherman writing for Wired</a>:</p>\n\n<blockquote>\n  <p>Earlier this month, when the Kremlin told multiple Big Tech companies to suppress political opposition amid nationwide elections in Russia, their answer was unequivocal: no. Yet just two weeks later, Apple and Google deleted from their app stores the Smart Voting app, opposition leader Alexey Navalny and his party’s primary tool for consolidating votes against Vladimir Putin’s regime. Then Telegram and Google-owned YouTube also restricted access to the recommendations for opposition candidates that Navalny was sharing on these platforms.</p>\n</blockquote>\n\n<p>See also: <a href=\"https://www.reuters.com/world/europe/google-apple-remove-navalny-app-stores-russian-elections-begin-2021-09-17/\">Reuters</a>.</p>\n\n<h5 id=\"august-2022--apple-warns-suppliers-to-follow-china-rules-on-taiwan-labeling\">August 2022 — Apple warns suppliers to follow China rules on ‘Taiwan’ labeling</h5>\n\n<p><a href=\"https://asia.nikkei.com/Spotlight/Supply-Chain/Apple-warns-suppliers-to-follow-China-rules-on-Taiwan-labeling\">Cheng Ting-Fang and Lauly Li, reporting for Nikkei</a>:</p>\n\n<blockquote>\n  <p>Apple has asked suppliers to ensure that shipments from Taiwan to China strictly comply with Chinese customs regulations after a recent visit by senior U.S. lawmaker Nancy Pelosi to Taipei stoked fears of rising trade barriers.</p>\n\n  <p>Apple told suppliers on Friday that China has started strictly enforcing a long-standing rule that Taiwanese-made parts and components must be labeled as being made either in “Taiwan, China” or “Chinese Taipei,” sources familiar with the matter told Nikkei Asia, language that indicates the island is part of China.</p>\n</blockquote>\n\n<p>It is certainly not a good look when you force your suppliers to print CCP state propaganda on Taiwanese-made parts.</p>\n\n<h5 id=\"january-2023--apple-brings-mainland-chinese-web-censorship-to-hong-kong\">January 2023 — Apple Brings Mainland Chinese Web Censorship To Hong Kong</h5>\n\n<p><a href=\"https://theintercept.com/2023/01/26/apple-china-censorship-hong-kong-gitlab/\">Sam Biddle, reporting for The Intercept</a>:</p>\n\n<blockquote>\n  <p>Apple quietly expanded the use of Chinese company Tencent’s website blacklist to users in Hong Kong — and no one will answer questions about it.</p>\n\n  <p>When Safari users in Hong Kong recently tried to load the popular code-sharing website GitLab, they received a strange warning instead: Apple’s browser was blocking the site for their own safety. The access was temporarily cut off thanks to Apple’s use of a Chinese corporate website blacklist, which resulted in the innocuous site being flagged as a purveyor of misinformation. Neither Tencent, the massive Chinese firm behind the web filter, nor Apple will say how or why the site was censored.</p>\n</blockquote>\n\n<h5 id=\"december-2023--governments-spying-on-apple-google-users-through-push-notifications\">December 2023 — Governments spying on Apple, Google users through push notifications</h5>\n\n<p><a href=\"https://www.reuters.com/technology/cybersecurity/governments-spying-apple-google-users-through-push-notifications-us-senator-2023-12-06/\">Raphael Satter, reporting for Reuters</a>:</p>\n\n<blockquote>\n  <p>Unidentified governments are surveilling smartphone users via their apps’ push notifications, a U.S. senator warned on Wednesday.</p>\n\n  <p><a href=\"https://www.documentcloud.org/documents/24191267-wyden_smartphone_push_notification_surveillance_letter_to_doj_-_signed\">In a letter to the Department of Justice</a>, Senator Ron Wyden said foreign officials were demanding the data from Alphabet’s Google and Apple. Although details were sparse, the letter lays out yet another path by which governments can track smartphones.</p>\n</blockquote>\n\n<p>See also: <a href=\"https://www.macrumors.com/2023/12/06/apple-governments-surveil-push-notifications/\">MacRumors</a>, <a href=\"https://mjtsai.com/blog/2023/12/06/governments-using-push-notifications-to-surveil-users/\">Michael Tsai</a>, <a href=\"https://9to5mac.com/2023/12/06/push-notification-spying/\">9to5Mac</a>, <a href=\"https://www.washingtonpost.com/technology/2023/12/06/push-notifications-surveillance-apple-google/\">Washington Post</a>, and <a href=\"https://www.404media.co/us-government-warrant-monitoring-push-notifications-apple-google-yahoo/\">404 Media: <em>Here’s a Warrant Showing the U.S. Government is Monitoring Push Notifications</em></a>.</p>\n\n<h5 id=\"april-2024--apple-removes-popular-messaging-apps-from-app-store-in-china\">April 2024 — Apple removes popular messaging apps from App Store in China</h5>\n\n<p><a href=\"https://www.macrumors.com/2024/04/19/apple-pulls-whatsapp-telegram-signal-threads-china/\">MacRumors</a>:</p>\n\n<blockquote>\n  <p>Apple on late Thursday into Friday removed the popular messaging and social media apps WhatsApp, Telegram, Signal, and Threads from its App Store in China at the request of the Chinese government […]</p>\n\n  <p>Apple has complied with similar App Store removal orders from the Chinese government in the past for apps related to VPNs, news, and more.</p>\n</blockquote>\n\n<h5 id=\"july-2024--apple-removes-vpn-apps-from-the-app-store-in-russia\">July 2024 — Apple removes VPN apps from the App Store in Russia</h5>\n\n<p><a href=\"https://appleinsider.com/articles/24/07/05/russia-forces-apple-to-remove-vpn-apps-from-the-app-store\">William Gallagher, reporting for Apple Insider</a>:</p>\n\n<blockquote>\n  <p>Apple has caved to pressure from Russian authorities and removed a number of the best iPhone VPN apps from the App Store in the country.</p>\n\n  <p>Apple’s App Store team has been notifying VPN developers that their apps are being removed “per demand from Roskomnadzor.” This the state media watchdog that previously forced both Apple and Google to remove a political app backed by the leader of the country’s opposition.</p>\n\n  <p>[…]</p>\n\n  <p>Reportedly, VPN usage increased dramatically in Russia following the start of the war with Ukraine. At that point, Russian authorities blocked access to a number of Western social media sites.</p>\n</blockquote>\n\n<p>See also: <a href=\"https://mjtsai.com/blog/2024/07/05/apple-removes-vpn-apps-from-russian-app-store/\">Michael Tsai</a></p>\n\n<h5 id=\"october-2025--apple-removes-multiple-ice-reporting-apps-from-the-app-store\">October 2025 — Apple removes multiple ICE reporting apps from the App Store</h5>\n\n<p><a href=\"https://migrantinsider.com/p/scoop-apple-quietly-made-ice-agents\">Pablo Manriquez, reporting for Migrant Insider</a>:</p>\n\n<blockquote>\n  <p>Apple has quietly removed DeICER, a civic-reporting app used to log immigration enforcement activity, from its App Store after a law enforcement complaint — invoking a rule normally reserved for protecting marginalized groups from hate speech.</p>\n</blockquote>\n\n<p>See also: <a href=\"https://mjtsai.com/blog/2025/10/10/deicer-removed-from-the-app-store/\">Michael Tsai</a>, <a href=\"https://daringfireball.net/linked/2025/10/09/apple-app-store-deicer\">Daring Fireball</a></p>\n\n<p><a href=\"https://www.404media.co/apple-banned-an-app-that-simply-archived-videos-of-ice-abuses/\">JOSEPH COX, report for 404 Media</a>:</p>\n\n<blockquote>\n  <p>Apple removed an app for preserving TikToks, Instagram reels, news reports, and videos documenting abuses by ICE, 404 Media has learned. The app, called Eyes Up, differs from other banned apps such as ICEBlock which were designed to report sightings of ICE officials in real-time to warn local communities. Eyes Up, meanwhile, was more of an aggregation service pooling together information to preserve evidence in case the material is needed in the future in court.</p>\n</blockquote>\n\n<p>See also: <a href=\"https://mjtsai.com/blog/2025/10/09/eyes-up-removed-from-the-app-store/\">Michael Tsai</a>, <a href=\"https://daringfireball.net/linked/2025/10/09/apple-banned-eyes-up\">Daring Fireball</a></p>\n\n<p><a href=\"https://www.businessinsider.com/apple-iceblock-app-store-removed-2025-10\">Business Insider</a>:</p>\n\n<blockquote>\n  <p>Apple has removed ICEBlock, an app that allowed users to monitor and report the location of immigration enforcement officers, from the App Store.</p>\n</blockquote>\n\n<p>See also: <a href=\"https://mjtsai.com/blog/2025/10/03/iceblock-removed-from-the-app-store/\">Michael Tsai</a></p>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<h3 id=\"conclusion\">Conclusion</h3>\n\n<p>So what does any of this have to do with app developers? Why should we care? When it comes to the iOS App Store, Apple controls where we are allowed to distribute our apps. More importantly, Apple has the unilateral power remove our apps from <em>any</em> App Store region <em>at any time</em> to nurture its relationship with whatever unsavory government it is interested in pleasing in order to pursue its political motives or financial objectives.</p>\n\n<p>Apple’s centralized power over app distribution combined with its willingness to surrender to political pressures is incredibly concerning as ostensibly “democratic” governments across the globe (including the United Sates!) increasingly exhibit far-right, fascist behavior and implement fascist policies. What will happen when you need to build your own <a href=\"https://www.nytimes.com/2019/10/09/technology/apple-hong-kong-app.html\">HKmap.live</a>?</p>\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2021/03/30/apple-cooperation-with-authoritarian-governments/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2024/06/05/swift-concurrency-non-sendable-closures/#updated-05-june-2024",
            "url": "https://www.jessesquires.com/blog/2024/06/05/swift-concurrency-non-sendable-closures/",
            "title": "[Updated] Swift concurrency hack for passing non-sendable closures: Uncheck yourself before you wreck yourself",
            "date_published": "2024-06-05T18:24:16-07:00",
            "date_modified": "2024-06-05T18:24:16-07:00",
            "summary": "<p>If you have attempted to adopt <a href=\"https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/\">Swift Concurrency</a> in your codebase, you have certainly needed to address dozens — likely, hundreds — of warnings and errors. Sometimes the issues can be resolved by addressing them directly. That is, your code was incorrect and you simply have to fix it to make it correct. In other scenarios, the resolution is not so straightforward. In particular, it is difficult to satisfy the compiler when working with APIs that you do not own that have not been updated for concurrency. Or, you may have found yourself in a situation where <em>you</em> know your code is correct, but <em>the compiler</em> is unable to verify its correctness — either because of a few remaining bugs in Swift Concurrency, or because you are using <code class=\"language-plaintext highlighter-rouge\">@preconcurrency</code> APIs.</p>\n\n",
            "tags": [
                "swift","concurrency",
                "software-dev"
            ],
            "content_html": "<p>If you have attempted to adopt <a href=\"https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/\">Swift Concurrency</a> in your codebase, you have certainly needed to address dozens — likely, hundreds — of warnings and errors. Sometimes the issues can be resolved by addressing them directly. That is, your code was incorrect and you simply have to fix it to make it correct. In other scenarios, the resolution is not so straightforward. In particular, it is difficult to satisfy the compiler when working with APIs that you do not own that have not been updated for concurrency. Or, you may have found yourself in a situation where <em>you</em> know your code is correct, but <em>the compiler</em> is unable to verify its correctness — either because of a few remaining bugs in Swift Concurrency, or because you are using <code class=\"language-plaintext highlighter-rouge\">@preconcurrency</code> APIs.</p>\n\n<!--excerpt-->\n\n<p>One of the warnings you have probably seen multiple times is <em>“Capture of ‘variable’ with non-sendable type in a <code class=\"language-plaintext highlighter-rouge\">@Sendable</code> closure.”</em> I was confronted with this warning in a project recently and I want to share the hack for how I worked around it. The issue was the result of a combination of factors I mentioned above. I was interacting with <code class=\"language-plaintext highlighter-rouge\">@preconcurrency</code> APIs <strong>and</strong> I knew my code was concurrency-safe, but I was unable to accurately express that to the compiler.</p>\n\n<h3 id=\"background\">Background</h3>\n\n<p>First, let’s discuss the context in which I was dealing with this concurrency warning. I have been working on a project that uses <code class=\"language-plaintext highlighter-rouge\">UICollectionViewDiffableDataSource</code> and I am wrapping the UIKit APIs with something more user-friendly. For the purposes of this post, I have omitted much of the complexity to provide a clear, simple example.</p>\n\n<p>I have a custom diffable data source that performs the diffing on a background thread:</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">@MainActor</span>\n<span class=\"kd\">final</span> <span class=\"kd\">class</span> <span class=\"kt\">DiffableDataSource</span><span class=\"p\">:</span> <span class=\"kt\">UICollectionViewDiffableDataSource</span><span class=\"o\">&lt;</span><span class=\"kt\">String</span><span class=\"p\">,</span> <span class=\"kt\">String</span><span class=\"o\">&gt;</span> <span class=\"p\">{</span>\n    <span class=\"kd\">typealias</span> <span class=\"kt\">Snapshot</span> <span class=\"o\">=</span> <span class=\"kt\">NSDiffableDataSourceSnapshot</span><span class=\"o\">&lt;</span><span class=\"kt\">String</span><span class=\"p\">,</span> <span class=\"kt\">String</span><span class=\"o\">&gt;</span>\n\n    <span class=\"kd\">typealias</span> <span class=\"kt\">SnapshotCompletion</span> <span class=\"o\">=</span> <span class=\"kd\">@MainActor</span> <span class=\"p\">()</span> <span class=\"o\">-&gt;</span> <span class=\"kt\">Void</span>\n\n    <span class=\"k\">let</span> <span class=\"nv\">diffingQueue</span> <span class=\"o\">=</span> <span class=\"kt\">DispatchQueue</span><span class=\"p\">(</span><span class=\"nv\">label</span><span class=\"p\">:</span> <span class=\"s\">\"diffingQueue\"</span><span class=\"p\">)</span>\n\n    <span class=\"kd\">func</span> <span class=\"nf\">applyDiff</span><span class=\"p\">(</span><span class=\"nv\">snapshot</span><span class=\"p\">:</span> <span class=\"kt\">Snapshot</span><span class=\"p\">,</span> <span class=\"nv\">animated</span><span class=\"p\">:</span> <span class=\"kt\">Bool</span> <span class=\"o\">=</span> <span class=\"kc\">true</span><span class=\"p\">,</span> <span class=\"nv\">completion</span><span class=\"p\">:</span> <span class=\"kt\">SnapshotCompletion</span><span class=\"p\">?</span> <span class=\"o\">=</span> <span class=\"kc\">nil</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n        <span class=\"k\">self</span><span class=\"o\">.</span><span class=\"n\">diffingQueue</span><span class=\"o\">.</span><span class=\"k\">async</span> <span class=\"p\">{</span>\n            <span class=\"k\">self</span><span class=\"o\">.</span><span class=\"nf\">apply</span><span class=\"p\">(</span><span class=\"n\">snapshot</span><span class=\"p\">,</span> <span class=\"nv\">animatingDifferences</span><span class=\"p\">:</span> <span class=\"n\">animated</span><span class=\"p\">,</span> <span class=\"nv\">completion</span><span class=\"p\">:</span> <span class=\"n\">completion</span><span class=\"p\">)</span>\n        <span class=\"p\">}</span>\n    <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>Per <a href=\"https://developer.apple.com/documentation/uikit/uicollectionviewdiffabledatasource/3375795-apply\">the documentation</a>, the <code class=\"language-plaintext highlighter-rouge\">completion</code> closure is always called on the main queue and you can call <code class=\"language-plaintext highlighter-rouge\">apply(_:animatingDifferences:completion:)</code> from a background queue:</p>\n\n<blockquote>\n  <p>[…]</p>\n\n  <p><strong>completion</strong><br />\nA closure to execute when the animations are complete. This closure has no return value and takes no parameters. The system calls this closure from the main queue.</p>\n\n  <p>[…]</p>\n\n  <p>You can safely call this method from a background queue, but you must do so consistently in your app. Always call this method exclusively from the main queue or from a background queue.</p>\n</blockquote>\n\n<p>Also note that <code class=\"language-plaintext highlighter-rouge\">UICollectionViewDiffableDataSource</code> is annotated with <code class=\"language-plaintext highlighter-rouge\">@MainActor @preconcurrency</code>.</p>\n\n<p>With the strictest concurrency checking enabled, the code above produces 2 warnings:</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">func</span> <span class=\"nf\">applyDiff</span><span class=\"p\">(</span><span class=\"nv\">snapshot</span><span class=\"p\">:</span> <span class=\"kt\">Snapshot</span><span class=\"p\">,</span> <span class=\"nv\">animated</span><span class=\"p\">:</span> <span class=\"kt\">Bool</span> <span class=\"o\">=</span> <span class=\"kc\">true</span><span class=\"p\">,</span> <span class=\"nv\">completion</span><span class=\"p\">:</span> <span class=\"kt\">SnapshotCompletion</span><span class=\"p\">?</span> <span class=\"o\">=</span> <span class=\"kc\">nil</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n    <span class=\"k\">self</span><span class=\"o\">.</span><span class=\"n\">diffingQueue</span><span class=\"o\">.</span><span class=\"k\">async</span> <span class=\"p\">{</span>\n        <span class=\"k\">self</span><span class=\"o\">.</span><span class=\"nf\">apply</span><span class=\"p\">(</span><span class=\"n\">snapshot</span><span class=\"p\">,</span> <span class=\"nv\">animatingDifferences</span><span class=\"p\">:</span> <span class=\"n\">animated</span><span class=\"p\">,</span> <span class=\"nv\">completion</span><span class=\"p\">:</span> <span class=\"n\">completion</span><span class=\"p\">)</span>\n        <span class=\"c1\">//                                                               ^</span>\n        <span class=\"c1\">// Capture of 'completion' with non-sendable type 'DiffableDataSource.SnapshotCompletion?' (aka 'Optional&lt;@MainActor () -&gt; ()&gt;') in a `@Sendable` closure</span>\n        <span class=\"c1\">// Converting function value of type '@MainActor () -&gt; Void' to '() -&gt; Void' loses global actor 'MainActor'; this is an error in Swift 6</span>\n    <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>One solution would be to make the completion closure <code class=\"language-plaintext highlighter-rouge\">@Sendable</code>:</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">func</span> <span class=\"nf\">applyDiff</span><span class=\"p\">(</span><span class=\"nv\">snapshot</span><span class=\"p\">:</span> <span class=\"kt\">Snapshot</span><span class=\"p\">,</span> <span class=\"nv\">animated</span><span class=\"p\">:</span> <span class=\"kt\">Bool</span> <span class=\"o\">=</span> <span class=\"kc\">true</span><span class=\"p\">,</span> <span class=\"nv\">completion</span><span class=\"p\">:</span> <span class=\"kd\">@escaping</span> <span class=\"kd\">@Sendable</span> <span class=\"p\">()</span> <span class=\"o\">-&gt;</span> <span class=\"kt\">Void</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n    <span class=\"k\">self</span><span class=\"o\">.</span><span class=\"n\">diffingQueue</span><span class=\"o\">.</span><span class=\"k\">async</span> <span class=\"p\">{</span>\n        <span class=\"k\">self</span><span class=\"o\">.</span><span class=\"nf\">apply</span><span class=\"p\">(</span><span class=\"n\">snapshot</span><span class=\"p\">,</span> <span class=\"nv\">animatingDifferences</span><span class=\"p\">:</span> <span class=\"n\">animated</span><span class=\"p\">,</span> <span class=\"nv\">completion</span><span class=\"p\">:</span> <span class=\"n\">completion</span><span class=\"p\">)</span>\n    <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>(Note that we cannot use the defined typealias <code class=\"language-plaintext highlighter-rouge\">SnapshotCompletion</code> in this case, and it must not be optional.)</p>\n\n<p>However, this will not work. This API updates the collection view to reflect the state of the data in the snapshot and, as mentioned, <code class=\"language-plaintext highlighter-rouge\">completion</code> is called on the main thread. Callers of <code class=\"language-plaintext highlighter-rouge\">applyDiff(snapshot:)</code> do additional UI updates and animations, or otherwise deal with <code class=\"language-plaintext highlighter-rouge\">@MainActor</code> members and types in this completion closure. Those members and types cannot be marked as <code class=\"language-plaintext highlighter-rouge\">@Sendable</code>. For example, the owner of the <code class=\"language-plaintext highlighter-rouge\">DiffableDataSource</code> instance could be a view controller. Furthermore, I do not want to impose a <code class=\"language-plaintext highlighter-rouge\">@Sendable</code> restriction upon callers.</p>\n\n<p>Thus, making the closure <code class=\"language-plaintext highlighter-rouge\">@Sendable</code> produces the following kinds of errors at the call site:</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">self</span><span class=\"o\">.</span><span class=\"n\">dataSource</span><span class=\"o\">.</span><span class=\"nf\">applyDiff</span><span class=\"p\">(</span><span class=\"nv\">snapshot</span><span class=\"p\">:</span> <span class=\"n\">snapshot</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n\n    <span class=\"c1\">// Call to main actor-isolated instance method 'someMethod()' in a synchronous nonisolated context</span>\n\n    <span class=\"c1\">// Main actor-isolated property 'someProperty' can not be referenced from a Sendable closure; this is an error in Swift 6</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>Callers could wrap the offending lines in <code class=\"language-plaintext highlighter-rouge\">Task { @MainActor in }</code> or <code class=\"language-plaintext highlighter-rouge\">MainActor.assumeIsolated { }</code> to silence these issues. But, that’s a burden for callers. Not to mention, the wrapper API does not accurately communicate what is happening here. We do not want a <code class=\"language-plaintext highlighter-rouge\">@Sendable () -&gt; Void</code> closure. We want a <code class=\"language-plaintext highlighter-rouge\">@MainActor () -&gt; Void</code> closure.</p>\n\n<p>So, we have situation where the Swift compiler is telling us that the closure being captured needs to be <code class=\"language-plaintext highlighter-rouge\">@Sendable</code> but we cannot make it <code class=\"language-plaintext highlighter-rouge\">@Sendable</code>. It is also telling us that the closure loses its <code class=\"language-plaintext highlighter-rouge\">@MainActor</code> but we know that the closure will always be called from the main queue. Because of these two problems, we need to find a way to work around the warnings and coerce the compiler into doing what we want.</p>\n\n<h3 id=\"solution-its-a-hack\">Solution (It’s a hack)</h3>\n\n<p>We can wrap the completion closure in another type that is <code class=\"language-plaintext highlighter-rouge\">@unchecked Sendable</code>.</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">struct</span> <span class=\"kt\">UncheckedCompletion</span><span class=\"p\">:</span> <span class=\"kd\">@unchecked</span> <span class=\"kt\">Sendable</span> <span class=\"p\">{</span>\n    <span class=\"kd\">typealias</span> <span class=\"kt\">Block</span> <span class=\"o\">=</span> <span class=\"p\">()</span> <span class=\"o\">-&gt;</span> <span class=\"kt\">Void</span>\n\n    <span class=\"k\">let</span> <span class=\"nv\">block</span><span class=\"p\">:</span> <span class=\"kt\">Block</span><span class=\"p\">?</span>\n\n    <span class=\"nf\">init</span><span class=\"p\">(</span><span class=\"n\">_</span> <span class=\"nv\">block</span><span class=\"p\">:</span> <span class=\"kt\">Block</span><span class=\"p\">?)</span> <span class=\"p\">{</span>\n        <span class=\"k\">if</span> <span class=\"k\">let</span> <span class=\"nv\">block</span> <span class=\"p\">{</span>\n            <span class=\"k\">self</span><span class=\"o\">.</span><span class=\"n\">block</span> <span class=\"o\">=</span> <span class=\"p\">{</span>\n                <span class=\"nf\">dispatchPrecondition</span><span class=\"p\">(</span><span class=\"nv\">condition</span><span class=\"p\">:</span> <span class=\"o\">.</span><span class=\"nf\">onQueue</span><span class=\"p\">(</span><span class=\"o\">.</span><span class=\"n\">main</span><span class=\"p\">))</span>\n                <span class=\"nf\">block</span><span class=\"p\">()</span>\n            <span class=\"p\">}</span>\n        <span class=\"p\">}</span> <span class=\"k\">else</span> <span class=\"p\">{</span>\n            <span class=\"k\">self</span><span class=\"o\">.</span><span class=\"n\">block</span> <span class=\"o\">=</span> <span class=\"kc\">nil</span>\n        <span class=\"p\">}</span>\n    <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>This will silence the warning about <em>“capturing a non-sendable type in a <code class=\"language-plaintext highlighter-rouge\">@Sendable</code> closure.”</em> Again, UIKit guarantees that this completion closure will always be called on the main thread, and we can use a <code class=\"language-plaintext highlighter-rouge\">dispatchPrecondition()</code> to verify this is happening.</p>\n\n<p>We can update our API to use this new <code class=\"language-plaintext highlighter-rouge\">UncheckedCompletion</code> wrapper.</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">func</span> <span class=\"nf\">applyDiff</span><span class=\"p\">(</span><span class=\"nv\">snapshot</span><span class=\"p\">:</span> <span class=\"kt\">Snapshot</span><span class=\"p\">,</span> <span class=\"nv\">animated</span><span class=\"p\">:</span> <span class=\"kt\">Bool</span> <span class=\"o\">=</span> <span class=\"kc\">true</span><span class=\"p\">,</span> <span class=\"nv\">completion</span><span class=\"p\">:</span> <span class=\"kt\">UncheckedCompletion</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n    <span class=\"k\">self</span><span class=\"o\">.</span><span class=\"n\">diffingQueue</span><span class=\"o\">.</span><span class=\"k\">async</span> <span class=\"p\">{</span>\n        <span class=\"k\">self</span><span class=\"o\">.</span><span class=\"nf\">apply</span><span class=\"p\">(</span><span class=\"n\">snapshot</span><span class=\"p\">,</span> <span class=\"nv\">animatingDifferences</span><span class=\"p\">:</span> <span class=\"n\">animated</span><span class=\"p\">,</span> <span class=\"nv\">completion</span><span class=\"p\">:</span> <span class=\"n\">completion</span><span class=\"o\">.</span><span class=\"n\">block</span><span class=\"p\">)</span>\n    <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>However, exposing <code class=\"language-plaintext highlighter-rouge\">UncheckedCompletion</code> to callers is also not a great API. We should hide this detail. We can wrap this <code class=\"language-plaintext highlighter-rouge\">applyDiff()</code> method with another that uses the original <code class=\"language-plaintext highlighter-rouge\">SnapshotCompletion</code> typealias.</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">func</span> <span class=\"nf\">applyDiff</span><span class=\"p\">(</span><span class=\"n\">_</span> <span class=\"nv\">snapshot</span><span class=\"p\">:</span> <span class=\"kt\">Snapshot</span><span class=\"p\">,</span> <span class=\"nv\">animated</span><span class=\"p\">:</span> <span class=\"kt\">Bool</span> <span class=\"o\">=</span> <span class=\"kc\">true</span><span class=\"p\">,</span> <span class=\"nv\">completion</span><span class=\"p\">:</span> <span class=\"kt\">SnapshotCompletion</span><span class=\"p\">?</span> <span class=\"o\">=</span> <span class=\"kc\">nil</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n    <span class=\"k\">self</span><span class=\"o\">.</span><span class=\"nf\">applyDiff</span><span class=\"p\">(</span><span class=\"nv\">snapshot</span><span class=\"p\">:</span> <span class=\"n\">snapshot</span><span class=\"p\">,</span> <span class=\"nv\">animated</span><span class=\"p\">:</span> <span class=\"n\">animated</span><span class=\"p\">,</span> <span class=\"nv\">completion</span><span class=\"p\">:</span> <span class=\"kt\">UncheckedCompletion</span><span class=\"p\">(</span><span class=\"n\">completion</span><span class=\"p\">))</span>\n    <span class=\"c1\">//                                                                 ^ wrapped in UncheckedCompletion</span>\n<span class=\"p\">}</span>\n\n<span class=\"kd\">private</span> <span class=\"kd\">func</span> <span class=\"nf\">applyDiff</span><span class=\"p\">(</span><span class=\"nv\">snapshot</span><span class=\"p\">:</span> <span class=\"kt\">Snapshot</span><span class=\"p\">,</span> <span class=\"nv\">animated</span><span class=\"p\">:</span> <span class=\"kt\">Bool</span><span class=\"p\">,</span> <span class=\"nv\">completion</span><span class=\"p\">:</span> <span class=\"kt\">UncheckedCompletion</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n    <span class=\"k\">self</span><span class=\"o\">.</span><span class=\"n\">diffingQueue</span><span class=\"o\">.</span><span class=\"k\">async</span> <span class=\"p\">{</span>\n        <span class=\"k\">self</span><span class=\"o\">.</span><span class=\"nf\">apply</span><span class=\"p\">(</span><span class=\"n\">snapshot</span><span class=\"p\">,</span> <span class=\"nv\">animatingDifferences</span><span class=\"p\">:</span> <span class=\"n\">animated</span><span class=\"p\">,</span> <span class=\"nv\">completion</span><span class=\"p\">:</span> <span class=\"n\">completion</span><span class=\"o\">.</span><span class=\"n\">block</span><span class=\"p\">)</span>\n        <span class=\"c1\">//                                                               ^ access underlying closure</span>\n    <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>And now, the public API looks exactly the same as before to callers, but they can safely use <code class=\"language-plaintext highlighter-rouge\">@MainActor</code> members and types in the completion closure without any warnings or errors.</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">self</span><span class=\"o\">.</span><span class=\"n\">dataSource</span><span class=\"o\">.</span><span class=\"nf\">applyDiff</span><span class=\"p\">(</span><span class=\"nv\">snapshot</span><span class=\"p\">:</span> <span class=\"n\">snapshot</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n    <span class=\"c1\">// do anything here safely with @MainActor with no warnings or errors</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"is-this-good\">Is this good?</h3>\n\n<p>Is this a good idea? I am actually not sure! But, it seems like the best thing to do in this scenario. If you are facing a similar situation — namely, <strong>you know</strong> a captured closure is always called on the main thread and you cannot make it <code class=\"language-plaintext highlighter-rouge\">@Sendable</code> — this might be a good solution for you too! However, this is probably <em>a bad idea</em> to attempt to generalize. Use wisely!</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-05-june-2024\">\n        <a href=\"#updated-05-june-2024\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"05 Jun 2024 06:24:16 PM PDT\">\n            <i>05 June 2024</i>\n        </small>\n    </h5>\n    \n<p>As anticipated, <a href=\"https://mastodon.social/@mattiem\">Matt Massicotte</a> has come to the rescue, <a href=\"https://mastodon.social/@mattiem/112565464652182320\">offering a simpler solution here</a>. While my clever hack works, we can instead make the closure both <code class=\"language-plaintext highlighter-rouge\">@Sendable</code> <strong>and</strong> <code class=\"language-plaintext highlighter-rouge\">@MainActor</code>. After that, we can simply wrap calling the completion closure in <code class=\"language-plaintext highlighter-rouge\">MainActor.assumeIsolated { }</code>. Here are the changes needed:</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">// The addition of @Sendable is bizarre, but Swift 5.10 needs it.</span>\n<span class=\"c1\">// Swift 6 (via SE-0434) will make it unnecessary.</span>\n<span class=\"kd\">typealias</span> <span class=\"kt\">SnapshotCompletion</span> <span class=\"o\">=</span> <span class=\"kd\">@Sendable</span> <span class=\"kd\">@MainActor</span> <span class=\"p\">()</span> <span class=\"o\">-&gt;</span> <span class=\"kt\">Void</span>\n\n<span class=\"kd\">func</span> <span class=\"nf\">applyDiff</span><span class=\"p\">(</span><span class=\"nv\">snapshot</span><span class=\"p\">:</span> <span class=\"kt\">Snapshot</span><span class=\"p\">,</span> <span class=\"nv\">animated</span><span class=\"p\">:</span> <span class=\"kt\">Bool</span> <span class=\"o\">=</span> <span class=\"kc\">true</span><span class=\"p\">,</span> <span class=\"nv\">completion</span><span class=\"p\">:</span> <span class=\"kt\">SnapshotCompletion</span><span class=\"p\">?</span> <span class=\"o\">=</span> <span class=\"kc\">nil</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n    <span class=\"k\">self</span><span class=\"o\">.</span><span class=\"n\">diffingQueue</span><span class=\"o\">.</span><span class=\"k\">async</span> <span class=\"p\">{</span>\n        <span class=\"c1\">// UIKit guarantees `completion` is called on the main queue.</span>\n        <span class=\"k\">self</span><span class=\"o\">.</span><span class=\"nf\">apply</span><span class=\"p\">(</span><span class=\"n\">snapshot</span><span class=\"p\">,</span> <span class=\"nv\">animatingDifferences</span><span class=\"p\">:</span> <span class=\"n\">animated</span><span class=\"p\">,</span> <span class=\"nv\">completion</span><span class=\"p\">:</span> <span class=\"p\">{</span>\n            <span class=\"c1\">// when you know its on the main actor, perhaps from documentation, but it isn't</span>\n            <span class=\"c1\">// encoded in the API, you can use dynamic isolation to make it work</span>\n            <span class=\"kt\">MainActor</span><span class=\"o\">.</span><span class=\"n\">assumeIsolated</span> <span class=\"p\">{</span>\n                <span class=\"nf\">completion</span><span class=\"p\">?()</span>\n            <span class=\"p\">}</span>\n        <span class=\"p\">})</span>\n    <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>This is great. It is much less code. I’m not sure why I didn’t <em>try</em> using <code class=\"language-plaintext highlighter-rouge\">@Sendable @MainActor</code> for the closure. It’s probably because I assumed that <code class=\"language-plaintext highlighter-rouge\">@MainActor</code> <em>implied</em> <code class=\"language-plaintext highlighter-rouge\">@Sendable</code> — which apparently <em>it should</em> and <em>it will</em> after <a href=\"https://github.com/apple/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md\">SE-0434</a> in Swift 6.</p>\n\n<p>Anyway, the general idea of this hack might still be useful in other contexts or scenarios — especially if you cannot adopt Swift 6 and need to stay on Swift 5.10.</p>\n\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2024/06/05/swift-concurrency-non-sendable-closures/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2024/05/29/swiftpm-package-resolved-xcode/#updated-30-may-2024",
            "url": "https://www.jessesquires.com/blog/2024/05/29/swiftpm-package-resolved-xcode/",
            "title": "[Updated] Workaround: Xcode deletes Package.resolved file and produces 'missing package product' errors",
            "date_published": "2024-05-30T09:49:22-07:00",
            "date_modified": "2024-05-30T09:49:22-07:00",
            "summary": "<p>More and more Apple Platform developers are migrating away from <a href=\"https://cocoapods.org\">CocoaPods</a> in favor the <a href=\"https://www.swift.org/documentation/package-manager/\">Swift Package Manager</a>, which is Apple’s first-party tool for managing and integrating dependencies. While it is still <a href=\"/blog/2020/02/24/replacing-cocoapods-with-swiftpm/\">not quite a complete replacement</a> for CocoaPods, it is getting closer. Unfortunately, SwiftPM’s integration with Xcode still has a number of shortcomings, even though it was introduced with <a href=\"https://developer.apple.com/documentation/xcode-release-notes/xcode-11-release-notes\">Xcode 11</a> — 4 years ago. The worst bug is that Xcode frequently and randomly deletes the <code class=\"language-plaintext highlighter-rouge\">Package.resolved</code>, which in turn produces dozens or hundreds of <code class=\"language-plaintext highlighter-rouge\">'missing package product'</code> errors. Here’s how I’ve worked around this bug on a team I work on.</p>\n\n",
            "tags": [
                "xcode","ios","macos","bugs","swiftpm","swift","git",
                "software-dev"
            ],
            "content_html": "<p>More and more Apple Platform developers are migrating away from <a href=\"https://cocoapods.org\">CocoaPods</a> in favor the <a href=\"https://www.swift.org/documentation/package-manager/\">Swift Package Manager</a>, which is Apple’s first-party tool for managing and integrating dependencies. While it is still <a href=\"/blog/2020/02/24/replacing-cocoapods-with-swiftpm/\">not quite a complete replacement</a> for CocoaPods, it is getting closer. Unfortunately, SwiftPM’s integration with Xcode still has a number of shortcomings, even though it was introduced with <a href=\"https://developer.apple.com/documentation/xcode-release-notes/xcode-11-release-notes\">Xcode 11</a> — 4 years ago. The worst bug is that Xcode frequently and randomly deletes the <code class=\"language-plaintext highlighter-rouge\">Package.resolved</code>, which in turn produces dozens or hundreds of <code class=\"language-plaintext highlighter-rouge\">'missing package product'</code> errors. Here’s how I’ve worked around this bug on a team I work on.</p>\n\n<!--excerpt-->\n\n<h3 id=\"the-bug\">The bug</h3>\n\n<p>Xcode seems to specifically delete <code class=\"language-plaintext highlighter-rouge\">Package.resolved</code> when changing branches in git. Then, depending on how large your project is and how many Swift packages are included, you’ll see dozens or hundreds or thousands of <code class=\"language-plaintext highlighter-rouge\">'Missing package product &lt;product_name&gt;'</code> errors.</p>\n\n<p>However, I have also experienced Xcode deleting the file for seemingly no reason at all. In fact, I’ve had moments where Xcode deletes the file immediately after I restore it via <code class=\"language-plaintext highlighter-rouge\">git restore</code> — and I can just do that infinitely. I put the file back, then Xcode deletes it. In that beautiful scenario, the Xcode project must be closed and re-opened.</p>\n\n<p>Searching the Apple Developer Forums <a href=\"https://developer.apple.com/forums/search?q=missing+package+product\">returns dozens of results</a> for this bug. Here is <a href=\"https://developer.apple.com/forums/thread/755772\">a recent one</a> that correctly describes the problem. Here is <a href=\"https://developer.apple.com/forums/thread/687275\">another post from 2021</a> that has new replies from 2 weeks ago. There is also <a href=\"https://forums.swift.org/t/missing-package-product-error-for-all-local-swift-packages-when-switching-git-branches/38041\">this post from the Swift.org forums</a> — from June 2020 — describing the issue. If you read through that thread, you will see the problem was briefly fixed around Xcode 12, but regressed in either the 12.5 or 13.0 release. This forum thread also has new comments from the past 2 weeks (including from yours truly). Unfortunately, the Swift forums are not the right place to report bugs in Xcode.</p>\n\n<p>Most “solutions” on the forums revolve around some magical combination of cleaning your project (<code class=\"language-plaintext highlighter-rouge\">cmd-shift-K</code>), deleting Xcode’s <code class=\"language-plaintext highlighter-rouge\">DerivedData/</code>, running <code class=\"language-plaintext highlighter-rouge\">File &gt; Packages &gt; Reset Package Caches</code>, running <code class=\"language-plaintext highlighter-rouge\">File &gt; Packages &gt; Resolve Package Versions</code>, and closing and re-opening Xcode. All of this is very heavy-handed and there’s a much lighter weight approach you can take to work around it.</p>\n\n<p>This bug is happening in the latest release, Xcode 15.4 (15F31d). It is a widespread problem reported across the various forums, as well as on Mastodon and other platforms. Any developer on a team that is using SwiftPM with Xcode is experiencing this bug daily. Furthermore, running <code class=\"language-plaintext highlighter-rouge\">xcodebuild -resolvePackageDependencies</code> seems to have no effect and it does not regenerate <code class=\"language-plaintext highlighter-rouge\">Package.resolved</code>.</p>\n\n<h3 id=\"the-packageresolved-file-and-git\">The <code class=\"language-plaintext highlighter-rouge\">Package.resolved</code> file and git</h3>\n\n<p>First, what is <code class=\"language-plaintext highlighter-rouge\">Package.resolved</code> and why is it important? This file is equivalent to <a href=\"https://guides.cocoapods.org/using/the-podfile.html\"><code class=\"language-plaintext highlighter-rouge\">Podfile.lock</code></a> in CocoaPods — similar to <a href=\"https://bundler.io/guides/using_bundler_in_applications.html#gemfilelock\"><code class=\"language-plaintext highlighter-rouge\">Gemfile.lock</code></a> in Ruby/Bundler, or <a href=\"https://docs.npmjs.com/cli/v7/configuring-npm/package-lock-json\"><code class=\"language-plaintext highlighter-rouge\">package-lock.json</code></a> in Node.js/npm. This file is a manifest that describes the exact versions used for every package dependency. It allows SwiftPM to always install the exact same packages on multiple machines, for example the machines of each member on a team and CI machines.</p>\n\n<p>It turns out, it’s just a JSON file. Here’s an example of <code class=\"language-plaintext highlighter-rouge\">Package.resolved</code> for an app that imports one library called <code class=\"language-plaintext highlighter-rouge\">Foil</code>.</p>\n\n<div class=\"language-json highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"p\">{</span><span class=\"w\">\n  </span><span class=\"nl\">\"pins\"</span><span class=\"w\"> </span><span class=\"p\">:</span><span class=\"w\"> </span><span class=\"p\">[</span><span class=\"w\">\n    </span><span class=\"p\">{</span><span class=\"w\">\n      </span><span class=\"nl\">\"identity\"</span><span class=\"w\"> </span><span class=\"p\">:</span><span class=\"w\"> </span><span class=\"s2\">\"foil\"</span><span class=\"p\">,</span><span class=\"w\">\n      </span><span class=\"nl\">\"kind\"</span><span class=\"w\"> </span><span class=\"p\">:</span><span class=\"w\"> </span><span class=\"s2\">\"remoteSourceControl\"</span><span class=\"p\">,</span><span class=\"w\">\n      </span><span class=\"nl\">\"location\"</span><span class=\"w\"> </span><span class=\"p\">:</span><span class=\"w\"> </span><span class=\"s2\">\"https://github.com/jessesquires/Foil\"</span><span class=\"p\">,</span><span class=\"w\">\n      </span><span class=\"nl\">\"state\"</span><span class=\"w\"> </span><span class=\"p\">:</span><span class=\"w\"> </span><span class=\"p\">{</span><span class=\"w\">\n        </span><span class=\"nl\">\"revision\"</span><span class=\"w\"> </span><span class=\"p\">:</span><span class=\"w\"> </span><span class=\"s2\">\"bc08a46268cb3bb22fee2c8465d97e6d7bf981e1\"</span><span class=\"p\">,</span><span class=\"w\">\n        </span><span class=\"nl\">\"version\"</span><span class=\"w\"> </span><span class=\"p\">:</span><span class=\"w\"> </span><span class=\"s2\">\"5.0.1\"</span><span class=\"w\">\n      </span><span class=\"p\">}</span><span class=\"w\">\n    </span><span class=\"p\">}</span><span class=\"w\">\n  </span><span class=\"p\">],</span><span class=\"w\">\n  </span><span class=\"nl\">\"version\"</span><span class=\"w\"> </span><span class=\"p\">:</span><span class=\"w\"> </span><span class=\"mi\">2</span><span class=\"w\">\n</span><span class=\"p\">}</span><span class=\"w\">\n</span></code></pre></div></div>\n\n<p>Because <code class=\"language-plaintext highlighter-rouge\">Package.resolved</code> is necessary to resolve the exact packages at their exact specified versions, it should be checked-in to source control.</p>\n\n<p>If you are working with an Xcode project, <code class=\"language-plaintext highlighter-rouge\">Package.resolved</code> is located at:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>MyApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved\n</code></pre></div></div>\n\n<p>If you are working with an Xcode workspace, the file is located at:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>MyApp.xcworkspace/xcshareddata/swiftpm/Package.resolved\n</code></pre></div></div>\n\n<p>When working with only Swift packages, your <code class=\"language-plaintext highlighter-rouge\">Package.swift</code> file defines all of your package dependencies. This plays a similar role to the <code class=\"language-plaintext highlighter-rouge\">Podfile</code> in CocoaPods. With SwiftPM’s Xcode integration, the project file <code class=\"language-plaintext highlighter-rouge\">project.pbxproj</code> — everyone’s favorite file — maintains the list of Swift package dependencies and their pinned versions. Again, this fulfills the same role as the <code class=\"language-plaintext highlighter-rouge\">Podfile</code>.</p>\n\n<p>The project file is located at <code class=\"language-plaintext highlighter-rouge\">MyApp.xcodeproj/project.pbxproj</code>. If you search the <code class=\"language-plaintext highlighter-rouge\">project.pbxproj</code> file for <code class=\"language-plaintext highlighter-rouge\">XCRemoteSwiftPackageReference</code> and <code class=\"language-plaintext highlighter-rouge\">XCSwiftPackageProductDependency</code>, you will find all the Swift package references for your project. For the example <code class=\"language-plaintext highlighter-rouge\">Package.resolved</code> file above, here is the <code class=\"language-plaintext highlighter-rouge\">project.pbxproj</code> representation:</p>\n\n<div class=\"language-xml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>/* Begin XCRemoteSwiftPackageReference section */\n0B3E8A0A28AD7D9C006FB785 /* XCRemoteSwiftPackageReference \"Foil\" */ = {\n    isa = XCRemoteSwiftPackageReference;\n    repositoryURL = \"https://github.com/jessesquires/Foil\";\n    requirement = {\n        kind = upToNextMajorVersion;\n        minimumVersion = 5.0.0;\n    };\n};\n/* End XCRemoteSwiftPackageReference section */\n</code></pre></div></div>\n\n<p>For small teams or individuals that already include their entire <code class=\"language-plaintext highlighter-rouge\">.xcodeproj</code> or <code class=\"language-plaintext highlighter-rouge\">.xcworkspace</code> bundles in git, there is nothing else you need to do. However, most teams explicitly ignore <code class=\"language-plaintext highlighter-rouge\">.xcodeproj</code> and <code class=\"language-plaintext highlighter-rouge\">.xcworkspace</code> — ironically, because of <code class=\"language-plaintext highlighter-rouge\">project.pbxproj</code>, which is a nightmare to resolve when you have conflicts in git. These teams typically use <a href=\"https://github.com/yonaskolb/XcodeGen\">Xcodegen</a> or a similar tool to generate their project files, thus entirely avoiding merge conflicts on <code class=\"language-plaintext highlighter-rouge\">project.pbxproj</code>. In this scenario, including the <code class=\"language-plaintext highlighter-rouge\">Package.resolved</code> file but ignoring everything else requires a bit of work. Here are the git ignore rules needed:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># .gitignore file</span>\n\nMyApp.xcworkspace/<span class=\"k\">*</span>\n<span class=\"o\">!</span>MyApp.xcworkspace/xcshareddata/\nMyApp.xcworkspace/xcshareddata/<span class=\"k\">*</span>\n<span class=\"o\">!</span>MyApp.xcworkspace/xcshareddata/swiftpm/\nMyApp.xcworkspace/xcshareddata/swiftpm/<span class=\"k\">*</span>\n<span class=\"o\">!</span>MyApp.xcworkspace/xcshareddata/swiftpm/Package.resolved\n</code></pre></div></div>\n\n<p>Note that with tools like Xcodegen, you <a href=\"https://github.com/yonaskolb/XcodeGen/blob/master/Docs/ProjectSpec.md#swift-package\">define your packages</a> in your <code class=\"language-plaintext highlighter-rouge\">project.yml</code> file, which essentially is a replacement for <code class=\"language-plaintext highlighter-rouge\">project.pbxproj</code>.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-30-may-2024\">\n        <a href=\"#updated-30-may-2024\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"30 May 2024 09:49:22 AM PDT\">\n            <i>30 May 2024</i>\n        </small>\n    </h5>\n    \n<p>Thanks to <a href=\"https://mastodon.social/@kylebshr/112528585781867745#.\">Kyle Bashour for pointing out</a> a couple of details I got wrong about <code class=\"language-plaintext highlighter-rouge\">project.pbxproj</code>. This section has been corrected.</p>\n\n</div>\n\n<h3 id=\"handling-packageresolved-deletions\">Handling <code class=\"language-plaintext highlighter-rouge\">Package.resolved</code> deletions</h3>\n\n<p>Now that the <code class=\"language-plaintext highlighter-rouge\">Package.resolved</code> file is saved in git, we can easily handle when Xcode deletes it by simply restoring it via git. For teams that ignore their project and workspace files and use Xcodegen (or similar), you also have the issue where Xcodegen deletes <code class=\"language-plaintext highlighter-rouge\">Package.resolved</code> when it regenerates your project file. If you are also still using CocoaPods in conjunction with SwiftPM, then you have the issue where CocoaPods will delete <code class=\"language-plaintext highlighter-rouge\">Package.resolved</code> when it regenerates your workspace file.</p>\n\n<p>When you notice the file gets deleted, you can restore it using <code class=\"language-plaintext highlighter-rouge\">git restore</code>. And then all the <code class=\"language-plaintext highlighter-rouge\">'Missing package product'</code> errors will go away when you build your project.</p>\n\n<p>It is common for iOS and macOS projects to use a <code class=\"language-plaintext highlighter-rouge\">Makefile</code> in order to bootstrap project setup, which often includes: generating the project file via Xcodegen, installing CocoaPods via Bundler, running <code class=\"language-plaintext highlighter-rouge\">pod install</code>, etc. In addition, makefiles are also a great place to write “shortcut” commands that are relevant to your project, for example, linting files or running tests.</p>\n\n<p>Here’s a target you can add to your <code class=\"language-plaintext highlighter-rouge\">Makefile</code> to make restoring a deleted <code class=\"language-plaintext highlighter-rouge\">Package.resolved</code> file easier.</p>\n\n<div class=\"language-makefile highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">PACKAGE_FILE</span> <span class=\"o\">:=</span> <span class=\"s2\">\"MyApp.xcworkspace/xcshareddata/swiftpm/Package.resolved\"</span>\n\n<span class=\"nl\">.PHONY</span><span class=\"o\">:</span> <span class=\"nf\">swiftpm</span>\n<span class=\"nl\">swiftpm</span><span class=\"o\">:</span>\n    <span class=\"err\">@</span><span class=\"c\"># Restore Package.resolved, which gets deleted when re-generating the project/workspace.\n</span>    <span class=\"err\">@</span><span class=\"c\"># Or, it gets deleted by Xcode.\n</span>    <span class=\"err\">@</span><span class=\"c\"># Only do this if the file was completely deleted.\n</span>    <span class=\"err\">@</span><span class=\"c\"># Otherwise, the user could be modifying packages which updates Package.resolved, so do not git restore it.\n</span>    <span class=\"err\">@if</span> <span class=\"err\">[</span> <span class=\"err\">!</span> <span class=\"err\">-f</span> <span class=\"s2\">\"$(PACKAGE_FILE)\"</span> <span class=\"err\">];</span> <span class=\"err\">then</span> <span class=\"err\">\\</span>\n        <span class=\"err\">echo</span> <span class=\"s2\">\"Restoring Package.resolved...\"</span><span class=\"err\">;</span> <span class=\"err\">\\</span>\n        <span class=\"err\">git</span> <span class=\"err\">restore</span> <span class=\"s2\">\"$(PACKAGE_FILE)\"</span><span class=\"err\">;</span> <span class=\"err\">\\</span>\n        <span class=\"err\">xcodebuild</span> <span class=\"err\">-resolvePackageDependencies;</span> <span class=\"err\">\\</span>\n    <span class=\"err\">fi</span>\n</code></pre></div></div>\n\n<p>With this target in your <code class=\"language-plaintext highlighter-rouge\">Makefile</code>, you can simply run <code class=\"language-plaintext highlighter-rouge\">make swiftpm</code> as needed.</p>\n\n<p>Note that this only calls <code class=\"language-plaintext highlighter-rouge\">git restore</code> if the file is <em>deleted</em>, in case it has been (correctly) modified after updating packages. At the end, it calls <code class=\"language-plaintext highlighter-rouge\">xcodebuild -resolvePackageDependencies</code>, which <em>should</em> regenerate the <code class=\"language-plaintext highlighter-rouge\">Package.resolved</code> on its own, but that’s also broken. It will, however, download packages to the package cache if they are missing, which is what Xcode does when opening a project.</p>\n\n<p>To make this even better, you can include running this target as part of your bootstrapping process if you are using CocoaPods, Xcodegen, etc. Include this target part of your primary setup flow, and you should rarely have to run it manually.</p>\n\n<p>If you do not use a <code class=\"language-plaintext highlighter-rouge\">Makefile</code>, then you can write a simple bash script instead with the contents above.</p>\n\n<h3 id=\"danger-accidental-deletions\">Danger: accidental deletions</h3>\n\n<p>Unfortunately (again), what we discovered on one of my teams is that the above is not foolproof. Because Xcode can (and literally <em>does</em>) randomly delete <code class=\"language-plaintext highlighter-rouge\">Package.resolved</code> at any time, someone on your team might not notice and commit the file deletion. Oops! If you use a tool like <a href=\"https://danger.systems/ruby/\">Danger</a> to automate code reviews (which, <em>you should</em>), then you can add a rule to catch this mistake.</p>\n\n<div class=\"language-ruby highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Dangerfile</span>\n\n<span class=\"k\">if</span> <span class=\"n\">git</span><span class=\"p\">.</span><span class=\"nf\">deleted_files</span><span class=\"p\">.</span><span class=\"nf\">include?</span><span class=\"p\">(</span><span class=\"s2\">\"MyApp.xcworkspace/xcshareddata/swiftpm/Package.resolved\"</span><span class=\"p\">)</span>\n    <span class=\"nb\">fail</span><span class=\"p\">(</span><span class=\"s2\">\"It looks like you deleted `Package.resolved`. Please don't do that.\"</span><span class=\"p\">)</span>\n<span class=\"k\">end</span>\n</code></pre></div></div>\n\n<p>This will fail a pull request if <code class=\"language-plaintext highlighter-rouge\">Package.resolved</code> was deleted.</p>\n\n<h3 id=\"summary\">Summary</h3>\n\n<p>To recap: make sure <code class=\"language-plaintext highlighter-rouge\">Package.resolved</code> is saved in git, use a <code class=\"language-plaintext highlighter-rouge\">Makefile</code> to quickly and easily restore it, and add a Danger rule to catch mistakes, if needed. This should eliminate all <code class=\"language-plaintext highlighter-rouge\">'Missing package product'</code> errors. As a last resort, if you are still having trouble, you can run <code class=\"language-plaintext highlighter-rouge\">File &gt; Packages &gt; Resolve Package Versions</code>. If you search for solutions on StackOverflow or elsewhere, you will find some really elaborate bash scripts that use <a href=\"https://formulae.brew.sh/formula/fswatch\">fswatch</a> to monitor the file system to workaround this problem. I think this is a bit overkill and prefer the simpler solutions I’ve offered here.</p>\n\n<p>This is all a bit ridiculous, and I would say it’s shocking that this bug still hasn’t been fixed after 4 years, but this kind of thing is not all that surprising for Apple. It is rather routine that many bugs remain unfixed and tools remain broken for years.</p>\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2024/05/29/swiftpm-package-resolved-xcode/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2023/12/16/macbook-notch-and-menu-bar-fixes/#updated-29-may-2024",
            "url": "https://www.jessesquires.com/blog/2023/12/16/macbook-notch-and-menu-bar-fixes/",
            "title": "[Updated] How to fix Mac menu bar icons hidden by the MacBook notch",
            "date_published": "2024-05-29T18:20:52-07:00",
            "date_modified": "2024-05-29T18:20:52-07:00",
            "summary": "<p>Last week I <a href=\"/blog/2023/12/04/new-m3-mbp/\">wrote about setting up a new MacBook Pro</a> — my first Apple Silicon Mac, and thus my first MacBook with a notch. I lamented how poorly macOS interacts with the notch, specifically how menu bar apps and icons simply get hidden if you have too many to display. Lots of folks on Mastodon offered various solutions, and some readers emailed me with options as well. I figured it was worth making a separate post about this specific issue to list all of the workarounds and alternatives. It is clear that this is a widespread problem that users are having.</p>\n\n",
            "tags": [
                "apple","macbook-pro","macbook","macos",
                "essays"
            ],
            "content_html": "<p>Last week I <a href=\"/blog/2023/12/04/new-m3-mbp/\">wrote about setting up a new MacBook Pro</a> — my first Apple Silicon Mac, and thus my first MacBook with a notch. I lamented how poorly macOS interacts with the notch, specifically how menu bar apps and icons simply get hidden if you have too many to display. Lots of folks on Mastodon offered various solutions, and some readers emailed me with options as well. I figured it was worth making a separate post about this specific issue to list all of the workarounds and alternatives. It is clear that this is a widespread problem that users are having.</p>\n\n<!--excerpt-->\n\n<h3 id=\"problems-with-the-notch\">Problems with the notch</h3>\n\n<p><a href=\"/blog/2023/12/04/new-m3-mbp/\">I previously wrote</a>:</p>\n\n<blockquote>\n  <p>I have gripes about the notch. There isn’t enough room to display all of my menu bar apps and icons, so… they just get hidden!? Apparently, everyone in Cupertino thinks the best solution to this problem is to hide them with zero indication that there are more that simply can’t be displayed because of the notch. I wasted so much time trying to figure out why Little Snitch and 1Password were not running on my new machine. Was there a compatibility issue with Apple Silicon that I didn’t know about? That couldn’t be. It turns out, they were running the whole time but they were hidden by the notch.</p>\n\n  <p>[…]</p>\n\n  <p>This “design” (or lack thereof) is so dumb. It is utterly ridiculous to me that this is still how it “works” <strong>two years after</strong> the introduction of the redesigned MacBook Pro with a notch. How hard could it be to add an overflow menu with a “«” (or should it be “»”?) button that shows the remaining apps and icons that can’t be displayed? This entire situation with the notch is ironic, because the iPhone notch and “dynamic island” are so <strong>thoughtfully designed</strong> with zero compromises regarding the functionality of iOS. In fact, they actually provide a <em>better</em> user experience. Yet on the Mac, how the notch interacts with macOS is laughably incompetent. It is shockingly lazy regarding attention to detail, and results in an outright disruptive and confusing user experience.</p>\n</blockquote>\n\n<p>And as <a href=\"https://mjtsai.com/blog/2023/12/08/mac-menu-bar-icons-and-the-notch/\">Michael Tsai pointed out</a>, the situation from a developer’s perspective is just as bad:</p>\n\n<blockquote>\n  <p>Aside from the problem of the icons being hidden, there’s no API for an app to <em>tell</em> whether its icon is hidden. <code class=\"language-plaintext highlighter-rouge\">NSStatusItem.isVisible</code> tells you whether the app or user <em>wants</em> the icon to be visible, but it will return <code class=\"language-plaintext highlighter-rouge\">true</code> if the icon is hidden in the notch—or even if it’s hidden behind a menu title.</p>\n</blockquote>\n\n<h3 id=\"first-party-workarounds\">First-party workarounds</h3>\n\n<p>If you prefer <em>not</em> to install any third-party apps (like me), there are a number of steps you can take to alleviate your crowded menu bar and possibly remedy the issue entirely (which I’ve successfully done). My goal with the steps below is to get all of my apps and icons to display when Finder is active. Some applications have more menu items that span across the other side of the notch, and in this scenario there is not much we can do to prevent hidden menu bar icons — which was actually the case <em>before</em> the notch existed for applications with a very large number of menu items.</p>\n\n<ol>\n  <li>\n    <p>Move macOS system-provided icons into Control Center. A number of icons can be configured to display only in Control Center, which frees up space in your menu bar for icons and third-party apps that must be displayed in the menu bar. For example, you can move WiFi, Bluetooth, Battery level, AirDrop, etc. into Control Center. Additionally, some icons can be configured to only display when they are active, like Focus status or Screen Mirroring.</p>\n  </li>\n  <li>\n    <p>Reorder your apps and icons from right to left to display the ones that are <em>most important</em> to you on the right and least important on the left. The result is that the least important icons are the ones that get hidden by application menus or the notch, while the most important icons are more likely to remain visible. For example, I like having Time Machine in the menu bar, but it doesn’t matter if it gets hidden while I’m working in an app with a large menu that spans to the other side of the notch, thus hiding Time Machine. Therefore, I have placed Time Machine in the left-most position. Furthermore, I place system icons that display <em>only when active</em> (like Focus status) in the <em>right most</em> position. This is important for me, because I want to make sure I always see when these things are active.</p>\n  </li>\n  <li>\n    <p>Reduce the menu bar item spacing and padding via <code class=\"language-plaintext highlighter-rouge\">UserDefaults</code>. (Thanks to <a href=\"https://mastodon.social/@gummibando/111546699397435187\">Oliver Busch</a> for the tip. Also see <a href=\"https://www.reddit.com/r/MacOS/comments/16lpfg5/hidden_preference_to_alter_the_menubar_spacing/\">this Reddit post</a>.) There are two defaults settings you can configure via Terminal, <code class=\"language-plaintext highlighter-rouge\">NSStatusItemSpacing</code> and <code class=\"language-plaintext highlighter-rouge\">NSStatusItemSelectionPadding</code>.</p>\n  </li>\n</ol>\n\n<p><strong>Read the current defaults:</strong></p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>defaults <span class=\"nt\">-currentHost</span> <span class=\"nb\">read</span> <span class=\"nt\">-globalDomain</span> NSStatusItemSpacing\ndefaults <span class=\"nt\">-currentHost</span> <span class=\"nb\">read</span> <span class=\"nt\">-globalDomain</span> NSStatusItemSelectionPadding\n</code></pre></div></div>\n\n<p><strong>Note:</strong> These values are <em>not set</em> by default. This means you will get an error that the keys and values do not exist if you have not previously set them.</p>\n\n<p><strong>Write the defaults by providing an integer value:</strong></p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>defaults <span class=\"nt\">-currentHost</span> write <span class=\"nt\">-globalDomain</span> NSStatusItemSpacing <span class=\"nt\">-int</span> 12\ndefaults <span class=\"nt\">-currentHost</span> write <span class=\"nt\">-globalDomain</span> NSStatusItemSelectionPadding <span class=\"nt\">-int</span> 8\nkillall SystemUIServer\n</code></pre></div></div>\n\n<p>After some experimentation, I landed on the values above — <code class=\"language-plaintext highlighter-rouge\">12</code> for spacing and <code class=\"language-plaintext highlighter-rouge\">8</code> for padding fit my needs. You should experiment as well. The smallest tolerable values are probably around <code class=\"language-plaintext highlighter-rouge\">6</code> or <code class=\"language-plaintext highlighter-rouge\">8</code>.</p>\n\n<p><strong>Remove the values to restore the default behavior:</strong></p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>defaults <span class=\"nt\">-currentHost</span> delete <span class=\"nt\">-globalDomain</span> NSStatusItemSpacing\ndefaults <span class=\"nt\">-currentHost</span> delete <span class=\"nt\">-globalDomain</span> NSStatusItemSelectionPadding\nkillall SystemUIServer\n</code></pre></div></div>\n\n<h3 id=\"third-party-solutions\">Third-party solutions</h3>\n\n<p>There are several third-party applications to help organize and manage all of your menu bar apps and icons. The apps I have listed below seem to capture the three broad categories of possible solutions. I know there are a few others out there with similar functionality, but I think these are the most representative.</p>\n\n<ol>\n  <li>\n    <p><strong>Bartender</strong>. (Paid with Free Trial. <a href=\"https://www.macbartender.com\">Website</a>). This one is very advanced and complex with elaborate customization options, including styling the entire menu bar. It works well, but I found it to be very heavy-handed and a bit cumbersome for my needs. It felt clunky and glitchy to me, in ways that I think the developer has tried hard to mitigate, but I imagine Apple does not make developing this sort of app easy. (I don’t think there are any public APIs for this stuff.) Bartender seems to rely on some hacks (like screen recording) to dynamically hide and show your overflowed menu bar apps. Like iOS, macOS now has a privacy feature where you are notified when apps are using the camera, microphone, screen recording, etc. On iOS there’s a little dot indicator that shows in the status bar, on macOS there’s an icon that displays in your menu bar. One specific issue I had with Bartender, was that the privacy indicator icon for screen recording appears very frequently, which was annoying.</p>\n  </li>\n  <li>\n    <p><strong>Hidden Bar</strong>. (Free. <a href=\"https://apps.apple.com/us/app/hidden-bar/id1452453066\">Mac App Store</a>, <a href=\"https://github.com/dwarvesf/hidden\">GitHub</a>). This one is extremely lightweight, providing a simple chevron “&lt;” icon to expand and collapse the extra icons. You can customize which icons are always shown, and which are hidden when the menu is collapsed.</p>\n  </li>\n  <li>\n    <p><strong>Say No to Notch</strong>. (Free with IAPs, <a href=\"https://apps.apple.com/us/app/say-no-to-notch/id1639306886\">Mac App Store</a>). This is another heavy-handed approach. This one adds a letterboxed-style black bar to the top of your screen and shifts the entire menu bar down below the notch — along with the entire contents of your screen. I do not like the reduced screen real estate that this creates.</p>\n  </li>\n</ol>\n\n<p>All of these apps have pros and cons. Unfortunately, I was not quite satisfied with any of them. However, your experience and preferences might be very different!</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-29-may-2024\">\n        <a href=\"#updated-29-may-2024\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"29 May 2024 06:20:52 PM PDT\">\n            <i>29 May 2024</i>\n        </small>\n    </h5>\n    \n<p>Thanks to reader <a href=\"https://github.com/thielem\">Moritz Thiele</a> who <a href=\"https://github.com/jessesquires/jessesquires.com/issues/194\">suggested some</a> alternative third-party solutions. They use <a href=\"https://github.com/zkondor/znotch\">zNotch</a> in combination with <a href=\"https://www.raycast.com\">Raycast</a> shortcuts.</p>\n\n</div>\n\n<h3 id=\"conclusion\">Conclusion</h3>\n\n<p>As noted above, I have opted for the collection of first-party workarounds. For me, all of the third-party apps felt cumbersome, wonky, lacking, or simply did not match my aesthetic taste. That’s not to say they aren’t great apps and creative solutions — they just aren’t for me. In any case, I hope this post has been helpful if you’ve been searching for solutions to the MacBook notch problem.</p>\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2023/12/16/macbook-notch-and-menu-bar-fixes/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2024/04/29/sdk-privacy-manifests/#updated-30-april-2024",
            "url": "https://www.jessesquires.com/blog/2024/04/29/sdk-privacy-manifests/",
            "title": "[Updated] The curious case of Apple's third-party SDK list for privacy manifests",
            "date_published": "2024-04-30T09:53:30-07:00",
            "date_modified": "2024-04-30T09:53:30-07:00",
            "summary": "<p>At last year’s WWDC, Apple <a href=\"https://developer.apple.com/videos/play/wwdc2023/10060/\">introduced privacy manifests</a>. They recently sent out <a href=\"https://developer.apple.com/news/?id=pvszzano\">a reminder</a> that the deadline for complying with these new requirements is May 1. Privacy manifests expand on the previously introduced <a href=\"https://www.theverge.com/2020/11/5/21551926/apple-privacy-developers-nutrition-labels-app-store-ios-14\">privacy “nutrition labels”</a> that are self-reported by developers and displayed on the App Store. Developers must start including a privacy manifest in their apps by the aforementioned deadline, but what’s more interesting is that Apple is, for the first time, imposing these new privacy rules on third-party SDKs as well. Even more interesting is <a href=\"https://developer.apple.com/support/third-party-SDK-requirements/\">the list of SDKs</a> that Apple has published, which, upon inspection is quite bizarre.</p>\n\n",
            "tags": [
                "xcode","apple","ios","app-store","privacy",
                "software-dev"
            ],
            "content_html": "<p>At last year’s WWDC, Apple <a href=\"https://developer.apple.com/videos/play/wwdc2023/10060/\">introduced privacy manifests</a>. They recently sent out <a href=\"https://developer.apple.com/news/?id=pvszzano\">a reminder</a> that the deadline for complying with these new requirements is May 1. Privacy manifests expand on the previously introduced <a href=\"https://www.theverge.com/2020/11/5/21551926/apple-privacy-developers-nutrition-labels-app-store-ios-14\">privacy “nutrition labels”</a> that are self-reported by developers and displayed on the App Store. Developers must start including a privacy manifest in their apps by the aforementioned deadline, but what’s more interesting is that Apple is, for the first time, imposing these new privacy rules on third-party SDKs as well. Even more interesting is <a href=\"https://developer.apple.com/support/third-party-SDK-requirements/\">the list of SDKs</a> that Apple has published, which, upon inspection is quite bizarre.</p>\n\n<!--excerpt-->\n\n<p>Historically, Apple has rarely, if ever, explicitly acknowledged <em>any</em> third-party SDK or library. It took years for them to even acknowledge community tools like CocoaPods in Xcode’s release notes (usually when they made a change that broke it). Thus, it is interesting to see which SDKs they have deemed important or concerning enough to explicitly mandate a privacy manifest. And, in typical Apple fashion, I’m pretty sure SDKs authors were <em>not</em> notified about this in advance. We all learned which SDKs need privacy manifests at the same time — <em>when the list was published</em>.</p>\n\n<p>The first few entries in the list make sense:</p>\n\n<ul>\n  <li><a href=\"https://github.com/abseil/abseil-cpp\">Abseil</a>, a low-level C++ library.</li>\n  <li><a href=\"https://github.com/openid/AppAuth-iOS\">AppAuth</a>, an SDK for communicating with OAuth 2.0 and OpenID Connect providers.</li>\n  <li><a href=\"https://github.com/AFNetworking/AFNetworking\">AFNetworking</a> and it’s successor <a href=\"https://github.com/Alamofire/Alamofire\">Alamofire</a>, networking libraries that wrap Apple’s APIs, which almost every iOS developer has encountered.</li>\n  <li><a href=\"https://github.com/google/boringssl\">BoringSSL</a>, a fork of OpenSSL maintained by Google.</li>\n</ul>\n\n<p>I can see how these libraries could be concerning with regard to user privacy, they are all dealing with networking, authentication, and security (except for Abseil) — these are common vectors for privacy-related issues. Abseil is the exception, but I could see an argument for why a low-level C++ library <em>might</em> be a concern. There are also a lot of SDKs from Google and Facebook on the list — neither of those companies have a particularly good reputation when it comes to user privacy. It makes sense for those to be included.</p>\n\n<p>But then… you see that the list contains UI libraries that haven’t seen significant updates or any activity for multiple years, like <a href=\"https://github.com/SVProgressHUD/SVProgressHUD\">SVProgressHUD</a>. Why does a library that provides a single UI component need a privacy manifest? Is it as concerning and as potentially privacy invasive as the Facebook SDK? Some of the UI-only SDKs on the list haven’t seen significant updates (or any updates at all) within the last 4-5 years. Furthermore, even AFNetworking hasn’t had an update in <strong>4 years</strong> because it was deprecated long ago after being supplanted by Alamofire. The <a href=\"https://github.com/AFNetworking/AFNetworking\">AFNetworking repo on GitHub</a> has been archived and read-only for <strong>over a year</strong>! Who’s going to bother adding a privacy manifest to that?</p>\n\n<p>And then… there are some entries that are simply obscure and absurd: connectivity_plus, image_picker_ios, video_player_avfoundation, file_picker. What the hell are those?! They don’t even sound like SDK or library names. I have never heard of any of these, and I’ve been involved in the iOS open source community for a decade.</p>\n\n<p>And then… you know what’s <em>even more</em> bizarre about <a href=\"https://developer.apple.com/support/third-party-SDK-requirements/\">this list</a>? There are no links! <strong>There are no links to the SDK project homepages or GitHub repos.</strong> It is a plain text list of names, and in some cases, seemingly random names like “file_picker”. Ok LOL. SDK and library names are not <em>necessarily</em> unique. How are you supposed to know exactly which SDKs they are referencing with so little information? Searching for “file_picker” or “image_picker_ios” or any of the other obscure names on both <a href=\"https://cocoapods.org\">CocoaPods</a> and the <a href=\"https://swiftpackageindex.com\">Swift Package Index</a> returns <strong>no results</strong>!</p>\n\n<p>Finally, wouldn’t you expect some sort of reason or justification for each of these SDKs being on the list? We don’t need a 10-page essay but a brief explanation of a few sentences explaining <em>why</em> each of these SDKs is on the list would be helpful in understanding the logic and reasoning behind it.</p>\n\n<p>Nothing about <a href=\"https://developer.apple.com/support/third-party-SDK-requirements/\">this list</a> makes any sense.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-30-april-2024\">\n        <a href=\"#updated-30-april-2024\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"30 Apr 2024 09:53:30 AM PDT\">\n            <i>30 April 2024</i>\n        </small>\n    </h5>\n    \n<p>As many readers have pointed out, there are also a number of popular SDKs that really <em>should</em> be on this list if Apple is concerned about privacy. For example, the <a href=\"https://developers.tiktok.com/doc/getting-started-ios-download/\">TikTok SDK</a>, <a href=\"https://developers.google.com/admob/ios/download\">GoogleAds</a>, and the <a href=\"https://docs.unity.com/ads/en-us/manual/InstallingTheiOSSDK\">Unity Ads SDK</a> are all missing from <a href=\"https://developer.apple.com/support/third-party-SDK-requirements/\">the list</a>, just to name a few. How strange!</p>\n\n<p>And apparently, all of the obscure SDK names like “file_picker” are actually <a href=\"https://flutter.dev\">Flutter</a> packages. Again, what an odd list!</p>\n\n</div>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<p>For a company that has positioned itself as a staunch privacy advocate, this list of SDKs is slapdash at best. The lack of attention to detail, like simply including links to SDK homepages, makes the list appear like it was assembled hastily and carelessly. It makes you wonder, <em>how was this list compiled?</em> What was the criteria for including or excluding an SDK from this list?</p>\n\n<p>I was venting about the list on Mastodon, and the general consensus is that it was most likely just a script dump from a static analysis of app binaries on the app store, with the sole criterion being “what are the most popular libraries” used across all apps, with some minimum threshold for inclusion. It is quite clear from the list that no one at Apple really put much thought into it. 🤡</p>\n\n<p>If we operate under the hypothesis that this list is merely the output of a script that someone at Apple wrote to check off the line item <em>“determine which third-party SDKs should be required to included privacy manifests”</em>, then it all starts to make more sense. This list is ultimately the result of a popularity contest, not a thoughtful analysis of SDKs that have meaningful implications for user privacy. They couldn’t even bother to link to the projects or provide brief explanations. There’s literally an entry titled “file_picker” with no other explanation. Did anyone at Apple even look into any of these libraries? Did anyone at Apple even read through this list after some script vomited it out?</p>\n\n<p>When Apple imposes new privacy regulations in such a slipshod manner, how are we, as developers and as users, supposed to take this seriously? This feels like more bureaucratic security and privacy theater. Let’s all take off our shoes and throw away sealed bottles of water we purchased at the airport before we proceed through the TSA security checkpoint — meanwhile doors and wheels are falling off the damn plane.</p>\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2024/04/29/sdk-privacy-manifests/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2023/12/06/xcode-shell-env/#updated-15-december-2023",
            "url": "https://www.jessesquires.com/blog/2023/12/06/xcode-shell-env/",
            "title": "[Updated] Xcode does not have access to your shell environment",
            "date_published": "2023-12-15T15:50:32-08:00",
            "date_modified": "2023-12-15T15:50:32-08:00",
            "summary": "<p>I recently discovered, while <a href=\"/blog/2023/12/04/new-m3-mbp/\">setting up my first Apple Silicon Mac</a>, that Xcode does not have access to your shell environment. But there’s one caveat to that. (Thanks to <a href=\"https://mastodon.social/@NeoNacho/111494454201420440#.\">Boris for confirming</a>!) This post will hopefully be a reminder to my future self when I encounter this issue again.</p>\n\n",
            "tags": [
                "xcode","apple-silicon","m3","homebrew",
                "software-dev"
            ],
            "content_html": "<p>I recently discovered, while <a href=\"/blog/2023/12/04/new-m3-mbp/\">setting up my first Apple Silicon Mac</a>, that Xcode does not have access to your shell environment. But there’s one caveat to that. (Thanks to <a href=\"https://mastodon.social/@NeoNacho/111494454201420440#.\">Boris for confirming</a>!) This post will hopefully be a reminder to my future self when I encounter this issue again.</p>\n\n<!--excerpt-->\n\n<p>I realized the problem when an Xcode build failed because a Run Script <a href=\"https://developer.apple.com/documentation/xcode/customizing-the-build-phases-of-a-target\">Build Phase</a> that runs <a href=\"https://github.com/realm/SwiftLint\">SwiftLint</a> failed because the SwiftLint binary could not be found. I have SwiftLint installed via <a href=\"https://brew.sh\">Homebrew</a>. I knew SwiftLint was in my <code class=\"language-plaintext highlighter-rouge\">PATH</code> because I could run it successfully on the command line. This meant Xcode did not have access to my <code class=\"language-plaintext highlighter-rouge\">PATH</code> (and, as I learned later, my entire shell environment).</p>\n\n<p>This only started to happen after switching from an Intel Mac to an Apple Silicon Mac — and that was the issue. Homebrew <a href=\"https://docs.brew.sh/Installation\">installs at different locations</a> for Intel (<code class=\"language-plaintext highlighter-rouge\">/usr/local</code>) and Apple Silicon (<code class=\"language-plaintext highlighter-rouge\">/opt/homebrew</code>). Invoking SwiftLint from a Run Script Build Phase in Xcode on Intel Macs <em>just happens to work</em> because <code class=\"language-plaintext highlighter-rouge\">/usr/local</code> is part of your default <code class=\"language-plaintext highlighter-rouge\">PATH</code>. To fix the issue on Apple Silicon Macs, you need to tell Xcode where to look for Homebrew packages in your script.</p>\n\n<div class=\"language-sh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">export </span><span class=\"nv\">PATH</span><span class=\"o\">=</span><span class=\"s2\">\"</span><span class=\"nv\">$PATH</span><span class=\"s2\">:/opt/homebrew/bin\"</span>\n</code></pre></div></div>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<p>Anyway, the moral of this story is that Xcode does not have access to your shell environment. This is not necessarily an obvious thing, especially on Intel machines. Xcode only gets whatever the default <code class=\"language-plaintext highlighter-rouge\">PATH</code> is, configured via launch services — <strong>unless</strong> you launch it through a terminal. What this means is that <code class=\"language-plaintext highlighter-rouge\">xcodebuild</code> run from terminal and Xcode could end up with potentially different behavior in their Run Script Build Phases because they expose different environments. Good to know!</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-15-december-2023\">\n        <a href=\"#updated-15-december-2023\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"15 Dec 2023 03:50:32 PM PST\">\n            <i>15 December 2023</i>\n        </small>\n    </h5>\n    \n<p>Thanks to Dave for <a href=\"https://iosdevweekly.com/issues/640?#tools\">linking to this post this week</a> and sharing a very relevant tip on this topic:</p>\n\n<blockquote>\n  <p>Don’t forget you can still set environment variables from your project’s scheme configuration. Edit your scheme from the Product menu, select the Arguments tab against the Run behaviour, and set Environment Variables.</p>\n</blockquote>\n\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2023/12/06/xcode-shell-env/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2023/08/16/going-indie-3/#updated-17-august-2023",
            "url": "https://www.jessesquires.com/blog/2023/08/16/going-indie-3/",
            "title": "[Updated] Going indie: business structure, taxes, and retirement",
            "date_published": "2023-08-17T02:59:12-07:00",
            "date_modified": "2023-08-17T02:59:12-07:00",
            "summary": "<p>Welcome to the third part of my going indie series! In the <a href=\"/blog/2023/05/08/going-indie-2/\">previous post</a>, I discussed building a foundation, getting started, and finding clients. In this post, I am going to discuss many of the decidedly <em>un-fun</em> administrative aspects of being freelance and contracting like saving for retirement and — everyone’s favorite — taxes. Most folks consider these topics to be boring and tedious, but understanding them is critical to your success. The best approach is one of curiosity. As a software developer, you might find the task of optimizing (and minimizing!) your tax burden to be an interesting problem to solve — I definitely do!</p>\n\n",
            "tags": [
                "series-going-indie","indie-dev","contracting","freelance","consulting",
                "essays"
            ],
            "content_html": "<p>Welcome to the third part of my going indie series! In the <a href=\"/blog/2023/05/08/going-indie-2/\">previous post</a>, I discussed building a foundation, getting started, and finding clients. In this post, I am going to discuss many of the decidedly <em>un-fun</em> administrative aspects of being freelance and contracting like saving for retirement and — everyone’s favorite — taxes. Most folks consider these topics to be boring and tedious, but understanding them is critical to your success. The best approach is one of curiosity. As a software developer, you might find the task of optimizing (and minimizing!) your tax burden to be an interesting problem to solve — I definitely do!</p>\n\n<!--excerpt-->\n\n<blockquote>\n  <p><em>Disclaimer: I am not an accountant, nor a financial advisor. I highly recommend you get both! All the information in this post is derived from my experience, conversations with my accountant, and advice from my financial advisor. Of course, your tax situation and financial situation differ from mine, so “your mileage may vary” as they say. For example, I do not have children, but if you do that has a significant impact on your taxes and finances.</em></p>\n\n  <p><em>Also, as I noted <a href=\"/blog/2023/04/10/going-indie/\">in my first post</a></em>, this series assumes you are based in the United States. While many aspects of this series are widely applicable, this post is very specific to the US.</p>\n</blockquote>\n\n<p>What you may not realize is that going indie means <em>starting your own business</em>. Congratulations, you are now a small business owner as far as the IRS is concerned. Don’t worry, that does not mean some sort of formal business entity is necessary (as you will see below). It only means you need to shift your thinking a bit. My goal with this post is to give you a head start on learning how to structure your business, and what to expect regarding taxes. My hope is that you can begin your journey with more information than I had — which was literally zero.</p>\n\n<p>If you do not have an accountant or financial advisor, I highly recommend you get both. Find someone you trust. Their expertise will help guide you in making the correct decisions for yourself and they will save you a ton of time and potential headaches. Likely, someone in your network can make a recommendation for both.</p>\n\n<h3 id=\"business-structure\">Business structure</h3>\n\n<p>The first task after deciding to go independent and do freelance/contract work is deciding how to structure your self-employed business. You have a few options, each with its own pros and cons. The factors that differ between the options primarily center around taxes, reporting, paperwork, and bookkeeping. There are many resources on the internet that explain the differences, benefits, and drawbacks of each self-employed business entity — so I will be brief. I am not an expert on these topics, so you should ultimately consult your accountant. My goal here is to make you aware of your options and give you the gist of each.</p>\n\n<p>In all scenarios, you should open a new bank account for your business. It’s also a good idea to get a separate credit card that you use solely for business expenses. This is not strictly necessary as a sole proprietor (and the accounts can be normal accounts in your name, not actual business accounts), but it is a good practice to keep everything separate. This will make bookkeeping significantly easier for you.</p>\n\n<ol>\n  <li>\n    <p><strong>Sole Proprietorship.</strong> [<a href=\"https://www.irs.gov/businesses/small-businesses-self-employed/sole-proprietorships\">IRS</a>, <a href=\"https://en.wikipedia.org/wiki/Sole_proprietorship\">Wiki</a>] A Sole Proprietorship is owned and run by one person and in which there is no legal distinction between the owner and the business entity. Being a sole proprietor is the simplest and easiest structure. You do not need to establish a formal legal entity. There is no paperwork or setup. You operate as yourself, as an individual, under your legal name. Similar to being a full-time W-2 worker, you file taxes using your social security number (SSN), etc. You can (optionally) “level-up” by getting a <em>Fictitious Business Name</em> (FBN), which allows you to operate under a different “business” name. However, as the name indicates, this is not a “real” business entity. It merely provides a facade to your sole proprietorship, which can be useful in some situations. If you opt to have an FBN, then you also need to get an <a href=\"https://www.irs.gov/businesses/small-businesses-self-employed/employer-id-numbers\">Employer Identification Number</a> (EIN) to associate with it. An EIN is basically an “SSN for your business” — you file your taxes using the EIN instead of your SSN and you provide your EIN to your clients for your 1099 forms. In addition to income tax, you also pay self-employment tax. You “pay yourself” simply by withdrawing or transferring funds from your business account.</p>\n  </li>\n  <li>\n    <p><strong>S Corporation (S-Corp).</strong> [<a href=\"https://www.irs.gov/businesses/small-businesses-self-employed/s-corporations\">IRS</a>, <a href=\"https://en.wikipedia.org/wiki/S_corporation\">Wiki</a>] An S-Corp is a more formal structure that requires you to establish a payroll system and pay yourself a salary. You need an EIN and there is various paperwork to complete to establish the S-Corp entity. The process can take a couple weeks or up to 3 months, depending on your circumstances. It differs in that you can have multiple shareholders. An S-Corp is similar to a sole proprietorship in that it is a “pass-through entity” meaning the corporation’s income and losses are divided among and passed through to its shareholders (i.e., you). In this scenario, you are the singular shareholder so you more or less file taxes like an individual would. However, S-Corps have access to additional deductions and can yield higher tax savings. Notably, an S-Corp is not a “full” corporation and alleviates you from double taxation where you would otherwise pay corporate tax as well as individual income tax. You still pay self-employment tax like a sole proprietor, and because you have to run a payroll system you also pay payroll taxes. You “pay yourself” via your payroll system. S-Corps also come with some additional fees and specific states impose their own taxes on S-Corps. Also note that some states tax S-Corps as C-Corps, negating the pass-through benefit. Because of the additional fees and taxes, it usually does not make financial sense to establish an S-Corp until your annual earnings pass a threshold of around $100,000 — at which point you are better positioned to reap the tax benefits. (Again, consult your accountant here.)</p>\n  </li>\n  <li>\n    <p><strong>Limited Liability Company (LLC).</strong> [<a href=\"https://www.irs.gov/businesses/small-businesses-self-employed/limited-liability-company-llc\">IRS</a>, <a href=\"https://en.wikipedia.org/wiki/Limited_liability_company\">Wiki</a>] An LLC is what I know the least about. It is an established business entity that you also have to file paperwork to legally setup, pay fees, get an EIN, etc. An LLC does not need to have payroll. It is not a corporation but it is a legal form of a company. It is a hybrid business structure that can combine the pass-through taxation of a sole proprietorship with the limited liability of a corporation. Regulation of LLCs varies from state to state. LLCs generally have fewer formalities and reporting requirements than S-Corps, which can make them easier and less expensive to manage.</p>\n  </li>\n</ol>\n\n<p>My advice is: <strong>do the simplest thing first.</strong> If you are just getting started, operate as a sole proprietor. You do not need to complete any legal paperwork and you can start working immediately. This is beneficial because if you decide that you <em>do not like</em> freelancing and contracting, then you can stop and go back to full-time work without having to do anything else. At the same time, if you <em>do</em> decide to stay independent, then it is easy to transition to an S-Corp or LLC later. I have a friend that established an LLC once for a project that eventually fizzled out, and he said the process to dissolve it was a nightmare. He wished he had just gone with a sole proprietorship first.</p>\n\n<p>Finally, one important caveat is that sole proprietorships do not shield you from liability, whereas S-Corps and LLCs do. For example, say something terrible happens and a client files a lawsuit against you. With an S-Corp or an LLC, your personal assets are protected. That is, the client is suing <em>the business entity</em> and they can only go after the <em>business entity’s assets</em>. If you are a sole proprietor, then the client is suing <em>you</em> personally. Technically, that puts everything you own at risk of seizure and liquidation — bank accounts, investments, property. <strong>However</strong> — this is <em>extremely</em> unlikely and especially rare in our line of work. You are not going to get sued for a software bug. (I mean, <em>maybe</em> you could? But let’s be real.)</p>\n\n<p>If you were starting a construction business or a restaurant, that is a very different story with very real scenarios involving liability. So do not allow the issue of liability scare you away from starting with a sole proprietorship! Tons of people are sole proprietors without any problems. It is important to weigh the potential <em>risks</em> of your type of business when choosing the appropriate structure. For software development and design, that risk is <em>extremely</em> low. Still, ask your accountant!</p>\n\n<h3 id=\"taxes\">Taxes</h3>\n\n<p>Taxes are terrible. Everyone knows this. And they are even worse when you are self-employed. However, you can mitigate your tax burden with deductions, which I’ll discuss below. Regardless of your business structure you will pay your normal income tax, which is the same as when you are a full-time worker at a company. The same federal and state income tax brackets apply.</p>\n\n<p>What differs when you are self-employed is that you must also pay self-employment tax, or SECA (named after the Self-Employed Contributions Act). SECA taxes include Social Security and Medicare. When you are a full-time worker at a company, you also pay these taxes (called FICA, per the Federal Insurance Contributions Act) and you can see the deductions taken from your paychecks. So what’s the difference? When you work at a company, the company pays <em>half</em> of these taxes (FICA) and you pay the other half. When you are self-employed, you pay the <em>full amount</em> (SECA) — because you are <em>the employee</em> <strong>and</strong> <em>the employer</em>. The total self-employment tax is 15.3 percent. When you work at a company, you and the company each pay 7.65 percent.</p>\n\n<p>There are a lot of details that go into these calculations that I will not cover in this post, but you are in luck — I made an app for that! <a href=\"https://www.hexedbits.com/taxatio/\">Taxatio</a> is tax calculator for freelancers that I released last year. It is available for iOS and macOS. It will help you with these calculations and tax estimations, as well as explain what’s going on with detailed breakdowns.</p>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<p>I would be remiss if I did not take this opportunity to highlight the injustices of our tax system. SECA/FICA taxes are <strong>regressive taxes</strong> — the more money you make, the less you pay as a percentage of your total income. This imposes an undue burden on the poor and lower wage workers, not to mention the rich usually pay <em>nothing</em>. SECA/FICA are taxes on <em>earned income</em>, that is, income you earn through labor. Because the rich do not work, but rather “earn” money through their pre-existing assets and investments, they pay nothing into Social Security and Medicare. Furthermore, their wealth — that is, their property, investments, inheritance, and other assets — is taxed at significantly lower rates. Thus, the rich get richer simply because they <em>own things</em> all while doing no labor whatsoever, yet they still reap the benefits of our decaying social safety net that <em>real workers</em> provide.</p>\n\n<p>The more you learn about our tax system, the more obvious it becomes that income taxes are designed to punish poor and working class people — and if you <strong>must work</strong> to pay your bills and survive, then <strong>you are working class</strong>. You may consider yourself middle class, but this is a facade propped up by our precarious and predatory credit system. Being “middle class” is a status that can be revoked at a moment’s notice, as we witnessed during the 2008 housing crisis. The middle class is an artificial bifurcation of working people manufactured by the rich via credit that deludes us into thinking there exists a ladder we can simply climb to join the wealthy elite at the top. It is a scam. No one <em>earns</em> a billion dollars — they <em>steal</em> it. (And before someone tries to lecture me about the “risk” of investments — there is no “risk” when you have a literal mountain of money and wealth at your disposal, not to mention a government that will bail you out after you make billions of dollars in derivatives evaporate overnight.)</p>\n\n<p>SECA taxes have a significant impact on independent workers. It is wrong that you must pay <em>double</em> as a single person, as if you have the same influence and wealth as an entire corporation. Even full-time workers should not have to split the burden of FICA taxes with corporations reap millions in profits while suppressing wages and destroying our environment with impunity.</p>\n\n<p>But, I digress.</p>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<h3 id=\"estimated-taxes\">Estimated taxes</h3>\n\n<p>The most significant difference regarding taxes when you are self-employed and doing 1099 freelance/contract work is <em>how</em> and <em>when</em> you pay taxes. Your clients <em>do not</em> do any tax withholding. When you are a full-time W-2 worker, FICA taxes are withheld from each paycheck you receive. When you are self-employed, you <em>must</em> pay quarterly estimated taxes. This includes federal <em>and</em> state income tax (unless, of course, you live in a state that does not collect income tax). Instead of paying a small amount on each paycheck, you pay lump sum once per quarter.</p>\n\n<p>Take note — quarterly estimated taxes are no joke. As an example, suppose your adjusted gross income for the year is $200,000. Your quarterly estimated tax payments may be as high as $18,000 <em>per quarter</em>. That’s <em>a lot</em> of money to drop at once. This means you need to be prepared. When clients pay you, do not withdraw the full amount from your business account to pay yourself. You always need to leave money in your business account for tax payments. Again, you are in luck because <a href=\"https://www.hexedbits.com/taxatio/\">Taxatio</a> will help you here by showing you the complete quarterly payments breakdown.</p>\n\n<p>The hardest part about estimated taxes is… well, estimating them! When you first get started, this is a moving target. You are not sure how much you will make for the year. Do your best to make projections. Your accountant can help you here, too. After your first year of being independent is over, you can use the previous year’s income as your estimate for the next year. If you do not pay your quarterlies on time, in full, or at all, you will get hit with fees and penalties when you file your tax returns.</p>\n\n<p>One reason I recommend going independent at the beginning of the calendar year is to simplify your taxes and estimates. If you start your freelance/contract work during the same year that you quit your full-time job, then you will have mixed income: some W-2, some 1099. This makes your estimations a bit more of a moving target, and they will not stabilize until after your second year — that is, your first <em>full calendar year</em> of being independent. However, you should not worry about this too much.</p>\n\n<p>There is one nice thing about estimated taxes (and using <a href=\"https://www.hexedbits.com/taxatio/\">Taxatio</a> to plan). If you have accurate projections for your annual income, like me, that means you have an accurate understanding of your annual tax burden. This allows me to optimize my time and work. For example, once I have made enough money for the year and know I have enough to cover my taxes, I can stop working. Historically, this means I can not work for 2-3 months each year.</p>\n\n<p>Conveniently, you can pay (and automatically schedule!) your federal estimated tax payments by creating an account with <a href=\"https://www.eftps.gov/eftps/\">EFTPS.gov</a>. For state estimated taxes, you will need to figure out the equivalent for your state. If you live in California, like me, then you can create an account with the <a href=\"https://www.ftb.ca.gov\">California FTB</a> where you can similarly schedule and pay your estimated payments.</p>\n\n<h3 id=\"deductions\">Deductions</h3>\n\n<p>Of course, we cannot discuss taxes without also discussing the available mechanisms we can employ to <em>reduce</em> them. In the example above, we discovered than an annual income of $200,000 could result in quarterly tax payments around $18,000. However, you can dramatically decrease this amount with deductions.</p>\n\n<p>It is important to understand the difference between your <em>gross income</em> (the total amount of money you earn) and your <em>taxable income</em> (the portion of your income that is subject to income tax). Deductions are subtracted from your gross income, which results in reducing your taxable income. Some of the most common deductions include health insurance premiums and a home office. Consult your accountant for advice.</p>\n\n<p>In <a href=\"/blog/2023/04/10/going-indie/\">part one</a> I mentioned that you can purchase health insurance via the Affordable Care Act (ACA). Every state is different, but I have been happy with my experience in California. If you get health insurance (which, you probably should), the entirety of your premiums are tax deductible. Also note that failing to purchase insurance coverage will result in a tax penalty.</p>\n\n<p>Another substantial deduction is self-employment tax (SECA). You are allowed to deduct half of SECA — that is, the “employer” portion. While I think paying double is an unnecessary burden on sole proprietors, it does help to be able to deduct this.</p>\n\n<p>Finally, and most importantly, you should be keeping track of your expenses. Expenses are subtracted from your gross income, which again results in reducing your taxable income. I will discuss bookkeeping in detail in the next part of this series.</p>\n\n<p>And, of course, <a href=\"https://www.hexedbits.com/taxatio/\">Taxatio</a> does these calculations and will show you a breakdown. The current version is limited with a single field for your total amount of deductions, but a future update will expand this functionality to allow a full itemized list.</p>\n\n<h3 id=\"saving-for-retirement\">Saving for retirement</h3>\n\n<p>The last significant piece of this puzzle involving business structure, taxes, and deductions is how you can continue to save for retirement despite no longer working full-time at a company. Typically, most companies provide retirement plans and many often offer matching contributions. Most commonly, you will have the opportunity to open a <a href=\"https://en.wikipedia.org/wiki/401(k)\">401(k) plan</a>. If you are lucky, the company will match a portion of your contributions.</p>\n\n<p>In my experience, many folks are unaware that as a self-employed business owner you can open a <a href=\"https://www.irs.gov/retirement-plans/one-participant-401k-plans\">Solo 401(k) plan</a>. The rules are the same in terms of the maximum annual contribution amounts — except you are <em>the employee</em> <strong>and</strong> <em>the company</em>. This means you can contribute as an individual and then “match yourself” as the company. In 2023, the maximum individual contribution is $22,500. The maximum company contribution is $43,500 <em>or</em> 25% of your earned income — whichever is smaller. This means you can put <em>up to</em> $66,000 into your Solo 401(k) <em>per year</em>, assuming you make enough to maximize the company contribution. <strong>Yes</strong> — <em>that is a sweet fucking deal</em>. You might be missing out on stock options at a company, but you can dump a ton of savings into retirement.</p>\n\n<p>With a 401(k), your contributions can be traditional (pre-tax) or Roth (after-tax). The difference is that traditional contributions reduce your taxable income, thus reducing the taxes you pay now. However, you will then pay taxes on the funds you withdraw during retirement. For Roth, you pay taxes now before making contributions and then when you withdraw funds during retirement, they are not taxed. The question to ask yourself is: <em>do you want to pay taxes now, or later?</em></p>\n\n<p>The other popular option is a <a href=\"https://en.wikipedia.org/wiki/Roth_IRA\">Roth IRA</a>. However, the maximum contribution for 2023 is only $6,500. You can have both a Solo 401(k) and an IRA, if you want. Be aware that there are many rules and regulations regarding contributions. I do not recommend trying to setup these accounts on your own. If you must choose one, a Solo 401(k) is often the better choice as it allows you to save significantly more. However, paperwork for an IRA can be much simpler, so that might be a good start depending on your circumstances.</p>\n\n<p>For all of these important decisions, you should consult your financial advisor to make the choices that work best for you and your financial goals.</p>\n\n<p>Finally, do I need to say it? <a href=\"https://www.hexedbits.com/taxatio/\">Taxatio</a> does these calculations for you. Currently, it only supports Solo 401(k) plans, but I would like to add support for Roth IRAs in a future update.</p>\n\n<h3 id=\"conclusion\">Conclusion</h3>\n\n<p>Today we covered some ugly, mundane — but nonetheless important — details of going independent. I hope this post was helpful if you are considering going independent. There is still more to come in this series. In the next part, I will discuss bookkeeping and invoicing! Stay tuned.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-17-august-2023\">\n        <a href=\"#updated-17-august-2023\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"17 Aug 2023 11:59:12 AM \">\n            <i>17 August 2023</i>\n        </small>\n    </h5>\n    \n<p>Many thanks to <a href=\"https://mastodon.social/@mjtsai/110900729704601727\">Michael Tsai</a> and <a href=\"https://github.com/jessesquires/jessesquires.com/issues/186#issue-1853100101\">@ljmatkins</a> for their feedback on this post regarding details about LLCs and retirement plans. I have updated this post to reflect their corrections and other notes.</p>\n\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2023/08/16/going-indie-3/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2023/07/17/stop-prefixing-userdefaults-keys/#updated-17-july-2023",
            "url": "https://www.jessesquires.com/blog/2023/07/17/stop-prefixing-userdefaults-keys/",
            "title": "[Updated] Stop prefixing your UserDefaults keys",
            "date_published": "2023-07-17T11:37:12-07:00",
            "date_modified": "2023-07-17T11:37:12-07:00",
            "summary": "<p><a href=\"https://developer.apple.com/documentation/foundation/userdefaults\"><code class=\"language-plaintext highlighter-rouge\">UserDefaults</code></a> is probably one of the most popular APIs on Apple Platforms. It is a highly-optimized key-value persisted store that is backed by a property list, and it is most commonly used for saving small pieces of data like user preferences. Despite its ease-of-use, there is one common anti-pattern I see developers use often.</p>\n\n",
            "tags": [
                "ios","macos",
                "software-dev"
            ],
            "content_html": "<p><a href=\"https://developer.apple.com/documentation/foundation/userdefaults\"><code class=\"language-plaintext highlighter-rouge\">UserDefaults</code></a> is probably one of the most popular APIs on Apple Platforms. It is a highly-optimized key-value persisted store that is backed by a property list, and it is most commonly used for saving small pieces of data like user preferences. Despite its ease-of-use, there is one common anti-pattern I see developers use often.</p>\n\n<!--excerpt-->\n\n<p>In dozens of iOS projects over the years, I find that developers are prefixing key names with the app’s bundle identifier. For example, instead of naming a key <code class=\"language-plaintext highlighter-rouge\">\"sounds-enabled\"</code> it will be named <code class=\"language-plaintext highlighter-rouge\">\"com.mycompany.MyApp.sounds-enabled\"</code>. I am writing this post as a PSA to tell you that you <strong>do not need to do this!</strong></p>\n\n<p>On iOS, in your application’s user data sandbox you will find the backing plist for <code class=\"language-plaintext highlighter-rouge\">UserDefaults</code> at <code class=\"language-plaintext highlighter-rouge\">~/Library/Preferences/com.mycompany.MyApp.plist</code>. On macOS, for a non-sanboxed app you will find the plist at the same path and for an app that <em>is</em> sandboxed you will find the plist at <code class=\"language-plaintext highlighter-rouge\">~/Library/Containers/MyApp/Data/Library/Preferences/com.mycompany.MyApp.plist</code>.</p>\n\n<p>As you can see, your instance of <code class=\"language-plaintext highlighter-rouge\">UserDefaults</code> is <strong>already</strong> namespaced by your app’s bundle identifier! Prefixing your keys is redundant. So please — do not prefix your key names with your app’s bundle identifier!</p>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<p>You might argue that prefixing key names is harmless, and that could be true sometimes. However, there are <a href=\"https://github.com/jessesquires/Foil/pull/61#issuecomment-1253147705\">known issues</a> with <code class=\"language-plaintext highlighter-rouge\">UserDefaults</code> <a href=\"https://developer.apple.com/documentation/foundation/userdefaults#2926902\">key-value observing</a> if key names have periods in the name — KVO does not work in that scenario. Furthermore, if you use debug tools (like <a href=\"https://github.com/FLEXTool/FLEX\">FLEX</a>) that allow you to inspect the values saved in <code class=\"language-plaintext highlighter-rouge\">UserDefaults</code> or if you manually open the plist (which is just XML) to examine it, it gets really cumbersome to search and read through if every single key is prefixed with the same ~30 characters.</p>\n\n<p>Lastly, if you are convinced to drop the unnecessary prefixes, beware that you will need to write migration code to save your values from the old key to the new one, otherwise you will lose data.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-17-july-2023\">\n        <a href=\"#updated-17-july-2023\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"17 Jul 2023 11:37:12 AM PDT\">\n            <i>17 July 2023</i>\n        </small>\n    </h5>\n    \n<p>A few folks on Mastodon have pointed out situations where prefixing might be warranted. Or rather, pointed out problematic scenarios to generally watch out for.</p>\n\n<p><a href=\"https://mastodon.social/@gregheo/110730986118754748\">Greg Heo mentioned</a> that you should carefully consider your key names if you are using <code class=\"language-plaintext highlighter-rouge\">UserDefaults</code> <em>shared domains</em>. Typically, this scenario would be sharing data between your main app and an extension — so hopefully you are not colliding with your own key names.</p>\n\n<p><a href=\"https://mastodon.social/@mattiem/110730470926606357\">Matt Massicotte noted</a> that some system frameworks store values in your app’s <code class=\"language-plaintext highlighter-rouge\">UserDefaults</code>. For example, AppKit and SwiftUI on macOS store state restoration data, most commonly window size and location. I had forgotten about this. However, I <strong>highly doubt</strong> you’ll have naming collisions. For example, here are a couple of keys for one of my apps: <code class=\"language-plaintext highlighter-rouge\">NSWindow Frame com_apple_SwiftUI_Settings_window</code>, <code class=\"language-plaintext highlighter-rouge\">com_apple_SwiftUI_Settings_selectedTabIndex</code>.</p>\n\n<p>More importantly, <a href=\"https://mastodon.social/@andy@iosdev.space/110730254596336650\">Andy Ibanez warned</a> that a third-party dependency might read and write to the same key name. That is very bad! If you are a library author, you <strong>should not</strong> be writing to <code class=\"language-plaintext highlighter-rouge\">UserDefaults.standard</code>. This is why <a href=\"https://developer.apple.com/documentation/foundation/userdefaults/1409957-init\"><code class=\"language-plaintext highlighter-rouge\">UserDefaults.init(suiteName:)</code></a> exists — your library should initialize its own suite.</p>\n\n<p>All of these are valid concerns. Still, I think for the <em>majority</em> of use cases, prefixing is not necessary.</p>\n\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2023/07/17/stop-prefixing-userdefaults-keys/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2020/01/21/xcode-tip-breakpoints-as-bookmarks/#updated-11-july-2023",
            "url": "https://www.jessesquires.com/blog/2020/01/21/xcode-tip-breakpoints-as-bookmarks/",
            "title": "[Updated] Xcode Tip: Using breakpoints as bookmarks",
            "date_published": "2023-07-11T12:06:52-07:00",
            "date_modified": "2023-07-11T12:06:52-07:00",
            "summary": "<p>Xcode has a great UI for setting and editing breakpoints. I use breakpoints all the time while working and debugging, but I want to share another, unconventional way that I use them.</p>\n\n",
            "tags": [
                "xcode","debugging","ios","macos","xcode-tips",
                "software-dev"
            ],
            "content_html": "<p>Xcode has a great UI for setting and editing breakpoints. I use breakpoints all the time while working and debugging, but I want to share another, unconventional way that I use them.</p>\n\n<!--excerpt-->\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-11-july-2023\">\n        <a href=\"#updated-11-july-2023\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"11 Jul 2023 12:06:52 PM PDT\">\n            <i>11 July 2023</i>\n        </small>\n    </h5>\n    \n<p>I’m happy to share that bookmarks are now a first-class feature in <a href=\"https://developer.apple.com/documentation/xcode-release-notes/xcode-15-release-notes\">Xcode 15</a>! We no longer have to hack breakpoints like I describe in this post. You can learn more about how they work in <a href=\"https://sarunw.com/posts/bookmark-in-xcode15/\">Sarun’s recent blog post</a>.</p>\n\n</div>\n\n<p>One of the best features is being able to <a href=\"https://help.apple.com/xcode/mac/10.2/#/dev9a374afc9\">set and keep breakpoints</a> in place, but quickly enable or disable them individually. In the sidebar, you can quickly see a list of all your breakpoints in the <a href=\"https://help.apple.com/xcode/mac/10.2/#/dev1cf0a324f\">breakpoint navigator</a> and toggle them on or off. Even better, Xcode preserves them across app launches for each project, but they do not affect your git working directory. This makes disabled breakpoints perfect “bookmarks” in your code, that only you can see.</p>\n\n<p>Any time I am exploring or getting familiar with a new codebase in Xcode, especially very large projects, I use disabled breakpoints as “bookmarks” to keep track of where I am, where I have been, and things I want to remember or need to revisit. Sometimes I even do this when debugging issues in codebases that I know well.</p>\n\n<p>This “bookmarking” is particularly useful when tracing through a long path of function calls to understand how something works, or if you are frequently jumping around (via “jump-to-definition”) to view the full declaration of new types that you encounter.</p>\n\n<p>When attempting to fix that first “starter bug” you get assigned, it is hard to know exactly which files you will need to edit to fix it. Perhaps a veteran maintainer on the project gets you started with a class name, or the name of a view controller. As you track down the bug, you confront dozens of new types for the first time. All of those different types are interacting together, and any of them may be relevant to fixing the issue. It is difficult to remember everything you have seen once you start digging around. Unobtrusively clicking in the gutter of the editor to add a breakpoint as you go along can be a huge help. And after browsing through dozens of files for the first time, you will have a list of relevant locations to investigate further, or examine tomorrow.</p>\n\n<p>Next time you are thrown into a new project, trying leaving some disabled breakpoint breadcrumbs so you can find your way back.</p>\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2020/01/21/xcode-tip-breakpoints-as-bookmarks/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2017/10/01/floating-point-swift-ulp-and-epsilon/#updated-03-april-2023",
            "url": "https://www.jessesquires.com/blog/2017/10/01/floating-point-swift-ulp-and-epsilon/",
            "title": "[Updated] Floating-point Swift, ulp, and epsilon: Exploring floating-point precision",
            "date_published": "2023-04-03T20:22:50-07:00",
            "date_modified": "2023-04-03T20:22:50-07:00",
            "summary": "<p>Epsilon. <code class=\"language-plaintext highlighter-rouge\">ε</code>. The fifth letter of the Greek alphabet. In calculus, an arbitrarily small positive quantity. In formal language theory, <a href=\"https://en.wikipedia.org/wiki/Empty_string\">the empty string</a>. In the theory of computation, the empty transition of an automaton. In the <a href=\"http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1570.pdf\">ISO C Standard</a>, <code class=\"language-plaintext highlighter-rouge\">1.19e-07</code> for single precision and <code class=\"language-plaintext highlighter-rouge\">2.22e-16</code> for double precision.</p>\n\n<p>The other day I was attempting to use <code class=\"language-plaintext highlighter-rouge\">FLT_EPSILON</code> (which I later learned was laughably incorrect) when the Swift 4 compiler emitted a warning saying that <code class=\"language-plaintext highlighter-rouge\">FLT_EPSILON</code> is deprecated and to use <code class=\"language-plaintext highlighter-rouge\">.ulpOfOne</code> instead. <em>What the hell is <code class=\"language-plaintext highlighter-rouge\">ulpOfOne</code>?</em> I <a href=\"https://developer.apple.com/documentation/swift/floatingpoint/2884058-ulpofone\">read the documentation</a> and then everything made sense — ha, just kidding. The <code class=\"language-plaintext highlighter-rouge\">FloatingPoint.ulpOfOne</code> docs generously describe the static variable as <em>the unit in the last place of 1.0</em> — whatever <em>that</em> means. Let’s find out.</p>\n\n",
            "tags": [
                "swift","floating-point",
                "software-dev"
            ],
            "content_html": "<p>Epsilon. <code class=\"language-plaintext highlighter-rouge\">ε</code>. The fifth letter of the Greek alphabet. In calculus, an arbitrarily small positive quantity. In formal language theory, <a href=\"https://en.wikipedia.org/wiki/Empty_string\">the empty string</a>. In the theory of computation, the empty transition of an automaton. In the <a href=\"http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1570.pdf\">ISO C Standard</a>, <code class=\"language-plaintext highlighter-rouge\">1.19e-07</code> for single precision and <code class=\"language-plaintext highlighter-rouge\">2.22e-16</code> for double precision.</p>\n\n<p>The other day I was attempting to use <code class=\"language-plaintext highlighter-rouge\">FLT_EPSILON</code> (which I later learned was laughably incorrect) when the Swift 4 compiler emitted a warning saying that <code class=\"language-plaintext highlighter-rouge\">FLT_EPSILON</code> is deprecated and to use <code class=\"language-plaintext highlighter-rouge\">.ulpOfOne</code> instead. <em>What the hell is <code class=\"language-plaintext highlighter-rouge\">ulpOfOne</code>?</em> I <a href=\"https://developer.apple.com/documentation/swift/floatingpoint/2884058-ulpofone\">read the documentation</a> and then everything made sense — ha, just kidding. The <code class=\"language-plaintext highlighter-rouge\">FloatingPoint.ulpOfOne</code> docs generously describe the static variable as <em>the unit in the last place of 1.0</em> — whatever <em>that</em> means. Let’s find out.</p>\n\n<!--excerpt-->\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-18-april-2018\">\n        <a href=\"#updated-18-april-2018\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"18 Apr 2018 08:00:00 AM PDT\">\n            <i>18 April 2018</i>\n        </small>\n    </h5>\n    <p>I gave a talk on the topics discussed in this post and more at iOS Conf Singapore. You can <a href=\"https://youtu.be/cdRn4DJq9eY\">watch the video here</a>!</p>\n\n</div>\n\n<div class=\"row\">\n    <div class=\"col\"></div>\n    <div class=\"d-flex justify-content-center col-12 \">\n        <figure>\n            <img class=\"img-thumbnail img-fluid\" src=\"/img/blog/dogscience.gif\" title=\"dog science\" alt=\"dog science\" />\n            <div class=\"row\">\n                <div class=\"col-12 d-flex justify-content-center\">\n                \n                </div>\n            </div>\n        </figure>\n    </div>\n    <div class=\"col\"></div>\n</div>\n\n<h3 id=\"aside-new-protocols-in-swift-4\">Aside: new protocols in Swift 4</h3>\n\n<p>The various numeric protocols got a facelift in Swift 4 (as well as in Swift 3). There’s a new, top-level <a href=\"https://developer.apple.com/documentation/swift/numeric\"><code class=\"language-plaintext highlighter-rouge\">Numeric</code></a> protocol, some new integer proposals, numerous API additions to the <a href=\"https://developer.apple.com/documentation/swift/floatingpoint\"><code class=\"language-plaintext highlighter-rouge\">FloatingPoint</code></a> protocol (including <code class=\"language-plaintext highlighter-rouge\">ulpOfOne</code>), as well as additions to the other numeric protocols.</p>\n\n<p>Relevant Swift Evolution proposals:</p>\n\n<ul>\n  <li><a href=\"https://github.com/apple/swift-evolution/blob/master/proposals/0104-improved-integers.md\">SE-0104</a>: <em>Protocol-oriented integers</em> <strong>(Swift 4)</strong></li>\n  <li><a href=\"https://github.com/apple/swift-evolution/blob/master/proposals/0113-rounding-functions-on-floatingpoint.md\">SE-0113</a>: <em>Add integral rounding functions to FloatingPoint</em> <strong>(Swift 3)</strong></li>\n  <li><a href=\"https://github.com/apple/swift-evolution/blob/master/proposals/0067-floating-point-protocols.md\">SE-0067</a>: <em>Enhanced Floating Point Protocols</em> <strong>(Swift 3)</strong></li>\n</ul>\n\n<h3 id=\"ulpofone-and-ulp\"><code class=\"language-plaintext highlighter-rouge\">ulpOfOne</code> and <code class=\"language-plaintext highlighter-rouge\">ulp</code></h3>\n\n<p>The <a href=\"https://developer.apple.com/documentation/swift/floatingpoint/2884058-ulpofone\">discussion</a> section for <code class=\"language-plaintext highlighter-rouge\">.ulpOfOne</code> elaborates a bit more:</p>\n\n<blockquote>\n  <p>The positive difference between <code class=\"language-plaintext highlighter-rouge\">1.0</code> and the next greater representable number. The <code class=\"language-plaintext highlighter-rouge\">ulpOfOne</code> constant corresponds to the C macros <code class=\"language-plaintext highlighter-rouge\">FLT_EPSILON</code>, <code class=\"language-plaintext highlighter-rouge\">DBL_EPSILON</code>, and others with a similar purpose.</p>\n</blockquote>\n\n<p>There’s also a computed property for Swift <code class=\"language-plaintext highlighter-rouge\">FloatingPoint</code> protocol conformers called <code class=\"language-plaintext highlighter-rouge\">ulp</code>. It’s a property, so you can write something like <code class=\"language-plaintext highlighter-rouge\">1.0.ulp</code> or <code class=\"language-plaintext highlighter-rouge\">3.14159.ulp</code>. According to <a href=\"https://developer.apple.com/documentation/swift/floatingpoint/1847492-ulp\">the documentation</a>, <code class=\"language-plaintext highlighter-rouge\">ulp</code> is <em>the unit in the last place of this value.</em> So, it’s the same as <code class=\"language-plaintext highlighter-rouge\">.ulpOfOne</code> but for any value. The <a href=\"https://developer.apple.com/documentation/swift/floatingpoint/1847492-ulp\">discussion</a> section explains:</p>\n\n<blockquote>\n  <p>This is the unit of the least significant digit in this value’s significand. For most numbers x, this is the difference between x and the next greater (in magnitude) representable number. There are some edge cases to be aware of […]</p>\n\n  <p>This quantity, or a related quantity, is sometimes called <em>epsilon</em> or <em>machine epsilon</em>. Avoid that name because it has different meanings in different languages, which can lead to confusion, and because it suggests that it is a good tolerance to use for comparisons, which it almost never is.</p>\n</blockquote>\n\n<p><em>For most numbers x, this is the difference between x and the next greater (in magnitude) representable number.</em> This makes more sense — floating-point numbers are difficult to represent in computing because they are inherently imprecise due to rounding. Given the above, it’s clear why <code class=\"language-plaintext highlighter-rouge\">FLT_EPSILON</code> is <strong>not</strong> intended to be used as a tolerance for comparisons. I’ll blame the terrible naming for my mistake there. 😉</p>\n\n<p>As <a href=\"http://alisoftware.github.io\">Olivier Halligon</a> pointed out to me, there are <a href=\"https://github.com/apple/swift/blob/master/stdlib/public/core/FloatingPoint.swift.gyb#L358-L399\">additional details in the header docs</a>:</p>\n\n<blockquote>\n  <p>NOTE: Rationale for “ulp” instead of “epsilon”:\nWe do not use that name because it is ambiguous at best and misleading at worst:</p>\n\n  <ul>\n    <li>\n      <p>Historically several definitions of “machine epsilon” have commonly been used, which differ by up to a factor of two or so. By contrast “ulp” is a term with a specific unambiguous definition.</p>\n    </li>\n    <li>\n      <p>Some languages have used “epsilon” to refer to wildly different values, such as <code class=\"language-plaintext highlighter-rouge\">leastNonzeroMagnitude</code>.</p>\n    </li>\n    <li>\n      <p>Inexperienced users often believe that “epsilon” should be used as a tolerance for floating-point comparisons, because of the name. It is nearly always the wrong value to use for this purpose.</p>\n    </li>\n  </ul>\n</blockquote>\n\n<p><em>By contrast “ulp” is a term with a specific unambiguous definition.</em> I’m not sure who wrote that, but “unambiguous” is a term I’d rarely use to describe anything regarding <a href=\"https://www.martinfowler.com/bliki/TwoHardThings.html\">naming in computer science</a> and programming. 😆 <a href=\"https://en.wikipedia.org/wiki/Unit_in_the_last_place\">ULP is short</a> for <strong>U</strong>nit in the <strong>L</strong>ast <strong>P</strong>lace, or <strong>U</strong>nit of <strong>L</strong>east <strong>P</strong>recision. It’s a unit, but what does it measure? It measures the distance from a value to the next representable value.</p>\n\n<h3 id=\"why-we-need-ulp\">Why we need <code class=\"language-plaintext highlighter-rouge\">ulp</code></h3>\n\n<p>Consider integer values which can be represented exactly in binary. The distance from <code class=\"language-plaintext highlighter-rouge\">0</code> to <code class=\"language-plaintext highlighter-rouge\">1</code> is <code class=\"language-plaintext highlighter-rouge\">1</code>, the distance from <code class=\"language-plaintext highlighter-rouge\">1</code> to <code class=\"language-plaintext highlighter-rouge\">2</code> is <code class=\"language-plaintext highlighter-rouge\">1</code>, and so on. In fact, it’s always <code class=\"language-plaintext highlighter-rouge\">1</code> — the distance between integers is uniform across all representable values and precisely equal to <code class=\"language-plaintext highlighter-rouge\">1</code>. In other words, for any value the next representable value is <code class=\"language-plaintext highlighter-rouge\">1</code> away. This is intuitive if you imagine a number line with only integer values. We do not need any notion of “ulp” for integers. They are simple and easy to represent in a base-2 number system.</p>\n\n<p>Floating-point numbers, however, are more complex and difficult to represent in bits. In Number theory, this is the set of <a href=\"https://en.wikipedia.org/wiki/Real_number\">real numbers</a> <strong>R</strong>, which includes the irrational numbers <strong>R/Q</strong>, the rationals <strong>Q</strong>, the integers <strong>Z</strong>, and the natural numbers <strong>N</strong>. Basically, all the numbers. 😆 While the set of all real numbers is <a href=\"https://en.wikipedia.org/wiki/Uncountable_set\">uncountably infinite</a>, the set of all rational numbers is <a href=\"https://en.wikipedia.org/wiki/Countable_set\">countable</a>, which means that almost all real numbers are irrational. Irrational numbers never terminate, so obviously need to be rounded. What’s more critical is that the rational numbers are <a href=\"https://en.wikipedia.org/wiki/Dense_order\">densely ordered</a> — for any two rational numbers there are infinitely many between them. In fact, there are more numbers (<strong>R/Q</strong> + <strong>Q</strong>) between <code class=\"language-plaintext highlighter-rouge\">0</code> and <code class=\"language-plaintext highlighter-rouge\">1</code> than there are in the entire set of integers <strong>Z</strong>.</p>\n\n<p>Computers obviously cannot represent <em>all</em> of the integers — from negative to positive infinity — which is why we have <code class=\"language-plaintext highlighter-rouge\">INT_MIN</code>, <code class=\"language-plaintext highlighter-rouge\">INT_MAX</code>, and other constants. Similarly, floating-point numbers are bound by <code class=\"language-plaintext highlighter-rouge\">FLT_MIN</code> and <code class=\"language-plaintext highlighter-rouge\">FLT_MAX</code> (or <code class=\"language-plaintext highlighter-rouge\">DBL_MIN</code> and <code class=\"language-plaintext highlighter-rouge\">DBL_MAX</code>). But more importantly, computers cannot represent <strong>all</strong> of the rational and irrational numbers between these bounds. While the representable integers are countable within their specified bounds of <code class=\"language-plaintext highlighter-rouge\">INT_MIN</code> and <code class=\"language-plaintext highlighter-rouge\">INT_MAX</code>, the floating-point numbers that occur between <code class=\"language-plaintext highlighter-rouge\">FLT_MIN</code> and <code class=\"language-plaintext highlighter-rouge\">FLT_MAX</code> are <em>infinite</em>. It’s clear that we need some kind of rounding mechanism, which means not all numbers are representable. Such a rounding mechanism will provide a way to determine <em>the distance from one value to the next representable value</em> — or, ulp. Thus, aside from the obvious limitations of silicon chips, this is why ulp needs to exist.</p>\n\n<div class=\"row\">\n    <div class=\"col\"></div>\n    <div class=\"d-flex justify-content-center col-12 \">\n        <figure>\n            <img class=\"img-thumbnail img-fluid\" src=\"/img/blog/numbers.jpg\" title=\"Numbers\" alt=\"Numbers\" />\n            <div class=\"row\">\n                <div class=\"col-12 d-flex justify-content-center\">\n                \n                </div>\n            </div>\n        </figure>\n    </div>\n    <div class=\"col\"></div>\n</div>\n\n<h3 id=\"memory-layout-of-floating-point-numbers\">Memory layout of floating-point numbers</h3>\n\n<p>Before we define ulp, let’s briefly review the memory layout for floating-point numbers.</p>\n\n<p>First, integers are exact and can be represented directly in binary format. Signed integers are typically represented in <a href=\"https://en.wikipedia.org/wiki/Two%27s_complement\">two’s complement</a>, but that’s an implementation detail. Conceptually, you have an integer which is stored as some bits. For example, <code class=\"language-plaintext highlighter-rouge\">6</code> which is <code class=\"language-plaintext highlighter-rouge\">0b0110</code>. Floating-point numbers have three components: a sign, an exponent (which is <a href=\"https://en.wikipedia.org/wiki/Exponent_bias\">biased</a>), and a significand. For single precision, there’s 1 bit for the sign, 8 bits for the exponent, and 23 bits for the significand — 32 bits total. Double precision provides 11 bits for the exponent and 52 bits for the significand, which totals 64 bits. I won’t discuss the more elaborate details about how floating-point numbers work in this post, but you’ll find additional reading at the end.</p>\n\n<div class=\"row\">\n    <div class=\"col\"></div>\n    <div class=\"d-flex justify-content-center col-12 \">\n        <figure>\n            <img class=\"img-thumbnail img-fluid\" src=\"/img/blog/float32memory.png\" title=\"Floating-point format, binary32\" alt=\"Floating-point format, binary32\" />\n            <div class=\"row\">\n                <div class=\"col-12 d-flex justify-content-center\">\n                \n                    <figcaption>\n                        <p><small class=\"text-muted text-center\">\n                            IEEE 754 single-precision binary floating-point format  / <a href=\"http://blog.reverberate.org/2014/09/what-every-computer-programmer-should.html\" target=\"_blank\" rel=\"noreferrer\">Source</a> \n                        </small></p>\n                    </figcaption>\n                \n                </div>\n            </div>\n        </figure>\n    </div>\n    <div class=\"col\"></div>\n</div>\n\n<p>Note that with this representation, positive and negative zero (<code class=\"language-plaintext highlighter-rouge\">+0</code> and <code class=\"language-plaintext highlighter-rouge\">-0</code>) are two distinct values, though they compare as equal. If you ever wondered why computers have signed zero, this is why.</p>\n\n<p>Let’s look at an example in base-10. We can represent 123.45 with a significand of 12345 and exponent of -2: <code class=\"language-plaintext highlighter-rouge\">123.45 = 12345 * 10e−2</code>. In base-2, computing a value from these three components is a bit more complicated but conceptually the same. Swift provides a clear and expressive API that can really help us understand how all of this works.</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">let</span> <span class=\"nv\">value</span> <span class=\"o\">=</span> <span class=\"kt\">Float</span><span class=\"p\">(</span><span class=\"mf\">0.15625</span><span class=\"p\">)</span> <span class=\"c1\">// 1/8 + 1/32</span>\n\n<span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">sign</span>          <span class=\"c1\">// plus</span>\n<span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">exponent</span>      <span class=\"c1\">// -3</span>\n<span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">significand</span>   <span class=\"c1\">// 1.25</span>\n</code></pre></div></div>\n\n<p>We can recompute the decimal value from its component parts. Note that the <code class=\"language-plaintext highlighter-rouge\">radix</code>, or base, is 2 — as in base-2 for binary.</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">// significand * 2^exponent</span>\n<span class=\"kt\">Float</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">significand</span><span class=\"p\">)</span> <span class=\"o\">*</span> <span class=\"nf\">powf</span><span class=\"p\">(</span><span class=\"kt\">Float</span><span class=\"p\">(</span><span class=\"kt\">Float</span><span class=\"o\">.</span><span class=\"n\">radix</span><span class=\"p\">),</span> <span class=\"kt\">Float</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">exponent</span><span class=\"p\">))</span>\n<span class=\"c1\">// 0.15625</span>\n</code></pre></div></div>\n\n<p>Additionally, Swift’s APIs allow us to explore the memory layout. We can look at the bit patterns and verify them with this <a href=\"https://www.h-schmidt.net/FloatConverter/IEEE754.html\">handy IEEE-754 floating-point converter</a>.</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">// 0.15625</span>\n<span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">exponentBitPattern</span>    <span class=\"c1\">// 124</span>\n<span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">significandBitPattern</span> <span class=\"c1\">// 2097152</span>\n</code></pre></div></div>\n\n<p>We can also check the bit counts for each component:</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">Float</span><span class=\"o\">.</span><span class=\"n\">exponentBitCount</span>       <span class=\"c1\">// 8, bits for the exponent</span>\n<span class=\"kt\">Float</span><span class=\"o\">.</span><span class=\"n\">significandBitCount</span>    <span class=\"c1\">// 23, bits for the significand</span>\n\n<span class=\"kt\">Double</span><span class=\"o\">.</span><span class=\"n\">exponentBitCount</span>      <span class=\"c1\">// 11, bits for the exponent</span>\n<span class=\"kt\">Double</span><span class=\"o\">.</span><span class=\"n\">significandBitCount</span>   <span class=\"c1\">// 52, bits for the significand</span>\n</code></pre></div></div>\n\n<h3 id=\"defining-ulp\">Defining <code class=\"language-plaintext highlighter-rouge\">ulp</code></h3>\n\n<p>Surprisingly, the IEEE standard doesn’t define <code class=\"language-plaintext highlighter-rouge\">ulpOfOne</code> (or <a href=\"https://en.wikipedia.org/wiki/Machine_epsilon\">machine epsilon</a>) explicitly, so there are a couple of varying definitions. However, most standard libraries provide constants for these values. The most prevalent is the <a href=\"http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1570.pdf\">ISO C Standard</a> — <code class=\"language-plaintext highlighter-rouge\">1.19e-07</code> for 32-bit values (<code class=\"language-plaintext highlighter-rouge\">Float</code>) and <code class=\"language-plaintext highlighter-rouge\">2.22e-16</code> for 64-bit values (<code class=\"language-plaintext highlighter-rouge\">Double</code>). As expected, this is what we see in Swift:</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">Float</span><span class=\"o\">.</span><span class=\"n\">ulpOfOne</span>\n<span class=\"c1\">// 1.192093e-07, or</span>\n<span class=\"c1\">// 0.00000011920928955078125</span>\n\n<span class=\"kt\">Double</span><span class=\"o\">.</span><span class=\"n\">ulpOfOne</span>\n<span class=\"c1\">// 2.220446049250313e-16, or</span>\n<span class=\"c1\">// 0.00000000000000022204460492503130808472633361816406250</span>\n</code></pre></div></div>\n\n<p>Given these initial epsilon or <code class=\"language-plaintext highlighter-rouge\">ulpOfOne</code> values, <a href=\"https://en.wikipedia.org/wiki/Unit_in_the_last_place#Definition\">we can compute</a> the <code class=\"language-plaintext highlighter-rouge\">ulp</code> for any value <code class=\"language-plaintext highlighter-rouge\">v</code> with an exponent <code class=\"language-plaintext highlighter-rouge\">exp</code> as: <code class=\"language-plaintext highlighter-rouge\">epsilon * 2^exp</code>, where 2 is the radix, or base.</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">let</span> <span class=\"nv\">value</span> <span class=\"o\">=</span> <span class=\"kt\">Float</span><span class=\"p\">(</span><span class=\"mf\">3.14</span><span class=\"p\">)</span>\n<span class=\"k\">let</span> <span class=\"nv\">ulpOfValue</span> <span class=\"o\">=</span> <span class=\"kt\">Float</span><span class=\"o\">.</span><span class=\"n\">ulpOfOne</span> <span class=\"o\">*</span> <span class=\"nf\">powf</span><span class=\"p\">(</span><span class=\"mf\">2.0</span><span class=\"p\">,</span> <span class=\"kt\">Float</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">exponent</span><span class=\"p\">))</span>\n\n<span class=\"n\">ulpOfValue</span>  <span class=\"c1\">// 0.00000023841857910156250</span>\n<span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">ulp</span>   <span class=\"c1\">// 0.00000023841857910156250</span>\n</code></pre></div></div>\n\n<p>Again, Swift provides a great, readable API for manipulating and exploring the properties of floating-point values. For example, for any value we can check <a href=\"https://developer.apple.com/documentation/swift/floatingpoint/1848104-nextup\"><code class=\"language-plaintext highlighter-rouge\">.nextUp</code></a> to see the next representable value. Given what we’ve learned so far, we can intuitively reason about what the next number (<code class=\"language-plaintext highlighter-rouge\">.nextUp</code>) must be and verify our result.</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">let</span> <span class=\"nv\">value</span> <span class=\"o\">=</span> <span class=\"kt\">Float</span><span class=\"p\">(</span><span class=\"mf\">1.0</span><span class=\"p\">)</span>\n<span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">ulp</span>           <span class=\"c1\">// 0.00000011920928955078125</span>\n<span class=\"n\">value</span> <span class=\"o\">+</span> <span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">ulp</span>   <span class=\"c1\">// 1.00000011920928955078125</span>\n<span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">nextUp</span>        <span class=\"c1\">// 1.00000011920928955078125</span>\n</code></pre></div></div>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">let</span> <span class=\"nv\">value</span> <span class=\"o\">=</span> <span class=\"kt\">Float</span><span class=\"p\">(</span><span class=\"mf\">1_000.0</span><span class=\"p\">)</span>\n<span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">ulp</span>           <span class=\"c1\">//    0.00006103515625000000000</span>\n<span class=\"n\">value</span> <span class=\"o\">+</span> <span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">ulp</span>   <span class=\"c1\">// 1000.00006103515625000000000</span>\n<span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">nextUp</span>        <span class=\"c1\">// 1000.00006103515625000000000</span>\n</code></pre></div></div>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">let</span> <span class=\"nv\">value</span> <span class=\"o\">=</span> <span class=\"kt\">Float</span><span class=\"p\">(</span><span class=\"mf\">3.14</span><span class=\"p\">)</span>\n<span class=\"c1\">// actually 3.14000010490417480468750 -- because rounding</span>\n\n<span class=\"n\">value</span>               <span class=\"c1\">// 3.14000010490417480468750</span>\n<span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">ulp</span>           <span class=\"c1\">// 0.00000023841857910156250</span>\n<span class=\"n\">value</span> <span class=\"o\">+</span> <span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">ulp</span>   <span class=\"c1\">// 3.14000034332275390625000</span>\n<span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">nextUp</span>        <span class=\"c1\">// 3.14000034332275390625000</span>\n</code></pre></div></div>\n\n<p>Notice that the <code class=\"language-plaintext highlighter-rouge\">ulp</code> of 1 is not the same as the <code class=\"language-plaintext highlighter-rouge\">ulp</code> of 1,000. For each floating-point number <code class=\"language-plaintext highlighter-rouge\">ulp</code> varies. In fact, the precision of a floating-point value is proportional to its magnitude. The larger a value, the less precise.</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">let</span> <span class=\"nv\">value</span> <span class=\"o\">=</span> <span class=\"kt\">Float</span><span class=\"p\">(</span><span class=\"mf\">1_000_000_000.0</span><span class=\"p\">)</span>\n<span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">ulp</span>     <span class=\"c1\">// 64.0</span>\n<span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">nextUp</span>  <span class=\"c1\">// 1000000064.0</span>\n</code></pre></div></div>\n\n<h3 id=\"viewing-the-source\">Viewing the source</h3>\n\n<p>We can view the Swift standard library source, which lives in <a href=\"https://github.com/apple/swift/blob/master/stdlib/public/core/FloatingPointTypes.swift.gyb#L541-L562\"><code class=\"language-plaintext highlighter-rouge\">stdlib/public/core/ FloatingPointTypes.swift.gyb</code></a>. If you’ve never seen <code class=\"language-plaintext highlighter-rouge\">.gyb</code> (‘generate your boilerplate’) files, read <a href=\"https://oleb.net/blog/2016/10/swift-stdlib-source/\">Ole Begemann’s post</a>. Per Ole’s instructions, we can generate the specific implementation for <code class=\"language-plaintext highlighter-rouge\">Float</code>.</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">public</span> <span class=\"k\">var</span> <span class=\"nv\">ulp</span><span class=\"p\">:</span> <span class=\"kt\">Float</span> <span class=\"p\">{</span>\n    <span class=\"k\">if</span> <span class=\"o\">!</span><span class=\"n\">isFinite</span> <span class=\"p\">{</span> <span class=\"k\">return</span> <span class=\"kt\">Float</span><span class=\"o\">.</span><span class=\"n\">nan</span> <span class=\"p\">}</span>\n    <span class=\"k\">if</span> <span class=\"n\">exponentBitPattern</span> <span class=\"o\">&gt;</span> <span class=\"kt\">UInt</span><span class=\"p\">(</span><span class=\"kt\">Float</span><span class=\"o\">.</span><span class=\"n\">significandBitCount</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n      <span class=\"c1\">// self is large enough that self.ulp is normal, so we just compute its</span>\n      <span class=\"c1\">// exponent and construct it with a significand of zero.</span>\n      <span class=\"k\">let</span> <span class=\"nv\">ulpExponent</span> <span class=\"o\">=</span>\n        <span class=\"n\">exponentBitPattern</span> <span class=\"o\">-</span> <span class=\"kt\">UInt</span><span class=\"p\">(</span><span class=\"kt\">Float</span><span class=\"o\">.</span><span class=\"n\">significandBitCount</span><span class=\"p\">)</span>\n      <span class=\"k\">return</span> <span class=\"kt\">Float</span><span class=\"p\">(</span><span class=\"nv\">sign</span><span class=\"p\">:</span> <span class=\"o\">.</span><span class=\"n\">plus</span><span class=\"p\">,</span>\n        <span class=\"nv\">exponentBitPattern</span><span class=\"p\">:</span> <span class=\"n\">ulpExponent</span><span class=\"p\">,</span>\n        <span class=\"nv\">significandBitPattern</span><span class=\"p\">:</span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n    <span class=\"p\">}</span>\n    <span class=\"k\">if</span> <span class=\"n\">exponentBitPattern</span> <span class=\"o\">&gt;=</span> <span class=\"mi\">1</span> <span class=\"p\">{</span>\n      <span class=\"c1\">// self is normal but ulp is subnormal.</span>\n      <span class=\"k\">let</span> <span class=\"nv\">ulpShift</span> <span class=\"o\">=</span> <span class=\"kt\">UInt32</span><span class=\"p\">(</span><span class=\"n\">exponentBitPattern</span> <span class=\"o\">-</span> <span class=\"mi\">1</span><span class=\"p\">)</span>\n      <span class=\"k\">return</span> <span class=\"kt\">Float</span><span class=\"p\">(</span><span class=\"nv\">sign</span><span class=\"p\">:</span> <span class=\"o\">.</span><span class=\"n\">plus</span><span class=\"p\">,</span>\n        <span class=\"nv\">exponentBitPattern</span><span class=\"p\">:</span> <span class=\"mi\">0</span><span class=\"p\">,</span>\n        <span class=\"nv\">significandBitPattern</span><span class=\"p\">:</span> <span class=\"mi\">1</span> <span class=\"o\">&amp;&lt;&lt;</span> <span class=\"n\">ulpShift</span><span class=\"p\">)</span>\n    <span class=\"p\">}</span>\n    <span class=\"k\">return</span> <span class=\"kt\">Float</span><span class=\"p\">(</span><span class=\"nv\">sign</span><span class=\"p\">:</span> <span class=\"o\">.</span><span class=\"n\">plus</span><span class=\"p\">,</span>\n      <span class=\"nv\">exponentBitPattern</span><span class=\"p\">:</span> <span class=\"mi\">0</span><span class=\"p\">,</span>\n      <span class=\"nv\">significandBitPattern</span><span class=\"p\">:</span> <span class=\"mi\">1</span><span class=\"p\">)</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>Surprising at an initial glance, this is not the simple formula noted above (<code class=\"language-plaintext highlighter-rouge\">epsilon * 2^exponent</code>). First there are some edge cases to handle, like <code class=\"language-plaintext highlighter-rouge\">Float.nan.ulp</code> which is <code class=\"language-plaintext highlighter-rouge\">nan</code>. Then it constructs a new <code class=\"language-plaintext highlighter-rouge\">Float</code> after computing its constituent components — the sign, exponent, and significand. This code eventually calls into the public initializer: <code class=\"language-plaintext highlighter-rouge\">init(sign: exponentBitPattern: significandBitPattern:)</code>. Note that the final return is equivalent to <code class=\"language-plaintext highlighter-rouge\">Float.leastNonzeroMagnitude</code> (or <code class=\"language-plaintext highlighter-rouge\">FLT_MIN</code>).</p>\n\n<p>We can view the implementation of this initializer. We see a lot of <a href=\"https://en.wikipedia.org/wiki/Bit_manipulation\">bit manipulation</a>, namely <a href=\"https://developer.apple.com/documentation/swift/binaryinteger/2886547\">bitwise AND</a> (<code class=\"language-plaintext highlighter-rouge\">&amp;</code>) and <a href=\"https://developer.apple.com/documentation/swift/fixedwidthinteger/2924902\">masking left shift</a> (<code class=\"language-plaintext highlighter-rouge\">&amp;&lt;&lt;</code>).</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">public</span> <span class=\"nf\">init</span><span class=\"p\">(</span><span class=\"nv\">sign</span><span class=\"p\">:</span> <span class=\"kt\">FloatingPointSign</span><span class=\"p\">,</span>\n            <span class=\"nv\">exponentBitPattern</span><span class=\"p\">:</span> <span class=\"kt\">UInt</span><span class=\"p\">,</span>\n            <span class=\"nv\">significandBitPattern</span><span class=\"p\">:</span> <span class=\"kt\">UInt32</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n    <span class=\"k\">let</span> <span class=\"nv\">signShift</span> <span class=\"o\">=</span> <span class=\"kt\">Float</span><span class=\"o\">.</span><span class=\"n\">significandBitCount</span> <span class=\"o\">+</span> <span class=\"kt\">Float</span><span class=\"o\">.</span><span class=\"n\">exponentBitCount</span>\n    <span class=\"k\">let</span> <span class=\"nv\">sign</span> <span class=\"o\">=</span> <span class=\"kt\">UInt32</span><span class=\"p\">(</span><span class=\"n\">sign</span> <span class=\"o\">==</span> <span class=\"o\">.</span><span class=\"n\">minus</span> <span class=\"p\">?</span> <span class=\"mi\">1</span> <span class=\"p\">:</span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n    <span class=\"k\">let</span> <span class=\"nv\">exponent</span> <span class=\"o\">=</span> <span class=\"kt\">UInt32</span><span class=\"p\">(</span>\n      <span class=\"n\">exponentBitPattern</span> <span class=\"o\">&amp;</span> <span class=\"kt\">Float</span><span class=\"o\">.</span><span class=\"n\">_infinityExponent</span><span class=\"p\">)</span>\n    <span class=\"k\">let</span> <span class=\"nv\">significand</span> <span class=\"o\">=</span> <span class=\"kt\">UInt32</span><span class=\"p\">(</span>\n      <span class=\"n\">significandBitPattern</span> <span class=\"o\">&amp;</span> <span class=\"kt\">Float</span><span class=\"o\">.</span><span class=\"n\">_significandMask</span><span class=\"p\">)</span>\n    <span class=\"k\">self</span><span class=\"o\">.</span><span class=\"nf\">init</span><span class=\"p\">(</span><span class=\"nv\">bitPattern</span><span class=\"p\">:</span>\n      <span class=\"n\">sign</span> <span class=\"o\">&amp;&lt;&lt;</span> <span class=\"kt\">UInt32</span><span class=\"p\">(</span><span class=\"n\">signShift</span><span class=\"p\">)</span> <span class=\"o\">|</span>\n      <span class=\"n\">exponent</span> <span class=\"o\">&amp;&lt;&lt;</span> <span class=\"kt\">UInt32</span><span class=\"p\">(</span><span class=\"kt\">Float</span><span class=\"o\">.</span><span class=\"n\">significandBitCount</span><span class=\"p\">)</span> <span class=\"o\">|</span>\n      <span class=\"n\">significand</span><span class=\"p\">)</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>This initializer eventually calls into <code class=\"language-plaintext highlighter-rouge\">init(bitPattern:)</code>, where it finally constructs a primitive LLVM 32-bit float (FPIEEE32) from the raw bit pattern.</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">public</span> <span class=\"nf\">init</span><span class=\"p\">(</span><span class=\"nv\">bitPattern</span><span class=\"p\">:</span> <span class=\"kt\">UInt32</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n    <span class=\"k\">self</span><span class=\"o\">.</span><span class=\"nf\">init</span><span class=\"p\">(</span><span class=\"nv\">_bits</span><span class=\"p\">:</span> <span class=\"kt\">Builtin</span><span class=\"o\">.</span><span class=\"nf\">bitcast_Int32_FPIEEE32</span><span class=\"p\">(</span><span class=\"n\">bitPattern</span><span class=\"o\">.</span><span class=\"n\">_value</span><span class=\"p\">))</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>We can break this down and observe each step in a Swift playground. For private APIs like <code class=\"language-plaintext highlighter-rouge\">Float._infinityExponent</code>, we can look at the source and define them inline.</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">// starting value</span>\n<span class=\"k\">let</span> <span class=\"nv\">value</span> <span class=\"o\">=</span> <span class=\"kt\">Float</span><span class=\"p\">(</span><span class=\"mf\">3.1415</span><span class=\"p\">)</span>\n\n<span class=\"c1\">// 1. the first if is satisfied:</span>\n<span class=\"c1\">// var ulp: Float {</span>\n<span class=\"c1\">//     if exponentBitPattern &gt; UInt(Float.significandBitCount) {</span>\n\n<span class=\"k\">let</span> <span class=\"nv\">ulpSign</span> <span class=\"o\">=</span> <span class=\"kt\">FloatingPointSign</span><span class=\"o\">.</span><span class=\"n\">plus</span> <span class=\"c1\">// plus</span>\n<span class=\"k\">let</span> <span class=\"nv\">ulpExponent</span> <span class=\"o\">=</span> <span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">exponentBitPattern</span> <span class=\"o\">-</span> <span class=\"kt\">UInt</span><span class=\"p\">(</span><span class=\"kt\">Float</span><span class=\"o\">.</span><span class=\"n\">significandBitCount</span><span class=\"p\">)</span> <span class=\"c1\">// 105</span>\n<span class=\"k\">let</span> <span class=\"nv\">ulpSignificandBitPattern</span> <span class=\"o\">=</span> <span class=\"kt\">UInt32</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"c1\">// 0</span>\n\n<span class=\"c1\">// 2. we call init with the 3 values above:</span>\n<span class=\"c1\">// init(sign: FloatingPointSign,</span>\n<span class=\"c1\">//      exponentBitPattern: UInt,</span>\n<span class=\"c1\">//      significandBitPattern: UInt32) {</span>\n\n<span class=\"k\">let</span> <span class=\"nv\">signShift</span> <span class=\"o\">=</span> <span class=\"kt\">Float</span><span class=\"o\">.</span><span class=\"n\">significandBitCount</span> <span class=\"o\">+</span> <span class=\"kt\">Float</span><span class=\"o\">.</span><span class=\"n\">exponentBitCount</span> <span class=\"c1\">// 31</span>\n<span class=\"k\">let</span> <span class=\"nv\">sign</span> <span class=\"o\">=</span> <span class=\"kt\">UInt32</span><span class=\"p\">(</span><span class=\"n\">ulpSign</span> <span class=\"o\">==</span> <span class=\"o\">.</span><span class=\"n\">minus</span> <span class=\"p\">?</span> <span class=\"mi\">1</span> <span class=\"p\">:</span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"c1\">// 0</span>\n\n<span class=\"k\">let</span> <span class=\"nv\">_infinityExponent</span> <span class=\"o\">=</span> <span class=\"mi\">1</span> <span class=\"o\">&amp;&lt;&lt;</span> <span class=\"kt\">UInt</span><span class=\"p\">(</span><span class=\"kt\">Float</span><span class=\"o\">.</span><span class=\"n\">exponentBitCount</span><span class=\"p\">)</span> <span class=\"o\">-</span> <span class=\"mi\">1</span> <span class=\"c1\">// 255</span>\n<span class=\"k\">let</span> <span class=\"nv\">exponent</span> <span class=\"o\">=</span> <span class=\"kt\">UInt32</span><span class=\"p\">(</span><span class=\"n\">ulpExponent</span> <span class=\"o\">&amp;</span> <span class=\"n\">_infinityExponent</span><span class=\"p\">)</span> <span class=\"c1\">// 105, or 2^-22</span>\n\n<span class=\"k\">let</span> <span class=\"nv\">_significandMask</span> <span class=\"o\">=</span> <span class=\"mi\">1</span> <span class=\"o\">&amp;&lt;&lt;</span> <span class=\"kt\">UInt32</span><span class=\"p\">(</span><span class=\"kt\">Float</span><span class=\"o\">.</span><span class=\"n\">significandBitCount</span><span class=\"p\">)</span> <span class=\"o\">-</span> <span class=\"mi\">1</span> <span class=\"c1\">// 8388607</span>\n<span class=\"k\">let</span> <span class=\"nv\">significand</span> <span class=\"o\">=</span> <span class=\"kt\">UInt32</span><span class=\"p\">(</span><span class=\"n\">ulpSignificandBitPattern</span> <span class=\"o\">&amp;</span> <span class=\"n\">_significandMask</span><span class=\"p\">)</span> <span class=\"c1\">// 0</span>\n\n<span class=\"k\">let</span> <span class=\"nv\">signMaskLeftShift</span> <span class=\"o\">=</span> <span class=\"n\">sign</span> <span class=\"o\">&amp;&lt;&lt;</span> <span class=\"kt\">UInt32</span><span class=\"p\">(</span><span class=\"n\">signShift</span><span class=\"p\">)</span> <span class=\"c1\">// 0</span>\n<span class=\"k\">let</span> <span class=\"nv\">exponentMaskLeftShift</span> <span class=\"o\">=</span> <span class=\"n\">exponent</span> <span class=\"o\">&amp;&lt;&lt;</span> <span class=\"kt\">UInt32</span><span class=\"p\">(</span><span class=\"kt\">Float</span><span class=\"o\">.</span><span class=\"n\">significandBitCount</span><span class=\"p\">)</span> <span class=\"c1\">// 880803840</span>\n<span class=\"k\">let</span> <span class=\"nv\">bitPattern</span> <span class=\"o\">=</span> <span class=\"n\">signMaskLeftShift</span> <span class=\"o\">|</span> <span class=\"n\">exponentMaskLeftShift</span> <span class=\"o\">|</span> <span class=\"n\">significand</span> <span class=\"c1\">// 880803840</span>\n\n<span class=\"c1\">// 880803840 in binary is 00110100100000000000000000000000</span>\n<span class=\"c1\">// Sign  Exponent  Significand</span>\n<span class=\"c1\">// [0]  [01101001]  [00000000000000000000000]</span>\n\n<span class=\"c1\">// 3. initialize with the computed bit pattern</span>\n<span class=\"k\">let</span> <span class=\"nv\">finalUlp</span> <span class=\"o\">=</span> <span class=\"kt\">Float</span><span class=\"p\">(</span><span class=\"nv\">bitPattern</span><span class=\"p\">:</span> <span class=\"n\">bitPattern</span><span class=\"p\">)</span> <span class=\"c1\">// 2.384186e-07, or 0.00000023841857910156250</span>\n</code></pre></div></div>\n\n<p>This code is admittedly quite difficult to follow, but suffice to say it is equivalent to what we originally computed above.</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">let</span> <span class=\"nv\">value</span> <span class=\"o\">=</span> <span class=\"kt\">Float</span><span class=\"p\">(</span><span class=\"mf\">3.1415</span><span class=\"p\">)</span>\n<span class=\"k\">let</span> <span class=\"nv\">computedUlp</span> <span class=\"o\">=</span> <span class=\"kt\">Float</span><span class=\"o\">.</span><span class=\"n\">ulpOfOne</span> <span class=\"o\">*</span> <span class=\"nf\">powf</span><span class=\"p\">(</span><span class=\"kt\">Float</span><span class=\"p\">(</span><span class=\"kt\">Float</span><span class=\"o\">.</span><span class=\"n\">radix</span><span class=\"p\">),</span> <span class=\"kt\">Float</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">exponent</span><span class=\"p\">))</span>\n\n<span class=\"n\">value</span>       <span class=\"c1\">// 3.14149999618530273437500</span>\n<span class=\"n\">computedUlp</span> <span class=\"c1\">// 0.00000023841857910156250</span>\n<span class=\"n\">value</span><span class=\"o\">.</span><span class=\"n\">ulp</span>   <span class=\"c1\">// 0.00000023841857910156250</span>\n</code></pre></div></div>\n\n<h3 id=\"conclusion\">Conclusion</h3>\n\n<p>So that’s ulp. There’s still a lot to unpack here. I’ve skipped over some details, but this rabbit hole gets deeper and deeper — and I had to end this post somewhere. Hopefully this was helpful to better understand ulp and a little about floating-point precision. To see how far the rabbit hole of floating-point numbers goes, check out the links below.</p>\n\n<p>If I got anything wrong, please <a href=\"https://twitter.com/jesse_squires\">let me know</a> or <a href=\"https://github.com/jessesquires/jessesquires.com/issues/new\">open an issue</a>! 😅</p>\n\n<h3 id=\"further-reading-and-resources\">Further reading and resources</h3>\n\n<ul>\n  <li><a href=\"https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/\">Comparing Floating Point Numbers, 2012 Edition</a>, Bruce Dawson</li>\n  <li><a href=\"http://blog.reverberate.org/2014/09/what-every-computer-programmer-should.html\">Floating Point Demystified, Part 1</a>, Josh Haberman</li>\n  <li><a href=\"http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html\">What Every Computer Scientist Should Know About Floating-Point Arithmetic</a>, David Goldberg</li>\n  <li><a href=\"http://fabiensanglard.net/floating_point_visually_explained/\">Floating Point Visually Explained</a>, Fabien Sanglard</li>\n  <li><a href=\"https://people.eecs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF\">Lecture Notes on the Status of IEEE 754</a>, Prof. W. Kahan, UC Berkeley</li>\n  <li><a href=\"https://www.h-schmidt.net/FloatConverter/IEEE754.html\">IEEE-754 Floating Point Converter</a></li>\n</ul>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-03-april-2023\">\n        <a href=\"#updated-03-april-2023\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"03 Apr 2023 08:22:50 PM PDT\">\n            <i>03 April 2023</i>\n        </small>\n    </h5>\n    \n<p>I recently discovered <a href=\"https://twitter.com/xwu\">Xiaodi Wu’s</a> excellent series of articles, <a href=\"https://numerics.diploid.ca\">Notes on numerics in Swift</a>, which were written in 2018-2019. If you want to dive deeper into Swift’s numerics, I highly recommend reading these!</p>\n\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2017/10/01/floating-point-swift-ulp-and-epsilon/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2023/02/06/goodbye-twitter/#updated-03-april-2023",
            "url": "https://www.jessesquires.com/blog/2023/02/06/goodbye-twitter/",
            "title": "[Updated] Goodbye, Twitter",
            "date_published": "2023-04-03T20:06:16-07:00",
            "date_modified": "2023-04-03T20:06:16-07:00",
            "summary": "<p>When I <a href=\"/blog/2022/12/14/mastodon/\">wrote about joining Mastodon</a>, I said I would stay on Twitter for the moment and see what happens. Well, unsurprisingly, the service has continued to erode. It really is a shame, because I’ve found the software developer community there very helpful over the years. I met a lot of friends on Twitter, and later met them for the very first time in person at WWDC.</p>\n\n",
            "tags": [
                "social-media","mastodon",
                "essays"
            ],
            "content_html": "<p>When I <a href=\"/blog/2022/12/14/mastodon/\">wrote about joining Mastodon</a>, I said I would stay on Twitter for the moment and see what happens. Well, unsurprisingly, the service has continued to erode. It really is a shame, because I’ve found the software developer community there very helpful over the years. I met a lot of friends on Twitter, and later met them for the very first time in person at WWDC.</p>\n\n<!--excerpt-->\n\n<p>I’ve been less active on Twitter for over a year, prior to the flippant billionaire crybaby takeover. Since then, I’ve been <em>even less</em> active on the site. I tried, briefly, to continue posting to both Twitter and Mastodon, but I simply don’t have time for that — especially as Twitter seems to be <a href=\"https://mjtsai.com/blog/2023/01/26/missing-tweets/\">quickly deteriorating</a> and <a href=\"https://daringfireball.net/linked/2023/01/23/twitter-frum-crumbling\">increasingly unreliable</a>. Am I seeing content I care about? Are my own tweets even being seen? Meanwhile, Mastodon continues to improve — and by improve, I mean more of my friends and connections are joining. Mastodon, as ever, is also free of ads, corporate surveillance, and manipulative algorithmic bullshit. So really, what choice did I have? I naturally gravitated away from Twitter and toward Mastodon.</p>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<p>Anyway, a <a href=\"https://twitter.com/TwitterDev/status/1621026986784337922\">recent announcement</a> from the Twitter Dev account is why I’m writing today. Apparently, they plan to revoke free access to the Twitter API on February 9, in just a few days. (See also: <a href=\"https://mjtsai.com/blog/2023/02/02/twitter-to-charge-for-api/\">Michael Tsai</a> and <a href=\"https://daringfireball.net/linked/2023/02/02/twitter-apis\">Daring Fireball</a>.) As I’ve <a href=\"/blog/2022/12/15/rss-to-mastodon/\">written before</a>, I use <a href=\"https://zapier.com\">Zapier</a> to automatically tweet blog posts (and <a href=\"https://ifttt.com\">IFTTT</a> for Mastodon). I also use <a href=\"https://tweetdelete.net\">TweetDelete</a> to periodically delete old tweets.</p>\n\n<p>It remains to be seen, but the expectation is that these services will stop working after Twitter makes this change. I suppose these companies could pay for Twitter API access to keep their own services running. If that happens, I expect the cost to be forwarded to users. Depending on how much they charge, I might do it — but given the progressively dismal state of Twitter, it’s hard to see how any fee would be worth it. Why automatically tweet blog posts when folks won’t even see the tweets? Why continue to invest in a platform that is clearly crumbling, especially when there is an exciting and growing alternative?</p>\n\n<p>And it gets worse. Twitter has blocked API access for a popular tool, <a href=\"https://www.movetodon.org/twitterlogin/\">Movetodon</a>, which facilitates migrating from Twitter to Mastodon. You can follow <a href=\"https://mastodon.social/@Tibor/109800904950500383\">this thread on Mastodon</a> for updates from the author, Tibor Martini. Sadly, I doubt it will start working again. How petty and childish can Twitter get? I do not have time for this kind of weak-ass bullshit.</p>\n\n<p>What will transpire after February 9 is unclear at the moment, so I’m operating as if these things will simply stop working, or be too expensive for me to justify paying for them. I sure as hell will not be paying Twitter directly for API access. Had this API change happened prior to Twitter embarking on its journey into self-inflicted ruin, I would have gladly paid for these tools that save me so much time. But now? I gaze over at the state of affairs at this company and I think, <em>you want me to pay… <strong>for this</strong>?</em> Nah, I’ll pass.</p>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<p>Because of the announcement, I’ve run TweetDelete to delete all my content on Twitter and I won’t be participating there until we see what happens on February 9. If the automation services to tweet my blog posts continue to work, I will leave them intact. However, I won’t be active there much, if at all, going forward. Also, I will not be deleting my account. I’ll keep it for historical reasons — or just to auto-tweet blog posts, if that remains an option.</p>\n\n<p>I know deleting tweets is controversial. Many folks are interested in preserving content and preventing broken links, or saving links to tweets for reference later. My position is that social media is inherently ephemeral and should not persist indefinitely. That’s <a href=\"/blog/2021/03/16/deleting-tweets-and-other-social-media-content/\">how I use it</a>. Like in real life, I don’t record and keep transcripts of every conversation or thought that I have. If something is important, it should exist permanently outside of the guarded corporate walls of social media companies, because they could make any content inaccessible on a whim.</p>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<p>If you primarily see my blog posts through Twitter, you can instead <a href=\"/subscribe/\">subscribe via RSS</a> and follow me at <a href=\"https://mastodon.social/@jsq\">@jsq@mastodon.social</a>.</p>\n\n<p>And if you want to join the party, you can use <a href=\"https://mastodon.social/invite/Bw4iPeR9\">this invite link</a> for Mastodon.social. As of this writing, it looks like <a href=\"https://fedifinder.glitch.me\">Fedifinder</a>, a similar tool to Movetodon, is still working. I would recommend using it while you can. It involves a more manual process than Movetodon, but it is better than nothing.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-14-february-2023\">\n        <a href=\"#updated-14-february-2023\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"14 Feb 2023 10:11:50 AM PST\">\n            <i>14 February 2023</i>\n        </small>\n    </h5>\n    \n<p>It looks like Twitter’s <a href=\"https://www.cnbc.com/2023/02/08/twitter-daily-limit-error-prevents-users-from-posting.html\">attempted API changes</a> did not <a href=\"https://arstechnica.com/tech-policy/2023/02/twitter-experiencing-international-outages-most-users-cant-tweet-or-dm/\">turn out well</a>. It is truly comical how disorganized and chaotic the company seems to be. On February 8, I received an email from Zapier Support stating they anticipate that any Zap using the Twitter integration will stop working. Then on February 10, Zapier Support sent a second email confirming that Zaps will continue to work:</p>\n\n<blockquote>\n  <p>Our team has been working to ensure your Zaps continue working regardless of Twitter’s unexpected API changes. We’re happy to announce that we’ve found a solution to keep your Twitter Zaps running for the time being. No action is required from you at this time.</p>\n</blockquote>\n\n<p>It’s unclear if Twitter conceded or came to an agreement with Zapier (and, presumably other services like IFTTT). Or, perhaps Zapier is using an unsanctioned workaround, like extracting the API keys from the Twitter mobile apps. (lolz)</p>\n\n<p>Anyway — for now, that’s good news! This means my blog posts should continue to be tweeted automatically. I still have no plans to engage with the service beyond this.</p>\n\n</div>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-03-april-2023\">\n        <a href=\"#updated-03-april-2023\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"03 Apr 2023 08:06:16 PM PDT\">\n            <i>03 April 2023</i>\n        </small>\n    </h5>\n    \n<p>You may have noticed that my new blog posts and updated posts are still being tweeted automatically. A few readers have mistaken this automation as me returning to Twitter, so I want to be clear: <strong>I am not returning to Twitter</strong>. I have no plans to continue to use the service beyond the automation I have configured with Zapier, which is why I’m writing this update today.</p>\n\n<p>Twitter has officially <a href=\"https://twittercommunity.com/t/announcing-new-access-tiers-for-the-twitter-api/188728\">announced</a> their new API changes. See also: Michael Tsai’s <a href=\"https://mjtsai.com/blog/2023/04/03/new-twitter-api-tiers/\">roundup</a>. It is unclear if my automation via Zapier will break at the end of the month, or if they will start charging me for Twitter integration. Either way, I don’t plan on fixing it nor paying for it.</p>\n\n<p>Let’s hope it keeps working. Otherwise, you know where else to find me.</p>\n\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2023/02/06/goodbye-twitter/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2022/01/16/screen-time-is-drunk/#updated-14-february-2023",
            "url": "https://www.jessesquires.com/blog/2022/01/16/screen-time-is-drunk/",
            "title": "[Updated] Screen Time is drunk",
            "date_published": "2023-02-14T10:28:59-08:00",
            "date_modified": "2023-02-14T10:28:59-08:00",
            "summary": "<p>Speaking of <a href=\"/blog/2022/01/11/ios-app-library-is-drunk/\">drunk software</a> and <a href=\"/blog/2022/01/16/the-best-and-the-worst/\">not being in service to our possessions</a>, Screen Time on iOS and macOS has been shockingly buggy for me lately. It reports that I spent over 22 hours on my devices in a single day last week, and nearly 10 hours on another day this week. In both instances, a significant portion of the usage is supposedly occurring after midnight.</p>\n\n",
            "tags": [
                "ios","macos","apple","screen-time","bugs",
                "software-dev"
            ],
            "content_html": "<p>Speaking of <a href=\"/blog/2022/01/11/ios-app-library-is-drunk/\">drunk software</a> and <a href=\"/blog/2022/01/16/the-best-and-the-worst/\">not being in service to our possessions</a>, Screen Time on iOS and macOS has been shockingly buggy for me lately. It reports that I spent over 22 hours on my devices in a single day last week, and nearly 10 hours on another day this week. In both instances, a significant portion of the usage is supposedly occurring after midnight.</p>\n\n<!--excerpt-->\n\n<p>This has happened to be a number of times now, and it is always a web address that is the culprit for making up the excessive time spent. See the screenshot below. Apparently I was going wild, nonstop from midnight until just before 6pm.</p>\n\n<div class=\"row\">\n    <div class=\"col\"></div>\n    <div class=\"d-flex justify-content-center col-12  col-sm-8 col-md-6 \">\n        <figure>\n            <img class=\"img-thumbnail img-fluid\" src=\"/img/blog/screen-time-1.jpg\" title=\"Screen Time screenshot\" alt=\"Screen Time screenshot\" />\n            <div class=\"row\">\n                <div class=\"col-12 d-flex justify-content-center\">\n                \n                    <figcaption>\n                        <p><small class=\"text-muted text-center\">\n                            Buggy Screen Time usage report \n                        </small></p>\n                    </figcaption>\n                \n                </div>\n            </div>\n        </figure>\n    </div>\n    <div class=\"col\"></div>\n</div>\n\n<p>It is quite clearly a bug, as evidenced by the per-app breakdown that Screen Time provides. It reports that I visited <code class=\"language-plaintext highlighter-rouge\">http://hexedbits.localhost</code> for over 18 hours, while simultaneously reporting that I used Safari for only 44 minutes. I <em>was</em> doing some local web development for <a href=\"https://hexedbits.com\">hexedbits.com</a> on my Mac that day using Safari, but certainly <em>not</em> for 18 hours.</p>\n\n<div class=\"row\">\n    <div class=\"col\"></div>\n    <div class=\"d-flex justify-content-center col-12  col-sm-8 col-md-6 \">\n        <figure>\n            <img class=\"img-thumbnail img-fluid\" src=\"/img/blog/screen-time-2.jpg\" title=\"Screen Time screenshot\" alt=\"Screen Time screenshot\" />\n            <div class=\"row\">\n                <div class=\"col-12 d-flex justify-content-center\">\n                \n                    <figcaption>\n                        <p><small class=\"text-muted text-center\">\n                            Screen Time showing inconsistent usage data \n                        </small></p>\n                    </figcaption>\n                \n                </div>\n            </div>\n        </figure>\n    </div>\n    <div class=\"col\"></div>\n</div>\n\n<p>For yesterday’s usage, Screen Time similarly reports that I visited one website for over 8 hours though used Safari for only 39 minutes. I remember the web page, which I only opened briefly on my phone. I’m not a “keep my tabs open” kind of guy. All tabs get closed by the end of every day, which makes this even more peculiar. I don’t know how Screen Time works under-the-hood, but I assume part of it is simply tracking the active processes. And so, my best guess is that Safari — on iOS and macOS — has a leak. In any case, you would think that Screen Time would be able to intelligently identity and throw out obviously bad data.</p>\n\n<p>It’s all quite unfortunate, because I feel like I can’t trust <em>any</em> of the Screen Time reports now. This feature has helped me catch when I’m spending too much time on my devices, which usually means I’m feeling shitty. I suppose it’s all kind of ironic — a device telling me when I’ve used it too much, even though my mental health is obviously deteriorating from said usage, and I should just fucking go outside.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-14-february-2023\">\n        <a href=\"#updated-14-february-2023\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"14 Feb 2023 10:28:59 AM PST\">\n            <i>14 February 2023</i>\n        </small>\n    </h5>\n    \n<p>I’m disappointed but not surprised to report that Screen Time is still drunk. Over a year later and this bug persists. Like before, the issue is with Safari and the web. This supports my hypothesis that there must be a memory leak or something in the tracking code.</p>\n\n<p>In this case, my Screen Time report is even more absurd than before. It thinks I spent <strong>over 24 hours</strong> each day on Friday, Saturday, and Sunday looking at a screen — specifically using github.com for a weekly total of 85 hours. See the screenshot below. There are 168 hours in a week, yet Screen Time thinks I was using a device for nearly 113 of them.</p>\n\n<p>It’s a shame, because this bug leaves Screen Time mostly useless for me. In particular, because these obviously erroneous reports skew historical data and weekly comparisons. If I’m trying to reduce my Screen Time, this bug prevents me from accurately tracking that. And isn’t that the entire point of Screen Time?</p>\n\n</div>\n\n<div class=\"row\">\n    <div class=\"col\"></div>\n    <div class=\"d-flex justify-content-center col-12  col-sm-8 col-md-6 \">\n        <figure>\n            <img class=\"img-thumbnail img-fluid\" src=\"/img/blog/screen-time-3.jpg\" title=\"Screen Time screenshot\" alt=\"Screen Time screenshot\" />\n            <div class=\"row\">\n                <div class=\"col-12 d-flex justify-content-center\">\n                \n                    <figcaption>\n                        <p><small class=\"text-muted text-center\">\n                            UPDATE: Screen Time showing usage of over 24 hours in a day \n                        </small></p>\n                    </figcaption>\n                \n                </div>\n            </div>\n        </figure>\n    </div>\n    <div class=\"col\"></div>\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2022/01/16/screen-time-is-drunk/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2022/12/14/simctrl-status_bar-broken/#updated-14-december-2022",
            "url": "https://www.jessesquires.com/blog/2022/12/14/simctrl-status_bar-broken/",
            "title": "[Updated] Workaround: simctl status_bar broken for iOS 16 simulators",
            "date_published": "2022-12-14T20:45:36-08:00",
            "date_modified": "2022-12-14T20:45:36-08:00",
            "summary": "<p>Xcode 11 shipped with <code class=\"language-plaintext highlighter-rouge\">simctl status_bar</code>, a tool to override the status bar values in the simulator so you can take perfect screenshots. I’ve written about this tool before <a href=\"/blog/2019/09/26/overriding-status-bar-settings-ios-simulator/\">here</a>, <a href=\"/blog/2019/09/30/automating-simctl-status-bar/\">here</a>, and <a href=\"/blog/2020/04/13/fully-automating-perfect-status-bar-overrides-for-ios-simulators/\">here</a>. Unfortunately, <code class=\"language-plaintext highlighter-rouge\">simctl status_bar</code> is broken in Xcode 14 with the iOS 16 simulators.</p>\n\n",
            "tags": [
                "series-perfect-status-bars","ios","xcode","nine41",
                "software-dev"
            ],
            "content_html": "<p>Xcode 11 shipped with <code class=\"language-plaintext highlighter-rouge\">simctl status_bar</code>, a tool to override the status bar values in the simulator so you can take perfect screenshots. I’ve written about this tool before <a href=\"/blog/2019/09/26/overriding-status-bar-settings-ios-simulator/\">here</a>, <a href=\"/blog/2019/09/30/automating-simctl-status-bar/\">here</a>, and <a href=\"/blog/2020/04/13/fully-automating-perfect-status-bar-overrides-for-ios-simulators/\">here</a>. Unfortunately, <code class=\"language-plaintext highlighter-rouge\">simctl status_bar</code> is broken in Xcode 14 with the iOS 16 simulators.</p>\n\n<!--excerpt-->\n\n<p>I noticed this bug in Xcode 14 when attempting to use my utility, <a href=\"https://github.com/jessesquires/nine41\">Nine41</a> to prepare for taking screenshots for an app. The tool ran without reporting any issues, yet the status bars in the simulator did not update. I thought something might have changed in Xcode 14, so I invoked <code class=\"language-plaintext highlighter-rouge\">simctl status_bar</code> manually — still nothing happened. As an extra sanity check, I also tried using <a href=\"https://simgenie.app\">Sim Genie</a>, which also failed.</p>\n\n<p>This is a new regression with Xcode 14, but it only affects the iOS 16 simulators. I tested again with yesterday’s release of <a href=\"https://developer.apple.com/documentation/xcode-release-notes/xcode-14_2-release-notes\">Xcode 14.2</a> and the issue has not been fixed. If you run your app using the iOS 15 or earlier simulators, then <code class=\"language-plaintext highlighter-rouge\">simctl status_bar</code> (and <a href=\"https://github.com/jessesquires/nine41\">Nine41</a>) will work correctly. I have no idea what caused the regression, but perhaps it has something to do with the <a href=\"https://developer.apple.com/documentation/widgetkit/dynamicisland\">Dynamic Island</a> on iOS 16?</p>\n\n<p>Luckily, you do not have to open Xcode 13 just to take screenshots. However, if your app’s minimum deployment target is iOS 16, then you currently have no choice but to take screenshots with ugly status bars.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-14-december-2022\">\n        <a href=\"#updated-14-december-2022\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"14 Dec 2022 08:45:36 PM PST\">\n            <i>14 December 2022</i>\n        </small>\n    </h5>\n    \n<p>Thanks to reader Neil Macy for <a href=\"https://mastodon.social/@neilgmacy/109515484181675167\">letting me know</a> that it is actually only the iOS 16.1 and 16.2 simulators that are broken. I’ve confirmed that using the iOS 16.0 simulator does work! This means you can still take perfect screenshots with a minimum deployment target of iOS 16. However, you will need to work around any features that are only available on 16.1 or 16.2 until this is fixed. This confirms my suspicion that the regression is related to the Dynamic Island — or at least the new devices released this fall.</p>\n\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2022/12/14/simctrl-status_bar-broken/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2022/01/13/imessage-spam-and-reporting-abuse/#updated-08-november-2022",
            "url": "https://www.jessesquires.com/blog/2022/01/13/imessage-spam-and-reporting-abuse/",
            "title": "[Updated] iMessage spam and reporting abuse: How many taps does it take to block a bad actor?",
            "date_published": "2022-11-08T11:11:16-08:00",
            "date_modified": "2022-11-08T11:11:16-08:00",
            "summary": "<p>iMessage is <a href=\"https://mjtsai.com/blog/2022/01/11/blue-bubble-envy-is-real/\">in the news again</a> recently with a <a href=\"https://pxlnv.com/linklog/wsj-green-bubbles/\">revival of years-old stories</a> about “green bubbles” versus “blue bubbles” — and the social dynamics among teens who prefer blue bubbles while ostracizing their peers with green bubbles. There’s a lot to like and dislike about iMessage, but one thing that amazes me is that there is still no way to easily report abuse and the process for blocking spam is needlessly difficult.</p>\n\n",
            "tags": [
                "apple","imessage","tech",
                "software-dev"
            ],
            "content_html": "<p>iMessage is <a href=\"https://mjtsai.com/blog/2022/01/11/blue-bubble-envy-is-real/\">in the news again</a> recently with a <a href=\"https://pxlnv.com/linklog/wsj-green-bubbles/\">revival of years-old stories</a> about “green bubbles” versus “blue bubbles” — and the social dynamics among teens who prefer blue bubbles while ostracizing their peers with green bubbles. There’s a lot to like and dislike about iMessage, but one thing that amazes me is that there is still no way to easily report abuse and the process for blocking spam is needlessly difficult.</p>\n\n<!--excerpt-->\n\n<p>At this point, I get regular iMessage spam and junk on a weekly or monthly basis. It is nearly as bad as email, although all modern email clients have sophisticated blocking and filtering tools — so I rarely get bothered by email spam or phishing. Aside from the absence of tools to automate blocking and report abuse, iMessage is significantly worse than email ever was because I always <em>receive a notification in real time</em>. Since iMessage is my primary messaging app, I cannot turn off notifications.</p>\n\n<div class=\"row\">\n    <div class=\"col\"></div>\n    <div class=\"d-flex justify-content-center col-12  col-sm-8 col-md-6 \">\n        <figure>\n            <img class=\"img-thumbnail img-fluid\" src=\"/img/blog/imessage-spam-1.jpg\" title=\"iMessage spam screenshot\" alt=\"iMessage spam screenshot\" />\n            <div class=\"row\">\n                <div class=\"col-12 d-flex justify-content-center\">\n                \n                    <figcaption>\n                        <p><small class=\"text-muted text-center\">\n                            An iMessage spam message that I received this morning \n                        </small></p>\n                    </figcaption>\n                \n                </div>\n            </div>\n        </figure>\n    </div>\n    <div class=\"col\"></div>\n</div>\n\n<p>When you receive iMessage spam, you might think there’s a quick action available in the swipe menu to block or report. Sadly, no. You can only delete, silence notifications, or pin it — none of which prevent future junk from this spammer. Also, why would anyone want to pin this thread? Shouldn’t iMessage be smart enough to offer an option to report abuse here instead?</p>\n\n<div class=\"row\">\n    <div class=\"col\"></div>\n    <div class=\"d-flex justify-content-center col-12  col-sm-8 col-md-6 \">\n        <figure>\n            <img class=\"img-thumbnail img-fluid\" src=\"/img/blog/imessage-spam-2.jpg\" title=\"iMessage swipe actions\" alt=\"iMessage swipe actions\" />\n            <div class=\"row\">\n                <div class=\"col-12 d-flex justify-content-center\">\n                \n                    <figcaption>\n                        <p><small class=\"text-muted text-center\">\n                            iMessage swipe actions \n                        </small></p>\n                    </figcaption>\n                \n                </div>\n            </div>\n        </figure>\n    </div>\n    <div class=\"col\"></div>\n</div>\n\n<p>Or, perhaps you could long-press to bring up the context menu options? Still, nothing.</p>\n\n<div class=\"row\">\n    <div class=\"col\"></div>\n    <div class=\"d-flex justify-content-center col-12  col-sm-8 col-md-6 \">\n        <figure>\n            <img class=\"img-thumbnail img-fluid\" src=\"/img/blog/imessage-spam-3.jpg\" title=\"iMessage context menu options\" alt=\"iMessage context menu options\" />\n            <div class=\"row\">\n                <div class=\"col-12 d-flex justify-content-center\">\n                \n                    <figcaption>\n                        <p><small class=\"text-muted text-center\">\n                            iMessage context menu options \n                        </small></p>\n                    </figcaption>\n                \n                </div>\n            </div>\n        </figure>\n    </div>\n    <div class=\"col\"></div>\n</div>\n\n<p>How can it be that in 2022 iMessage provides no way to report abuse like every other social media platform? (And yes, we should consider iMessage a <a href=\"https://daringfireball.net/2016/10/imessage_stickiness\">social media platform</a>. It has <a href=\"https://techcrunch.com/2012/06/11/imessage-has-more-than-140m-users-and-has-150b-messages-over-1b-a-day/\">hundreds of millions of users</a> that send <a href=\"https://appleinsider.com/articles/13/01/23/apple-sees-2b-imessages-sent-every-day-from-half-a-billion-ios-devices\">billions of messages</a> daily.) According to <a href=\"https://support.apple.com/en-us/HT201229\">this Apple support document</a>, you should see a “Report Junk” button when viewing messages from unknown senders. However, I <strong>have never seen</strong> that button. (See screenshots below.) That button never appears for unknown contacts that send me junk. Is this a bug or is that support document outdated?</p>\n\n<p>iMessage does offer a setting to “Filter Unknown Senders” but the only benefit is that it will silence notifications for unknown contacts, and it introduces additional drawbacks. In addition to making the UI <em>even more clunky</em> and difficult to navigate, it implements wholesale filtering of <em>all</em> unknown numbers. This is <em>not</em> what most people want or need. In today’s world there are dozens of notification and reservation systems that send texts — restaurant reservations, package delivery notifications, haircut appointment reminders, SMS-based 2-factor authentication systems, and so on. If you “Filter Unknown Senders”, then you don’t get notifications from any of these systems, most of which are very time-sensitive! This setting is too naive to be useful in any way.</p>\n\n<div class=\"row\">\n    <div class=\"col\"></div>\n    <div class=\"d-flex justify-content-center col-12  col-sm-8 col-md-6 \">\n        <figure>\n            <img class=\"img-thumbnail img-fluid\" src=\"/img/blog/imessage-spam-5.jpg\" title=\"iMessage Support Doc\" alt=\"iMessage Support Doc\" />\n            <div class=\"row\">\n                <div class=\"col-12 d-flex justify-content-center\">\n                \n                    <figcaption>\n                        <p><small class=\"text-muted text-center\">\n                            Apple Support claims there is a \"Report Junk\" option  / <a href=\"https://support.apple.com/en-us/HT201229\" target=\"_blank\" rel=\"noreferrer\">Source</a> \n                        </small></p>\n                    </figcaption>\n                \n                </div>\n            </div>\n        </figure>\n    </div>\n    <div class=\"col\"></div>\n</div>\n\n<p>The only recourse I have is to block bad actors manually every single time I receive junk. Thankfully Apple provides that functionality. The only problem is that the option to block a message is buried so deep in the UI, I bet most users never find it. How many taps does it take to block a contact on iMessage? <strong>Five!</strong> Also known as way too fucking many.</p>\n\n<div class=\"row\">\n    <div class=\"col\"></div>\n    <div class=\"d-flex justify-content-center col-12 \">\n        <figure>\n            <img class=\"img-thumbnail img-fluid\" src=\"/img/blog/imessage-spam-4.jpg\" title=\"Steps to block an iMessage sender\" alt=\"Steps to block an iMessage sender\" />\n            <div class=\"row\">\n                <div class=\"col-12 d-flex justify-content-center\">\n                \n                    <figcaption>\n                        <p><small class=\"text-muted text-center\">\n                            Steps to block an iMessage sender \n                        </small></p>\n                    </figcaption>\n                \n                </div>\n            </div>\n        </figure>\n    </div>\n    <div class=\"col\"></div>\n</div>\n\n<ol>\n  <li>Tap to view the message, even though you already know it is spam from the contact info and preview text. <strong>Note:</strong> there is <em>no</em> “Report Junk” button in the screenshot above.</li>\n  <li>Tap the sender’s contact photo/info in the nav bar.</li>\n  <li>Tap “Info” on the sender’s contact card.</li>\n  <li>Tap “Block this Caller”.</li>\n  <li>Tap “Block Contact”.</li>\n</ol>\n\n<p>In fact, claiming there are only 5 steps is actually quite generous. After you block a contact, you then have to unwind from all these views to <em>get back</em> to your iMessage inbox, which requires 3 more taps. <strong>And then</strong> you have to <em>manually</em> delete the message, which requires another 2 taps. In total, that’s 10 fucking taps to block a spammer and delete their message. Absolute madness is an understatement. Who designed this?!</p>\n\n<p>iMessage should offer a quick and easy way to block (and optionally report) bad actors. Rather than give me an option to pin a thread for an unknown (and obvious spammer) contact, it should offer the option to block the sender and report abuse to Apple. Apple could then maintain a list of known bad actors to help curb abuse of its platform.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-08-november-2022\">\n        <a href=\"#updated-08-november-2022\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"08 Nov 2022 11:11:16 AM PST\">\n            <i>08 November 2022</i>\n        </small>\n    </h5>\n    \n<p>Good news! Beginning in iOS 16, some of these issues have been addressed. Now when you delete a message thread from any unknown sender, there is a new option to “Delete and Report Junk” and the “Report Junk” button always appears in threads from unknown senders. (See the screenshot below.) This is great.</p>\n\n<p>Unfortunately, blocking a contact still requires the litany of steps listed above.</p>\n\n</div>\n\n<div class=\"row\">\n    <div class=\"col\"></div>\n    <div class=\"d-flex justify-content-center col-12  col-sm-8 col-md-6 \">\n        <figure>\n            <img class=\"img-thumbnail img-fluid\" src=\"/img/blog/imessage-spam-6.jpg\" title=\"iMessage on iOS 16\" alt=\"iMessage on iOS 16\" />\n            <div class=\"row\">\n                <div class=\"col-12 d-flex justify-content-center\">\n                \n                    <figcaption>\n                        <p><small class=\"text-muted text-center\">\n                            iMessage on iOS 16, Delete and Report Junk \n                        </small></p>\n                    </figcaption>\n                \n                </div>\n            </div>\n        </figure>\n    </div>\n    <div class=\"col\"></div>\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2022/01/13/imessage-spam-and-reporting-abuse/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2022/03/25/my-website-disappeared-from-bing-and-duckduckgo/#updated-28-july-2022",
            "url": "https://www.jessesquires.com/blog/2022/03/25/my-website-disappeared-from-bing-and-duckduckgo/",
            "title": "[Updated] My website disappeared from Bing and DuckDuckGo",
            "date_published": "2022-07-28T12:36:54-07:00",
            "date_modified": "2022-07-28T12:36:54-07:00",
            "summary": "<p>I discovered earlier this week that my website is no longer being indexed by <a href=\"https://www.bing.com/search?q=url%3ahttps%3a%2f%2fwww.jessesquires.com\">Bing</a> and <a href=\"https://duckduckgo.com/?q=site%3Ahttps%3A%2F%2Fwww.jessesquires.com\">DuckDuckGo</a>. In fact, it appears that it has been deliberately removed from their search indexes. On Bing, rather than display a <em>“no results”</em> message, it displays a <em>“Some results have been removed”</em> message, which is very concerning. Notably, however, <a href=\"https://www.google.com/search?q=site%3Awww.jessesquires.com\">Google search</a> is working fine.</p>\n\n",
            "tags": [
                "web","bing","duckduckgo","search","google",
                "software-dev"
            ],
            "content_html": "<p>I discovered earlier this week that my website is no longer being indexed by <a href=\"https://www.bing.com/search?q=url%3ahttps%3a%2f%2fwww.jessesquires.com\">Bing</a> and <a href=\"https://duckduckgo.com/?q=site%3Ahttps%3A%2F%2Fwww.jessesquires.com\">DuckDuckGo</a>. In fact, it appears that it has been deliberately removed from their search indexes. On Bing, rather than display a <em>“no results”</em> message, it displays a <em>“Some results have been removed”</em> message, which is very concerning. Notably, however, <a href=\"https://www.google.com/search?q=site%3Awww.jessesquires.com\">Google search</a> is working fine.</p>\n\n<!--excerpt-->\n\n<p>I triple-checked all of my server and website configurations and settings — DNS records, <code class=\"language-plaintext highlighter-rouge\">robots.txt</code>, occurrences of the <a href=\"https://developers.google.com/search/docs/advanced/crawling/block-indexing\"><code class=\"language-plaintext highlighter-rouge\">noindex</code> meta tag</a> (there are none), etc. Everything looks fine. No issues. No errors. And again, <a href=\"https://www.google.com/search?q=site%3Awww.jessesquires.com\">Google search</a> seems to be working great, which indicates that there are no problems on my end.</p>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<p>My website does not appear in any searches on Bing and DuckDuckGo, nor does a search for my name produce any relevant results. For contrast, my website is the first hit on Google when you search for my name. I’m not the kind of person who regularly searches my own name, but I discovered the issue when attempting to find one of my old blog posts. The <a href=\"/search/\">search page</a> on my site <a href=\"/blog/2018/02/25/replacing-google-with-duckduckgo/\">is implemented</a> using a basic HTML form that is hooked up to DuckDuckGo’s <a href=\"https://help.duckduckgo.com/duckduckgo-help-pages/results/syntax/\">site-specific search</a> functionality. Much to my surprise, when I tried to <a href=\"/search/\">search my site</a> for a blog post there were <em>zero results</em>.</p>\n\n<p>Normally, when <a href=\"/search/\">searching</a> from my site you will be redirected to DuckDuckGo with the search automatically populated with <code class=\"language-plaintext highlighter-rouge\">&lt;your search terms&gt; site:jessesquires.com</code>, and you will be able to see all pages on my site containing your search query. I know for certain that this was working at some point in the past.</p>\n\n<p>You can also use the <code class=\"language-plaintext highlighter-rouge\">site:</code> operator to verify that your site is indexed. For example, <code class=\"language-plaintext highlighter-rouge\">site:jessesquires.com</code>. You can see that this currently <a href=\"https://duckduckgo.com/?q=site%3Ahttps%3A%2F%2Fwww.jessesquires.com\">produces zero results</a>, indicating the site is not indexed at all. To see a working example, we can check Michael Tsai’s blog <a href=\"https://duckduckgo.com/?q=site%3Ahttps%3A%2F%2Fmjtsai.com\">using <code class=\"language-plaintext highlighter-rouge\">site:mjtsai.com</code></a>. So, aside from the primary issue of no longer being indexed, this means search on my site is also broken.</p>\n\n<p>Similar to DuckDuckGo’s <code class=\"language-plaintext highlighter-rouge\">site:</code> operator, Bing provides a <code class=\"language-plaintext highlighter-rouge\">url:</code> operator which indicates whether or not your site is indexed. Again, we can try Michael’s blog <a href=\"https://www.bing.com/search?q=url%3Amjtsai.com\">using <code class=\"language-plaintext highlighter-rouge\">url:mjtsai.com</code></a>, which produces the expected results. If you search for <a href=\"https://www.bing.com/search?q=url%3AthisIsNotARealWebsite.com\">nonsense like <code class=\"language-plaintext highlighter-rouge\">url:thisIsNotARealWebsite.com</code></a>, you’ll see a <em>“There are no results for…”</em> message. However, when you <a href=\"https://www.bing.com/search?q=url%3Ajessesquires.com\">search for my website</a>, you see a <em>“Some results have been removed”</em> message that links to <a href=\"https://support.microsoft.com/en-us/topic/how-bing-delivers-search-results-d18fc815-ac37-4723-bc67-9229ce3eb6a3\">this support page</a> about how Bing results are delivered and why results might be removed. I don’t know, but that seems <em>pretty bad</em>, right? Like, <em>my site was <strong>removed?!</strong></em></p>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<p>You might be wondering what the hell does Bing have to do with any of this? (Also, do people actually use Bing?) Unlike other major search engines, DuckDuckGo <a href=\"https://okeyravi.com/duckduckgo-search-engine-submission/\">does not provide</a> any kind of webmaster tools, like <a href=\"https://search.google.com/search-console/about\">Google Search Console</a> and <a href=\"https://www.bing.com/webmasters/about\">Bing Webmaster</a>. Thus, you <a href=\"https://www.quora.com/How-do-I-submit-my-URL-to-DuckDuckGo?share=1\">cannot can submit URLs</a> and sitemaps directly to DuckDuckGo, nor can you request indexing of pages. Instead, DuckDuckGo gets results <a href=\"https://help.duckduckgo.com/results/sources/\">from various other sources</a>, including Bing and specifically <strong>not</strong> from Google. According to various online forums, the best way to ensure your site gets indexed by DuckDuckGo is to submit it to Bing and Yandex.</p>\n\n<p>Thus, my current theory is that because my site was (seemingly) removed from Bing, it was therefore subsequently removed from DuckDuckGo. So now the question to answer is why was my website removed from Bing?</p>\n\n<p>I had a Bing Webmaster account at some point, which I used to submit my site. I remember testing it and seeing results on Bing, as well as DuckDuckGo, but this was years ago. However, I don’t know what happened to my account. I might have deleted it, but I don’t remember. Or, maybe Microsoft deleted it after a certain period of time with no activity. I basically never logged in to it after submitting my site. If my defunct Bing Webmaster account was in fact deleted, perhaps that resulted in my website being removed from the search index? Otherwise, this appears like a deliberate removal for some kind of violation of Bing’s terms? I have no idea.</p>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<p>So without a working Bing Webmaster account, I decided to create a new one. This time I logged in using my Google account. Bing Webmaster allows you to login with Google and will conveniently import all of your sites from Google Search Console. This had the effect of basically reinstating my old account, with my sites ready to go. I resubmitted my sitemaps, did a site scan, looked at the SEO reports, and checked on the search performance results in Bing Webmaster. Everything seemed ok.</p>\n\n<p>My traffic on Bing is rather low compared to Google. In the last month, Bing reports only a handful of clicks and impressions while Google reports over 10,000 clicks and over 368,000 impressions. I assume that’s because of popularity and preference amongst most software developers? I’m not sure. Anyway, all the crawl results look fine, seemingly indicating no problems with the site or Bing’s ability to index it. In fact, Bing reports that it has successfully indexed hundreds of pages on my site. The crawl information also dates back to 2017 — so I’m not crazy, this was working before. Furthermore, all results and metrics on Google Search Console look good and indicate there are no problems.</p>\n\n<p>But then, I noticed on Bing Webmaster that all search metrics dropped to zero starting March 11. Unfortunately, the Webmaster tools don’t provide any additional information or insight into why this happened. Again, the theory is that my website was deliberately removed from Bing. I also checked the “Copyright Removal Notices” page in Webmaster, which is where you can see <em>“URLs have been flagged and are liable to be removed from Bing search results”</em>, and there are <strong>zero</strong> notices listed.</p>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<p>This is quite the predicament. Seemingly, everything is fine. Yet, for some reason my website has been delisted from Bing. I’m perplexed. But to be honest, I’m not that concerned about Bing — I really just want my site indexed by DuckDuckGo again.</p>\n\n<p>Out of desperation to get my site back on DuckDuckGo, I debased myself and created a Yandex Search Webmaster account too. I submitted my sitemaps there, requested indexing, etc. Everything on Yandex looks good, and a <code class=\"language-plaintext highlighter-rouge\">site:</code> search already <a href=\"https://yandex.com/search/?text=site%3Ajessesquires.com\">produces results</a>. I’m hoping the indexing data from Yandex will make it over to DuckDuckGo eventually.</p>\n\n<p>In the meantime, I’ve submitted a support request with Bing Webmaster to inquire about the apparent removal of my site. When I hear back, I will update this post. Otherwise, all I can do is wait and periodically check if results start to appear again.</p>\n\n<p>If you have any advice, please let me know!</p>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<p>There’s one last thing. The issue described above was also happening with another one of my websites, <a href=\"https://www.hexedbits.com\">hexedbits.com</a>. Google search was working, but not Bing and DuckDuckGo. However, Bing showed a standard <em>“no results”</em> message for HexedBits. Since submitting the URL and sitemap to Bing and Yandex, content from hexedbits.com is now appearing correctly on Bing, DuckDuckGo, and Yandex. This really has me wondering what could have gone wrong. Hopefully, the indexing issues for jessesquires.com will be resolved soon.</p>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<p>All of this emphasizes the frustrating lack of transparency around search engines and how they work. It is infuriating to be essentially helpless trying to debug and resolve issues with your site not being properly indexed. I will forever lament that search was not a core component of the Internet itself, but was instead a feature that private corporations had to bolt on top.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-01-april-2022\">\n        <a href=\"#updated-01-april-2022\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"01 Apr 2022 05:19:24 PM PDT\">\n            <i>01 April 2022</i>\n        </small>\n    </h5>\n    <p>Good news! Well, sort of. I heard back from Bing Support. They confirmed that they received my report and have “escalated this request to the engineering team.” <em>[Editor’s note: not April fools.]</em></p>\n\n<p>Also of note, <a href=\"https://mobile.twitter.com/kattrali/status/1507666040561582086\">Delisa pointed out</a> that this might be related to the <a href=\"https://mjtsai.com/blog/2022/03/14/duckduckgo-will-down-rank-russian-disinformation-sites/\">algorithmic changes that DuckDuckGo has been making lately</a> and noted that queries for dev topics have been noticeably missing things over the past few weeks.</p>\n\n<p>In addition to algorithmic changes, DuckDuckGo has also <a href=\"https://www.protocol.com/bulletins/duckduckgo-yandex-ukraine\">“paused” its relationship with Yandex</a>.</p>\n\n</div>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-20-april-2022\">\n        <a href=\"#updated-20-april-2022\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"20 Apr 2022 03:14:46 PM PDT\">\n            <i>20 April 2022</i>\n        </small>\n    </h5>\n    \n<p>Reader <a href=\"https://github.com/nicolastjt\">Nicolas Magand</a> left <a href=\"https://github.com/jessesquires/jessesquires.com/issues/163\">a comment</a> that they are also having the same problem, and shared <a href=\"https://www.linkedin.com/pulse/bing-exploit-allows-website-owners-deindex-chase-watts/\">this blog post</a>, <em>Bing Exploit Allows Website Owners to Deindex Competitors</em>. The gist of the article is that Bing has an inferior search algorithm that is subject to negative SEO campaigns from malicious actors, a problem that Google <em>does not</em> have. The general issue of having your website de-indexed from Bing seems to be much more common than I expected.</p>\n\n<p>To reiterate, I really don’t give a shit about Bing, per se — I just want my site indexed by DuckDuckGo again. But, as noted above, it seems the best (only?) way to do that is through Bing. I sent a follow-up email to Bing Support today to check on the status of my support request.</p>\n\n</div>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-28-july-2022\">\n        <a href=\"#updated-28-july-2022\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"28 Jul 2022 12:36:54 PM PDT\">\n            <i>28 July 2022</i>\n        </small>\n    </h5>\n    \n<p>This post is still being circulated and showing up in Google search for folks, so I want to highlight <a href=\"/blog/2022/07/25/my-website-disappeared-from-bing-and-duckduckgo-part-2/\">my recent follow-up post here</a>.</p>\n\n<p>Also, another reader experiencing this issue emailed me after finding this post via Google. They also <a href=\"https://io.bikegremlin.com/28530/microsoft-bing-serp-gone-overnight/\">published a blog post here</a> detailing their experience.</p>\n\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2022/03/25/my-website-disappeared-from-bing-and-duckduckgo/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2022/04/22/docc-on-github-pages/#updated-25-april-2022",
            "url": "https://www.jessesquires.com/blog/2022/04/22/docc-on-github-pages/",
            "title": "[Updated] Using DocC on GitHub Pages: Pros and Cons",
            "date_published": "2022-04-25T10:27:23-07:00",
            "date_modified": "2022-04-25T10:27:23-07:00",
            "summary": "<p>When <a href=\"/blog/2021/06/29/apple-docc-great-but-useless-for-oss/\">I first wrote about DocC</a>, I lamented the fact that it was incompatible with static hosting on <a href=\"https://pages.github.com\">GitHub Pages</a>. Much has changed since my <a href=\"/blog/2021/06/29/apple-docc-great-but-useless-for-oss/\">last post</a>, so let’s take a fresh look. While there have been many welcome improvements to the tool, there are a few remaining issues that prevent me from adopting it for my open source projects.</p>\n\n",
            "tags": [
                "docc","documentation","github","jazzy","open-source","swift",
                "software-dev"
            ],
            "content_html": "<p>When <a href=\"/blog/2021/06/29/apple-docc-great-but-useless-for-oss/\">I first wrote about DocC</a>, I lamented the fact that it was incompatible with static hosting on <a href=\"https://pages.github.com\">GitHub Pages</a>. Much has changed since my <a href=\"/blog/2021/06/29/apple-docc-great-but-useless-for-oss/\">last post</a>, so let’s take a fresh look. While there have been many welcome improvements to the tool, there are a few remaining issues that prevent me from adopting it for my open source projects.</p>\n\n<!--excerpt-->\n\n<p>My previous post mentioned a few community projects that preceded DocC, but the most popular one — and the one that I use — is <a href=\"https://github.com/realm/jazzy\">Jazzy</a>. Thus, much of my feedback here will be in relation to how DocC compares to Jazzy.</p>\n\n<h3 id=\"the-good\">The good</h3>\n\n<p>As promised at last year’s WWDC, <a href=\"https://www.swift.org/blog/swift-docc/\">DocC is now open source</a>. You can find <a href=\"https://github.com/apple/swift-docc\">the repo here</a>. DocC is bundled with the <a href=\"https://developer.apple.com/documentation/xcode-release-notes/xcode-13_3-release-notes\">Xcode 13.3 release</a>. There’s also a <a href=\"https://github.com/apple/swift-docc-plugin\">Swift Package Manager plugin</a> to generate documentation for your package using DocC, which even includes <a href=\"https://apple.github.io/swift-docc-plugin/documentation/swiftdoccplugin/publishing-to-github-pages/\">a guide for publishing to GitHub Pages</a>. This guide and the <a href=\"https://apple.github.io/swift-docc-plugin/documentation/swiftdoccplugin\">primary docs for the plugin</a> are, of course, generated using DocC. Apple is clearly eating their own dog food. There’s a lot to love about the tool.</p>\n\n<p>The generated docs are beautiful. I love the design and aesthetic. Dark mode is built-in, natch. The generated docs are nearly identical to Apple’s official SDK documentation. Take a look at the <a href=\"https://apple.github.io/swift-docc-plugin/documentation/swiftdoccplugin/\">Swift DocC Plugin docs</a> and compare it to <a href=\"https://developer.apple.com/documentation/uikit\">UIKit</a>. This is great for open source maintainers, because your documentation will fit right in and be immediately familiar to other Apple platform developers.</p>\n\n<p>I <a href=\"/blog/2021/06/29/apple-docc-great-but-useless-for-oss/\">previously wrote</a>: <em>“The integration with Xcode’s Editor and Documentation Viewer is excellent. There is a command-line tool included, <code class=\"language-plaintext highlighter-rouge\">xcodebuild docbuild</code>, which you can invoke from automation scripts. The overall workflow for generating and exporting documentation is built-in to Xcode and its build process, making it very easy to use.”</em> This is still true, but now it also integrates directly with the Swift Package Manager via <a href=\"https://apple.github.io/swift-docc-plugin/documentation/swiftdoccplugin/\">the aforementioned plugin</a>.</p>\n\n<p>Adding the Swift DocC Plugin to your package is easy:</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">let</span> <span class=\"nv\">package</span> <span class=\"o\">=</span> <span class=\"kt\">Package</span><span class=\"p\">(</span>\n    <span class=\"c1\">// name, platforms, products, etc.</span>\n    <span class=\"nv\">dependencies</span><span class=\"p\">:</span> <span class=\"p\">[</span>\n        <span class=\"c1\">// other dependencies</span>\n        <span class=\"o\">.</span><span class=\"nf\">package</span><span class=\"p\">(</span><span class=\"nv\">url</span><span class=\"p\">:</span> <span class=\"s\">\"https://github.com/apple/swift-docc-plugin\"</span><span class=\"p\">,</span> <span class=\"nv\">from</span><span class=\"p\">:</span> <span class=\"s\">\"1.0.0\"</span><span class=\"p\">),</span>\n    <span class=\"p\">],</span>\n    <span class=\"nv\">targets</span><span class=\"p\">:</span> <span class=\"p\">[</span>\n        <span class=\"c1\">// targets</span>\n    <span class=\"p\">]</span>\n<span class=\"p\">)</span>\n</code></pre></div></div>\n\n<p>And then you can write a small script to generate docs for GitHub Pages:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\">#!/bin/zsh</span>\n\nswift package <span class=\"se\">\\</span>\n    <span class=\"nt\">--allow-writing-to-directory</span> ./docs <span class=\"se\">\\</span>\n    generate-documentation <span class=\"nt\">--target</span> MySwiftPackage <span class=\"se\">\\</span>\n    <span class=\"nt\">--disable-indexing</span> <span class=\"se\">\\</span>\n    <span class=\"nt\">--transform-for-static-hosting</span> <span class=\"se\">\\</span>\n    <span class=\"nt\">--hosting-base-path</span> MySwiftPackage <span class=\"se\">\\</span>\n    <span class=\"nt\">--output-path</span> ./docs\n</code></pre></div></div>\n\n<p>I setup an example project and repo to experiment with DocC and GitHub pages. <a href=\"https://github.com/jessesquires/my-swift-package\">You can find it here</a>. For a more in-depth look at hosting on GitHub pages, I recommend <a href=\"https://rhonabwy.com/2022/01/28/hosting-your-swift-library-docs-on-github-pages/\">Joseph Heck’s excellent post</a>.</p>\n\n<p>Finally, you <a href=\"https://apple.github.io/swift-docc-plugin/documentation/swiftdoccplugin/previewing-documentation\">can easily preview</a> your static site with the Swift DocC Plugin.</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>swift package <span class=\"nt\">--disable-sandbox</span> preview-documentation <span class=\"nt\">--target</span> MySwiftPackage\n</code></pre></div></div>\n\n<p>This runs a local web server, and you navigate to <code class=\"language-plaintext highlighter-rouge\">localhost:8000/documentation/MySwiftPackage</code> to preview your docs.</p>\n\n<h3 id=\"the-bad\">The Bad</h3>\n\n<p>Below are the main issues and pain points I experienced while experimenting with DocC as a possible replacement for <a href=\"https://github.com/realm/jazzy\">Jazzy</a>.</p>\n\n<h4 id=\"docc-url-schemes-are-strange\">DocC URL schemes are… strange</h4>\n\n<p>I strongly dislike the URL schemes that DocC produces. My first gripe is, admittedly, purely cosmetic. Consider my example project on GitHub at <a href=\"https://github.com/jessesquires/my-swift-package\"><code class=\"language-plaintext highlighter-rouge\">github.com/jessesquires/my-swift-package</code></a>. Normally, a GitHub Pages site root is hosted at <code class=\"language-plaintext highlighter-rouge\">USERNAME.github.io/REPO_NAME</code>. However, a DocC site changes this to <code class=\"language-plaintext highlighter-rouge\">USERNAME.github.io/REPO_NAME/documentation/PACKAGE_NAME</code>. That is, the path appends <code class=\"language-plaintext highlighter-rouge\">/documentation</code> and <code class=\"language-plaintext highlighter-rouge\">/PACKAGE_NAME</code>. Typically, the repo name and package name are the same, so you end up with an ugly URL like <code class=\"language-plaintext highlighter-rouge\">jessesquires.github.io/MyLibrary/documentation/MyLibrary</code>. I much prefer the shorter, cleaner URLs.</p>\n\n<p>Aside from being ugly, there are some usability issues. Firstly, navigating to the normal GitHub pages root at <code class=\"language-plaintext highlighter-rouge\">jessesquires.github.io/MyLibrary</code> loads a completely blank HTML page (<em>not</em> a 404), which is very confusing. However, navigating to <code class=\"language-plaintext highlighter-rouge\">jessesquires.github.io/MyLibrary/documentation</code> (without appending the final <code class=\"language-plaintext highlighter-rouge\">/MyLibrary</code>) <em>does</em> result in a 404. Sure, you just need to know and link to the correct URL, but it’s weird — especially if you are familiar with GitHub Pages.</p>\n\n<p>To recap:</p>\n\n<ul>\n  <li>The project repo is at: <a href=\"https://github.com/jessesquires/my-swift-package\">https://github.com/jessesquires/my-swift-package</a></li>\n  <li>The GitHub Pages site root is at: <a href=\"https://jessesquires.github.io/my-swift-package/\">https://jessesquires.github.io/my-swift-package/</a></li>\n  <li>The DocC site root is at: <a href=\"https://jessesquires.github.io/my-swift-package/documentation/my_swift_package/\">https://jessesquires.github.io/my-swift-package/documentation/my_swift_package/</a></li>\n</ul>\n\n<p>For this specific example, I should also note another quirk about DocC — it transforms dashes into underscores. Notice that my repo and package are titled <code class=\"language-plaintext highlighter-rouge\">my-swift-package</code> (with dashes), but the final path component in the DocC URL is <code class=\"language-plaintext highlighter-rouge\">my_swift_package</code> (with underscores). This isn’t terrible, but I was really confused when my site was not working at first. Then I realized that I was using the wrong URL with <code class=\"language-plaintext highlighter-rouge\">my-swift-package</code> at the end instead of <code class=\"language-plaintext highlighter-rouge\">my_swift_package</code>.</p>\n\n<h4 id=\"quirks-with-migrating-from-jazzy\">Quirks with migrating from Jazzy</h4>\n\n<p>The other issue with this URL scheme is migrating existing projects. Currently, all of my Apple platform projects use <a href=\"https://github.com/realm/jazzy\">Jazzy</a> and host docs at <code class=\"language-plaintext highlighter-rouge\">jessesquires.github.io/PROJECT_NAME</code>. For example, <a href=\"https://github.com/jessesquires/Foil\">Foil</a> which has docs at <a href=\"https://jessesquires.github.io/Foil/\"><code class=\"language-plaintext highlighter-rouge\">jessesquires.github.io/Foil</code></a>. If I immediately switched over to using DocC, I would break the documentation links for all of my projects — an Internet sin, to be sure. But it’s actually worse than that, because with DocC the root of the GitHub Pages site is just a blank page, as mentioned above.</p>\n\n<p>The obvious workaround here is to add a redirect. But because we’re in a static hosting environment, we can only accomplish this with <code class=\"language-plaintext highlighter-rouge\">meta</code> tags. This is not ideal, but it works.</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"cp\">&lt;!DOCTYPE html&gt;</span>\n<span class=\"nt\">&lt;html</span> <span class=\"na\">lang=</span><span class=\"s\">\"en\"</span><span class=\"nt\">&gt;</span>\n  <span class=\"nt\">&lt;head&gt;</span>\n    <span class=\"nt\">&lt;title&gt;</span>Redirecting<span class=\"nt\">&lt;/title&gt;</span>\n    <span class=\"c\">&lt;!--\n      Redirect\n      FROM: jessesquires.github.io/my-swift-package\n      TO: jessesquires.github.io/my-swift-package/documentation/my_swift_package\n    --&gt;</span>\n    <span class=\"nt\">&lt;meta</span> <span class=\"na\">http-equiv=</span><span class=\"s\">\"refresh\"</span> <span class=\"na\">content=</span><span class=\"s\">\"0; URL=https://jessesquires.github.io/my-swift-package/documentation/my_swift_package\"</span><span class=\"nt\">&gt;</span>\n    <span class=\"nt\">&lt;link</span> <span class=\"na\">rel=</span><span class=\"s\">\"canonical\"</span> <span class=\"na\">href=</span><span class=\"s\">\"https://jessesquires.github.io/my-swift-package/documentation/my_swift_package\"</span><span class=\"nt\">&gt;</span>\n\n    <span class=\"c\">&lt;!-- existing DocC code... --&gt;</span>\n   <span class=\"nt\">&lt;/head&gt;</span>\n   <span class=\"nt\">&lt;body&gt;</span>\n     <span class=\"c\">&lt;!-- DocC body... --&gt;</span>\n   <span class=\"nt\">&lt;/body&gt;</span>\n<span class=\"nt\">&lt;/html&gt;</span>\n</code></pre></div></div>\n\n<p>The above code should be added to the DocC generated <code class=\"language-plaintext highlighter-rouge\">docs/index.html</code> file. However, there’s a major caveat. You will need to edit the root <code class=\"language-plaintext highlighter-rouge\">docs/index.html</code> <strong>every time</strong> you regenerate your documentation to add this redirect. You could automate adding the redirect as part of the script that generates your documentation, but that’s also not ideal.</p>\n\n<h4 id=\"docc-generation-is-not-stable\">DocC generation is not stable</h4>\n\n<p>This brings me to another issue with DocC. Its documentation generation is not stable. That is, it produces a diff on every single run. With Jazzy, if you generate your docs, commit the changes, and immediately run Jazzy again it will not produce a diff. In contrast, DocC <em>always</em> produces a diff. Specifically, the various JSON files output to <code class=\"language-plaintext highlighter-rouge\">docs/data/...</code> change every single time.</p>\n\n<p>Aside from being a nuisance, this has implications for automation and scripting. Suppose you want to automatically generate documentation via GitHub Actions after every push to <code class=\"language-plaintext highlighter-rouge\">main</code>, and have a bot commit the doc updates. You will quickly accumulate a bunch of unnecessary commits. The ideal scenario would be that documentation generation is stable, and only produces a diff if the documentation has actually changed. Then, in automation scenarios, doc generation could no-op and avoid all these extra commits.</p>\n\n<h4 id=\"docc-local-previews-require-a-web-server\">DocC local previews require a web server</h4>\n\n<p>I mentioned above that DocC docs can be previewed easily via the Swift DocC Plugin, which I appreciate. It is easy and convenient enough. It works. However, it would be nice if it did not require running a web sever to do so. Isn’t this supposed to be a static site?</p>\n\n<p>This gets to the core of why DocC is a difficult drop-in replacement for Jazzy. While static hosting with DocC now <em>works</em>, it was not originally built with this in mind. Thus, it doesn’t behave like you would expect a static site to behave. The <code class=\"language-plaintext highlighter-rouge\">--transform-for-static-hosting</code> flag is really just a hack, a workaround for static hosting environments like GitHub Pages. You cannot simply open <code class=\"language-plaintext highlighter-rouge\">docs/index.html</code> or <code class=\"language-plaintext highlighter-rouge\">docs/documentation/MyLibrary/index.html</code> because your local paths do not match the paths on the server. This is why a local web server is necessary for previews. The result is static hosting feeling like a second-class citizen.</p>\n\n<p>To compare with Jazzy, all you need to do is open <code class=\"language-plaintext highlighter-rouge\">docs/index.html</code> directly in Safari and you can quickly preview your entire site. No web server needed.</p>\n\n<h4 id=\"other-missing-features\">Other missing features</h4>\n\n<p>DocC is missing a handful of useful features from Jazzy that I would like to see it adopt.</p>\n\n<p>Jazzy reports documentation coverage. If you look at the <a href=\"https://jessesquires.github.io/Foil/\">docs for Foil</a>, you’ll see it is 100% documented. Similarly, the <a href=\"http://quick.github.io/Quick/\">docs for Quick</a> report it is currently 88% documented. Even better, Jazzy outputs a <code class=\"language-plaintext highlighter-rouge\">docs/undocumented.json</code> file which specifies all undocumented symbols and their precise file and line location in the codebase. This is incredibly helpful for maintainers like me who strive for 100% documentation to avoid “<a href=\"https://github.com/nooverviewavailable/NoOverviewAvailable.com\">No Overview Available</a>”. Because Jazzy outputs structured data in <code class=\"language-plaintext highlighter-rouge\">undocumented.json</code>, you can build automation around this, like <a href=\"https://github.com/fwal/DangerJazzy\">a Danger plugin</a> that reports if your pull request added new APIs without documenting them. One last benefit is that contributors can also see which symbols need documentation — often, an easy first-time contribution for folks wishing to get involved.</p>\n\n<p>Jazzy docs include a prominent “View on GitHub” link in the site header that takes you directly to the GitHub repo. This is particularly convenient if someone first lands on your documentation pages instead of your repo.</p>\n\n<p>Jazzy includes a client-side search. It’s a bit simplistic and basic — limited by the static environment of GitHub Pages — but it works pretty well in practice and is nice to have.</p>\n\n<h3 id=\"conclusion\">Conclusion</h3>\n\n<p>I think DocC offers a lot of value and has some really cool features like <a href=\"https://developer.apple.com/videos/play/wwdc2021/10235/\">interactive tutorials</a>, and it looks like the best option available if you do not need a static site. However, DocC static hosting imposes <em>just enough</em> inconvenience to my existing setup and workflow to make switching away from Jazzy not worth the effort — for now. I might consider using DocC for new projects, but migrating existing projects would be more trouble than it is worth.</p>\n\n<p>The main improvement I would like to see with DocC static hosting is making this feature a first-class citizen — fix the quirky URL scheme and don’t require a web server to make it a truly static site. As for DocC features in general, the most important one for me would be reporting documentation coverage.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-25-april-2022\">\n        <a href=\"#updated-25-april-2022\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"25 Apr 2022 10:27:23 AM PDT\">\n            <i>25 April 2022</i>\n        </small>\n    </h5>\n    \n<p>Thanks to <a href=\"https://twitter.com/ethankusters\">Ethan Kusters</a> for sharing <a href=\"https://twitter.com/ethankusters/status/1517620941848678402\">some updates and feedback</a> on this post:</p>\n\n<ul>\n  <li>To workaround the unstable JSON output, you can set the env variable <code class=\"language-plaintext highlighter-rouge\">export DOCC_JSON_PRETTYPRINT=\"YES\"</code> as <a href=\"https://github.com/apple/swift-markdown/blob/main/bin/update-gh-pages-documentation-site#L36\">seen here</a>.</li>\n  <li>Turns out DocC <em>is not</em> transforming dashes into underscores. That’s Swift doing that, which makes sense now because you have to <code class=\"language-plaintext highlighter-rouge\">import my_swift_package</code>. My mistake there!</li>\n  <li>For search, Swift-DocC has a <a href=\"https://forums.swift.org/t/swift-docc-sidebar/55250\">sidebar in active development</a> that offers some basic filtering.</li>\n</ul>\n\n<p>The team is actively working on improving the UX with DocC. I’m excited to see what they come up with!</p>\n\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2022/04/22/docc-on-github-pages/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2022/04/19/github-suspending-russian-accounts/#updated-21-april-2022",
            "url": "https://www.jessesquires.com/blog/2022/04/19/github-suspending-russian-accounts/",
            "title": "[Updated] GitHub suspending Russian accounts deleted project history and pull requests",
            "date_published": "2022-04-21T13:49:59-07:00",
            "date_modified": "2022-04-21T13:49:59-07:00",
            "summary": "<p>According to various reports (<a href=\"https://www.pcmag.com/news/github-reportedly-suspends-accounts-related-to-sanctioned-russian-orgs\">[1]</a>, <a href=\"https://techweez.com/2022/04/18/github-suspending-accounts-russian-developers/\">[2]</a>, <a href=\"https://www.investing.com/news/cryptocurrency-news/github-suspends-accounts-of-russian-developers-linked-to-sanctioned-firms-2805302\">[3]</a>, <a href=\"https://techthelead.com/russian-developers-get-their-github-accounts-suspended-lose-work-without-warning/\">[4]</a>), GitHub is suspending accounts of Russian developers and organizations linked to or associated with organizations sanctioned by the US government over Russia’s invasion of Ukraine. But it appears that GitHub did not think this through entirely, because these account suspensions are fucking up my projects.</p>\n\n",
            "tags": [
                "github","git","open-source","politics",
                "software-dev"
            ],
            "content_html": "<p>According to various reports (<a href=\"https://www.pcmag.com/news/github-reportedly-suspends-accounts-related-to-sanctioned-russian-orgs\">[1]</a>, <a href=\"https://techweez.com/2022/04/18/github-suspending-accounts-russian-developers/\">[2]</a>, <a href=\"https://www.investing.com/news/cryptocurrency-news/github-suspends-accounts-of-russian-developers-linked-to-sanctioned-firms-2805302\">[3]</a>, <a href=\"https://techthelead.com/russian-developers-get-their-github-accounts-suspended-lose-work-without-warning/\">[4]</a>), GitHub is suspending accounts of Russian developers and organizations linked to or associated with organizations sanctioned by the US government over Russia’s invasion of Ukraine. But it appears that GitHub did not think this through entirely, because these account suspensions are fucking up my projects.</p>\n\n<!--excerpt-->\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<p>First, some brief context and background.</p>\n\n<p>I recently <a href=\"/blog/2022/04/17/quick-5-released/\">took over as a lead maintainer</a> for two popular projects in the Apple developer community, <a href=\"https://github.com/quick/quick\">Quick</a> and <a href=\"https://github.com/quick/nimble\">Nimble</a>. I <em>just released</em> <a href=\"https://github.com/Quick/Quick/releases/tag/v5.0.0\">version 5.0 of Quick</a> a few days ago. During the week leading up to the release, I was reviewing and merging many pull requests. But when it came time to write the release notes, I noticed very bizarre behavior. Mysteriously, some pull requests were <strong>deleted</strong>. Poof. Gone. Then I realized that an entire contributor’s presence had disappeared — all of their comments on issues were missing, all of the issues they opened were gone, all of the pull requests they opened had vanished. Every piece of activity related to the user was gone. What the fuck?!</p>\n\n<p>As an example, you can see this line from GitHub’s <a href=\"https://github.com/Quick/Quick/releases/tag/v5.0.0\">auto-generated release notes</a>:</p>\n\n<blockquote>\n  <p><a href=\"https://github.com/BobCatC\"><strong>@BobCatC</strong></a> made their first contribution in <a href=\"https://github.com/Quick/Quick/pull/1129\">#1129</a></p>\n</blockquote>\n\n<p>Both the user account and the pull request result in a 404. But you can find <a href=\"https://github.com/Quick/Quick/commit/a0a5fc857cea079fbe973e4faa80b6ceaf17bd46\">the merge commit here</a>, which is all that’s left in terms of an historical accounting of this change. It’s particularly notable for this project because PR #1129 was a critical bug fix.</p>\n\n<p>Today, maintainer <a href=\"https://github.com/younata\">Rachel Brindle</a> opened <a href=\"https://github.com/Quick/Quick/pull/1143\">a pull request</a> with another important bug fix, but lamented the fact that the <em>original pull request</em> that had introduced the bug had since been deleted:</p>\n\n<blockquote>\n  <p>The original PR that introduced it has since been deleted, so I’m unsure exactly of the intention of that contribution.</p>\n</blockquote>\n\n<p>So here I am for the past few days wondering what the fuck is going on. Why are <strong>multiple</strong> users and pull requests disappearing from our project? My gratitude goes out to <a href=\"https://github.com/tsapeta\">Tomasz Sapeta</a> for <a href=\"https://github.com/Quick/Quick/pull/1143#issuecomment-1103079607\">helping us realize</a> that all of these mysterious disappearances are due to GitHub flippantly suspending the accounts of Russian developers without any regard for the destructive side effects.</p>\n\n<p>There are multiple contributors to Quick whose accounts have been suspended, which means we have lost everything they contributed aside from raw commit history.</p>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<p>It is unclear to me what GitHub’s intended result was with these account suspensions, but it appears to be incredibly destructive for any open source project that has interacted with a now-suspended account. On a service like Twitter, you can visit the placeholder profile of a suspended account and see a message communicating that the account is suspended, and other users’ @mentions of the account still link to the suspended account’s profile. On GitHub, that’s not how it works at all.</p>\n\n<p>Apparently, “suspending an account” on GitHub actually means <strong>deleting all activity for a user</strong> — which results in (1) every pull request from the suspended account being <strong>deleted</strong>, (2) every issue opened by the suspended account being <strong>deleted</strong>, (3) every comment or discussion from the suspended account being <strong>deleted</strong>. In effect, the user’s entire activity and history is evaporated. <strong>All of this valuable data is lost.</strong> The only thing left intact is the raw Git commit history. It’s as if the user never existed.</p>\n\n<p>Again, at this time it is not clear to me if the data loss was GitHub’s goal or if this was a mistake. Either way, it’s a massive problem. Deleting this data with no notice is an abuse of trust. Should we continue to trust GitHub with important data?</p>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<p>This has been absolutely perplexing because there was no notification or communication from GitHub about this, aside from <a href=\"https://github.blog/2022-03-02-our-response-to-the-war-in-ukraine/\">their generic statement</a> in which they claimed <em>“we are continuing to ensure free open source services are available to all, including developers in Russia.”</em> So much for that. I’ve been working for only a week or so on this project that I inherited, trying to diligently keep track of changes like a good maintainer, and then all kinds of peculiar and unexpected weird shit started happening. Unbeknownst to me, GitHub was quietly joining the rest of the western world in its crusade to punish innocent Russian civilians whose only crime was being born in the wrong place and maybe being formerly associated with a bank that is now being sanctioned. Fuck Putin, but I don’t see how deleting GitHub accounts and causing food shortages for civilians is “winning” for anyone. As far as I could tell, the now-missing contributors were just ordinary iOS and macOS developers interested in contributing to a community open source project.</p>\n\n<p>These actions from GitHub are harmful and damaging to open source projects and the open source community. All of a sudden, I was seeing pull requests, issues, and comments disappear from users who were actively contributing to the project. <strong>We lost valuable contributions, information, context, and discussion history</strong> on issues and pull requests. We even lost pull requests that were open and under active review. That work is now entirely lost. Gone forever. For pull requests that <em>did</em> merge, we have the raw commit history — but that is not a substitute for a full code review and discussion.</p>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<p>It’s hard enough to maintain open source projects. It’s even harder to inherit an old, somewhat neglected project and try to get it back on track. In that scenario, every single pull request, issue, and comment is important for the long-term maintenance and success of the project. Comments, discussions, and code reviews provide valuable context that is not always captured in the commit history — especially for open source projects that have cycled through multiple maintainers over the years. I think a proper solution from GitHub would have been to leave all contributions intact, freeze the suspected accounts to prevent future activity, and clearly mark the account profile pages as suspended. And then, when possible, flip the switch to fully re-enable the accounts. But apparently GitHub thought the best thing to do was delete it all.</p>\n\n<p>So, thank you GitHub for royally fucking this up.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-21-april-2022\">\n        <a href=\"#updated-21-april-2022\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"21 Apr 2022 01:49:59 PM PDT\">\n            <i>21 April 2022</i>\n        </small>\n    </h5>\n    \n<p>Good news! <a href=\"https://twitter.com/martinwoodward\">Martin Woodward</a>, the Senior Director of Developer Relations at GitHub, reached out to let me know that GitHub has restored the missing pull requests, issues, comments, etc. from the Russian developers whose accounts have been suspended. User profiles are also restored, though they do not specifically mention that the accounts are suspended.</p>\n\n<p>According to him, the only mechanism GitHub previously had to suspend accounts was built to target spammers and other malicious actors — scenarios in which, usually, the best thing to is make the accounts and all activity entirely disappear. Clearly, that was not appropriate in this case. I really appreciate Martin contacting me and helping push for a better solution to this internally at GitHub.</p>\n\n<p>I still disagree with punishing ordinary individuals for atrocities perpetrated by The Russian State (and all States, for that matter), but I suppose most US corporations have little choice but to concede to governmental pressure. That’s a topic for another post.</p>\n\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2022/04/19/github-suspending-russian-accounts/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2022/04/17/duckduckgo-removing-other-sites/#updated-19-april-2022",
            "url": "https://www.jessesquires.com/blog/2022/04/17/duckduckgo-removing-other-sites/",
            "title": "[Updated] DuckDuckGo removing other sites",
            "date_published": "2022-04-19T17:48:30-07:00",
            "date_modified": "2022-04-19T17:48:30-07:00",
            "summary": "<p>My website <a href=\"/blog/2022/03/25/my-website-disappeared-from-bing-and-duckduckgo/\">is still missing from DuckDuckGo’s and Bing’s</a> search indexes. And now other sites are reporting similar issues.</p>\n\n",
            "tags": [
                "web","bing","duckduckgo","search",
                "software-dev"
            ],
            "content_html": "<p>My website <a href=\"/blog/2022/03/25/my-website-disappeared-from-bing-and-duckduckgo/\">is still missing from DuckDuckGo’s and Bing’s</a> search indexes. And now other sites are reporting similar issues.</p>\n\n<!--excerpt-->\n\n<p>Ernesto Van der Sar, <a href=\"https://torrentfreak.com/duckduckgo-removes-pirate-sites-and-youtube-dl-from-its-search-results-220415/\">writing for TorrentFreak</a>:</p>\n\n<blockquote>\n  <p>Privacy-centered search engine DuckDuckGo has completely removed the search results for many popular pirates sites including The Pirate Bay, 1337x, and Fmovies. Several YouTube ripping services have disappeared, too and even the homepage of the open-source software youtube-mp3 is unfindable.</p>\n\n  <p>[…]</p>\n\n  <p><strong>Update April 17:</strong> DuckDuckGo informs us that no domains were removed but they are having some issues and we still have questions. More details are at the bottom of the article.</p>\n\n  <p>For example, searching for “site:thepiratebay.org” is supposed to return all results DuckDuckGo has indexed for The Pirate Bay’s main domain name. In this case, there are none.</p>\n</blockquote>\n\n<p>This is <a href=\"/blog/2022/03/25/my-website-disappeared-from-bing-and-duckduckgo/\">the exact problem I’m currently facing</a>.</p>\n\n<blockquote>\n  <p><strong>Update April 17:</strong> DuckDuckGo has responded to our findings and says that no domains were removed, according to their records.</p>\n\n  <p>Before publishing the article we searched for YouTube-dl and The Pirate Bay without the “site:” operator the official domains were not showing up at our end. They do now.</p>\n</blockquote>\n\n<p>According to Gabriel Weinberg, the CEO and Founder, the company is <a href=\"https://twitter.com/yegg/status/1515636218691739653\">having issues</a> with their “site:” operator:</p>\n\n<blockquote>\n  <p>Similarly, we are not “purging” YouTube-dl or The Pirate Bay and they both have actually been continuously available in our results if you search for them by name (which most people do). Our site: operator (which hardly anyone uses) is having issues which we are looking into.</p>\n</blockquote>\n\n<p>However, this doesn’t explain the fact that even a search for my name or website without using the site: operator yields no relevant results. For comparison, Google Search is very accurate in this regard. Searching for my name there shows my website, Twitter profile, and GitHub profile — in that order — as the top 3 results. And <strong>I know</strong> the results <em>used to be</em> the same on DuckDuckGo.</p>\n\n<p><a href=\"https://torrentfreak.com/duckduckgo-removes-pirate-sites-and-youtube-dl-from-its-search-results-220415/\">TorrentFreak continues</a>:</p>\n\n<blockquote>\n  <p>We don’t doubt that DuckDuckGo hasn’t intentionally removed any URLs but there still appear to be strange issues with pirate-related searches.</p>\n\n  <p><strong>Update 2:</strong> A DuckDuckGo spokesperson confirmed to TorrentFreak that the issues are related to Bing data.</p>\n</blockquote>\n\n<p>Well, that seems <a href=\"/blog/2022/03/25/my-website-disappeared-from-bing-and-duckduckgo/\">to confirm my suspicion</a> that this was ultimately caused by Bing removing my site (and apparently everything else about me) from its index. And, as <a href=\"/blog/2022/03/25/my-website-disappeared-from-bing-and-duckduckgo/\">previously mentioned</a> I’m still waiting to hear back from Bing Support. Again, the lack of transparency into search engines in general is infuriating. I wonder, why doesn’t DuckDuckGo do more to build its own infrastructure to crawl the web and build its search index without relying solely on third parties?</p>\n\n<p>I <a href=\"/blog/2018/02/25/replacing-google-with-duckduckgo/\">used to be really excited about DuckDuckGo</a>, but I’m quickly becoming less of a fan and losing confidence in their results, which seem to be degrading lately. I’ve had to switch back to Google Search for many queries in the past few months.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-19-april-2022\">\n        <a href=\"#updated-19-april-2022\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"19 Apr 2022 05:48:30 PM PDT\">\n            <i>19 April 2022</i>\n        </small>\n    </h5>\n    \n<p>There’s <a href=\"https://torrentfreak.com/duckduckgo-restores-pirate-sites-and-points-to-bing-220419/\">another update from TorrentFreak</a>. All of the pirate sites they were tracking have since been restored to the DuckDuckGo search index:</p>\n\n<blockquote>\n  <p>At this point, it became clear that the search engine wasn’t at all happy with what was happening. They never actively removed any of these sites. Instead, a third-party data provider ‘removed’ the results for them.</p>\n\n  <p>Like many other smaller search engines, DuckDuckGo uses hundreds of data sources, including Bing. After some back and forths, DuckDuckGo’s spokesperson informed us that Microsoft’s search engine was the culprit.</p>\n</blockquote>\n\n<p>My site <a href=\"https://duckduckgo.com/?q=site%3Ajessesquires.com\">still does not appear on DuckDuckGo</a>, nor Bing. I’m still waiting to hear back from Bing Support. I wonder how many other sites are affected by this?</p>\n\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2022/04/17/duckduckgo-removing-other-sites/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2021/10/17/github-actions-workflows-for-automatic-rebasing-and-merging/#updated-13-april-2022",
            "url": "https://www.jessesquires.com/blog/2021/10/17/github-actions-workflows-for-automatic-rebasing-and-merging/",
            "title": "[Updated] GitHub Actions workflows for automatic rebasing and merging",
            "date_published": "2022-04-13T12:26:15-07:00",
            "date_modified": "2022-04-13T12:26:15-07:00",
            "summary": "<p>This post continues from my earlier post on <a href=\"/blog/2021/08/24/useful-label-based-github-actions-workflows/\">label-based GitHub Actions workflows</a>. A common and tedious task in a typical pull request workflow is updating your branch with changes from the default branch of your repo. In many scenarios, you may want to rebase your pull request on the main branch or merge the main branch into your pull request.</p>\n\n",
            "tags": [
                "ci","github-actions","github","automation",
                "software-dev"
            ],
            "content_html": "<p>This post continues from my earlier post on <a href=\"/blog/2021/08/24/useful-label-based-github-actions-workflows/\">label-based GitHub Actions workflows</a>. A common and tedious task in a typical pull request workflow is updating your branch with changes from the default branch of your repo. In many scenarios, you may want to rebase your pull request on the main branch or merge the main branch into your pull request.</p>\n\n<!--excerpt-->\n\n<p>Perhaps there are improvements from the main branch that you want incorporated into your pull request, or maybe you want to re-run the entire test suite with your changes and the latest changes from the main branch. It could also be the case that you require branches to be up-to-date before merging. With the way I work, this is a pain. After I submit a pull request for review, I typically start a new branch to start working on something else immediately. Later on, if I need to rebase an existing pull request, then I must stash or commit my current changes, switch branches and rebase, push the changes, then switch back to what I was working on before the interruption.</p>\n\n<p>One workaround to this is having multiple checkouts, where you do all the rebasing on the second checkout while leaving your current in-progress checkout untouched. This, however, is still tedious. If your branch has no conflicts with the main branch, then rebasing or merging can be fully automated. I’ve written two new workflows for each. This frees up my time to focus on things a machine can’t do for me.</p>\n\n<h3 id=\"automatic-rebasing\">Automatic rebasing</h3>\n\n<p>This workflow is triggered by adding a label called <code class=\"language-plaintext highlighter-rouge\">rebase</code> to your pull request. It uses the <a href=\"https://github.com/marketplace/actions/automatic-rebase\">automatic-rebase action</a> to perform the rebase, and <a href=\"https://github.com/marketplace/actions/actions-ecosystem-remove-labels\">actions-ecosystem-remove-labels action</a> to remove the label when complete. To use this, you’ll need to add the <code class=\"language-plaintext highlighter-rouge\">rebase</code> label to your repo. If you’d like to use a different label name you can, just be sure to edit the workflow accordingly. You’ll also need to add a <a href=\"https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token\">personal access token</a>.</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">Rebase Pull Request</span>\n\n<span class=\"na\">on</span><span class=\"pi\">:</span>\n  <span class=\"na\">pull_request</span><span class=\"pi\">:</span>\n    <span class=\"na\">types</span><span class=\"pi\">:</span> <span class=\"pi\">[</span><span class=\"nv\">labeled</span><span class=\"pi\">]</span>\n\n<span class=\"na\">jobs</span><span class=\"pi\">:</span>\n  <span class=\"na\">main</span><span class=\"pi\">:</span>\n    <span class=\"na\">if</span><span class=\"pi\">:</span> <span class=\"s\">${{ github.event.label.name == 'rebase' }}</span>\n    <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">Rebase</span>\n    <span class=\"na\">runs-on</span><span class=\"pi\">:</span> <span class=\"s\">ubuntu-latest</span>\n    <span class=\"na\">steps</span><span class=\"pi\">:</span>\n      <span class=\"pi\">-</span> <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">git checkout</span>\n        <span class=\"na\">uses</span><span class=\"pi\">:</span> <span class=\"s\">actions/checkout@v2</span>\n        <span class=\"na\">with</span><span class=\"pi\">:</span>\n          <span class=\"na\">token</span><span class=\"pi\">:</span> <span class=\"s\">${{ secrets.PERSONAL_ACCESS_TOKEN }}</span>\n          <span class=\"na\">fetch-depth</span><span class=\"pi\">:</span> <span class=\"m\">0</span>\n\n      <span class=\"pi\">-</span> <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">automatic rebase</span>\n        <span class=\"na\">uses</span><span class=\"pi\">:</span> <span class=\"s\">cirrus-actions/rebase@1.5</span>\n        <span class=\"na\">env</span><span class=\"pi\">:</span>\n          <span class=\"na\">GITHUB_TOKEN</span><span class=\"pi\">:</span> <span class=\"s\">${{ secrets.PERSONAL_ACCESS_TOKEN }}</span>\n\n      <span class=\"pi\">-</span> <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">remove label</span>\n        <span class=\"na\">if</span><span class=\"pi\">:</span> <span class=\"s\">always()</span>\n        <span class=\"na\">uses</span><span class=\"pi\">:</span> <span class=\"s\">actions-ecosystem/action-remove-labels@v1</span>\n        <span class=\"na\">with</span><span class=\"pi\">:</span>\n          <span class=\"na\">labels</span><span class=\"pi\">:</span> <span class=\"s\">rebase</span>\n</code></pre></div></div>\n\n<h3 id=\"automatic-merging\">Automatic merging</h3>\n\n<p>If you would rather merge the main branch into your pull request instead of rebase, I wrote a workflow for that too. I’ve titled it “Merge Main into PR”, but if your default branch is named differently, you can change this. You do not need to worry about updating the workflow based on what you’ve named your default branch, the workflow will detect the default branch name automatically. Similar to the rebase workflow, this one is triggered by adding a label named <code class=\"language-plaintext highlighter-rouge\">merge main</code>. Again, you can adjust this as needed for your own configuration. And finally, this also removes the label when finished.</p>\n\n<p>Note that I’ve generously used <code class=\"language-plaintext highlighter-rouge\">git status</code> during the merge to make debugging easier if something goes wrong.</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">Merge Main into PR</span>\n\n<span class=\"na\">on</span><span class=\"pi\">:</span>\n  <span class=\"na\">pull_request</span><span class=\"pi\">:</span>\n    <span class=\"na\">types</span><span class=\"pi\">:</span> <span class=\"pi\">[</span><span class=\"nv\">labeled</span><span class=\"pi\">]</span>\n\n<span class=\"na\">env</span><span class=\"pi\">:</span>\n  <span class=\"na\">DEFAULT_BRANCH</span><span class=\"pi\">:</span> <span class=\"s\">${{ github.event.repository.default_branch }}</span>\n\n<span class=\"na\">jobs</span><span class=\"pi\">:</span>\n  <span class=\"na\">main</span><span class=\"pi\">:</span>\n    <span class=\"na\">if</span><span class=\"pi\">:</span> <span class=\"s\">${{ github.event.label.name == 'merge main' }}</span>\n    <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">Merge Main</span>\n    <span class=\"na\">runs-on</span><span class=\"pi\">:</span> <span class=\"s\">ubuntu-latest</span>\n    <span class=\"na\">steps</span><span class=\"pi\">:</span>\n      <span class=\"pi\">-</span> <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">git checkout</span>\n        <span class=\"na\">uses</span><span class=\"pi\">:</span> <span class=\"s\">actions/checkout@v2</span>\n        <span class=\"na\">with</span><span class=\"pi\">:</span>\n          <span class=\"na\">token</span><span class=\"pi\">:</span> <span class=\"s\">${{ secrets.PERSONAL_ACCESS_TOKEN }}</span>\n          <span class=\"na\">ref</span><span class=\"pi\">:</span> <span class=\"s\">${{ github.event.pull_request.head.ref }}</span>\n          <span class=\"na\">fetch-depth</span><span class=\"pi\">:</span> <span class=\"m\">0</span>\n\n      <span class=\"pi\">-</span> <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">perform merge</span>\n        <span class=\"na\">run</span><span class=\"pi\">:</span> <span class=\"pi\">|</span>\n          <span class=\"s\">git config --global user.email \"${GITHUB_ACTOR}@users.noreply.github.com\"</span>\n          <span class=\"s\">git config --global user.name \"${GITHUB_ACTOR}\"</span>\n          <span class=\"s\">git status</span>\n          <span class=\"s\">git pull</span>\n          <span class=\"s\">git checkout \"$DEFAULT_BRANCH\"</span>\n          <span class=\"s\">git status</span>\n          <span class=\"s\">git checkout \"$GITHUB_HEAD_REF\"</span>\n          <span class=\"s\">git merge \"$DEFAULT_BRANCH\" --no-edit</span>\n          <span class=\"s\">git push</span>\n          <span class=\"s\">git status</span>\n\n      <span class=\"pi\">-</span> <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">remove label</span>\n        <span class=\"na\">if</span><span class=\"pi\">:</span> <span class=\"s\">always()</span>\n        <span class=\"na\">uses</span><span class=\"pi\">:</span> <span class=\"s\">actions-ecosystem/action-remove-labels@v1</span>\n        <span class=\"na\">with</span><span class=\"pi\">:</span>\n          <span class=\"na\">labels</span><span class=\"pi\">:</span> <span class=\"s\">merge main</span>\n</code></pre></div></div>\n\n<h3 id=\"collecting-useful-workflows\">Collecting useful workflows</h3>\n\n<p>When combined with the workflows <a href=\"/blog/2021/08/24/useful-label-based-github-actions-workflows/\">from my previous post</a>, I have started to accumulate a decent collection of general-purpose workflows. To keep track, I created a new repo to house them all and track changes, which <a href=\"https://github.com/jessesquires/gh-workflows\">you can find here</a>. All you need to do is copy them to <code class=\"language-plaintext highlighter-rouge\">.github/workflows/</code> in your own repo, and edit them as needed. The current workflows are centered around automating tedious aspects of managing pull requests, but one can imagine all kinds of use cases for managing GitHub issues, releases, and more. As I write new workflows, I’ll be sure to include them in this project.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-13-april-2022\">\n        <a href=\"#updated-13-april-2022\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"13 Apr 2022 12:26:15 PM PDT\">\n            <i>13 April 2022</i>\n        </small>\n    </h5>\n    <p>Good news, GitHub <a href=\"https://github.blog/changelog/2022-02-03-more-ways-to-keep-your-pull-request-branch-up-to-date/\">now has these features built-in to pull requests</a>. You can use the “Update branch” button to either rebase or merge.</p>\n\n<p>However, these label-based workflows can still be useful for performing these operations in large batches. For example, if you need to rebase many pull requests at once you could select them all and add the label rather than visit each pull request individually and click the “Update branch” button.</p>\n\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2021/10/17/github-actions-workflows-for-automatic-rebasing-and-merging/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2020/04/10/running-danger-on-github-actions/#updated-28-march-2022",
            "url": "https://www.jessesquires.com/blog/2020/04/10/running-danger-on-github-actions/",
            "title": "[Updated] Running Danger on GitHub Actions",
            "date_published": "2022-03-28T10:48:49-07:00",
            "date_modified": "2022-03-28T10:48:49-07:00",
            "summary": "<p>I have started using <a href=\"https://github.com/features/actions\">GitHub Actions</a> for CI for my newer open source projects. I recently setup <a href=\"https://danger.systems\">Danger</a> for the first time on GitHub Actions and it was quite easy. Here is how to do it.</p>\n\n",
            "tags": [
                "ci","danger","github","github-actions","open-source","automation",
                "software-dev"
            ],
            "content_html": "<p>I have started using <a href=\"https://github.com/features/actions\">GitHub Actions</a> for CI for my newer open source projects. I recently setup <a href=\"https://danger.systems\">Danger</a> for the first time on GitHub Actions and it was quite easy. Here is how to do it.</p>\n\n<!--excerpt-->\n\n<p>You will need to follow Danger’s <a href=\"https://danger.systems/guides/getting_started.html\">Getting Set Up</a> guide, which involves creating a bot account and <a href=\"https://github.com/settings/tokens/new\">adding a new personal access token</a> for it. After that, you will need to navigate to <code class=\"language-plaintext highlighter-rouge\">Settings &gt; Secrets</code> for your repo on GitHub, and add the personal access token you created as the value of the secret and name it <code class=\"language-plaintext highlighter-rouge\">DANGER_GITHUB_API_TOKEN</code>. Technically, you could name this differently, but I think this is easiest to use this name to remember its purpose.</p>\n\n<p>With the setup complete, all you need to do is implement your <a href=\"https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions\">workflow</a>. Here is a template you can use for iOS or macOS projects, but with a few tweaks this can be used for any project.</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">Danger</span>\n\n<span class=\"na\">on</span><span class=\"pi\">:</span>\n  <span class=\"na\">pull_request</span><span class=\"pi\">:</span>\n    <span class=\"na\">types</span><span class=\"pi\">:</span> <span class=\"pi\">[</span><span class=\"nv\">synchronize</span><span class=\"pi\">,</span> <span class=\"nv\">opened</span><span class=\"pi\">,</span> <span class=\"nv\">reopened</span><span class=\"pi\">,</span> <span class=\"nv\">labeled</span><span class=\"pi\">,</span> <span class=\"nv\">unlabeled</span><span class=\"pi\">,</span> <span class=\"nv\">edited</span><span class=\"pi\">]</span>\n\n<span class=\"na\">env</span><span class=\"pi\">:</span>\n  <span class=\"na\">DEVELOPER_DIR</span><span class=\"pi\">:</span> <span class=\"s\">/Applications/Xcode_13.2.1.app/Contents/Developer</span>\n\n<span class=\"na\">jobs</span><span class=\"pi\">:</span>\n  <span class=\"na\">main</span><span class=\"pi\">:</span>\n    <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">Review, Lint, Verify</span>\n    <span class=\"na\">runs-on</span><span class=\"pi\">:</span> <span class=\"s\">macOS-latest</span>\n    <span class=\"na\">steps</span><span class=\"pi\">:</span>\n      <span class=\"pi\">-</span> <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">git checkout</span>\n        <span class=\"na\">uses</span><span class=\"pi\">:</span> <span class=\"s\">actions/checkout@v3</span>\n\n      <span class=\"pi\">-</span> <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">ruby versions</span>\n        <span class=\"na\">run</span><span class=\"pi\">:</span> <span class=\"pi\">|</span>\n          <span class=\"s\">ruby --version</span>\n          <span class=\"s\">gem --version</span>\n          <span class=\"s\">bundler --version</span>\n\n      <span class=\"pi\">-</span> <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">ruby setup</span>\n        <span class=\"na\">uses</span><span class=\"pi\">:</span> <span class=\"s\">ruby/setup-ruby@v1</span>\n        <span class=\"na\">with</span><span class=\"pi\">:</span>\n          <span class=\"na\">ruby-version</span><span class=\"pi\">:</span> <span class=\"m\">2.7</span>\n          <span class=\"na\">bundler-cache</span><span class=\"pi\">:</span> <span class=\"kc\">true</span>\n\n      <span class=\"c1\"># additional steps here, if needed</span>\n\n      <span class=\"pi\">-</span> <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">danger</span>\n        <span class=\"na\">env</span><span class=\"pi\">:</span>\n          <span class=\"na\">DANGER_GITHUB_API_TOKEN</span><span class=\"pi\">:</span> <span class=\"s\">${{ secrets.DANGER_GITHUB_API_TOKEN }}</span>\n        <span class=\"na\">run</span><span class=\"pi\">:</span> <span class=\"s\">bundle exec danger --verbose</span>\n</code></pre></div></div>\n\n<p>This workflow will only be triggered on pull requests, which is convenient. The first four steps are just boilerplate for <a href=\"https://github.com/actions/checkout\">checking out the repo</a> and <a href=\"https://github.com/ruby/setup-ruby\">setting up ruby</a>. The final step executes <code class=\"language-plaintext highlighter-rouge\">danger</code> and makes use of the <code class=\"language-plaintext highlighter-rouge\">DANGER_GITHUB_API_TOKEN</code> secret that you created.</p>\n\n<p>If your <code class=\"language-plaintext highlighter-rouge\">Dangerfile</code> does not require any additional data, you can use this workflow as-is. Otherwise, you can add additional steps after the <code class=\"language-plaintext highlighter-rouge\">ruby setup</code> step and before the <code class=\"language-plaintext highlighter-rouge\">danger</code> step. For example, suppose you need access to <code class=\"language-plaintext highlighter-rouge\">xcodebuild</code> output for a specific Danger plugin, you can add a step that builds your project. For non-Xcode projects, the same applies. Add any additional steps you need before <code class=\"language-plaintext highlighter-rouge\">danger</code> is called.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-28-march-2022\">\n        <a href=\"#updated-28-march-2022\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"28 Mar 2022 10:48:49 AM PDT\">\n            <i>28 March 2022</i>\n        </small>\n    </h5>\n    <p>This workflow has been updated to reflect various changes and improvements to GitHub Actions, namely by using the more modern approach to <a href=\"https://github.com/ruby/setup-ruby\">setting up ruby and caching gems</a>.</p>\n\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2020/04/10/running-danger-on-github-actions/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2021/08/24/useful-label-based-github-actions-workflows/#updated-21-march-2022",
            "url": "https://www.jessesquires.com/blog/2021/08/24/useful-label-based-github-actions-workflows/",
            "title": "[Updated] Useful label-based GitHub Actions workflows",
            "date_published": "2022-03-21T10:48:00-07:00",
            "date_modified": "2022-03-21T10:48:00-07:00",
            "summary": "<p>My current team has started using GitHub Actions to automate some tedious tasks for pull requests. In particular, we use labels on GitHub to categorize pull requests or highlight important metadata about them. Most of the time, a machine can figure out which labels are appropriate to add or remove. This is a great use case for GitHub Actions.</p>\n\n",
            "tags": [
                "ci","github-actions","github","automation",
                "software-dev"
            ],
            "content_html": "<p>My current team has started using GitHub Actions to automate some tedious tasks for pull requests. In particular, we use labels on GitHub to categorize pull requests or highlight important metadata about them. Most of the time, a machine can figure out which labels are appropriate to add or remove. This is a great use case for GitHub Actions.</p>\n\n<!--excerpt-->\n\n<h3 id=\"labeling-stacked-pull-requests\">Labeling stacked pull requests</h3>\n\n<p>One common workflow (or problem, depending on how you view it) in git and GitHub is stacking pull requests. This is necessary when you have multiple pull requests that depend on each other and must merge into your main branch sequentially. For example, pull request <code class=\"language-plaintext highlighter-rouge\">A</code> is opened to merge into <code class=\"language-plaintext highlighter-rouge\">main</code>, pull request <code class=\"language-plaintext highlighter-rouge\">B</code> is opened to merge into <code class=\"language-plaintext highlighter-rouge\">A</code>, pull request <code class=\"language-plaintext highlighter-rouge\">C</code> is opened to merge into <code class=\"language-plaintext highlighter-rouge\">B</code>, and so on. After <code class=\"language-plaintext highlighter-rouge\">A</code> is merged, <code class=\"language-plaintext highlighter-rouge\">B</code> must be updated to merge into <code class=\"language-plaintext highlighter-rouge\">main</code>, and so on.</p>\n\n<p>We like to highlight this and make it obvious. We have a brightly colored <code class=\"language-plaintext highlighter-rouge\">stacked PR</code> label, which communicates to the reviewer <em>“hey! this PR is part of a stack, just so you know.”</em> It is tedious to repeatedly manually add and remove this label for every pull request in the stack, so I wrote this workflow to automate it.</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">Label stacked PRs</span>\n\n<span class=\"na\">on</span><span class=\"pi\">:</span>\n  <span class=\"na\">pull_request</span><span class=\"pi\">:</span>\n    <span class=\"na\">types</span><span class=\"pi\">:</span> <span class=\"pi\">[</span><span class=\"nv\">synchronize</span><span class=\"pi\">,</span> <span class=\"nv\">opened</span><span class=\"pi\">,</span> <span class=\"nv\">reopened</span><span class=\"pi\">,</span> <span class=\"nv\">edited</span><span class=\"pi\">,</span> <span class=\"nv\">closed</span><span class=\"pi\">]</span>\n\n<span class=\"na\">env</span><span class=\"pi\">:</span>\n  <span class=\"na\">BASE_BRANCH</span><span class=\"pi\">:</span> <span class=\"s\">${{ github.base_ref }}</span>\n\n<span class=\"na\">jobs</span><span class=\"pi\">:</span>\n  <span class=\"na\">remove-labels</span><span class=\"pi\">:</span>\n    <span class=\"na\">runs-on</span><span class=\"pi\">:</span> <span class=\"s\">ubuntu-latest</span>\n    <span class=\"na\">steps</span><span class=\"pi\">:</span>\n      <span class=\"pi\">-</span> <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">remove labels</span>\n        <span class=\"na\">uses</span><span class=\"pi\">:</span> <span class=\"s\">actions-ecosystem/action-remove-labels@v1</span>\n        <span class=\"na\">if</span><span class=\"pi\">:</span> <span class=\"s\">env.BASE_BRANCH == 'main'</span>\n        <span class=\"na\">with</span><span class=\"pi\">:</span>\n          <span class=\"na\">github_token</span><span class=\"pi\">:</span> <span class=\"s\">${{ github.token }}</span>\n          <span class=\"na\">labels</span><span class=\"pi\">:</span> <span class=\"s\">stacked PR</span>\n\n  <span class=\"na\">add-labels</span><span class=\"pi\">:</span>\n    <span class=\"na\">runs-on</span><span class=\"pi\">:</span> <span class=\"s\">ubuntu-latest</span>\n    <span class=\"na\">steps</span><span class=\"pi\">:</span>\n      <span class=\"pi\">-</span> <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">add labels</span>\n        <span class=\"na\">uses</span><span class=\"pi\">:</span> <span class=\"s\">actions-ecosystem/action-add-labels@v1</span>\n        <span class=\"na\">if</span><span class=\"pi\">:</span> <span class=\"s\">env.BASE_BRANCH != 'main'</span>\n        <span class=\"na\">with</span><span class=\"pi\">:</span>\n          <span class=\"na\">github_token</span><span class=\"pi\">:</span> <span class=\"s\">${{ github.token }}</span>\n          <span class=\"na\">labels</span><span class=\"pi\">:</span> <span class=\"s\">stacked PR</span>\n</code></pre></div></div>\n\n<p>This workflow contains two jobs that are nearly identical. The first checks if the pull request is merging into the <code class=\"language-plaintext highlighter-rouge\">main</code> branch, and if so, it will remove the <code class=\"language-plaintext highlighter-rouge\">stacked PR</code> label, if present. The second job does the opposite, it will add the <code class=\"language-plaintext highlighter-rouge\">stacked PR</code> label to any pull request that is not targeting the <code class=\"language-plaintext highlighter-rouge\">main</code> branch. It uses two actions from the <a href=\"https://github.com/actions-ecosystem\">Actions Ecosystem</a> community project, <a href=\"https://github.com/marketplace/actions/actions-ecosystem-add-labels\">add-labels</a> and <a href=\"https://github.com/marketplace/actions/actions-ecosystem-remove-labels\">remove-labels</a>.</p>\n\n<h3 id=\"do-not-merge\">Do Not Merge</h3>\n\n<p>Another important label we have is <code class=\"language-plaintext highlighter-rouge\">do not merge</code>, which (unsurprisingly) indicates that a pull request should not be merged. Despite the label being a striking bright red, it could still be overlooked if all of our other CI status checks pass and you see that tempting, big green “merge” button on GitHub. It would be much safer if adding this label would <strong>fail</strong> the pull request to prevent you from merging it. We can write a workflow to do that using Michael Heap’s <a href=\"https://github.com/marketplace/actions/require-labels\">required-labels action</a>.</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">Do Not Merge</span>\n\n<span class=\"na\">on</span><span class=\"pi\">:</span>\n  <span class=\"na\">pull_request</span><span class=\"pi\">:</span>\n    <span class=\"na\">types</span><span class=\"pi\">:</span> <span class=\"pi\">[</span><span class=\"nv\">opened</span><span class=\"pi\">,</span> <span class=\"nv\">reopened</span><span class=\"pi\">,</span> <span class=\"nv\">labeled</span><span class=\"pi\">,</span> <span class=\"nv\">unlabeled</span><span class=\"pi\">]</span>\n\n<span class=\"na\">jobs</span><span class=\"pi\">:</span>\n  <span class=\"na\">do-not-merge</span><span class=\"pi\">:</span>\n    <span class=\"na\">runs-on</span><span class=\"pi\">:</span> <span class=\"s\">ubuntu-latest</span>\n    <span class=\"na\">steps</span><span class=\"pi\">:</span>\n      <span class=\"pi\">-</span> <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">check labels</span>\n        <span class=\"na\">uses</span><span class=\"pi\">:</span> <span class=\"s\">mheap/github-action-required-labels@v1</span>\n        <span class=\"na\">with</span><span class=\"pi\">:</span>\n          <span class=\"na\">mode</span><span class=\"pi\">:</span> <span class=\"s\">exactly</span>\n          <span class=\"na\">count</span><span class=\"pi\">:</span> <span class=\"m\">0</span>\n          <span class=\"na\">labels</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">do</span><span class=\"nv\"> </span><span class=\"s\">not</span><span class=\"nv\"> </span><span class=\"s\">merge\"</span>\n</code></pre></div></div>\n\n<p>This will check if the pull request has the <code class=\"language-plaintext highlighter-rouge\">do not merge</code> label. If it does, the workflow will fail. Once you remove the label, the workflow will pass.</p>\n\n<p>To make this workflow actually prevent merging requires a few extra steps. You need to set up <a href=\"https://docs.github.com/en/github/administering-a-repository/defining-the-mergeability-of-pull-requests/managing-a-branch-protection-rule\">branch protection rules</a> for your main branch and require this workflow to pass before a pull request can be merged. Once configured, the “merge” button will only be enabled if this workflow succeeds.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-21-march-2022\">\n        <a href=\"#updated-21-march-2022\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"21 Mar 2022 10:48:00 AM PDT\">\n            <i>21 March 2022</i>\n        </small>\n    </h5>\n    <p>Thanks to my friend <a href=\"https://benasher.co\">Ben Asher</a> for <a href=\"https://twitter.com/benasher44/status/1503460237331734528\">sharing</a> an improvement to the ‘Do Not Merge’ workflow, which can be rewritten <strong>without</strong> using third-party actions. You can find it below.</p>\n\n<p>Also, I’ve started maintaining a collection of useful workflows <a href=\"https://github.com/jessesquires/gh-workflows\">on GitHub</a>. Check them out.</p>\n\n</div>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">Do Not Merge</span>\n\n<span class=\"na\">on</span><span class=\"pi\">:</span>\n  <span class=\"na\">pull_request</span><span class=\"pi\">:</span>\n    <span class=\"na\">types</span><span class=\"pi\">:</span> <span class=\"pi\">[</span><span class=\"nv\">synchronize</span><span class=\"pi\">,</span> <span class=\"nv\">opened</span><span class=\"pi\">,</span> <span class=\"nv\">reopened</span><span class=\"pi\">,</span> <span class=\"nv\">labeled</span><span class=\"pi\">,</span> <span class=\"nv\">unlabeled</span><span class=\"pi\">]</span>\n\n<span class=\"na\">jobs</span><span class=\"pi\">:</span>\n  <span class=\"na\">do-not-merge</span><span class=\"pi\">:</span>\n    <span class=\"na\">if</span><span class=\"pi\">:</span> <span class=\"s\">${{ contains(github.event.*.labels.*.name, 'do not merge') }}</span>\n    <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">Prevent Merging</span>\n    <span class=\"na\">runs-on</span><span class=\"pi\">:</span> <span class=\"s\">ubuntu-latest</span>\n    <span class=\"na\">steps</span><span class=\"pi\">:</span>\n      <span class=\"pi\">-</span> <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s\">Check for label</span>\n        <span class=\"na\">run</span><span class=\"pi\">:</span> <span class=\"pi\">|</span>\n          <span class=\"s\">echo \"Pull request is labeled as 'do not merge'\"</span>\n          <span class=\"s\">echo \"This workflow fails so that the pull request cannot be merged\"</span>\n          <span class=\"s\">exit 1</span>\n</code></pre></div></div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2021/08/24/useful-label-based-github-actions-workflows/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2022/03/04/quickly-displaying-the-swift-version-that-ships-with-xcode/#updated-09-march-2022",
            "url": "https://www.jessesquires.com/blog/2022/03/04/quickly-displaying-the-swift-version-that-ships-with-xcode/",
            "title": "[Updated] Quickly displaying the Swift version that ships with Xcode",
            "date_published": "2022-03-09T13:48:45-08:00",
            "date_modified": "2022-03-09T13:48:45-08:00",
            "summary": "<p>I previously <a href=\"/blog/2020/07/07/quickly-switching-between-xcodes/\">wrote about</a> writing a custom shell command to quickly switch between Xcodes. But recently, I needed to determine the version of Swift that is bundled with Xcode — specifically the version of Swift that is shipping with the current Xcode 13.3 beta. I was pretty sure that it is Swift 5.6, but I wanted to know for certain.</p>\n\n",
            "tags": [
                "xcode","ios","macos",
                "software-dev"
            ],
            "content_html": "<p>I previously <a href=\"/blog/2020/07/07/quickly-switching-between-xcodes/\">wrote about</a> writing a custom shell command to quickly switch between Xcodes. But recently, I needed to determine the version of Swift that is bundled with Xcode — specifically the version of Swift that is shipping with the current Xcode 13.3 beta. I was pretty sure that it is Swift 5.6, but I wanted to know for certain.</p>\n\n<!--excerpt-->\n\n<p>Strangely, the <a href=\"https://developer.apple.com/documentation/xcode-release-notes/xcode-13_3-release-notes\">Xcode beta release notes</a> do not explicitly mention the Swift version included with the release. I expected this information to be available in the “Overview” section of the release notes, which is where all the SDK versions are listed, but the Swift version not present. If you are not following Swift development and Swift Evolution closely, then it isn’t immediately clear which version is shipping with Xcode.</p>\n\n<p>Luckily, we can find this by calling <code class=\"language-plaintext highlighter-rouge\">swiftc --version</code>, but for the Xcode Beta we need to know the full path bundled with the app. It is located at <code class=\"language-plaintext highlighter-rouge\">/Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc</code>, which is a mouthful and difficult to remember.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-09-march-2022\">\n        <a href=\"#updated-09-march-2022\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"09 Mar 2022 01:48:45 PM PST\">\n            <i>09 March 2022</i>\n        </small>\n    </h5>\n    \n<p>Thanks to <a href=\"https://mobile.twitter.com/_sa_s/status/1500379619744178179\">Sven Schmidt</a> for offering improvements here to simplify the script and make it more robust. We can use <code class=\"language-plaintext highlighter-rouge\">DEVELOPER_DIR</code> to specify which Xcode to use and then invoke <code class=\"language-plaintext highlighter-rouge\">xcrun</code> to avoid having to use the exact path to the binaries. The script below has been updated.</p>\n\n</div>\n\n<p>The custom commands below build on <a href=\"/blog/2020/07/07/quickly-switching-between-xcodes/\">what I previously wrote</a> to switch between Xcode versions. I’ve added a new command, <code class=\"language-plaintext highlighter-rouge\">xcwhich</code> that displays the currently selected Xcode, and then prints all the version information for both Xcode.app and Xcode-beta.app.</p>\n\n<div class=\"language-zsh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># print current Xcode info</span>\n<span class=\"k\">function </span>xcwhich<span class=\"o\">()</span> <span class=\"o\">{</span>\n    _xcselect <span class=\"nb\">false\n    echo</span> <span class=\"s2\">\"\"</span>\n    <span class=\"nb\">echo</span> <span class=\"s2\">\"RELEASE\"</span>\n    <span class=\"nb\">env </span><span class=\"nv\">DEVELOPER_DIR</span><span class=\"o\">=</span>/Applications/Xcode.app xcrun xcodebuild <span class=\"nt\">-version</span>\n    <span class=\"nb\">env </span><span class=\"nv\">DEVELOPER_DIR</span><span class=\"o\">=</span>/Applications/Xcode.app xcrun swift <span class=\"nt\">--version</span>\n\n    <span class=\"nb\">echo</span> <span class=\"s2\">\"</span><span class=\"se\">\\n</span><span class=\"s2\">BETA\"</span>\n    <span class=\"nb\">env </span><span class=\"nv\">DEVELOPER_DIR</span><span class=\"o\">=</span>/Applications/Xcode-beta.app xcrun xcodebuild <span class=\"nt\">-version</span>\n    <span class=\"nb\">env </span><span class=\"nv\">DEVELOPER_DIR</span><span class=\"o\">=</span>/Applications/Xcode-beta.app xcrun swift <span class=\"nt\">--version</span>\n<span class=\"o\">}</span>\n\n<span class=\"c\"># switch between release and beta Xcodes</span>\n<span class=\"k\">function </span>xcswitch<span class=\"o\">()</span> <span class=\"o\">{</span>\n    _xcselect <span class=\"nb\">true</span>\n<span class=\"o\">}</span>\n\n<span class=\"c\"># shared between `xcswitch` and `xcwhich`</span>\n<span class=\"c\"># determines current Xcode (beta or release)</span>\n<span class=\"c\"># pass true to switch, pass false to print current</span>\n<span class=\"k\">function </span>_xcselect<span class=\"o\">()</span> <span class=\"o\">{</span>\n    <span class=\"nv\">RELEASE</span><span class=\"o\">=</span><span class=\"s2\">\"Xcode.app\"</span>\n    <span class=\"nv\">BETA</span><span class=\"o\">=</span><span class=\"s2\">\"Xcode-beta.app\"</span>\n\n    <span class=\"nv\">CURRENT</span><span class=\"o\">=</span><span class=\"si\">$(</span>xcode-select <span class=\"nt\">-p</span><span class=\"si\">)</span>\n    <span class=\"nv\">NEXT</span><span class=\"o\">=</span><span class=\"s2\">\"\"</span>\n\n    <span class=\"k\">if</span> <span class=\"o\">[[</span> <span class=\"s2\">\"</span><span class=\"nv\">$CURRENT</span><span class=\"s2\">\"</span> <span class=\"o\">=</span>~ <span class=\"s2\">\"</span><span class=\"nv\">$RELEASE</span><span class=\"s2\">\"</span> <span class=\"o\">]]</span>\n    <span class=\"k\">then\n        </span><span class=\"nv\">NEXT</span><span class=\"o\">=</span><span class=\"s2\">\"</span><span class=\"nv\">$BETA</span><span class=\"s2\">\"</span>\n        <span class=\"nv\">CURRENT</span><span class=\"o\">=</span><span class=\"s2\">\"</span><span class=\"nv\">$RELEASE</span><span class=\"s2\">\"</span>\n    <span class=\"k\">else\n        </span><span class=\"nv\">NEXT</span><span class=\"o\">=</span><span class=\"s2\">\"</span><span class=\"nv\">$RELEASE</span><span class=\"s2\">\"</span>\n        <span class=\"nv\">CURRENT</span><span class=\"o\">=</span><span class=\"s2\">\"</span><span class=\"nv\">$BETA</span><span class=\"s2\">\"</span>\n    <span class=\"k\">fi\n\n    if</span> <span class=\"o\">[</span> <span class=\"s2\">\"</span><span class=\"nv\">$1</span><span class=\"s2\">\"</span> <span class=\"o\">=</span> <span class=\"nb\">true</span> <span class=\"o\">]</span>\n    <span class=\"k\">then\n        </span><span class=\"nb\">sudo </span>xcode-select <span class=\"nt\">-s</span> <span class=\"s2\">\"/Applications/</span><span class=\"nv\">$NEXT</span><span class=\"s2\">\"</span>\n        <span class=\"nb\">echo</span> <span class=\"s2\">\"Switched to </span><span class=\"nv\">$NEXT</span><span class=\"s2\">\"</span>\n    <span class=\"k\">else\n        </span><span class=\"nb\">echo</span> <span class=\"s2\">\"Current: </span><span class=\"nv\">$CURRENT</span><span class=\"s2\">\"</span>\n    <span class=\"k\">fi</span>\n<span class=\"o\">}</span>\n</code></pre></div></div>\n\n<p>If you’d like to use this, you can paste the code above into your <code class=\"language-plaintext highlighter-rouge\">.zprofile</code> (or similar). Here’s an example usage and output:</p>\n\n<div class=\"language-zsh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"o\">&gt;</span> xcwhich\nCurrent: Xcode.app\n\nRELEASE\nXcode 13.2.1\nBuild version 13C100\nswift-driver version: 1.26.21 Apple Swift version 5.5.2 <span class=\"o\">(</span>swiftlang-1300.0.47.5 clang-1300.0.29.30<span class=\"o\">)</span>\nTarget: x86_64-apple-macosx12.0\n\nBETA\nXcode 13.3\nBuild version 13E5104i\nswift-driver version: 1.45.2 Apple Swift version 5.6 <span class=\"o\">(</span>swiftlang-5.6.0.323.60 clang-1316.0.20.8<span class=\"o\">)</span>\nTarget: x86_64-apple-macosx12.0\n</code></pre></div></div>\n\n<p>An additional benefit of this is that you can see the <em>exact</em> Swift version that is bundled with the stable release of Xcode. Typically, point releases of Xcode bump the Swift version too. In this case, Xcode 13.2.x ships with Swift 5.5.2. This rarely matters for development purposes as you often only need to know which major and minor version of Swift that you are using — Swift 5.5 in this example. However, it is convenient to see the complete version information in one place.</p>\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2022/03/04/quickly-displaying-the-swift-version-that-ships-with-xcode/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2020/12/22/swift-self-executing-anonymous-closures/#updated-02-march-2022",
            "url": "https://www.jessesquires.com/blog/2020/12/22/swift-self-executing-anonymous-closures/",
            "title": "[Updated] What type is self in a Swift self-executing anonymous closure used to initialize a stored property?: The answer might surprise you",
            "date_published": "2022-03-02T11:12:41-08:00",
            "date_modified": "2022-03-02T11:12:41-08:00",
            "summary": "<p>In JavaScript, this <a href=\"https://developer.mozilla.org/en-US/docs/Glossary/IIFE\">pattern is called</a> an Immediately Invoked Function Expression (IIFE) or a Self-Executing Anonymous Function. Swift doesn’t have an “official” name for this, but IIFE works as well as “immediately executed anonymous closure” or “self-executing anonymous closure”. (Thanks to folks on Twitter for helping with this.)</p>\n\n",
            "tags": [
                "ios","swift","closures","debugging","uikit",
                "software-dev"
            ],
            "content_html": "<p>In JavaScript, this <a href=\"https://developer.mozilla.org/en-US/docs/Glossary/IIFE\">pattern is called</a> an Immediately Invoked Function Expression (IIFE) or a Self-Executing Anonymous Function. Swift doesn’t have an “official” name for this, but IIFE works as well as “immediately executed anonymous closure” or “self-executing anonymous closure”. (Thanks to folks on Twitter for helping with this.)</p>\n\n<!--excerpt-->\n\n<p>Most Swift developers have seen and used this now-common approach to initialize properties for a type:</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">class</span> <span class=\"kt\">MyClass</span> <span class=\"p\">{</span>\n\n    <span class=\"k\">let</span> <span class=\"nv\">dateFormatter</span><span class=\"p\">:</span> <span class=\"kt\">DateFormatter</span> <span class=\"o\">=</span> <span class=\"p\">{</span>\n        <span class=\"k\">let</span> <span class=\"nv\">df</span> <span class=\"o\">=</span> <span class=\"kt\">DateFormatter</span><span class=\"p\">()</span>\n        <span class=\"n\">df</span><span class=\"o\">.</span><span class=\"n\">timeStyle</span> <span class=\"o\">=</span> <span class=\"o\">.</span><span class=\"n\">medium</span>\n        <span class=\"n\">df</span><span class=\"o\">.</span><span class=\"n\">dateStyle</span> <span class=\"o\">=</span> <span class=\"o\">.</span><span class=\"n\">long</span>\n        <span class=\"k\">return</span> <span class=\"n\">df</span>\n    <span class=\"p\">}()</span>\n\n    <span class=\"c1\">// other members here...</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>It is a convenient and concise pattern that helps organize your code. This approach is especially popular when working with UIKit and defining custom views. It simplifies type initializers and is generally easy to read. Recently, I was using this approach and discovered a bug in my code, some very unexpected behavior in Swift, and <em>maybe</em> a bug in Swift.</p>\n\n<p>I was building a typical table view which contained cells that had a button subview. Tapping the entire cell and tapping the button within the cell performed different actions. You have probably built something similar before. Here is simplified sample code to illustrate:</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">class</span> <span class=\"kt\">MyTableCell</span><span class=\"p\">:</span> <span class=\"kt\">UITableViewCell</span> <span class=\"p\">{</span>\n\n    <span class=\"k\">let</span> <span class=\"nv\">button</span><span class=\"p\">:</span> <span class=\"kt\">UIButton</span> <span class=\"o\">=</span> <span class=\"p\">{</span>\n        <span class=\"k\">let</span> <span class=\"nv\">button</span> <span class=\"o\">=</span> <span class=\"kt\">UIButton</span><span class=\"p\">()</span>\n        <span class=\"n\">button</span><span class=\"o\">.</span><span class=\"nf\">setTitle</span><span class=\"p\">(</span><span class=\"s\">\"Title\"</span><span class=\"p\">,</span> <span class=\"nv\">for</span><span class=\"p\">:</span> <span class=\"o\">.</span><span class=\"n\">normal</span><span class=\"p\">)</span>\n        <span class=\"n\">button</span><span class=\"o\">.</span><span class=\"nf\">addTarget</span><span class=\"p\">(</span><span class=\"k\">self</span><span class=\"p\">,</span> <span class=\"nv\">action</span><span class=\"p\">:</span> <span class=\"k\">#selector</span><span class=\"p\">(</span><span class=\"nf\">didTapButton</span><span class=\"p\">(</span><span class=\"nv\">_</span><span class=\"p\">:)),</span> <span class=\"nv\">for</span><span class=\"p\">:</span> <span class=\"o\">.</span><span class=\"n\">touchUpInside</span><span class=\"p\">)</span>\n        <span class=\"k\">return</span> <span class=\"n\">button</span>\n    <span class=\"p\">}()</span>\n\n    <span class=\"k\">override</span> <span class=\"nf\">init</span><span class=\"p\">(</span><span class=\"nv\">style</span><span class=\"p\">:</span> <span class=\"kt\">UITableViewCell</span><span class=\"o\">.</span><span class=\"kt\">CellStyle</span><span class=\"p\">,</span> <span class=\"nv\">reuseIdentifier</span><span class=\"p\">:</span> <span class=\"kt\">String</span><span class=\"p\">?)</span> <span class=\"p\">{</span>\n        <span class=\"k\">super</span><span class=\"o\">.</span><span class=\"nf\">init</span><span class=\"p\">(</span><span class=\"nv\">style</span><span class=\"p\">:</span> <span class=\"n\">style</span><span class=\"p\">,</span> <span class=\"nv\">reuseIdentifier</span><span class=\"p\">:</span> <span class=\"n\">reuseIdentifier</span><span class=\"p\">)</span>\n        <span class=\"k\">self</span><span class=\"o\">.</span><span class=\"n\">contentView</span><span class=\"o\">.</span><span class=\"nf\">addSubview</span><span class=\"p\">(</span><span class=\"k\">self</span><span class=\"o\">.</span><span class=\"n\">button</span><span class=\"p\">)</span>\n        <span class=\"c1\">// add constraints, other setup, etc ...</span>\n    <span class=\"p\">}</span>\n\n    <span class=\"kd\">@objc</span>\n    <span class=\"kd\">func</span> <span class=\"nf\">didTapButton</span><span class=\"p\">(</span><span class=\"n\">_</span> <span class=\"nv\">sender</span><span class=\"p\">:</span> <span class=\"kt\">UIButton</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n        <span class=\"c1\">// do something</span>\n    <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>I was building such a routine, mundane feature that I completed a large portion of the code before even running and testing it. I’ve written code like this thousands of times. So, you can imagine my surprise when the button inside the cell was not triggering the action. Tapping the button did nothing, but everything else seemed to work as expected. There’s a bug in that code above. Can you find it? I scoured line after line, trying to find what I missed. What I had overlooked out of habit?</p>\n\n<p>The problem was the call to <code class=\"language-plaintext highlighter-rouge\">addTarget(_:, action:, for:)</code>. Moving this line to the initializer fixed the issue, and everything worked as expected.</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">override</span> <span class=\"nf\">init</span><span class=\"p\">(</span><span class=\"nv\">style</span><span class=\"p\">:</span> <span class=\"kt\">UITableViewCell</span><span class=\"o\">.</span><span class=\"kt\">CellStyle</span><span class=\"p\">,</span> <span class=\"nv\">reuseIdentifier</span><span class=\"p\">:</span> <span class=\"kt\">String</span><span class=\"p\">?)</span> <span class=\"p\">{</span>\n    <span class=\"k\">super</span><span class=\"o\">.</span><span class=\"nf\">init</span><span class=\"p\">(</span><span class=\"nv\">style</span><span class=\"p\">:</span> <span class=\"n\">style</span><span class=\"p\">,</span> <span class=\"nv\">reuseIdentifier</span><span class=\"p\">:</span> <span class=\"n\">reuseIdentifier</span><span class=\"p\">)</span>\n    <span class=\"k\">self</span><span class=\"o\">.</span><span class=\"n\">contentView</span><span class=\"o\">.</span><span class=\"nf\">addSubview</span><span class=\"p\">(</span><span class=\"k\">self</span><span class=\"o\">.</span><span class=\"n\">button</span><span class=\"p\">)</span>\n\n    <span class=\"c1\">// fix: add target/action here</span>\n    <span class=\"k\">self</span><span class=\"o\">.</span><span class=\"n\">button</span><span class=\"o\">.</span><span class=\"nf\">addTarget</span><span class=\"p\">(</span><span class=\"k\">self</span><span class=\"p\">,</span> <span class=\"nv\">action</span><span class=\"p\">:</span> <span class=\"k\">#selector</span><span class=\"p\">(</span><span class=\"nf\">didTapButton</span><span class=\"p\">(</span><span class=\"nv\">_</span><span class=\"p\">:)),</span> <span class=\"nv\">for</span><span class=\"p\">:</span> <span class=\"o\">.</span><span class=\"n\">touchUpInside</span><span class=\"p\">)</span>\n\n    <span class=\"c1\">// add constraints, other setup, etc ...</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>Upon discovering <em>what</em> the problem was, it was now time to ask <em>why</em>. My initial intuition was the following:</p>\n\n<ol>\n  <li>The <code class=\"language-plaintext highlighter-rouge\">button</code> is a stored constant property that gets initialized immediately via a self-executing anonymous closure</li>\n  <li>Swift’s initialization rules mean that stored properties (with values, like this one) <em>must</em> be initialized <em>before</em> the type that owns them</li>\n  <li>Thus, at the time that <code class=\"language-plaintext highlighter-rouge\">button</code> is initialized (when the closure is executed), <code class=\"language-plaintext highlighter-rouge\">self</code> must be <code class=\"language-plaintext highlighter-rouge\">nil</code></li>\n  <li>The method <code class=\"language-plaintext highlighter-rouge\">addTarget(_:, action:, for:)</code> accepts <code class=\"language-plaintext highlighter-rouge\">Any?</code> for the target parameter, so passing <code class=\"language-plaintext highlighter-rouge\">nil</code> would be ok</li>\n  <li>Conclusion: <code class=\"language-plaintext highlighter-rouge\">self</code> was just <code class=\"language-plaintext highlighter-rouge\">nil</code> the whole time! What a goofy mistake!</li>\n</ol>\n\n<p>However, that was not the case. Specifically, <code class=\"language-plaintext highlighter-rouge\">self</code> <strong>was not</strong> <code class=\"language-plaintext highlighter-rouge\">nil</code>. Not only that, but <code class=\"language-plaintext highlighter-rouge\">self</code> wasn’t the <code class=\"language-plaintext highlighter-rouge\">self</code> I expected. Looking at this snippet, can you determine what type <code class=\"language-plaintext highlighter-rouge\">self</code> is and why?</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">class</span> <span class=\"kt\">MyTableCell</span><span class=\"p\">:</span> <span class=\"kt\">UITableViewCell</span> <span class=\"p\">{</span>\n\n    <span class=\"k\">let</span> <span class=\"nv\">button</span><span class=\"p\">:</span> <span class=\"kt\">UIButton</span> <span class=\"o\">=</span> <span class=\"p\">{</span>\n        <span class=\"k\">let</span> <span class=\"nv\">button</span> <span class=\"o\">=</span> <span class=\"kt\">UIButton</span><span class=\"p\">()</span>\n        <span class=\"n\">button</span><span class=\"o\">.</span><span class=\"nf\">setTitle</span><span class=\"p\">(</span><span class=\"s\">\"Title\"</span><span class=\"p\">,</span> <span class=\"nv\">for</span><span class=\"p\">:</span> <span class=\"o\">.</span><span class=\"n\">normal</span><span class=\"p\">)</span>\n\n        <span class=\"c1\">// what is self?</span>\n        <span class=\"n\">button</span><span class=\"o\">.</span><span class=\"nf\">addTarget</span><span class=\"p\">(</span><span class=\"k\">self</span><span class=\"p\">,</span> <span class=\"nv\">action</span><span class=\"p\">:</span> <span class=\"k\">#selector</span><span class=\"p\">(</span><span class=\"nf\">didTapButton</span><span class=\"p\">(</span><span class=\"nv\">_</span><span class=\"p\">:)),</span> <span class=\"nv\">for</span><span class=\"p\">:</span> <span class=\"o\">.</span><span class=\"n\">touchUpInside</span><span class=\"p\">)</span>\n\n        <span class=\"k\">return</span> <span class=\"n\">button</span>\n    <span class=\"p\">}()</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>I paused execution in the debugger to check if <code class=\"language-plaintext highlighter-rouge\">po self</code> printed <code class=\"language-plaintext highlighter-rouge\">nil</code>. Instead, it printed <code class=\"language-plaintext highlighter-rouge\">(Function)</code>. In Swift, <a href=\"https://docs.swift.org/swift-book/LanguageGuide/Closures.html\">closures are first-class reference types</a>. Thus, I concluded that <code class=\"language-plaintext highlighter-rouge\">self</code> is referring to <strong>the closure</strong>, which is the type <code class=\"language-plaintext highlighter-rouge\">() -&gt; UIButton</code>. Right? Actually… no.</p>\n\n<p>Continuing the investigation, I printed <code class=\"language-plaintext highlighter-rouge\">type(of: self)</code> which returned <code class=\"language-plaintext highlighter-rouge\">(MyTableCell) -&gt; () -&gt; MyTableCell</code>. What?! This is a closure that receives a <code class=\"language-plaintext highlighter-rouge\">MyTableCell</code> instance and returns a closure with the type <code class=\"language-plaintext highlighter-rouge\">() -&gt; MyTableCell</code>, which accepts nothing and returns a <code class=\"language-plaintext highlighter-rouge\">MyTableCell</code>. I do not understand why this is the case.</p>\n\n<p>I think most devs would share my initial intuition that <code class=\"language-plaintext highlighter-rouge\">self</code> refers to <code class=\"language-plaintext highlighter-rouge\">MyTableCell</code> here, because that is true in other patterns that <em>look similar</em> to this one, like <a href=\"https://docs.swift.org/swift-book/LanguageGuide/Properties.html#ID259\">computed properties</a>. Despite this, once realizing that <code class=\"language-plaintext highlighter-rouge\">self</code> is a function, I think most devs would also intuit that the type <strong>must</strong> be <code class=\"language-plaintext highlighter-rouge\">() -&gt; UIButton</code>.</p>\n\n<p>This raises some interesting questions.</p>\n\n<p>First, I realize that the <code class=\"language-plaintext highlighter-rouge\">#selector</code> construct, which was introduced by <a href=\"https://github.com/apple/swift-evolution/blob/main/proposals/0022-objc-selectors.md\">SE-0022</a>, has limited abilities. As mentioned in the <a href=\"https://github.com/apple/swift-evolution/blob/main/proposals/0022-objc-selectors.md#alternatives-considered\">“Alternatives considered”</a> section of the proposal, <code class=\"language-plaintext highlighter-rouge\">#selector</code> is <strong>not</strong> type-safe. However, <em>it is</em> capable of determining if a selector is in scope. If you attempt to pass a selector from another class, the compiler will provide a warning. For example, considering the following:</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">class</span> <span class=\"kt\">OuterClass</span> <span class=\"p\">{</span>\n    <span class=\"kd\">func</span> <span class=\"nf\">button</span><span class=\"p\">()</span> <span class=\"o\">-&gt;</span> <span class=\"kt\">UIButton</span> <span class=\"p\">{</span>\n        <span class=\"k\">let</span> <span class=\"nv\">button</span> <span class=\"o\">=</span> <span class=\"kt\">UIButton</span><span class=\"p\">()</span>\n        <span class=\"n\">button</span><span class=\"o\">.</span><span class=\"nf\">addTarget</span><span class=\"p\">(</span><span class=\"k\">self</span><span class=\"p\">,</span> <span class=\"nv\">action</span><span class=\"p\">:</span> <span class=\"k\">#selector</span><span class=\"p\">(</span><span class=\"nf\">didTapButton</span><span class=\"p\">(</span><span class=\"nv\">_</span><span class=\"p\">:)),</span> <span class=\"nv\">for</span><span class=\"p\">:</span> <span class=\"o\">.</span><span class=\"n\">touchUpInside</span><span class=\"p\">)</span>\n        <span class=\"k\">return</span> <span class=\"n\">button</span>\n    <span class=\"p\">}</span>\n\n    <span class=\"kd\">class</span> <span class=\"kt\">InnerClass</span> <span class=\"p\">{</span>\n        <span class=\"kd\">@objc</span>\n        <span class=\"kd\">func</span> <span class=\"nf\">didTapButton</span><span class=\"p\">(</span><span class=\"n\">_</span> <span class=\"nv\">sender</span><span class=\"p\">:</span> <span class=\"kt\">UIButton</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n            <span class=\"c1\">// do something</span>\n        <span class=\"p\">}</span>\n    <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>This produces the expected error: <code class=\"language-plaintext highlighter-rouge\">Cannot find 'didTapButton' in scope</code>. It is not clear to me how this is distinct from the <code class=\"language-plaintext highlighter-rouge\">MyTableCell</code> example regarding the scope of the selector. How is <code class=\"language-plaintext highlighter-rouge\">didTapButton(_:)</code> in scope for <code class=\"language-plaintext highlighter-rouge\">self</code> if <code class=\"language-plaintext highlighter-rouge\">self</code> <strong>is not</strong> an instance of <code class=\"language-plaintext highlighter-rouge\">MyTableCell</code>? Is this a bug with <code class=\"language-plaintext highlighter-rouge\">#selector</code>?</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-23-december-2020\">\n        <a href=\"#updated-23-december-2020\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"23 Dec 2020 01:24:29 AM PST\">\n            <i>23 December 2020</i>\n        </small>\n    </h5>\n    \n<p>This is not entirely accurate. You <em>could</em> write <code class=\"language-plaintext highlighter-rouge\">#selector(InnerClass.didTapButton(_:))</code>, which successfully compiles and is consistent with <a href=\"https://github.com/apple/swift-evolution/blob/main/proposals/0022-objc-selectors.md\">SE-0022</a>.</p>\n\n</div>\n\n<p>Second, why is <code class=\"language-plaintext highlighter-rouge\">self</code> an instance of <code class=\"language-plaintext highlighter-rouge\">(MyTableCell) -&gt; () -&gt; MyTableCell</code> and <strong>not</strong> <code class=\"language-plaintext highlighter-rouge\">() -&gt; UIButton</code>? Is this a bug in Swift?</p>\n\n<p>I’m hoping someone can answer these two questions and explain what is happening. If so, I’ll update this post!</p>\n\n<p>In any case, referencing <code class=\"language-plaintext highlighter-rouge\">self</code> in self-executing anonymous closures for stored properties should be discouraged, or avoided entirely! That <code class=\"language-plaintext highlighter-rouge\">self</code> is perhaps not the <code class=\"language-plaintext highlighter-rouge\">self</code> you might have been expecting. Watch yourself.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-22-december-2020\">\n        <a href=\"#updated-22-december-2020\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"22 Dec 2020 09:14:21 PM PST\">\n            <i>22 December 2020</i>\n        </small>\n    </h5>\n    \n<p>If you declare <code class=\"language-plaintext highlighter-rouge\">button</code> as <code class=\"language-plaintext highlighter-rouge\">lazy var</code> instead of <code class=\"language-plaintext highlighter-rouge\">let</code>, then the expected behavior occurs. That is, <code class=\"language-plaintext highlighter-rouge\">self</code> is an instance of <code class=\"language-plaintext highlighter-rouge\">MyTableCell</code> within the self-executing anonymous closure and the call to <code class=\"language-plaintext highlighter-rouge\">addTarget(_:, action:, for:)</code> works. (Thanks <a href=\"https://twitter.com/Geri_Borbas/status/1341594268293586944\">@Geri_Borbas</a>.) Also noteworthy, in this situation the initializer for <code class=\"language-plaintext highlighter-rouge\">MyTableCell</code> gets called <strong>before</strong> the <code class=\"language-plaintext highlighter-rouge\">button</code> closure is executed. However, this makes the situation more confusing — using <code class=\"language-plaintext highlighter-rouge\">let</code> versus <code class=\"language-plaintext highlighter-rouge\">lazy var</code> produces significantly different behavior that is not intuitive at all.</p>\n\n<p><del>The type <code class=\"language-plaintext highlighter-rouge\">(MyTableCell) -&gt; () -&gt; MyTableCell</code> appears to refer to the initializer for <code class=\"language-plaintext highlighter-rouge\">MyTableCell</code>. (Thanks <a href=\"https://twitter.com/eneko/status/1341605571984642048\">@eneko</a>.)</del> No, this is not the case.</p>\n\n</div>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-23-december-2020\">\n        <a href=\"#updated-23-december-2020\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"23 Dec 2020 01:24:29 AM PST\">\n            <i>23 December 2020</i>\n        </small>\n    </h5>\n    \n<p>Thanks to <a href=\"https://twitter.com/elliottwil/status/1341632285200683009\">@elliottwil</a> and <a href=\"https://twitter.com/noahsark769/status/1341635028657180672\">Noah Gilmore</a> for uncovering and investigating the true underlying issues here. (<a href=\"https://xkcd.com/356/\">Nerd sniped</a>. 😄) The type of <code class=\"language-plaintext highlighter-rouge\">self</code> resolving to <code class=\"language-plaintext highlighter-rouge\">(MyTableCell) -&gt; () -&gt; MyTableCell</code> is the unfortunate result of the <code class=\"language-plaintext highlighter-rouge\">NSObject</code> instance method <a href=\"https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418954-self?language=objc\"><code class=\"language-plaintext highlighter-rouge\">-[NSObject self]</code></a> and Swift’s <a href=\"https://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/\">curried functions</a>. (Note that <a href=\"https://github.com/apple/swift-evolution/blob/main/proposals/0002-remove-currying.md\">SE-0002</a> removed currying <code class=\"language-plaintext highlighter-rouge\">func</code> declaration syntax but did not change the semantics of methods.)</p>\n\n<p>Update continued below.</p>\n\n</div>\n\n<p>There are multiple layers of confusion here. Let’s peel back each one. First, let’s revisit the problematic code:</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">class</span> <span class=\"kt\">MyTableCell</span><span class=\"p\">:</span> <span class=\"kt\">UITableViewCell</span> <span class=\"p\">{</span>\n    <span class=\"k\">let</span> <span class=\"nv\">button</span><span class=\"p\">:</span> <span class=\"kt\">UIButton</span> <span class=\"o\">=</span> <span class=\"p\">{</span>\n        <span class=\"k\">let</span> <span class=\"nv\">button</span> <span class=\"o\">=</span> <span class=\"kt\">UIButton</span><span class=\"p\">()</span>\n\n        <span class=\"c1\">// what is self?</span>\n        <span class=\"n\">button</span><span class=\"o\">.</span><span class=\"nf\">addTarget</span><span class=\"p\">(</span><span class=\"k\">self</span><span class=\"p\">,</span> <span class=\"nv\">action</span><span class=\"p\">:</span> <span class=\"k\">#selector</span><span class=\"p\">(</span><span class=\"nf\">didTapButton</span><span class=\"p\">(</span><span class=\"nv\">_</span><span class=\"p\">:)),</span> <span class=\"nv\">for</span><span class=\"p\">:</span> <span class=\"o\">.</span><span class=\"n\">touchUpInside</span><span class=\"p\">)</span>\n\n        <span class=\"nf\">print</span><span class=\"p\">(</span><span class=\"nf\">type</span><span class=\"p\">(</span><span class=\"nv\">of</span><span class=\"p\">:</span> <span class=\"k\">self</span><span class=\"p\">))</span> <span class=\"c1\">// (MyTableCell) -&gt; () -&gt; MyTableCell</span>\n\n        <span class=\"k\">return</span> <span class=\"n\">button</span>\n    <span class=\"p\">}()</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>We know that <code class=\"language-plaintext highlighter-rouge\">self</code> is of type <code class=\"language-plaintext highlighter-rouge\">(MyTableCell) -&gt; () -&gt; MyTableCell</code> because of the <code class=\"language-plaintext highlighter-rouge\">-[NSObject self]</code> instance method. And this satisfies the (lack of) type constraints on the <code class=\"language-plaintext highlighter-rouge\">target</code> parameter, <code class=\"language-plaintext highlighter-rouge\">Any?</code>. This answers the question: why is <code class=\"language-plaintext highlighter-rouge\">self</code> an instance of <code class=\"language-plaintext highlighter-rouge\">(MyTableCell) -&gt; () -&gt; MyTableCell</code> and <strong>not</strong> <code class=\"language-plaintext highlighter-rouge\">() -&gt; UIButton</code>? We can verify by printing to the console:</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nf\">print</span><span class=\"p\">(</span><span class=\"nf\">type</span><span class=\"p\">(</span><span class=\"nv\">of</span><span class=\"p\">:</span> <span class=\"kt\">NSObject</span><span class=\"o\">.</span><span class=\"p\">`</span><span class=\"nv\">self</span><span class=\"p\">`))</span> <span class=\"c1\">// prints (NSObject) -&gt; () -&gt; NSObject</span>\n</code></pre></div></div>\n\n<p>Note that <code class=\"language-plaintext highlighter-rouge\">NSObject.self</code> (without the backticks) refers to the <a href=\"https://docs.swift.org/swift-book/ReferenceManual/Types.html#ID455\">metatype type</a> <code class=\"language-plaintext highlighter-rouge\">NSObject</code>, <strong>not</strong> the <a href=\"https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418954-self?language=objc\">instance method</a> <code class=\"language-plaintext highlighter-rouge\">-(instancetype)self</code>. Thus, the expression <code class=\"language-plaintext highlighter-rouge\">type(of: NSObject.self)</code> returns <code class=\"language-plaintext highlighter-rouge\">NSObject.Type</code>. Because <code class=\"language-plaintext highlighter-rouge\">self</code> is a reserved keyword, we must write it with backticks to reference the correct “self”. (You may have seen this before if you have ever tried to define an <code class=\"language-plaintext highlighter-rouge\">enum</code> case called “default”.)</p>\n\n<p>We can make this explicit in the code:</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">class</span> <span class=\"kt\">MyTableCell</span><span class=\"p\">:</span> <span class=\"kt\">UITableViewCell</span> <span class=\"p\">{</span>\n    <span class=\"k\">let</span> <span class=\"nv\">button</span><span class=\"p\">:</span> <span class=\"kt\">UIButton</span> <span class=\"o\">=</span> <span class=\"p\">{</span>\n        <span class=\"k\">let</span> <span class=\"nv\">button</span> <span class=\"o\">=</span> <span class=\"kt\">UIButton</span><span class=\"p\">()</span>\n\n        <span class=\"c1\">// use `self`</span>\n        <span class=\"n\">button</span><span class=\"o\">.</span><span class=\"nf\">addTarget</span><span class=\"p\">(`</span><span class=\"nv\">self</span><span class=\"p\">`,</span> <span class=\"nv\">action</span><span class=\"p\">:</span> <span class=\"k\">#selector</span><span class=\"p\">(</span><span class=\"nf\">didTapButton</span><span class=\"p\">(</span><span class=\"nv\">_</span><span class=\"p\">:)),</span> <span class=\"nv\">for</span><span class=\"p\">:</span> <span class=\"o\">.</span><span class=\"n\">touchUpInside</span><span class=\"p\">)</span>\n\n        <span class=\"nf\">print</span><span class=\"p\">(</span><span class=\"nf\">type</span><span class=\"p\">(</span><span class=\"nv\">of</span><span class=\"p\">:</span> <span class=\"p\">`</span><span class=\"nv\">self</span><span class=\"p\">`))</span> <span class=\"c1\">// (MyTableCell) -&gt; () -&gt; MyTableCell</span>\n\n        <span class=\"k\">return</span> <span class=\"n\">button</span>\n    <span class=\"p\">}()</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>Then we can make it even more explicit:</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">// Xcode's documentation popup correctly refers to the declaration let `self`: MyTableCell</span>\n<span class=\"nf\">print</span><span class=\"p\">(</span><span class=\"nf\">type</span><span class=\"p\">(</span><span class=\"nv\">of</span><span class=\"p\">:</span> <span class=\"kt\">MyTableCell</span><span class=\"o\">.</span><span class=\"p\">`</span><span class=\"nv\">self</span><span class=\"p\">`))</span> <span class=\"c1\">// let `self`: MyTableCell</span>\n</code></pre></div></div>\n\n<p>With this change, we can see that this is clearly wrong. Additionally, Xcode will syntax highlight “self” differently depending on if there are backticks or not.</p>\n\n<p>Next, which is now obvious, this only happens with classes that inherit from <code class=\"language-plaintext highlighter-rouge\">NSObject</code>. Because I was using <code class=\"language-plaintext highlighter-rouge\">UITableViewCell</code>, this was not something I considered at first. As an example, the following code will produce an error:</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">class</span> <span class=\"kt\">MyClass</span> <span class=\"p\">{</span>\n    <span class=\"k\">let</span> <span class=\"nv\">str</span><span class=\"p\">:</span> <span class=\"kt\">String</span> <span class=\"o\">=</span> <span class=\"p\">{</span>\n        <span class=\"nf\">print</span><span class=\"p\">(</span><span class=\"k\">self</span><span class=\"p\">)</span> <span class=\"c1\">// &lt;-- Cannot find 'self' in scope</span>\n        <span class=\"k\">return</span> <span class=\"s\">\"String\"</span>\n    <span class=\"p\">}()</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>Now we can attempt to answer my two remaining questions.</p>\n\n<p>Is this a bug with <code class=\"language-plaintext highlighter-rouge\">#selector</code>? No. This is the expected behavior of <code class=\"language-plaintext highlighter-rouge\">#selector</code> as described in <a href=\"https://github.com/apple/swift-evolution/blob/main/proposals/0022-objc-selectors.md\">SE-0022</a>. We could have written <code class=\"language-plaintext highlighter-rouge\">#selector(Self.didTapButton(_:))</code> instead, which is equivalent to <code class=\"language-plaintext highlighter-rouge\">#selector(MyTableCell.didTapButton(_:))</code>. The <code class=\"language-plaintext highlighter-rouge\">#selector</code> expression is not capable of checking itself against the <code class=\"language-plaintext highlighter-rouge\">target</code> parameter.</p>\n\n<p>Is this a bug in Swift? In my opinion, <strong>yes</strong>. I think the correct behavior would be that using <code class=\"language-plaintext highlighter-rouge\">self</code> <strong>without</strong> backticks always references the enclosing type (which happens when using <code class=\"language-plaintext highlighter-rouge\">lazy var</code>, as mentioned above) and using <code class=\"language-plaintext highlighter-rouge\">`self`</code> <strong>with</strong> backticks references <code class=\"language-plaintext highlighter-rouge\">-[NSObject self]</code> (which you almost never want). The fact that Xcode correctly syntax highlights each of these indicates that the correct information exists somewhere.</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">class</span> <span class=\"kt\">MyTableCell</span><span class=\"p\">:</span> <span class=\"kt\">UITableViewCell</span> <span class=\"p\">{</span>\n    <span class=\"k\">let</span> <span class=\"nv\">button</span><span class=\"p\">:</span> <span class=\"kt\">UIButton</span> <span class=\"o\">=</span> <span class=\"p\">{</span>\n\n        <span class=\"nf\">print</span><span class=\"p\">(</span><span class=\"nf\">type</span><span class=\"p\">(</span><span class=\"nv\">of</span><span class=\"p\">:</span> <span class=\"k\">self</span><span class=\"p\">))</span> <span class=\"c1\">// Bug: should be MyTableCell. Instead, same as `self` below.</span>\n\n        <span class=\"nf\">print</span><span class=\"p\">(</span><span class=\"nf\">type</span><span class=\"p\">(</span><span class=\"nv\">of</span><span class=\"p\">:</span> <span class=\"p\">`</span><span class=\"nv\">self</span><span class=\"p\">`))</span> <span class=\"c1\">// (MyTableCell) -&gt; () -&gt; MyTableCell</span>\n\n        <span class=\"k\">return</span> <span class=\"kt\">UIButton</span><span class=\"p\">()</span>\n    <span class=\"p\">}()</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>In other words, these two expressions are distinct in this context:</p>\n\n<div class=\"language-swift highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">// refers to instance of MyTableCell</span>\n<span class=\"k\">self</span>\n\n<span class=\"c1\">// refers to func `self`() -&gt; Self</span>\n<span class=\"kt\">MyTableCell</span><span class=\"o\">.</span><span class=\"p\">`</span><span class=\"nv\">self</span><span class=\"p\">`</span>\n</code></pre></div></div>\n\n<p>Yet, the Swift compiler treats them both as <code class=\"language-plaintext highlighter-rouge\">func `self`() -&gt; Self</code>.</p>\n\n<p>However, there is one last problem. Correcting the expression <code class=\"language-plaintext highlighter-rouge\">self</code> (without backticks) to reference the enclosing type introduces another interesting question: what should be order of operations during initialization? When using <code class=\"language-plaintext highlighter-rouge\">let</code>, the property is initialized <strong>before</strong> the enclosing type. When using <code class=\"language-plaintext highlighter-rouge\">lazy var</code>, the property is initialized <strong>after</strong> the enclosing type. I am not a compiler expert, so I will not attempt to answer which is better. But if initialization order cannot be changed in the compiler to fix this, then I think the expected behavior would be to produce the same error as non-<code class=\"language-plaintext highlighter-rouge\">NSObject</code> classes: “Cannot find ‘self’ in scope”.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-24-december-2020\">\n        <a href=\"#updated-24-december-2020\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"24 Dec 2020 11:18:19 AM PST\">\n            <i>24 December 2020</i>\n        </small>\n    </h5>\n    \n<p>This bug is being tracked at <a href=\"https://bugs.swift.org/browse/SR-4559\">SR-4559</a> and <a href=\"https://bugs.swift.org/browse/SR-4865\">SR-4865</a> (duplicate). Thanks to <a href=\"https://bugs.swift.org/secure/ViewProfile.jspa?name=nolanw\">Nolan Waite</a> for sharing.</p>\n\n</div>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-05-november-2021\">\n        <a href=\"#updated-05-november-2021\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"05 Nov 2021 10:01:20 PM PDT\">\n            <i>05 November 2021</i>\n        </small>\n    </h5>\n    \n<p>As of September 2021, <a href=\"https://bugs.swift.org/browse/SR-4559\">SR-4559</a> has been marked as resolved. The <a href=\"https://github.com/apple/swift/pull/37992\">pull request is here</a> and according to the description the fix is to emit a warning indicating that the <code class=\"language-plaintext highlighter-rouge\">self</code> reference is likely not the one you intended.</p>\n\n<p>The fix is not yet included in Xcode 13.1 (13A1030d), nor Xcode 13.2 beta (13C5066c). However, the <a href=\"https://github.com/realm/SwiftLint/releases/tag/0.45.0\">latest release of SwiftLint</a> includes <a href=\"https://realm.github.io/SwiftLint/self_in_property_initialization.html\">a new rule</a> (<code class=\"language-plaintext highlighter-rouge\">self_in_property_initialization</code>) to emit a warning for this exact issue.</p>\n\n</div>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-02-march-2022\">\n        <a href=\"#updated-02-march-2022\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"02 Mar 2022 11:12:41 AM PST\">\n            <i>02 March 2022</i>\n        </small>\n    </h5>\n    \n<p>Good news! As of <a href=\"https://developer.apple.com/documentation/xcode-release-notes/xcode-13_3-release-notes\">Xcode 13.3 beta</a>, which includes Swift 5.6, the compiler now emits a warning for this issue.</p>\n\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2020/12/22/swift-self-executing-anonymous-closures/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2019/09/27/icloud-backup-using-rsync/#updated-21-february-2022",
            "url": "https://www.jessesquires.com/blog/2019/09/27/icloud-backup-using-rsync/",
            "title": "[Updated] Backing up your iCloud Drive files using rsync",
            "date_published": "2022-02-21T18:52:38-08:00",
            "date_modified": "2022-02-21T18:52:38-08:00",
            "summary": "<p>Unfortunately, iCloud <a href=\"https://mjtsai.com/blog/2019/07/23/trusting-icloud-drive/\">does not have a good reputation</a> for being reliable, especially during <a href=\"https://mjtsai.com/blog/2019/07/11/icloud-data-loss-with-macos-10-15-and-ios-13-betas/\">beta releases of iOS and macOS</a>. Yet a lot people still use it, often without any problems. <em>I still use it</em>, despite a few bad experiences in the past, because the best alternatives are <a href=\"https://www.drop-dropbox.com\">questionable</a> for <a href=\"https://www.theregister.co.uk/2016/05/26/dropbox_kernel_access/\">other reasons</a>. I’ve had good luck with iCloud Drive for the past few years, but I am terrified and paranoid of getting caught in the middle of an <a href=\"https://furbo.org/2019/09/04/icloud-clusterfuck/\">iCloud clusterfuck</a>, so I backup what I have in iCloud periodically using <code class=\"language-plaintext highlighter-rouge\">rsync</code>.</p>\n\n",
            "tags": [
                "icloud","apple","ios","macos",
                "software-dev"
            ],
            "content_html": "<p>Unfortunately, iCloud <a href=\"https://mjtsai.com/blog/2019/07/23/trusting-icloud-drive/\">does not have a good reputation</a> for being reliable, especially during <a href=\"https://mjtsai.com/blog/2019/07/11/icloud-data-loss-with-macos-10-15-and-ios-13-betas/\">beta releases of iOS and macOS</a>. Yet a lot people still use it, often without any problems. <em>I still use it</em>, despite a few bad experiences in the past, because the best alternatives are <a href=\"https://www.drop-dropbox.com\">questionable</a> for <a href=\"https://www.theregister.co.uk/2016/05/26/dropbox_kernel_access/\">other reasons</a>. I’ve had good luck with iCloud Drive for the past few years, but I am terrified and paranoid of getting caught in the middle of an <a href=\"https://furbo.org/2019/09/04/icloud-clusterfuck/\">iCloud clusterfuck</a>, so I backup what I have in iCloud periodically using <code class=\"language-plaintext highlighter-rouge\">rsync</code>.</p>\n\n<!--excerpt-->\n\n<h4 id=\"exploring-icloud-drive\">Exploring iCloud Drive</h4>\n\n<p>The iCloud Drive folder on your Mac is tricky. <a href=\"https://www.youtube.com/watch?v=mbi7rq-TSk8&amp;feature=youtu.be&amp;t=110\">The files are not what they seem</a>. iCloud Drive displays as a special directory in the sidebar of Finder. Its actual location on disk is at <code class=\"language-plaintext highlighter-rouge\">~/Library/Mobile Documents/</code>. If you <code class=\"language-plaintext highlighter-rouge\">cd</code> there and <code class=\"language-plaintext highlighter-rouge\">ls</code>, you will see its entire contents, most of which is not viewable via Finder.</p>\n\n<p>There appear to be three general types of directories present. After some digging around, I could not discern a difference between them. They have the following formats:</p>\n\n<ul>\n  <li>Developer ID or “App ID Prefix” (I think?) followed by bundle identifier. Examples:\n    <ul>\n      <li><code class=\"language-plaintext highlighter-rouge\">8Z3V4F58RK~com~ustwo~monumentvalley/</code></li>\n      <li><code class=\"language-plaintext highlighter-rouge\">F3LWYJ7GM7~com~apple~mobilegarageband/</code></li>\n      <li><code class=\"language-plaintext highlighter-rouge\">F3LWYJ7GM7~com~apple~musicmemos~ideas/</code></li>\n    </ul>\n  </li>\n  <li>Apple first-party apps with specific iCloud support, prefixed with <code class=\"language-plaintext highlighter-rouge\">com~apple~</code>. These correspond to the “special” app-specific folders that you see in Finder’s view of iCloud Drive. However, not all of these are present in Finder, like Notes and Mail. Examples:\n    <ul>\n      <li><code class=\"language-plaintext highlighter-rouge\">com~apple~Automator/</code></li>\n      <li><code class=\"language-plaintext highlighter-rouge\">com~apple~Keynote/</code></li>\n      <li><code class=\"language-plaintext highlighter-rouge\">com~apple~Preview/</code></li>\n      <li><code class=\"language-plaintext highlighter-rouge\">com~apple~TextEdit/</code></li>\n      <li><code class=\"language-plaintext highlighter-rouge\">com~apple~Notes/</code></li>\n      <li><code class=\"language-plaintext highlighter-rouge\">com~apple~mail/</code></li>\n    </ul>\n  </li>\n  <li>Bundle identifiers prefixed with <code class=\"language-plaintext highlighter-rouge\">iCloud~com~</code>. Examples:\n    <ul>\n      <li><code class=\"language-plaintext highlighter-rouge\">iCloud~com~agilebits~onepassword-ios/</code></li>\n      <li><code class=\"language-plaintext highlighter-rouge\">iCloud~com~apple~iBooks/</code></li>\n      <li><code class=\"language-plaintext highlighter-rouge\">iCloud~com~ustwo~monumentvalley2/</code></li>\n      <li><code class=\"language-plaintext highlighter-rouge\">iCloud~com~apple~mobilesafari/</code></li>\n    </ul>\n  </li>\n</ul>\n\n<p>Aside from the <code class=\"language-plaintext highlighter-rouge\">com~apple~</code>-prefixed directories, both first-party and third-party apps appear in both formats. My guess is that the difference is legacy vs modern naming conventions, especially considering <a href=\"https://apps.apple.com/us/app/monument-valley/id728293409\">MomumentValley</a> (2014) and <a href=\"https://apps.apple.com/us/app/monument-valley-2/id1187265767\">MonumentValley2</a> (2017).</p>\n\n<h4 id=\"backing-up-your-documents\">Backing up your documents</h4>\n\n<p>Now that we sort of understand the layout of <code class=\"language-plaintext highlighter-rouge\">~/Library/Mobile Documents/</code>, where the hell are <strong>our</strong> iCloud Drive documents stored? Those live in <code class=\"language-plaintext highlighter-rouge\">com~apple~CloudDocs/</code>. If you <code class=\"language-plaintext highlighter-rouge\">cd</code> there, you should see all of the “custom”, non-app-specific files that you’ve stored in iCloud Drive. These should match what is viewable in Finder.</p>\n\n<p>This is the directory that we want to backup. We can use <code class=\"language-plaintext highlighter-rouge\">rsync</code> to do that. (Side note: the way that <code class=\"language-plaintext highlighter-rouge\">rsync</code> handles paths is a bit odd. It doesn’t like relative paths, or <code class=\"language-plaintext highlighter-rouge\">~</code>, or escaping spaces in directory names. Thus, this script uses absolute paths with spaces.) You just need to fill-in the <code class=\"language-plaintext highlighter-rouge\">USER</code> and <code class=\"language-plaintext highlighter-rouge\">DEST</code> variables.</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\">#!/bin/bash</span>\n\n<span class=\"nv\">USER</span><span class=\"o\">=</span><span class=\"s2\">\"&lt;user&gt;\"</span>\n\n<span class=\"nv\">DEST</span><span class=\"o\">=</span><span class=\"s2\">\"&lt;destination dir&gt;\"</span>\n\n<span class=\"nv\">SRC</span><span class=\"o\">=</span><span class=\"s2\">\"/Users/</span><span class=\"nv\">$USER</span><span class=\"s2\">/Library/Mobile Documents/com~apple~CloudDocs/\"</span>\n\nrsync <span class=\"nt\">--verbose</span> <span class=\"nt\">--recursive</span> <span class=\"nt\">--delete-before</span> <span class=\"nt\">--whole-file</span> <span class=\"nt\">--times</span> <span class=\"nt\">--exclude</span><span class=\"o\">=</span><span class=\"s2\">\".DS_Store\"</span> <span class=\"nt\">--exclude</span><span class=\"o\">=</span><span class=\"s2\">\".Trash/\"</span> <span class=\"s2\">\"</span><span class=\"nv\">$SRC</span><span class=\"s2\">\"</span> <span class=\"s2\">\"</span><span class=\"nv\">$DEST</span><span class=\"s2\">\"</span>\n</code></pre></div></div>\n\n<p>Explanation of the options:</p>\n\n<ul>\n  <li><code class=\"language-plaintext highlighter-rouge\">--verbose</code>: increase verbosity</li>\n  <li><code class=\"language-plaintext highlighter-rouge\">--recursive</code>: recurse into directories</li>\n  <li><code class=\"language-plaintext highlighter-rouge\">--delete-before</code>: receiver deletes before transfer</li>\n  <li><code class=\"language-plaintext highlighter-rouge\">--whole-file</code>: copy files whole (without rsync algorithm)</li>\n  <li><code class=\"language-plaintext highlighter-rouge\">--times</code>: transfer modification times along with the files and update them on the remote system</li>\n  <li><code class=\"language-plaintext highlighter-rouge\">--exclude</code>: exclude files matching PATTERN</li>\n</ul>\n\n<p>You can use the <code class=\"language-plaintext highlighter-rouge\">--dry-run</code> option to preview what would be transfered without actually executing <code class=\"language-plaintext highlighter-rouge\">rsync</code>.</p>\n\n<p>This will backup <strong>only your</strong> iCloud Drive files. Any app-specific files (like Pages documents in <code class=\"language-plaintext highlighter-rouge\">com~apple~Pages/</code>) will not be included. If you want to backup any of those files, you will need to write a similar script with those target directories as the source. If you want to backup <em>everything</em>, specify the root <code class=\"language-plaintext highlighter-rouge\">~/Library/Mobile Documents/</code> directory — but beware, this might be <em>a lot</em> of data. For example, <code class=\"language-plaintext highlighter-rouge\">iCloud~com~apple~iBooks/</code> contains <em>all</em> of your synced iBooks that were not purchased from the iBook Store. I have a lot of those.</p>\n\n<p>That’s it! You can read the <code class=\"language-plaintext highlighter-rouge\">rsync</code> docs (<code class=\"language-plaintext highlighter-rouge\">man rsync</code>) for more details and options, but this should get you started.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-21-february-2022\">\n        <a href=\"#updated-21-february-2022\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"21 Feb 2022 06:52:38 PM \">\n            <i>21 February 2022</i>\n        </small>\n    </h5>\n    <p>Howard Oakley has published a helpful, detailed <a href=\"https://eclecticlight.co/2022/02/21/can-you-back-up-icloud-documents/\">guide about backing up iCloud documents</a>. I recommend reading it if you are interested in learning more.</p>\n\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2019/09/27/icloud-backup-using-rsync/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2022/02/15/mac-sigh-of-death/#updated-16-february-2022",
            "url": "https://www.jessesquires.com/blog/2022/02/15/mac-sigh-of-death/",
            "title": "[Updated] The MacBook sigh of death",
            "date_published": "2022-02-16T11:39:03-08:00",
            "date_modified": "2022-02-16T11:39:03-08:00",
            "summary": "<p>I have been experiencing bizarre kernel panics with my Mac lately. I have a 2020 Intel MacBook Pro, the last Intel model before the M1 debuted. It has generally been working fine. Despite poor software quality and numerous bugs lurking around in macOS, I rarely see kernel panics anymore. In fact, I can’t remember the last time I had a kernel panic before this issue. There have been no major changes on my machine and I’m on the latest version of Monterey.</p>\n\n",
            "tags": [
                "apple","bugs","macos","macbook",
                "software-dev"
            ],
            "content_html": "<p>I have been experiencing bizarre kernel panics with my Mac lately. I have a 2020 Intel MacBook Pro, the last Intel model before the M1 debuted. It has generally been working fine. Despite poor software quality and numerous bugs lurking around in macOS, I rarely see kernel panics anymore. In fact, I can’t remember the last time I had a kernel panic before this issue. There have been no major changes on my machine and I’m on the latest version of Monterey.</p>\n\n<!--excerpt-->\n\n<p>Here’s what happens. Suddenly the MacBook display turns off, the fans turn on full blast for a couple seconds (they are louder than compiling the largest Xcode project you can think of), and then the machine shuts down completely. I’m calling it the “sigh of death” because it’s as if the machine is simply giving up and exhaling in an exasperated, dramatic sigh of defeat — like an exaggerated cartoon character would do. It has happened to me 3-4 times within the past few weeks.</p>\n\n<div class=\"row\">\n    <div class=\"col\"></div>\n    <div class=\"d-flex justify-content-center col-12  col-sm-8 col-md-6 \">\n        <figure>\n            <img class=\"img-thumbnail img-fluid\" src=\"/img/blog/mac-sigh-of-death-1.jpg\" title=\"macOS screen of death\" alt=\"macOS screen of death\" />\n            <div class=\"row\">\n                <div class=\"col-12 d-flex justify-content-center\">\n                \n                    <figcaption>\n                        <p><small class=\"text-muted text-center\">\n                            macOS screen of death \n                        </small></p>\n                    </figcaption>\n                \n                </div>\n            </div>\n        </figure>\n    </div>\n    <div class=\"col\"></div>\n</div>\n\n<p>If you have been using a Mac for any amount of time, you have likely seen the “screen of death” at least once. If not, consider yourself lucky. Usually when this happens, it is preceded by some notable event — your machine freezes, your cursor starts beach-balling, everything becomes completely unresponsive, and then you’ll see the screen of death. It’s also usually silent, or at least, there’s no notable change with the fans.</p>\n\n<p>That’s why this is so peculiar. It seems to be random and unprovoked in terms of software — there are not any specific apps, scripts, tasks, etc. that seem to trigger it. Prior to the kernel panic, the machine is fully responsive and functioning like normal. In some cases, I am actively using it. However, there is one commonality across all incidents so far. Each time it has happened, it is <em>after</em> I unplug the machine from power. I typically work at a desk with the MacBook plugged in, and occasionally I’ll move to the kitchen table or couch and work off battery power for a bit. Each time the sigh of death has occurred, it is shortly after being unplugged. It makes me wonder if there’s some hardware issue?</p>\n\n<p>Upon rebooting, the “Problem Report for macOS” window appears as expected. Usually, the details section will contain a stack trace from which you can derive <em>some</em> kind of useful information about what happened — but not with the sigh of death. All it says is “Intel CrashLog recorded due to unexpected reset” — whatever that means.</p>\n\n<div class=\"row\">\n    <div class=\"col\"></div>\n    <div class=\"d-flex justify-content-center col-12  col-sm-8 col-md-6 \">\n        <figure>\n            <img class=\"img-thumbnail img-fluid\" src=\"/img/blog/mac-sigh-of-death-2.jpg\" title=\"macOS report a problem\" alt=\"macOS report a problem\" />\n            <div class=\"row\">\n                <div class=\"col-12 d-flex justify-content-center\">\n                \n                </div>\n            </div>\n        </figure>\n    </div>\n    <div class=\"col\"></div>\n</div>\n\n<p>Finally, I vaguely remember reading another blog post from someone about this exact issue. It was awhile back, perhaps during a macOS beta, and they also called it the “sigh of death”. Unfortunately, I could not find the article anywhere on the web. If anyone recalls this post or can find it, please send me the link and I’ll update this post to link to it.</p>\n\n<p>And if you are experiencing this issue, or have any idea about what’s happening, please let me know.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-16-february-2022\">\n        <a href=\"#updated-16-february-2022\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"16 Feb 2022 11:39:03 AM \">\n            <i>16 February 2022</i>\n        </small>\n    </h5>\n    \n<p>Thank you <a href=\"https://twitter.com/mjtsai/status/1493986603156525056\">Michael Tsai</a> for finding the post I was referring to above, <a href=\"https://mjtsai.com/blog/2020/12/23/the-big-sur-sneeze/\">The Big Sur Sneeze</a>. My memory of that post was not entirely accurate. The details are slightly different than what I describe in this post, but it seems likely that the underlying issue is the same.</p>\n\n<p>Also, a few folks on Twitter commented that they have experienced this issue.</p>\n\n<p><a href=\"https://twitter.com/MrThon/status/1494004392122585093\">@MrThon</a>:</p>\n\n<blockquote>\n  <p>I have the same problem after I unplug the dock with external screen, then later plug it back, external screen does not come back and then sigh and reboot</p>\n</blockquote>\n\n<p><a href=\"https://twitter.com/grantjbutler/status/1494008856506101761\">Grant Butler</a>:</p>\n\n<blockquote>\n  <p>Yeah, that’s where I’ve been experiencing it. Plug in a USB-C/DisplayPort monitor, and then after a few seconds, the get the sigh and reboot.</p>\n</blockquote>\n\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2022/02/15/mac-sigh-of-death/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2022/01/11/time-machine-error-35-monterey/#updated-11-january-2022",
            "url": "https://www.jessesquires.com/blog/2022/01/11/time-machine-error-35-monterey/",
            "title": "[Updated] Time Machine error 35 in macOS Monterey",
            "date_published": "2022-01-11T16:18:22-08:00",
            "date_modified": "2022-01-11T16:18:22-08:00",
            "summary": "<p>Well, it appears my saga of <a href=\"/blog/tags/time-machine/\">obscure errors with Time Machine</a> continues. The first issue I had was in 2020 with <a href=\"/blog/2020/01/10/time-machine-failing-on-macos-catalina/\">macOS Catalina and “error 45”</a>. That error was <a href=\"/blog/2021/04/07/time-machine-error-45-big-sur/\">fixed (for me) in Big Sur</a> in 2021. As of this week, the error is back, though this time on macOS Monterey.</p>\n\n",
            "tags": [
                "macos","time-machine","macos-monterey","bugs",
                "software-dev"
            ],
            "content_html": "<p>Well, it appears my saga of <a href=\"/blog/tags/time-machine/\">obscure errors with Time Machine</a> continues. The first issue I had was in 2020 with <a href=\"/blog/2020/01/10/time-machine-failing-on-macos-catalina/\">macOS Catalina and “error 45”</a>. That error was <a href=\"/blog/2021/04/07/time-machine-error-45-big-sur/\">fixed (for me) in Big Sur</a> in 2021. As of this week, the error is back, though this time on macOS Monterey.</p>\n\n<!--excerpt-->\n\n<p>I’ve been on macOS Monterey for quite a few months now. I haven’t had any <em>major</em> issues with Monterey — only the thousands of paper cuts (like this error) representing the slow degradation of macOS that I’ve grown to expect in recent years.</p>\n\n<p>The error message is basically the same as before, <em>“the backup disk image could not be accessed (error 35)”</em> — however, now the error code is 35 instead of 45. Perhaps it is failing in a slightly different way, or perhaps the error code simply changed in Monterey. Like <a href=\"/blog/2020/01/10/time-machine-failing-on-macos-catalina/\">I mentioned before</a>, I use Time Machine with my NAS (“Leviathan.local”) and haven’t had any problems with my setup until these recent errors. Sadly, I still have no idea what causes the error nor how to fix it given the absolute dearth of useful information in the error message.</p>\n\n<p>For now, rebooting my MacBook has “fixed” the issue and backups are working again — but this is likely only temporary. I expect to see the error again soon, and I supposed I’ll just have to reboot again. And again. And again.</p>\n\n<div class=\"row\">\n    <div class=\"col\"></div>\n    <div class=\"d-flex justify-content-center col-12  col-sm-8 col-md-6 \">\n        <figure>\n            <img class=\"img-thumbnail img-fluid\" src=\"/img/blog/time-machine-fail-again.jpg\" title=\"Time Machine backups failing on macOS Monterey\" alt=\"Time Machine backups failing on macOS Monterey\" />\n            <div class=\"row\">\n                <div class=\"col-12 d-flex justify-content-center\">\n                \n                </div>\n            </div>\n        </figure>\n    </div>\n    <div class=\"col\"></div>\n</div>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-11-january-2022\">\n        <a href=\"#updated-11-january-2022\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"11 Jan 2022 04:18:22 PM \">\n            <i>11 January 2022</i>\n        </small>\n    </h5>\n    \n<p>Two readers have provided some tips on Twitter. <a href=\"https://twitter.com/humphd/status/1481049310888439814\">David Humphrey</a> mentioned a better possible workaround:</p>\n\n<blockquote>\n  <p>I had something like this with Time Machine backups to my Synology, and it turned out to be macOS holding open simultaneous connections, which is why rebooting fixed it. When it happens now, I know that I can go and force a disconnect of the user on the NAS and that fixes.</p>\n</blockquote>\n\n<p><a href=\"https://twitter.com/RodAEscobar/status/1481040662027898885\">Rodrigo Escobar</a> shared <a href=\"http://oldtoad.net/pondini.org/TM/Troubleshooting.html\">James Pond’s Time Machine troubleshooting docs</a>. Specifically, <a href=\"http://oldtoad.net/pondini.org/TM/C17.html\">item C17</a> (“sparse bundle could not be accessed”) seems to match! The options to resolve are consistent with David’s comments above.</p>\n\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2022/01/11/time-machine-error-35-monterey/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2021/06/29/apple-docc-great-but-useless-for-oss/#updated-30-december-2021",
            "url": "https://www.jessesquires.com/blog/2021/06/29/apple-docc-great-but-useless-for-oss/",
            "title": "[Updated] Apple's DocC is excellent, but unusable for open source projects",
            "date_published": "2021-12-30T17:31:10-08:00",
            "date_modified": "2021-12-30T17:31:10-08:00",
            "summary": "<p>I was very excited at this year’s WWDC when Apple <a href=\"https://developer.apple.com/videos/play/wwdc2021/10166/\">announced DocC</a>, their new “Documentation Compiler” tool that generates documentation from comments in your source code. Unfortunately, it’s not going to work for the majority of open source authors.</p>\n\n",
            "tags": [
                "docc","documentation","github","jazzy","open-source","swift","wwdc",
                "software-dev"
            ],
            "content_html": "<p>I was very excited at this year’s WWDC when Apple <a href=\"https://developer.apple.com/videos/play/wwdc2021/10166/\">announced DocC</a>, their new “Documentation Compiler” tool that generates documentation from comments in your source code. Unfortunately, it’s not going to work for the majority of open source authors.</p>\n\n<!--excerpt-->\n\n<h3 id=\"a-brief-history\">A brief history</h3>\n\n<p>Many folks are probably unaware that the company shipped and maintained a similar tool before, the now-defunct <a href=\"https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/HeaderDoc/intro/intro.html\">HeaderDoc</a>, which had a final <a href=\"https://en.wikipedia.org/wiki/HeaderDoc\">stable release in 2009</a>. Eventually, <a href=\"http://gentlebytes.com/contact/\">Tomaz Kragelj</a> wrote and released an open source replacement called <a href=\"http://gentlebytes.com/appledoc/\">AppleDoc</a>, which became the standard way for Apple platform developers to publish documentation for their open source libraries. While AppleDoc began to languish from a lack of regular contributors, Swift was announced in 2014. AppleDoc was soon replaced by another community-built tool, <a href=\"https://github.com/realm/jazzy\">Jazzy</a>, authored by <a href=\"https://www.jpsim.com\">JP Simard</a> and <a href=\"https://github.com/realm/jazzy/graphs/contributors\">maintained by various contributors</a>. There is also a lesser-known library called <a href=\"https://github.com/eneko/SourceDocs\">SourceDocs</a>, authored by <a href=\"https://github.com/eneko\">Eneko Alonso</a>. More recently, <a href=\"https://nshipster.com/authors/mattt/\">Mattt</a> took another approach at solving this problem with <a href=\"https://github.com/SwiftDocOrg/swift-doc\">swift-doc</a>. The community has mostly centered around Jazzy. After all these years of the open source community filling in the gaps and building their own tools, Apple finally announced a new first-party solution for generating documentation.</p>\n\n<h3 id=\"introducing-docc\">Introducing DocC</h3>\n\n<p>Overall, there are many things to like about <a href=\"https://developer.apple.com/videos/play/wwdc2021/10166/\">DocC</a>. The integration with Xcode’s Editor and Documentation Viewer is excellent. There is a command-line tool included, <code class=\"language-plaintext highlighter-rouge\">xcodebuild docbuild</code>, which you can invoke from automation scripts. The overall workflow for generating and exporting documentation is built-in to Xcode and its build process, making it very easy to use. Finally, the documentation itself looks incredible. It uses the same styling as Apple’s official documentation, making it very familiar to any Apple platform developer. The design is clean and well-organized. You can even include supplemental resources, including <a href=\"https://developer.apple.com/videos/play/wwdc2021/10235/\">interactive tutorials</a>.</p>\n\n<p>The problem with DocC is <a href=\"https://developer.apple.com/videos/play/wwdc2021/10236\">hosting and distribution</a> — arguably the most important aspect of this type of tool! What’s the point of generating amazing docs for your library or SDK if you can’t easily distribute or publish them for your users? When you run DocC, it produces a <code class=\"language-plaintext highlighter-rouge\">.doccarchive</code> package that includes all the HTML, CSS, JavaScript, and other resources for your documentation website. According to <a href=\"https://developer.apple.com/documentation/Xcode/distributing-documentation-to-external-developers\">the docs on distribution</a>, you must host this <code class=\"language-plaintext highlighter-rouge\">.doccarchive</code> on your web server and implement various rules to rewrite incoming URLs. The example provided defines rules in an <code class=\"language-plaintext highlighter-rouge\">.htaccess</code> file for Apache.</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># Enable custom routing.</span>\nRewriteEngine On\n\n<span class=\"c\"># Route documentation and tutorial pages.</span>\nRewriteRule ^<span class=\"o\">(</span>documentation|tutorials<span class=\"o\">)</span><span class=\"se\">\\/</span>.<span class=\"k\">*</span><span class=\"nv\">$ </span>MyProject.doccarchive/index.html <span class=\"o\">[</span>L]\n\n<span class=\"c\"># Route files and data for the documentation archive.</span>\n<span class=\"c\">#</span>\n<span class=\"c\"># If the file path doesn't exist in the website's root ...</span>\nRewriteCond %<span class=\"o\">{</span>REQUEST_FILENAME<span class=\"o\">}</span> <span class=\"o\">!</span><span class=\"nt\">-f</span>\nRewriteCond %<span class=\"o\">{</span>REQUEST_FILENAME<span class=\"o\">}</span> <span class=\"o\">!</span><span class=\"nt\">-d</span>\n\n<span class=\"c\"># ... route the request to that file path with the documentation archive.</span>\nRewriteRule .<span class=\"k\">*</span> MyProject.doccarchive/<span class=\"nv\">$0</span> <span class=\"o\">[</span>L]\n</code></pre></div></div>\n\n<p>The docs suggest keeping it up to date using a continuous integration workflow that generates and copies your <code class=\"language-plaintext highlighter-rouge\">.doccarchive</code> to your web server. For additional information on hosting, see <a href=\"https://josephduffy.co.uk/posts/hosting-docc-archives\">this post on Hosting DocC Archives</a> by <a href=\"https://twitter.com/Joe_Duffy\">Joseph Duffy</a>. Examples provided include Netlify, Vapor middleware, nginx, and Apache.</p>\n\n<p>There is one last quirk. The default output location for the DocC archive is buried in Xcode’s <code class=\"language-plaintext highlighter-rouge\">DerivedData/</code> directory, which is… certainly a place to put it. Apple seems to think that’s a good directory for a lot of things, but <a href=\"/blog/2020/03/04/another-issue-with-swiftpm-xcode-integration/\">I strongly disagree</a>. Luckily, you can specify another derived data directory location when using <code class=\"language-plaintext highlighter-rouge\">xcodebuild docbuild</code>.</p>\n\n<h3 id=\"ignoring-prior-art\">Ignoring prior art</h3>\n\n<p>So far, this may not seem like an issue — unless you are an open source author and maintainer like me. There is a particularly vibrant community of open source iOS developers on GitHub. Most open source communities have standardized on GitHub, but especially the Apple developer community — everyone hosts their projects on GitHub. Because of that, we all <a href=\"https://github.blog/2016-08-22-publish-your-project-documentation-with-github-pages/\">host our documentation alongside our projects</a> using <a href=\"https://pages.github.com\">GitHub Pages</a>. It is an incredibly valuable product and service that is easy to use. First, you generate your docs — we all use <a href=\"https://github.com/realm/jazzy\">Jazzy</a> — then you place them in <code class=\"language-plaintext highlighter-rouge\">docs/</code> on your default branch. For an example, see the popular <a href=\"https://github.com/Alamofire/Alamofire\">Alamofire</a> library and <a href=\"https://alamofire.github.io/Alamofire/\">its documentation</a>. I also <a href=\"/blog/2016/05/20/swift-documentation/\">wrote about this</a> in 2016.</p>\n\n<p>This is what nearly everyone does: host your project on GitHub, generate docs using Jazzy, and host your docs using GitHub Pages. In fact, no open source projects immediately come to mind that do not implement this workflow. I’m sure those projects exist, but they are the <em>exception</em>, not the norm. Even Apple <a href=\"https://github.com/apple\">hosts their open source projects on GitHub</a> now and uses GitHub Pages for <a href=\"https://apple.github.io/swift-evolution/\">the Swift Evolution proposal site</a>. More ironically, some teams at Apple actually use Jazzy and publish docs on GitHub Pages like I’ve described for some of their open source Swift packages, like <a href=\"https://github.com/apple/swift-nio\">SwiftNIO</a> (see <a href=\"https://apple.github.io/swift-nio/docs/current/NIO/index.html\">the docs here</a>).</p>\n\n<p>DocC fails to deliver for this extremely popular use case — in my opinion, the most popular. DocC <strong>does not</strong> work with GitHub Pages — a <strong>significant</strong> barrier for adoption for essentially all open source projects in the Apple developer community. I experimented with some hacks, but was unable to make DocC do what I need. The GitHub Pages server environment is a bit opaque to the user, but it is intended for <a href=\"https://docs.github.com/en/pages/getting-started-with-github-pages/about-github-pages\">hosting static sites</a> (<a href=\"https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll\">Jekyll</a>, for example), which DocC does not produce. DocC creates a <a href=\"https://vuejs.org\">Vue.js</a> web app and requires that you run your own web server to dynamically serve it, as described above. The process feels clunky and overcomplicated compared to GitHub Pages.</p>\n\n<p><strong>I badly want to use DocC.</strong> I would love to be able to use DocC for all my projects — but I <strong>cannot</strong> in its current form, nor can most anyone else. Unless, of course, you want to setup, maintain, and pay for a web server to host docs for your open source project that you work on for free. Who is going to do that!? Again, <a href=\"https://josephduffy.co.uk/posts/hosting-docc-archives\">Joseph Duffy provided</a> great examples for hosting, but how practical is it really? You would have to setup a new site for hosting the docs for each project you maintain. That is not tenable. Generating docs with Jazzy and hosting via GitHub Pages works great. Plus, it’s free.</p>\n\n<p>Unless something significant changes in DocC, I will continue using Jazzy. I do not have much of a choice, sadly. At the very least, it would be nice if DocC provided an option to generate <a href=\"https://stackoverflow.com/questions/68048298/running-xcode-docc-without-apache-server-htaccess\">a static site instead</a>. Aside from working with GitHub Pages, a static site would hopefully require much less JavaScript, of which there is currently a seemingly excessive amount. I think <a href=\"https://www.hackingwithswift.com/articles/238/how-to-document-your-project-with-docc\">Paul Hudson captured the core issue</a> well:</p>\n\n<blockquote>\n  <p>As things stand, the generated documentation is effectively a sealed box […] The web pages are also very heavy on the JavaScript and honestly I’m not sure why – the reference documentation and articles are simple beasts, and if DocC could flatten them to plain HTML then I imagine the rewrite rules would just go away. At the same time, Apple’s own documentation system is completely inaccessible with JavaScript turned off, so I don’t hold out a great deal of hope here.</p>\n</blockquote>\n\n<h3 id=\"for-whom-is-this-intended\">For whom is this intended?</h3>\n\n<p>This brings me to the final question at hand — <em>who is this tool for?</em> I’m not being sarcastic. I can think of two main groups of users for DocC: individual open source library authors and bigger companies that provide closed-source SDKs. I’m curious — what did the DocC team envision for this tool? It certainly does not feel like it was written for our community of open source library authors, and that is beyond frustrating and disappointing.</p>\n\n<p>Could companies that distribute popular SDKs make use of DocC? I’m thinking of SDKs like <a href=\"https://developer.paddle.com/reference/sdks\">Paddle</a>, <a href=\"https://segment.com/docs/connections/sources/catalog/libraries/mobile/ios/\">Segment</a>, <a href=\"https://firebase.google.com/docs/ios/setup\">Firebase</a>, and <a href=\"https://developers.facebook.com/docs/ios/\">Facebook</a>. However, it seems unlikely that any of these companies would use DocC. They (obviously) already have custom tooling and workflows to generate and host their own custom-branded documentation. They are not going to use DocC unless it can be heavily themed, and even then that’s probably not enough for them to switch from their current setup. <a href=\"https://www.hackingwithswift.com/articles/238/how-to-document-your-project-with-docc\">Paul Hudson also points this out</a>:</p>\n\n<blockquote>\n  <p>[…] the Apache rewrite rule references a theme-settings.json file, which suggest some customization is going to come, but I don’t know to what extent. I can imagine bigger companies wanting their corporate branding integrated, or their regular site navigation.</p>\n</blockquote>\n\n<p>It is clear that the team behind DocC worked hard on this tool — it shows. Like I mentioned at the start, the breadth and depth of what DocC offers is impressive. Still, it feels like the creators were somewhat clueless about what third-party developers actually need, which <a href=\"/blog/2020/03/04/another-issue-with-swiftpm-xcode-integration/\">seems to be a trend</a>. It feels like Apple built DocC for <em>Apple</em>. We’ve been using Jazzy with GitHub Pages <a href=\"https://github.com/realm/jazzy/releases/tag/0.0.4\">since 2014</a> — even <a href=\"https://blog.cocoapods.org/CocoaDocs-Documentation-Sunsetting/\">CocoaDocs was using Jazzy</a>. How could one overlook that when creating a new tool?</p>\n\n<p>This would not be so upsetting if Apple’s <a href=\"https://marco.org/2021/06/03/developer-relations\">developer relations</a> were not <a href=\"/blog/2020/09/15/why-is-apple-acting-like-an-asshole/\">abysmal</a> — not to mention, <a href=\"https://eclecticlight.co/2021/06/13/last-week-on-my-mac-the-elephant-at-wwdc/\">“the Elephant” at WWDC</a>. For years, our community has built and maintained its own tools that work <em>exactly</em> how we need them. Then Apple (finally!) releases a first-party tool that is amazing for addressing 90% of the problem, but omits the most important 10% of the solution.</p>\n\n<p>And so, we are unable to use the could-have-been-so-great tool that the highly-paid software engineers at Apple built because it <em>just barely</em> misses the mark. We are left in stark contrast to continue maintaining our community-built tools, primarily with unpaid labor.</p>\n\n<h3 id=\"to-be-open-source\">To be open source</h3>\n\n<p>Finally, Apple mentioned that DocC will be open sourced later this year. Some folks in the community seem optimistic about what that means for the future of DocC — maybe some determined, enthusiastic open source contributor will submit a patch that implements a “static site mode” for DocC. But at that point, what have we actually gained?</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-30-june-2021\">\n        <a href=\"#updated-30-june-2021\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"30 Jun 2021 11:08:46 AM PDT\">\n            <i>30 June 2021</i>\n        </small>\n    </h5>\n    \n<p>This post was updated to include mentioning SourceDocs, and that Apple actually uses Jazzy and GitHub pages for some of its Swift packages, like SwiftNIO.</p>\n\n<p>Additional updates:</p>\n\n<p>JP and friends maintaining Jazzy have <a href=\"https://github.com/realm/jazzy/issues/1256\">opened an issue</a> to track adding support for ingesting DocC archives as input, instead of relying on <a href=\"https://github.com/jpsim/SourceKitten\">SourceKitten</a>. So interestingly, what we may end up with is a community-built tool on top of DocC that provides the static site functionality we need.</p>\n\n<p><a href=\"https://twitter.com/_sa_s\">Sven Schmidt</a> who maintains the <a href=\"https://swiftpackageindex.com\">Swift Package Index</a> with <a href=\"https://daveverwer.com\">Dave</a>, suggested <a href=\"https://twitter.com/_sa_s/status/1410155365270966274\">on Twitter</a> that the Swift Package Index could conceivably generate and host DocC documentation. That would be pretty awesome in my opinion — it would be <a href=\"https://blog.cocoapods.org/CocoaDocs-Documentation-Sunsetting/\">CocoaDocs</a> 2.0!</p>\n\n<p>I think what I’m realizing is that community tooling is never going to be replaced by what Apple provides.</p>\n\n</div>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-11-july-2021\">\n        <a href=\"#updated-11-july-2021\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"11 Jul 2021 04:22:04 PM PDT\">\n            <i>11 July 2021</i>\n        </small>\n    </h5>\n    \n<p><a href=\"https://github.com/helje5\">Helge Heß</a> has written a post about <a href=\"http://www.alwaysrightinstitute.com/docz/\">converting DocC archives into static HTML sites</a>. You can find the project <a href=\"https://github.com/DoccZz/docc2html\">on GitHub</a>. It is currently still a work-in-progress, but a promising start.</p>\n\n</div>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-03-december-2021\">\n        <a href=\"#updated-03-december-2021\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"03 Dec 2021 01:19:44 PM PST\">\n            <i>03 December 2021</i>\n        </small>\n    </h5>\n    \n<p>Good news! There’s <a href=\"https://forums.swift.org/t/support-hosting-docc-archives-in-static-hosting-environments/53572\">a new thread</a> on the Swift forums to discuss an official solution to support hosting DocC archives in static hosting environments, like GitHub pages.</p>\n\n</div>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-30-december-2021\">\n        <a href=\"#updated-30-december-2021\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"30 Dec 2021 05:31:10 PM \">\n            <i>30 December 2021</i>\n        </small>\n    </h5>\n    \n<p>There’s more good news to share! And hopefully this will be the final update to this post. A pull request (<a href=\"https://github.com/apple/swift-docc/pull/44\">#44</a>) to add static hosting support to DocC has merged into the main branch of <a href=\"https://github.com/apple/swift-docc\">the swift-docc repo</a>. A second pull request (<a href=\"https://github.com/apple/swift-docc/pull/56\">#56</a>) is currently open to pick the changes into the <code class=\"language-plaintext highlighter-rouge\">release/5.6</code> branch, which matches the <a href=\"https://github.com/apple/swift/tree/release/5.6\">main Swift repo’s</a> <code class=\"language-plaintext highlighter-rouge\">release/5.6</code> branch. So, I believe we should expect an official release of DocC with support for static hosting alongside the release of Swift 5.6. However, there’s been no official post on <a href=\"https://www.swift.org\">Swift.org</a> about the timeline or roadmap for Swift 5.6. It will likely be summer 2022.</p>\n\n</div>\n\n<h4 id=\"further-reading\">Further Reading</h4>\n\n<ul>\n  <li><a href=\"https://developer.apple.com/videos/play/wwdc2021/10166/\">WWDC21: Meet DocC documentation in Xcode</a></li>\n  <li><a href=\"https://developer.apple.com/videos/play/wwdc2021/10236\">WWDC21: Host and automate your DocC documentation</a></li>\n  <li><a href=\"https://developer.apple.com/videos/play/wwdc2021/10167/\">WWDC21: Elevate your DocC documentation in Xcode</a></li>\n  <li><a href=\"https://developer.apple.com/videos/play/wwdc2021/10235/\">WWDC21: Build interactive tutorials using DocC</a></li>\n  <li><a href=\"https://developer.apple.com/documentation/Xcode/distributing-documentation-to-external-developers\">Apple: Distributing Documentation to External Developers</a></li>\n  <li><a href=\"https://eclecticlight.co/2021/06/13/last-week-on-my-mac-the-elephant-at-wwdc/\">Last Week on My Mac: The elephant at WWDC</a></li>\n  <li><a href=\"https://josephduffy.co.uk/posts/hosting-docc-archives\">Hosting DocC Archives</a></li>\n  <li><a href=\"https://www.hackingwithswift.com/articles/238/how-to-document-your-project-with-docc\">How to document your project with DocC</a></li>\n  <li><a href=\"https://useyourloaf.com/blog/xcode-docc-getting-started/\">Xcode DocC - Getting Started</a></li>\n  <li><a href=\"https://stackoverflow.com/questions/68048298/running-xcode-docc-without-apache-server-htaccess\">StackOverflow: Running Xcode DocC without Apache Server?</a></li>\n</ul>\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2021/06/29/apple-docc-great-but-useless-for-oss/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2021/11/03/swift-package-ios-tests/#updated-17-november-2021",
            "url": "https://www.jessesquires.com/blog/2021/11/03/swift-package-ios-tests/",
            "title": "[Updated] How to test an iOS Swift package without an Xcode project",
            "date_published": "2021-11-17T16:14:19-08:00",
            "date_modified": "2021-11-17T16:14:19-08:00",
            "summary": "<p>I came across a situation today where I needed to run an iOS test suite for a Swift Package. Previously, this required you to have an Xcode project but it no longer does.</p>\n\n",
            "tags": [
                "ios","xcode","swiftpm","swift",
                "software-dev"
            ],
            "content_html": "<p>I came across a situation today where I needed to run an iOS test suite for a Swift Package. Previously, this required you to have an Xcode project but it no longer does.</p>\n\n<!--excerpt-->\n\n<p>I’m not entirely sure when this changed, but before, if you had a Swift package that supported iOS then the only way to build, run, and test that package was to have an Xcode project. Only having a <code class=\"language-plaintext highlighter-rouge\">Package.swift</code> file would not suffice. The Swift Package Manager could generate a project for you.</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>swift package generate-xcodeproj\nxcodebuild <span class=\"nb\">test</span> <span class=\"nt\">-project</span> MyPackage.xcodeproj <span class=\"nt\">-scheme</span> MyPackage <span class=\"nt\">-sdk</span> iphonesimulator15.0 <span class=\"nt\">-destination</span> <span class=\"s2\">\"OS=15.0,name=iPhone 13 Mini\"</span>\n</code></pre></div></div>\n\n<p>Since it has been awhile since I’ve needed to do this, I wanted to see if there have been any changes. Luckily, there have and you no longer need an Xcode project for building and testing. This <a href=\"https://forums.swift.org/t/swiftpm-and-library-unit-testing/26255/19\">older thread on the Swift Forums explains</a>. The <code class=\"language-plaintext highlighter-rouge\">generate-xcodeproj</code> command has been deprecated, and you’ll get a warning when using it.</p>\n\n<p>If you have only a <code class=\"language-plaintext highlighter-rouge\">Package.swift</code>, you can build and test via <code class=\"language-plaintext highlighter-rouge\">xcodebuild</code> similar to how you would with a Xcode project. First, run <code class=\"language-plaintext highlighter-rouge\">xcodebuild -list</code> to find valid schemes from the package. Then invoke <code class=\"language-plaintext highlighter-rouge\">xcodebuild</code> with the desired scheme. Note that no project needs to be specified.</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>xcodebuild <span class=\"nb\">test</span> <span class=\"nt\">-scheme</span> MyScheme <span class=\"nt\">-sdk</span> iphonesimulator15.0 <span class=\"nt\">-destination</span> <span class=\"s2\">\"OS=15.0,name=iPhone 13 Mini\"</span>\n</code></pre></div></div>\n\n<p>This makes working with iOS packages much nicer, especially for non-UI iOS libraries where you never need to actually run on a simulator. This also simplifies running tests on CI.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-17-november-2021\">\n        <a href=\"#updated-17-november-2021\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"17 Nov 2021 04:14:19 PM \">\n            <i>17 November 2021</i>\n        </small>\n    </h5>\n    \n<p>Reader <a href=\"https://github.com/jessesquires/jessesquires.com/issues/158\">Bernd Rabe has pointed out</a> that testing packages this way prevents Xcode from collecting code coverage results, which I have also noticed. You win some, you lose some, I guess. I do not know of any workarounds at this time. If you need code coverage, you will need to keep using an Xcode project.</p>\n\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2021/11/03/swift-package-ios-tests/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2018/07/19/blaming-homelessness-on-the-homeless-sf/#updated-06-november-2021",
            "url": "https://www.jessesquires.com/blog/2018/07/19/blaming-homelessness-on-the-homeless-sf/",
            "title": "[Updated] Hold it! Or, how to blame homelessness on the homeless in San Francisco: An essay on capital and compassion",
            "date_published": "2021-11-06T11:47:05-07:00",
            "date_modified": "2021-11-06T11:47:05-07:00",
            "summary": "<p>The mayor of San Francisco called out feces on the sidewalks as a core problem to address in the city and wants homeless folks to <a href=\"https://www.nbcbayarea.com/news/local/SF-Mayor-Theres-more-feces-on-the-sidewalks-than-Ive-ever-seen-488156431.html\">“at least have respect” and “clean up after themselves”</a>. It’s an unfortunate response, but I’m sure a lot of folks agree with the sentiment. No one relishes walking through the dirty streets in this city and it certainly is a concern, but this kind of rhetoric is actively harmful. It deliberately shifts responsibility for the problem onto the victims and away from the system that produced it. Homeless folks are among the most vulnerable in our society. In addition to their lack of housing, persistent precarity, mental health issues, and emotional struggles, the city is now going to ask them for respect and cleanliness?</p>\n\n",
            "tags": [
                "ethics","capitalism","politics","homelessness",
                "essays"
            ],
            "content_html": "<p>The mayor of San Francisco called out feces on the sidewalks as a core problem to address in the city and wants homeless folks to <a href=\"https://www.nbcbayarea.com/news/local/SF-Mayor-Theres-more-feces-on-the-sidewalks-than-Ive-ever-seen-488156431.html\">“at least have respect” and “clean up after themselves”</a>. It’s an unfortunate response, but I’m sure a lot of folks agree with the sentiment. No one relishes walking through the dirty streets in this city and it certainly is a concern, but this kind of rhetoric is actively harmful. It deliberately shifts responsibility for the problem onto the victims and away from the system that produced it. Homeless folks are among the most vulnerable in our society. In addition to their lack of housing, persistent precarity, mental health issues, and emotional struggles, the city is now going to ask them for respect and cleanliness?</p>\n\n<!--excerpt-->\n\n<div class=\"row\">\n    <div class=\"col\"></div>\n    <div class=\"d-flex justify-content-center col-12 \">\n        <figure>\n            <img class=\"img-thumbnail img-fluid\" src=\"/img/blog/16th_st_bart.jpg\" title=\"16th Street and Mission BART\" alt=\"16th Street and Mission BART\" />\n            <div class=\"row\">\n                <div class=\"col-12 d-flex justify-content-center\">\n                \n                    <figcaption>\n                        <p><small class=\"text-muted text-center\">\n                            Signs at 16th Street and Mission BART. \n                        </small></p>\n                    </figcaption>\n                \n                </div>\n            </div>\n        </figure>\n    </div>\n    <div class=\"col\"></div>\n</div>\n\n<p>From <a href=\"https://www.nbcbayarea.com/news/local/SF-Mayor-Theres-more-feces-on-the-sidewalks-than-Ive-ever-seen-488156431.html\">the interview</a> with the mayor:</p>\n\n<blockquote>\n  <p>“There’s more feces on the sidewalks than I’ve ever seen here. And we’re not just talking about from dogs, we’re talking about from humans.”</p>\n\n  <p>“About 70 percent of the people who are estimated to be homeless in San Francisco were actually housed in San Francisco before they became homeless. We have to make sure people who live here, sadly, people who are homeless here, that they are also held accountable for taking care of our streets. This is our home.”</p>\n</blockquote>\n\n<p>Shit on the sidewalks is unpleasant, but the <em>human beings</em> on the sidewalks are more tragic. Directing the discussion toward the dirty streets, rather than on the people who are forced to live on them is a distraction. To whom is the mayor trying to appeal? It is certainly not the homeless folks. (Do you remember <a href=\"https://archives.sfweekly.com/thesnitch/2016/02/17/another-tech-guy-notices-sfs-homeless-issue-blogs-about-it-is-crucified\">“Tech Bro Justin”</a>?) This language serves the privileged class.</p>\n\n<p>The interview continues:</p>\n\n<blockquote>\n  <p>“Ask them to at least have respect for the community, at least clean up after themselves, and show respect to one another and people in the neighborhood.”</p>\n\n  <p>“I just solicited their help in trying to talk to their clients who, unfortunately, were mostly responsible for the conditions of the streets.”</p>\n</blockquote>\n\n<p>Do you realize how much this is to ask of a homeless person? That they should respect a society that has given them none?  They are denied the basic human right of housing. They are denied the dignity of free access to public toilets and showers. They hold little, if any, social or political power. They are vulnerable in every sense of the word. And the mayor suggests it is incumbent upon <em>the homeless</em> to clean up the streets, and that <em>they are responsible</em> for the conditions? Think for a moment how embarrassing and dehumanizing it must be to have no option but to defecate in public.</p>\n\n<p>What would it mean for someone on the street to “clean up after themselves”? How does a person without a home do this in a city with a dearth of public restrooms? With what resources will they clean the streets? It is an honest question. <strong>What do you want them to do? What do you <em>expect</em> them to do?</strong> The suggestion is not actionable by any measure I can imagine. What purpose, then, does this kind of language serve?\nIt is not offering a solution, but communicating to the homeless that their presence is unwelcome and should be concealed. It scapegoats the powerless and protects the brutality of state and capital by diverting public discussion away from critically examining and questioning the power structures that produced homelessness in the first place, a threat to current doctrine.</p>\n\n<p>The problem is not the shit on the sidewalks. The problem is that there are thousands of displaced and vulnerable people living on the street while corporations evade taxes and venture capitalists light money on fire to stay warm during a San Francisco summer.</p>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<p>What always comes to my mind is the song <a href=\"https://open.spotify.com/track/75UWHCVxZd9dJ1XSSQM0fS?si=yfa56tJKQeq7buON1Py2aQ\"><em>Land</em> by Fifteen</a>, an 80s-90s era punk band from Berkeley. East Bay punk will never let you down. Jeff Ott lived on the street for a number of years, and his lyrics get directly to the point.</p>\n\n<blockquote>\n  <p>The homeless are a problem for only one reason<br />\nTheir presence raises the question<br />\nWho owns the Land?<br />\nWho owns the Land?<br /></p>\n\n  <p>[…]</p>\n\n  <p>This morning I was awoken, by a man with a hand gun<br />\nHe’s got a book of rules that says, I ain’t got no right to sleep<br /></p>\n\n  <p>This morning I was awoken, by a man with a hand gun<br />\nHe’s got a book of rules that says, I ain’t got no right to be<br /></p>\n\n  <p>Who owns the Land?<br />\nWho owns the Land?<br /></p>\n\n  <p>[…]</p>\n\n  <p>Is there a connection<br />\nWho owns the Land?<br />\nTo my daily eviction<br />\nWho owns the Land?<br />\nAnd the extermination<br />\nWho owns the Land?<br />\nOf the Native American<br /></p>\n\n  <p>[…]</p>\n</blockquote>\n\n<p>Homelessness is not a failure of capitalism, but a product thereof. It underscores the unequal distribution of wealth and exposes the imbalances in access to land and resources that are created by the construct of property rights. That is why it must be concealed. The city claims the homeless folks need to pull themselves up by their bootstraps, without acknowledging that the bootstraps have been pulled from their boots and tied around their hands and feet. The city refuses to install public restrooms or build public housing on the premise that there is not enough funding. But the idea that there is not enough money in Silicon Valley is absurd. The problem is never a lack of capital, but who controls it.</p>\n\n<p>While venture capitalists and banks possess most of the money, and landlords and corporations own most of the land, it is, of course, the homeless people who should be cleaning up their own shit off the sidewalks in San Francisco.</p>\n\n<h4 class=\"text-center text-light-dark\">* * *</h4>\n\n<p>Resisting the atrocities of capitalism is a never-ending struggle. Aside from petitioning the city to install public restrooms, there are <strong>constructive</strong> actions you can take <em>right now</em> to help.</p>\n\n<ul>\n  <li>\n    <p><strong>Think with compassion and act with understanding.</strong> If you feel angry or disgusted by something you see on the street, think about how dehumanizing it would be to live there. Who should receive your grievances, the vulnerable or the powerful?</p>\n  </li>\n  <li>\n    <p><strong>Donate to the <a href=\"http://www.cohsf.org\">Coalition on Homelessness</a></strong> to help protect the human rights of the homeless folks.</p>\n  </li>\n  <li>\n    <p><strong>Donate to <a href=\"https://cjjc.org\">Causa Justa</a></strong> to help prevent evictions.</p>\n  </li>\n  <li>\n    <p><strong>Volunteer with <a href=\"http://www.ecs-sf.org\">ECS SF</a></strong> to have a direct impact in helping these folks by serving a meal or cleaning the facilities.</p>\n  </li>\n  <li>\n    <p><a href=\"http://brokeassstuart.com/blog/2016/02/18/what-to-do-when-someone-is-having-a-mental-health-crisis-on-the-street/\"><strong>Do not call the cops</strong></a> if you see a homeless person having a mental health crisis — contact the <a href=\"http://www.sfresourceconnect.org/detail.php?&amp;tax=RP-1500&amp;s=1&amp;id=18844997\">Mobile Crisis Team</a>, <a href=\"http://concrn.org\">Concrn.org</a>, or the <a href=\"http://sfhomeless.wikia.com/wiki/Homeless_Outreach_Team_-_HOT_Team_-_H.O.T._Team\">SF Homeless Outreach Team</a>.</p>\n  </li>\n</ul>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-06-november-2021\">\n        <a href=\"#updated-06-november-2021\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"06 Nov 2021 11:47:05 AM \">\n            <i>06 November 2021</i>\n        </small>\n    </h5>\n    \n<p>In the <a href=\"https://www.youtube.com/watch?v=liptMbjF3EE\">latest episode of Last Week Tonight</a>, John Oliver’s main story was on homelessness. You should watch it. And it should be no surprise that circumstances have worsened rather than improved since I wrote this essay about 3 years ago. The pandemic certainly hasn’t helped, but it’s not the main culprit. The issues are the same and have existed for decades. The pandemic just exposed and exacerbated our problems in new ways.</p>\n\n<p>As discussed in the episode, the solution is relatively simple: to end homelessness, you need to give people homes. House people, no questions asked. And pay for it by taxing greedy, predatory billionaires. Everything else — treatment for addition, employment, etc. — comes second. There is no hope to address any other concerns unless you house people first.</p>\n\n<p>Capitalism has conditioned us to view a person experiencing homelessness as an individual failure, rather than our collective failure as a society to provide basic human needs, rights, and dignity to everyone. But capitalism is not capable, nor designed, to care for and meet the needs of everyone — if it were, we wouldn’t be discussing this.</p>\n\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2018/07/19/blaming-homelessness-on-the-homeless-sf/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2021/11/01/how-to-start-a-blog/#updated-05-november-2021",
            "url": "https://www.jessesquires.com/blog/2021/11/01/how-to-start-a-blog/",
            "title": "[Updated] How to start a blog or portfolio website, for developers",
            "date_published": "2021-11-05T21:53:09-07:00",
            "date_modified": "2021-11-05T21:53:09-07:00",
            "summary": "<p>Ever so often an iOS developer asks me how to get started with making their own blog or portfolio website. Or, I’ll see a software developer from another community on Twitter ask the same thing. Often they are earlier in their career, or unfamiliar with web development, or unsure whether to build from scratch or use a platform, or all of the above. I find myself consistently making the same recommendations to folks. For this post, I want to share what I think is a great approach to get started, and how you can dive deeper once you master the basics.</p>\n\n",
            "tags": [
                "jekyll","web","bootstrap","nearlyfreespeech","nfsn","github",
                "software-dev"
            ],
            "content_html": "<p>Ever so often an iOS developer asks me how to get started with making their own blog or portfolio website. Or, I’ll see a software developer from another community on Twitter ask the same thing. Often they are earlier in their career, or unfamiliar with web development, or unsure whether to build from scratch or use a platform, or all of the above. I find myself consistently making the same recommendations to folks. For this post, I want to share what I think is a great approach to get started, and how you can dive deeper once you master the basics.</p>\n\n<!--excerpt-->\n\n<h3 id=\"things-to-consider\">Things to consider</h3>\n\n<p>The first thing to consider is how much time and effort you want to commit to not only creating a site, but maintaining it. You’ll also need to decide what you want your site to be. Some people only want a single-page site that links to their various online profiles — GitHub, LinkedIn, Twitter, etc. Others prefer a multi-page portfolio site. Some want to start a full blog where they will be regularly writing articles. You may want some combination of these, or you might want to start with a single-page site that eventually grows into a blog.</p>\n\n<p>If you are only interested in a simpler portfolio site and don’t want to worry about maintenance, it’s probably worth considering one of the popular platforms: Squarespace, Wordpress, Wix, etc. Those will get you up and running pretty quickly. However, using these types of platforms is not the focus of this post. I don’t have much experience with these platforms, but I know that they work as advertised, more or less.</p>\n\n<p>However, not only are these platforms typically more expensive compared to building your own site, they give you much less control. I personally find them extremely frustrating to use as a software developer. They might be fine to begin with, but eventually you’ll start running into their limitations. If you are planning to start a blog with technical writing, I highly discourage them as they are terrible with formatting code snippets and syntax highlighting.</p>\n\n<p>The other question to ask yourself before starting a blog is: <em>do you really want to start a blog?</em> You might be unsure, and that’s ok! The reason I bring this up is to encourage you to start small and do the simplest thing first. You may write a couple of blog posts and decide that you actually hate blogging. (That’s also ok.) What you want to avoid is spending a lot of time (and/or money) setting up some complex website that you actually don’t want.</p>\n\n<h3 id=\"prerequisites\">Prerequisites</h3>\n\n<p>This post assumes some basic working knowledge of HTML, CSS, and Javascript. You don’t need to be an expert. Being able to read and write simple HTML and CSS is all you need. Some Javascript experience is good too, but that’s less important for a static site. If you don’t have any experience with those, there are plenty of guides and tutorials online. These technologies lend themselves well to a “figure it out as you go” approach.</p>\n\n<p>The only other pre-requisite is having a GitHub account, which I think the vast majority of software developers do. In my experience, it’s the best platform out there for software projects — much better than BitBucket or GitLab.</p>\n\n<h3 id=\"getting-started\">Getting started</h3>\n\n<p>This post is primarily geared toward someone who is interested in making their own website that includes:</p>\n\n<ul>\n  <li>A handful of pages with information about yourself, your projects, your resume, your conference talks, etc.</li>\n  <li>A blog where you can write and publish posts — or, at least the potential to start a blog later if you are currently undecided</li>\n</ul>\n\n<p>What I highly recommend to folks is using <a href=\"https://pages.github.com\">GitHub Pages</a> to get started. It’s a great place to experiment, get started quickly, and build a great-looking site. Maintenance is also near zero if hosting on GitHub. And — it’s free.</p>\n\n<h3 id=\"about-github-pages\">About GitHub Pages</h3>\n\n<p>In many ways, <a href=\"https://pages.github.com\">GitHub Pages</a> a great middle-ground between building from scratch and choosing a platform like Squarespace. You can get your site up and running in a few minutes using their built-in templates and hosting on GitHub. The difference is that with GitHub Pages, you can keep advancing into the technical side of creating on your own site well beyond what any of these platforms offer. Better yet, you can move at your own pace and dive as deep as you like.</p>\n\n<p>Let’s step through the process, from the simplest options to the most advanced. Keep in mind that you can stop at any point when you reach a level with which you are comfortable. You can always progress to something more advanced later.</p>\n\n<h3 id=\"a-simple-github-pages-site\">A simple GitHub Pages site</h3>\n\n<p>The main <a href=\"https://pages.github.com\">GitHub Pages landing page</a> has a great getting started guide, complete with a video and links to additional guides and documentation. In short, it’s built on <a href=\"https://jekyllrb.com\">Jekyll</a>, a static site generator. This means you can write some markdown and Jekyll will turn that into a webpage.</p>\n\n<p>After stepping through their guides to create your new repository and choose a template, you’ll have your site up and running at <code class=\"language-plaintext highlighter-rouge\">username.github.io</code>. It should only take a few minutes. You can start adding new pages to your website or start writing blog posts right away. Better yet, this is all possible to do within the GitHub web UI. In fact, you can build an entire GitHub Pages site without ever opening terminal or cloning the repo to your own machine. In this way, it closely approximates how you would work with something like Squarespace. Of course, I would not necessarily recommend doing this. You may want to preview something before you publish it. In that scenario, you would need to <a href=\"https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/testing-your-github-pages-site-locally-with-jekyll\">build and preview your site locally</a>, then <code class=\"language-plaintext highlighter-rouge\">git push</code> the changes to be published.</p>\n\n<p>I use GitHub pages as a sort of homepage for my open source projects. If you want to see an example, you can find the <a href=\"https://github.com/jessesquires/jessesquires.github.io\">repo here</a> and the <a href=\"https://jessesquires.github.io\">rendered site here</a>. This is hosted on GitHub using one of their built-in templates. It’s just a simple, single-page website.</p>\n\n<p>If you are completely new to this, I would take some time to get acquainted with how GitHub Pages and <a href=\"https://jekyllrb.com\">Jekyll</a> work. Experiment, publish, iterate. Again, both have great tutorials and documentation, so I won’t reproduce them here.</p>\n\n<h3 id=\"getting-a-custom-domain\">Getting a custom domain</h3>\n\n<p>Once you feel comfortable working with and updating your site, the next big step will be <a href=\"https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site\">setting up a custom domain</a> instead of using the <code class=\"language-plaintext highlighter-rouge\">.github.io</code> subdomain. The good news is that the <code class=\"language-plaintext highlighter-rouge\">.github.io</code> domain will automatically redirect to your new custom domain. So if you have already been sharing your site, don’t worry about broken links.</p>\n\n<p>I view this step as “committing” to your website, since this is where you’ll start to spend money. Registering a domain is typically inexpensive, but I think it still represents a commitment. You have decided “yes, this is for me” and you want to continue. If you are not enthusiastic about continuing, you can stop here. You can even delete your repo, and thus, delete your website.</p>\n\n<p>Any registrar will work. I use <a href=\"https://www.nearlyfreespeech.net\">NearlyFreeSpeech.net</a>. The hardest part will be configuring the DNS records correctly, but the <a href=\"https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site\">GitHub guides</a> should help. Whatever registrar you choose should also have guides, or at least easy-to-find settings to configure.</p>\n\n<h3 id=\"a-more-advanced-github-pages-site\">A more advanced GitHub Pages site</h3>\n\n<p>At this point, you have a fully working website with a custom domain. That’s awesome! The next big step will be implementing a custom design for your site instead of relying on the limited selection of GitHub Pages themes. This sounds much more intimidating than it is, but it can be tricky.</p>\n\n<p>So far, GitHub Pages has hidden a lot of implementation details from you. As I mentioned before, it is built on <a href=\"https://jekyllrb.com\">Jekyll</a>. So diving deeper means “lowering” into Jekyll directly. I recommend going through Jekyll’s <a href=\"https://jekyllrb.com/docs/step-by-step/01-setup/\">step by step tutorial</a>. Don’t worry about your content, that will all be preserved. This step is only about abandoning your GitHub Pages theme and implementing a custom design.</p>\n\n<p>The tutorial explains everything quite well, but I want to call out one thing. You’ll be creating <a href=\"https://jekyllrb.com/docs/step-by-step/04-layouts/\">layouts</a>, or templates. Note that layouts can inherit from other layouts. Another useful concept is <a href=\"https://jekyllrb.com/docs/includes/\">includes</a>, which are reusable snippets of HTML. These are useful, for example, for building navigating menus or footers that should appear on every page.</p>\n\n<p>If, like me, you are not a web designer, then designing a website sounds intimidating. However, there are many tools and frameworks to help. I’ve been using <a href=\"https://getbootstrap.com\">Bootstrap</a> for years, and I really enjoy it. It is a well-establish and well-maintained frontend library. Think of it like UIKit, but for your website. There are many of these types of frameworks, like <a href=\"https://tailwindcss.com\">tailwind</a> and <a href=\"https://www.gatsbyjs.com\">gatsby</a>. Bootstrap has <a href=\"https://getbootstrap.com/docs/5.1/getting-started/introduction/\">excellent documentation</a> and a great <a href=\"https://getbootstrap.com/docs/5.1/examples/\">collection of examples</a>. You’ll use one of these frameworks to make your layout templates, then your workflow will be similar to GitHub Pages where you simply write markdown, then <code class=\"language-plaintext highlighter-rouge\">git push</code> to publish.</p>\n\n<p>If you would prefer to avoid a custom design from scratch, then I recommend checking out the vibrant collection of third-party themes at <a href=\"https://jekyllthemes.io\">JekyllThemes.io</a>.</p>\n\n<h3 id=\"diving-deeper-a-custom-jekyll-site\">Diving deeper: a custom Jekyll site</h3>\n\n<p>Now you have a custom-built, custom-themed website using Jekyll, which you host for free on GitHub Pages. This is what I used to have, but I eventually moved away from hosting on GitHub. I wrote about that <a href=\"/blog/2017/09/10/building-a-site-with-jekyll-on-nfsn/\">in-depth here</a>. Again, I <a href=\"/blog/2021/03/02/a-web-host-worth-using/\">highly recommend NearlyFreeSpeech.net</a> for hosting, but they are very bare-bones and DIY. If using them, expect to spend some time learning about hosting, DNS, and general web technologies, as well as trouble-shooting issues.  After a few years hosting on GitHub Pages, I feel like I simply outgrew it as I started to run into various limitations and desired more control. That’s why I decided to host my website elsewhere. You may never get to this point, and that’s fine.</p>\n\n<p>If you want to dive <em>even deeper</em> into Jekyll, you’ll need to learn some <a href=\"https://www.ruby-lang.org/en/\">Ruby</a>. Jekyll is written in Ruby and its templating language is called <a href=\"https://jekyllrb.com/docs/liquid/\">Liquid</a>. One neat, more advanced feature of Jekyll is writing <a href=\"https://jekyllrb.com/docs/plugins/\">custom plugins</a>, which are written in Ruby. This allows you to extend Jekyll itself.</p>\n\n<p>I should also note that if you prefer not to design your own site, you can keep using the GitHub Pages theme you originally selected, even if you are not hosting on GitHub. To do this, you’ll need to include the <a href=\"https://github.com/github/pages-gem\">GitHub Pages gem</a> instead of plain <a href=\"https://github.com/jekyll/jekyll\">Jekyll</a>.</p>\n\n<h3 id=\"other-resources\">Other resources</h3>\n\n<p>I occasionally write about <a href=\"/blog/tags/bootstrap/\">Bootstrap</a> and <a href=\"/blog/tags/nearlyfreespeech/\">Jekyll</a> on this blog. You may find those posts interesting as you advance.</p>\n\n<p>This entire website <a href=\"https://github.com/jessesquires/jessesquires.com\">is open source</a> if you need examples or want to see how all the pieces fit together. My only request is that you don’t reproduce an exact (or near-exact) copy of my site. The intent is to learn from it, not to duplicate it.</p>\n\n<p>Finally, I maintain a <a href=\"https://github.com/jessesquires/template-jekyll-site\">template repo on GitHub</a> that you can use to get started on a more advanced site using Jekyll and Bootstrap.</p>\n\n<h3 id=\"alternatives\">Alternatives</h3>\n\n<p>As I mentioned before, the mainstream platforms might be a better fit for what you are trying to achieve. If that’s the case, you should not feel like have to do something more technical just because you’re a software developer. Good software developers choose the right tool for the job.</p>\n\n<p>One alternative that is similar to Jekyll would be using John Sundell’s <a href=\"https://github.com/johnsundell/publish\">Publish</a>, which is a static site generator written in Swift. This might appeal more to iOS developers. You could write your entire site in Swift. I do not have any experience with this, but I’m sure you can find tutorials, examples, and guides online. <a href=\"https://twitter.com/kthomas901/status/1284692707537858561\">Kaya Thomas</a> used Publish for <a href=\"https://kaya.dev\">her personal website</a>, and I think it looks great. And of course, <a href=\"https://www.swiftbysundell.com\">Swift by Sundell</a> uses Publish, if you want another example.</p>\n\n<p>However, if you have the time, patience, and interest then I highly encourage you to explore a new technology stack. If you are primarily an iOS developer, building your own website with Jekyll is great way to explore something completely different.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-05-november-2021\">\n        <a href=\"#updated-05-november-2021\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"05 Nov 2021 09:53:09 PM \">\n            <i>05 November 2021</i>\n        </small>\n    </h5>\n    \n<p><a href=\"https://twitter.com/daiyitastic\">daiyi (chris)</a> just published an interesting article, <a href=\"https://daiyi.co/blog/pr-previews-for-github-pages/\"><em>How to automate previews for Github Pages</em></a>. It’s a pretty neat hack to get previews for website changes without having to run Jekyll locally or publish first. If you are going all-in on a GitHub pages site, this might be of interest to you.</p>\n\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2021/11/01/how-to-start-a-blog/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        } , 
    
        {
            
            
            
            

            

            
            

            "id": "https://www.jessesquires.com/blog/2018/02/25/replacing-google-with-duckduckgo/#updated-23-october-2021",
            "url": "https://www.jessesquires.com/blog/2018/02/25/replacing-google-with-duckduckgo/",
            "title": "[Updated] Replacing Google Search with DuckDuckGo: And how to add a custom DuckDuckGo search box to your site",
            "date_published": "2021-10-23T10:49:17-07:00",
            "date_modified": "2021-10-23T10:49:17-07:00",
            "summary": "<p>I’m not interested in being an advertising product for Google to exploit. I’m also not interested in the company’s <a href=\"https://daringfireball.net/linked/2017/09/01/hill-google-forbes\">unsavory practices</a>, in general. I’ve been using <a href=\"https://duckduckgo.com\">DuckDuckGo</a> for over a year now, and I’m incredibly happy with it as a replacement for Google Search — not only for personal usage, but also for implementing a <a href=\"/search/\">custom search component</a> for this site.</p>\n\n",
            "tags": [
                "ethics","tech","web","jekyll","bootstrap","duckduckgo","search","google",
                "software-dev"
            ],
            "content_html": "<p>I’m not interested in being an advertising product for Google to exploit. I’m also not interested in the company’s <a href=\"https://daringfireball.net/linked/2017/09/01/hill-google-forbes\">unsavory practices</a>, in general. I’ve been using <a href=\"https://duckduckgo.com\">DuckDuckGo</a> for over a year now, and I’m incredibly happy with it as a replacement for Google Search — not only for personal usage, but also for implementing a <a href=\"/search/\">custom search component</a> for this site.</p>\n\n<!--excerpt-->\n\n<div class=\"row\">\n    <div class=\"col\"></div>\n    <div class=\"d-flex justify-content-center col-12 \">\n        <figure>\n            <img class=\"img-thumbnail img-fluid\" src=\"/img/blog/duckduckgo.png\" title=\"DuckDuckGo\" alt=\"DuckDuckGo\" />\n            <div class=\"row\">\n                <div class=\"col-12 d-flex justify-content-center\">\n                \n                    <figcaption>\n                        <p><small class=\"text-muted text-center\">\n                            DuckDuckGo  / <a href=\"https://duckduckgo.com\" target=\"_blank\" rel=\"noreferrer\">Source</a> \n                        </small></p>\n                    </figcaption>\n                \n                </div>\n            </div>\n        </figure>\n    </div>\n    <div class=\"col\"></div>\n</div>\n\n<h3 id=\"why-not-google-search\">Why not Google Search?</h3>\n\n<p>Google no longer provides you with a simple list of ranked results. To become a major player in the <a href=\"https://en.wikipedia.org/wiki/Attention_economy\">attention economy</a>, Google Search must track you across the web and mine your personal information to provide you with “personalized results” and to sell your information to advertisers. Google can now even <a href=\"https://www.washingtonpost.com/news/the-switch/wp/2017/05/23/google-now-knows-when-you-are-at-a-cash-register-and-how-much-you-are-spending/?utm_term=.0554ea667e32\">track your <em>offline</em> purchases</a> — that is, at physical stores in the real world. Pretty creepy. The company strongly emphasizes how they’ve done this securely, to protect users — but that’s beside the point for me. <em>I do not want this intrusion into my daily, <strong>offline</strong> life.</em></p>\n\n<p>Google’s enormity gives it <a href=\"https://daringfireball.net/linked/2017/09/01/hill-google-forbes\">undue</a> <a href=\"https://80x24.net/post/the-problem-with-amp/\">influence</a> on content providers and publishers. In particular, <a href=\"https://www.ampproject.org\">Google AMP</a> results, which are diplayed prominently in search, <a href=\"https://danielmiessler.com/blog/google-amp-not-good-thing/\">threaten</a> <a href=\"https://www.theregister.co.uk/2017/05/19/open_source_insider_google_amp_bad_bad_bad/\">the web</a> at a fundamental level.</p>\n\n<p>The <a href=\"https://www.theguardian.com/technology/2016/dec/04/google-democracy-truth-internet-search-facebook\">lack of ethics</a> codified into Google search are disappointing and irresponsible. Google has positioned itself as the digital arbiter of truth. However, this is undermined by its pursuit to grab a slice of the attention economy, which requires curating unique search results for each user. This combined with an absence of fact-checking produces outcomes that range from <a href=\"https://gizmodo.com/googles-algorithm-is-lying-to-you-about-onions-and-blam-1793057789\">unfortunate</a> to <a href=\"https://searchengineland.com/googles-one-true-answer-problem-featured-snippets-270549\">alarming</a> to <a href=\"https://www.npr.org/sections/thetwo-way/2017/01/10/508363607/what-happened-when-dylann-roof-asked-google-for-information-about-race\">horrifying</a>. Google’s curated results have the power to shape and distort people’s views. That’s an ethical problem.</p>\n\n<p>I’ll give them credit though. Google has worked to address some of the problems in the articles that I’ve linked to above. However, the company’s financial incentives remain the same as far as I can tell. As long as they are beholden to satisfying advertisers and scrounging for users’ attention, I’m not hopeful that the trolls won’t find new ways to turn the search engine into an alt-right propaganda machine again.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-17-march-2021\">\n        <a href=\"#updated-17-march-2021\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"17 Mar 2021 11:07:11 AM PDT\">\n            <i>17 March 2021</i>\n        </small>\n    </h5>\n    <p>In completely unsurprising news, Google’s search results <a href=\"https://daringfireball.net/linked/2021/03/04/fowler-google-search\">have gotten worse</a> over time. And while I’m here — updating this post 3 years later — I have to say, DuckDuckGo has been great. I have no complaints.</p>\n\n</div>\n\n<h3 id=\"benefits-of-duckduckgo\">Benefits of DuckDuckGo</h3>\n\n<p>Similar to many Apple products and services, DuckDuckGo was established with <a href=\"https://duckduckgo.com/about\">privacy as a fundamental feature</a>, not to mention <a href=\"https://spreadprivacy.com\">a company value</a>. While their revenue model is still based on advertising, the service does not mine your data to do so. DuckDuckGo also sometimes adds an affiliate code to some eCommerce sites, but this is done without using any personally identifiable information. All the details are clearly explained <a href=\"https://duckduckgo.com/privacy\">in their privacy policy</a>, which you can compare with the <a href=\"https://www.google.com/policies/privacy/\">Google privacy policy</a>. Google <a href=\"https://www.google.com/policies/privacy/#infocollect\">collects</a> and <a href=\"https://www.google.com/policies/privacy/#nosharing\">shares</a> a staggering amount of your data that is personally identifiable. They obscure how they use it behind vague phrases like “improving your user experience”, and even after deleting your information from their services they reserve the right to retain a copy for “legitimate business or legal purposes”. DuckDuckGo provides a strikingly different policy, which puts privacy first and even includes an <a href=\"https://duckduckgo.com/privacy#s3\">“Information Not Collected”</a> section.</p>\n\n<blockquote>\n  <p>You can usually find out a lot about a person from their search history.</p>\n\n  <p>— Gabriel Weinberg, <a href=\"https://duckduckgo.com/privacy#s2\">DuckDuckGo Privacy</a></p>\n</blockquote>\n\n<p>Gabriel Weinberg has a rather <a href=\"https://www.quora.com/Why-should-I-use-DuckDuckGo-instead-of-Google\">comprehensive list</a> of reasons to use DuckDuckGo. I won’t bother reiterating each of them, but I will highlight one of my favorite features — <a href=\"https://duckduckgo.com/bang\">!bangs</a>, which let you search other sites directly. For example, you can type <code class=\"language-plaintext highlighter-rouge\">!giphy oh yeah</code> into DuckDuckGo and land directly on <a href=\"https://giphy.com/search/oh-yeah\">Giphy’s search results</a> for “oh yeah”.</p>\n\n<h3 id=\"quality-of-search\">Quality of search</h3>\n\n<p>After over a year of using DuckDuckGo, I think search quality is excellent. At times it could be better, but that’s a trade-off that I’m willing to make for privacy and a more ethical product. Overall, I’m incredibly happy using DuckDuckGo. If you aren’t satisfied with DuckDuckGo’s results, you can always <code class=\"language-plaintext highlighter-rouge\">!google</code> to search again. I use this occasionally, though it’s increasingly rare.</p>\n\n<h3 id=\"implementing-a-custom-search-box\">Implementing a custom search box</h3>\n\n<p>Google allows you to create a free <a href=\"https://cse.google.com/cse/\">custom search component</a> and embed it into your site, which I used here before switching to DuckDuckGo. You enter your site url, configure some settings, and then grab the code snippet to embed it. It sounds simple, but it is actually a heavy-handed and cumbersome script-based approach. It’s an exercise in pain to customize the appearance, and it won’t appear when using most ad-blockers.</p>\n\n<p>Creating a <a href=\"https://duckduckgo.com/search_box\">custom DuckDuckGo search</a> is much simpler. They provide an <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe\"><code class=\"language-plaintext highlighter-rouge\">&lt;iframe&gt;</code></a> snippet, which is not ideal. Luckily, you can create and style your own search box with basic html forms — something Google doesn’t offer.</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nt\">&lt;form</span> <span class=\"na\">name=</span><span class=\"s\">\"search\"</span> <span class=\"na\">action=</span><span class=\"s\">\"//duckduckgo.com/\"</span><span class=\"nt\">&gt;</span>\n    <span class=\"nt\">&lt;div</span> <span class=\"na\">class=</span><span class=\"s\">\"form-group\"</span><span class=\"nt\">&gt;</span>\n        <span class=\"nt\">&lt;div</span> <span class=\"na\">class=</span><span class=\"s\">\"input-group\"</span><span class=\"nt\">&gt;</span>\n            <span class=\"nt\">&lt;input</span> <span class=\"na\">type=</span><span class=\"s\">\"search\"</span> <span class=\"na\">class=</span><span class=\"s\">\"form-control\"</span> <span class=\"na\">placeholder=</span><span class=\"s\">\"DuckDuckGo\"</span> <span class=\"na\">name=</span><span class=\"s\">\"q\"</span><span class=\"nt\">&gt;</span>\n            <span class=\"nt\">&lt;input</span> <span class=\"na\">type=</span><span class=\"s\">\"hidden\"</span> <span class=\"na\">value=</span><span class=\"s\">\"jessesquires.com\"</span> <span class=\"na\">name=</span><span class=\"s\">\"sites\"</span><span class=\"nt\">&gt;</span>\n            <span class=\"nt\">&lt;input</span> <span class=\"na\">type=</span><span class=\"s\">\"hidden\"</span> <span class=\"na\">value=</span><span class=\"s\">\"1\"</span> <span class=\"na\">name=</span><span class=\"s\">\"kh\"</span><span class=\"nt\">&gt;</span>\n            <span class=\"nt\">&lt;input</span> <span class=\"na\">type=</span><span class=\"s\">\"hidden\"</span> <span class=\"na\">value=</span><span class=\"s\">\"1\"</span> <span class=\"na\">name=</span><span class=\"s\">\"kn\"</span><span class=\"nt\">&gt;</span>\n            <span class=\"nt\">&lt;input</span> <span class=\"na\">type=</span><span class=\"s\">\"hidden\"</span> <span class=\"na\">value=</span><span class=\"s\">\"1\"</span> <span class=\"na\">name=</span><span class=\"s\">\"kac\"</span><span class=\"nt\">&gt;</span>\n            <span class=\"nt\">&lt;input</span> <span class=\"na\">type=</span><span class=\"s\">\"hidden\"</span> <span class=\"na\">value=</span><span class=\"s\">\"1\"</span> <span class=\"na\">name=</span><span class=\"s\">\"kc\"</span><span class=\"nt\">&gt;</span>\n            <span class=\"nt\">&lt;span</span> <span class=\"na\">class=</span><span class=\"s\">\"input-group-btn\"</span><span class=\"nt\">&gt;</span>\n                <span class=\"nt\">&lt;button</span> <span class=\"na\">class=</span><span class=\"s\">\"btn btn-default\"</span> <span class=\"na\">type=</span><span class=\"s\">\"submit\"</span><span class=\"nt\">&gt;</span>Search<span class=\"nt\">&lt;/button&gt;</span>\n            <span class=\"nt\">&lt;/span&gt;</span>\n        <span class=\"nt\">&lt;/div&gt;</span>\n    <span class=\"nt\">&lt;/div&gt;</span>\n<span class=\"nt\">&lt;/form&gt;</span>\n</code></pre></div></div>\n\n<p>This adds a basic form with a few <a href=\"https://duckduckgo.com/params\">custom URL parameters</a> and custom styling using <a href=\"https://getbootstrap.com\">bootstrap</a>. You can add this to your own site by replacing <code class=\"language-plaintext highlighter-rouge\">jessesquires.com</code> on line 5 with your own domain. You can see and use my custom search <a href=\"/search/\">here</a>.</p>\n\n<p>You can find <a href=\"https://github.com/jessesquires/jessesquires.com/commit/d2126bacca43e5f9fb77f980c67fe178d6933673#diff-c9db0e13a328be0eaa311c5b24ad331c\">the diff on GitHub </a> for deleting Google custom search and replacing it with DuckDuckGo. It’s much more elegant, lightweight, and just looks better.</p>\n\n<p><span class=\"text-muted\">Thanks to <a href=\"https://muffinresearch.co.uk/adding-a-duckduckgo-search-box-to-your-blog/\">Stuard Colville</a> for his helpful post on doing this without using iframes.</span></p>\n\n<h3 id=\"making-the-switch\">Making the switch</h3>\n\n<p>If you value privacy and the open web, it’s time to switch. By supporting DuckDuckGo, you will only help improve their service. Contrary to what most giant tech companies would have you believe, <em>it is possible</em> to build a great service without exploiting users’ privacy and personal information.</p>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-09-june-2021\">\n        <a href=\"#updated-09-june-2021\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"09 Jun 2021 09:53:44 PM PDT\">\n            <i>09 June 2021</i>\n        </small>\n    </h5>\n    <p>Bing was <a href=\"https://mashable.com/article/microsoft-bing-tank-man/\">caught blocking results</a> for Tiananmen Square protests in Hong Kong. Per <a href=\"https://mobile.twitter.com/yegg/status/1401216879293874185\">Gabriel Weinberg</a>: <em>“China banned DuckDuckGo in 2014 and we have no plans to change that.”</em></p>\n\n<p>It is rare to see a tech company <strong>not</strong> cooperate with authoritarian governments.</p>\n\n</div>\n\n<div class=\"d-block bg-light-dark pt-3 px-3 pb-0 my-3 border border-2 border-secondary rounded update-notice\">\n    <h5 id=\"updated-23-october-2021\">\n        <a href=\"#updated-23-october-2021\" class=\"text-reset\"><i class=\"bi bi-link\"></i> Update</a>\n        <small class=\"text-body-secondary\" title=\"23 Oct 2021 10:49:17 AM \">\n            <i>23 October 2021</i>\n        </small>\n    </h5>\n    \n<p><a href=\"https://forbes.com/sites/thomasbrewster/2021/10/04/google-keyword-warrants-give-us-government-data-on-search-users/\">A new report by Thomas Brewster at Forbes</a>: <em>“The U.S. government is secretly ordering Google to provide data on anyone typing in certain search terms, an accidentally unsealed court document shows. There are fears such ‘keyword warrants’ threaten to implicate innocent Web users in serious crimes and are more common than previously thought.”</em></p>\n\n<p><a href=\"https://twitter.com/DuckDuckGo/status/1447559362906447874\">@DuckDuckGo</a>: <em>“Google complies with invasive ‘keyword warrants’ that identify anyone who searched for a term. DuckDuckGo doesn’t have any search histories by design and, bc of that, has had 0 search warrants (of any kind) since our founding in 2008.”</em></p>\n\n<p>Good to know.</p>\n\n</div>\n\n<br>\n<hr>\n\n<p>\n    <i>Originally published on <a href=\"https://www.jessesquires.com/blog/2018/02/25/replacing-google-with-duckduckgo/\">jessesquires.com</a>.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/hire-me/\">Hire me</a></b> for iOS freelance and contracting work.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.hexedbits.com\">Buy</a></b> my apps.</i>\n</p>\n\n<p>\n    <i><b><a href=\"https://www.jessesquires.com/sponsor/\">Sponsor</a></b> my blog and open source projects.<br></i>\n</p>\n"
        }  
    ]
}
