<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Mach-O blog]]></title><description><![CDATA[Erk Ekin's tech blog]]></description><link>https://erkek.in</link><generator>RSS for Node</generator><lastBuildDate>Fri, 09 Oct 2026 02:14:01 GMT</lastBuildDate><atom:link href="https://erkek.in/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Beyond the App: Engineering Software When Conversation Becomes the Interface]]></title><description><![CDATA[For forty years, our relationship with computers has followed one rule: direct manipulation. You open an application, scan a layout of menus and buttons, and manually click or tap your way to a result]]></description><link>https://erkek.in/beyond-the-app-engineering-software-when-conversation-becomes-the-interface</link><guid isPermaLink="true">https://erkek.in/beyond-the-app-engineering-software-when-conversation-becomes-the-interface</guid><dc:creator><![CDATA[Erk Ekin]]></dc:creator><pubDate>Tue, 22 Sep 2026 20:06:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/61b75a1bd2a5c9036e6ef87e/ad840cd4-ae32-4c12-b58f-00c072bae5e1.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For forty years, our relationship with computers has followed one rule: <strong>direct manipulation</strong>. You open an application, scan a layout of menus and buttons, and manually click or tap your way to a result. Whether using a Macintosh in 1984 or an iPhone today, you are the manual switchboard between software services.</p>
<p>That era is ending. We are moving from <strong>application-centric</strong> computing to <strong>intent-centric</strong> computing.</p>
<p>Instead of navigating an interface, we are beginning to delegate outcomes to autonomous AI agents. When intent drives execution, the traditional mobile app ceases to be the destination—it becomes an invisible backend service.</p>
<p>Screens will not disappear entirely, but they will lose their monopoly as the primary gateway to software. For software engineers, this marks a profound shift: <strong>we are moving from building visual interfaces that humans operate to building capabilities that intelligent agents can orchestrate.</strong></p>
<p>Here is what is happening, why it matters, and how engineering teams should prepare.</p>
<hr />
<h2>1. The App Switchboard Is Broken</h2>
<p>In 2025, consumers spent 5.3 trillion hours inside mobile apps, averaging 3.6 hours a day juggling 34 distinct apps per month, according to <a href="https://www.pocketgamer.biz/global-mobile-in-app-purchase-revenue-hit-167bn-in-2025/?utm_source=gemini">PocketGamer.biz</a>.</p>
<p>Think about how much manual effort goes into a routine task like coordinating dinner:</p>
<ul>
<li><p>You open WhatsApp to agree on a time and area.</p>
</li>
<li><p>You switch to Google Maps to check opening hours.</p>
</li>
<li><p>You switch to Uber to schedule a ride.</p>
</li>
<li><p>You open your banking app to split the bill.</p>
</li>
</ul>
<p>Each app is a walled garden with its own navigation hierarchy, login session, and notification spam.</p>
<p>In an <strong>intent-driven model</strong>, that entire sequence collapses into a single request:</p>
<blockquote>
<p><em>"Book a table at an Italian spot near Soho for 7:30 PM with Mark, call a ride when it's time to leave, and split the bill."</em></p>
</blockquote>
<p>The user specifies the outcome. The agent resolves identities, checks availability, runs the API calls, and triggers a single hardware confirmation for payment. The user never needs to know which rideshare API was invoked or what the restaurant's website looked like.</p>
<hr />
<h2>2. Hardware: Why Pins Failed, but Smart Glasses Won</h2>
<p>Early attempts at "screenless computing" stumbled by trying to kill the smartphone too early.</p>
<p>Standalone gadgets like the Humane AI Pin and Rabbit R1 tried to replace phones with tiny wearables and handheld boxes, as chronicled by <a href="https://www.laptopmag.com/ai/rabbit-r1-2024-ai-year-in-review?utm_source=gemini">Laptop Mag</a> and <a href="https://www.everydayaitech.com/en/articles/ai-gadgets-flop-2025?utm_source=gemini">Everyday AI Tech</a>. They collapsed under battery drain, severe thermal throttling, and high cloud latency.</p>
<p>You cannot out-engineer physics: a 40-gram wearable pinned to a shirt cannot dissipate the heat or carry the battery needed to replace a modern smartphone.</p>
<p>Instead, the market rewarded <strong>ambient transducers</strong> that offload compute to the phone in your pocket:</p>
<ul>
<li><p><a href="https://www.uploadvr.com/meta-essilorluxottica-sold-7-million-smart-glasses-in-2025/?utm_source=gemini">UploadVR</a> reported that EssilorLuxottica sold over 7 million AI smart glasses in 2025 alone—more than triple its previous cumulative sales.</p>
</li>
<li><p>Shipments of AI glasses surged 322% year-over-year to 8.7 million units in 2025, according to <a href="https://omdia.tech.informa.com/pr/2026/mar/global-ai-glasses-shipments-reach-8point7-million-units-with-mainland-china-emerging-as-the-fastest-growing-market?utm_source=gemini">Omdia</a>.</p>
</li>
<li><p>In 60% of European Ray-Ban stores, smart glasses became the number-one best-selling product.</p>
</li>
</ul>
<p>The emerging hardware landscape is a <strong>three-tier architecture</strong>:</p>
<ol>
<li><p><strong>Sensors at the Edge (Eyes &amp; Ears):</strong> Audio frames, smart glasses, and micro-gesture bands like the <a href="https://en.wikipedia.org/wiki/Meta_smart_glasses?utm_source=gemini">Meta Neural Band</a> capture voice, gaze, and subtle physical taps.</p>
</li>
<li><p><strong>The Pocket Compute Engine (The Phone):</strong> Your smartphone stays in your pocket as a silent "puck"—providing high-density battery life, 5G connectivity, and a secure cryptographic enclave.</p>
</li>
<li><p><strong>Hyperscale Cloud Intelligence:</strong> Cloud clusters handle complex multi-step reasoning and deep research.</p>
</li>
</ol>
<p>The phone isn’t dying. It is simply retreating into the background.</p>
<hr />
<h2>3. The Biology Check: Why Screens Aren't Going Extinct</h2>
<p>Will screens disappear completely? Absolutely not. Human sensory biology prevents it.</p>
<ul>
<li><p><strong>The Visual Channel (Parallel Processing):</strong> Research indexed on <a href="https://arxiv.org/html/2305.14080v3?utm_source=gemini">arXiv</a> shows the human visual cortex processes spatial data at an effective bandwidth of \(10^6\) to \(10^7\) bits per second. In 400 milliseconds, your eyes can scan a grid of five hotel rooms, evaluate photos, spot pricing anomalies, and check locations.</p>
</li>
<li><p><strong>The Auditory Channel (Serial Processing):</strong> Foundational research from the <a href="https://www.media.mit.edu/speech/papers/1993/schmandt_1993_from_desktop_audio_to_mobile_access.pdf">MIT Media Lab</a> notes that speech operates at 150 to 200 words per minute—a transmission rate of roughly 40 to 50 bits per second. Your ears cannot scan; they must listen sequentially.</p>
</li>
</ul>
<p>Listening to an AI assistant read aloud the flight schedules, layovers, and luggage policies of four airlines is exhausting.</p>
<p>Deterministic, low-entropy tasks (reordering groceries, sending funds, setting timers) will go screenless. But high-entropy, exploratory tasks (shopping for clothes, debugging code, comparing real estate) will always require high-density visual displays.</p>
<p>The future belongs to <strong>ephemeral UI</strong>: interfaces that render dynamically when human visual judgment is needed, and vanish the moment the task is complete, as analyzed by <a href="https://www.builder.io/blog/designing-generative-ui-in-an-agent-native-world?utm_source=gemini">Builder.io</a>.</p>
<hr />
<h2>4. Economic Disruption: The Zero-Click Threat</h2>
<p>This transition will radically reshape the business model of the internet.</p>
<h3>The Search Engine Crisis</h3>
<p>In 2025, Alphabet generated $402.8 billion in revenue, according to its <a href="https://s206.q4cdn.com/479360582/files/doc_financials/2025/q4/2025q4-alphabet-earnings-release.pdf">Q4 Earnings Release</a>. Segment reporting on <a href="https://bullfincher.io/companies/alphabet/revenue-by-segment?utm_source=gemini">Bullfincher</a> shows that \(224.53 billion—over 55%—came directly from Google Search ads, where US advertisers paid an average cost-per-click of \)5.26, per <a href="https://sqmagazine.co.uk/google-ads-statistics/?utm_source=gemini">SQ Magazine</a>.</p>
<ul>
<li><p><strong>Visual Search:</strong> A user searches for <code>"best running shoes"</code>, scans a page packed with sponsored ads, and clicks a paid link.</p>
</li>
<li><p><strong>Agent Delegation:</strong> A user says, <code>"Order the best-reviewed marathon shoe in size 10."</code> The agent handles the transaction via API.</p>
</li>
</ul>
<p>When an agent resolves an intent directly, <strong>the search results page never renders</strong>. No banner impressions. No sponsored links. The agent becomes the sole commercial gatekeeper.</p>
<h3>The App Store Bypass</h3>
<p>App stores extracted tens of billions in 2025 via 15% to 30% cuts on in-app purchases. But <a href="https://www.cryptopolitan.com/global-mobile-spending-shift-in-favor-ai/?utm_source=gemini">Cryptopolitan</a> reports that consumer spending on non-gaming apps hit $85 billion in 2025, driven heavily by generative AI apps ($5 billion in IAP spend). Crucially, 110 million Americans accessed AI tools exclusively on mobile in 2025—up from just 13 million in 2024.</p>
<p>As users accomplish tasks through centralized agents, they stop downloading single-purpose apps. Platform owners like Apple are already countering defensively: <a href="https://developer.apple.com/documentation/appintents/apple-intelligence-and-siri-ai?utm_source=gemini">Apple App Intents</a> forces developers to register capabilities inside native iOS bundles, ensuring transactions remain within reach of App Store rules.</p>
<hr />
<h2>5. What Software Engineers Need to Build</h2>
<p>If you are a software engineer, your core job is shifting from <strong>interface authoring</strong> to <strong>capability engineering</strong>.</p>
<h3>1. Build for Protocols, Not Just Screens</h3>
<p>AI agents need structured ways to discover and invoke tools.</p>
<ul>
<li><p><a href="https://www.anthropic.com/news/model-context-protocol?utm_source=gemini">Anthropic's Model Context Protocol (MCP)</a>, now governed by the Linux Foundation's <a href="https://en.wikipedia.org/wiki/Model_Context_Protocol?utm_source=gemini">Agentic AI Foundation</a>, has become the open standard for connecting LLMs to external tools and databases.</p>
</li>
<li><p>Backends no longer need endpoints shaped around specific UI views. They need idempotent, typed capabilities that expose self-describing parameter schemas and actionable error messages.</p>
</li>
</ul>
<h3>2. Embrace Dynamic, Ephemeral Frontend Architecture</h3>
<p>Frontend development isn't dying, but static layouts are giving way to Generative UI (GenUI):</p>
<ul>
<li><p>Instead of having LLMs write raw HTML on the fly (which is slow and introduces major security risks), production standards like <a href="https://developers.googleblog.com/a2ui-v0-9-generative-ui/?utm_source=gemini">Google's A2UI</a> and <a href="https://www.copilotkit.ai/generative-ui?utm_source=gemini">CopilotKit</a> use <strong>component hydration</strong>.</p>
</li>
<li><p>Frontend developers build a library of secure, accessible "primitives" (e.g., flight selector, checkout card, calendar picker).</p>
</li>
<li><p>The agent emits a structured JSON payload, and the client instantly hydrates the appropriate component.</p>
</li>
</ul>
<h3>3. Lock Down Delegated Authority</h3>
<p>Allowing an agent to act on your behalf creates serious security risks. Because LLMs cannot reliably separate system instructions from untrusted data, attacks like indirect prompt injection can hijack an agent's reasoning loop.</p>
<p>Engineering teams are securing agent actions through two standards:</p>
<ul>
<li><p><a href="https://datatracker.ietf.org/doc/rfc9396/history/?utm_source=gemini"><strong>RFC 9396 (OAuth Rich Authorization Requests)</strong></a><strong>:</strong> Replaces broad scopes (like <code>banking:write</code>) with structured payloads that cryptographically limit an agent's token to a specific amount, recipient, and time window.</p>
</li>
<li><p><strong>Hardware Enclaves:</strong> For high-impact actions (wire transfers, data deletion), execution is paused until the user confirms out-of-band via biometric verification (Face ID, fingerprint, or a physical gesture on a wearable).</p>
</li>
</ul>
<hr />
<h2>The Post-App Playbook</h2>
<p>The future of software is <strong>hybrid orchestration</strong>: applications will serve as capability engines, operating-system agents will manage routine tasks, and visual interfaces will appear on demand when human evaluation is required.</p>
<p>To stay ahead:</p>
<ol>
<li><p><strong>Decouple business logic from UI:</strong> Ensure your core service can execute headlessly via machine-readable API contracts.</p>
</li>
<li><p><strong>Adopt agent-ready standards:</strong> Implement MCP or App Intents so AI agents can discover and invoke your tools.</p>
</li>
<li><p><strong>Shift from static pages to elastic components:</strong> Prepare your design system for dynamic hydration via Generative UI.</p>
</li>
<li><p><strong>Harden your authorization boundaries:</strong> Adopt RFC 9396 to strictly constrain what delegated tokens can execute.</p>
</li>
</ol>
<p>The most successful software products of the next decade won't be the ones with the stickiest feeds. They will be the ones whose capabilities are the easiest for intelligent agents to discover, trust, and execute.</p>
]]></content:encoded></item><item><title><![CDATA[Don’t Build for the Hype: The Cold Economics of the iPhone Duo]]></title><description><![CDATA[Every time Apple unveils a new device category, the iOS developer community undergoes the same ritual. Keynote demos showcase fluid dual-screen animations, developer forums light up with discussions o]]></description><link>https://erkek.in/don-t-build-for-the-hype-the-cold-economics-of-the-iphone-duo</link><guid isPermaLink="true">https://erkek.in/don-t-build-for-the-hype-the-cold-economics-of-the-iphone-duo</guid><category><![CDATA[iphone duo,]]></category><category><![CDATA[hype]]></category><category><![CDATA[development]]></category><category><![CDATA[ios app development]]></category><dc:creator><![CDATA[Erk Ekin]]></dc:creator><pubDate>Mon, 14 Sep 2026 17:55:51 GMT</pubDate><content:encoded><![CDATA[<p>Every time Apple unveils a new device category, the iOS developer community undergoes the same ritual. Keynote demos showcase fluid dual-screen animations, developer forums light up with discussions on new APIs, and product managers invariably drop a message into Slack: <em>“Should we support the iPhone Duo on Day One?”</em></p>
<p>Apple’s pitch is undeniably seductive. The iPhone Duo unfolds from a 5.4-inch outer cover screen into an expansive 7.6-inch flexible OLED canvas. It runs on the A20 Pro chip, incorporates an intricate titanium hinge, and brings multi-window Split View to the iPhone lineup for the first time.</p>
<p>I love Apple hardware, and from an industrial design perspective, the Duo is a marvel. But software engineering teams do not operate on aesthetic admiration. They operate on sprint velocity, headcount budgets, and opportunity cost.</p>
<p>If your team is currently weighing whether to divert engineering cycles toward building custom experiences for the iPhone Duo, here is the unvarnished mathematical reality: for roughly 95% of existing applications, investing in bespoke iPhone Duo optimization at launch is an engineering trap.</p>
<p>Here is the math, the platform economics, and the technical breakdown of what it actually takes to adapt legacy codebases.</p>
<hr />
<h2>1. The Denominator Problem: 10 Million vs. 2.5 Billion</h2>
<p>The most dangerous bias in mobile product development is confusing hardware capability with addressable market size.</p>
<p>To evaluate whether a new device justifies engineering effort, consider where the iPhone Duo sits in the global hardware landscape:</p>
<ul>
<li><p><strong>The Active Apple Ecosystem:</strong> Apple’s active installed base exceeds <strong>2.5 billion devices</strong> worldwide. Standard iPhone shipments alone move between 240 million and 258 million units every year.</p>
</li>
<li><p><strong>The Global Foldable Baseline:</strong> Despite seven years of persistent investment by Samsung, Google, and Huawei, foldables account for less than <strong>2% of the global smartphone market</strong>, with 2026 industry shipments estimated around 22.9 million units.</p>
</li>
<li><p><strong>Projected iPhone Duo Shipments:</strong> Analysts at IDC forecast that Apple will ship approximately <strong>10 million iPhone Duo units</strong> during its first twelve months.</p>
</li>
</ul>
<p>Even if Apple hits every production milestone, the iPhone Duo will represent approximately <strong>0.4% of the active Apple device ecosystem</strong> and roughly 4% of annual iPhone sales.</p>
<p>When your team allocates sprint capacity, it is assigning finite engineering hours to an addressable audience that rounds down to zero compared to the core smartphone base. Building bespoke features for 0.4% of your user base is rarely sound commercial math.</p>
<hr />
<h2>2. A $1,999 Device Sits Firmly "Pre-Chasm"</h2>
<p>The Duo’s market footprint is constrained by its retail price. Starting at <strong>$1,999</strong> for the base tier and reaching toward <strong>$3,000</strong> for top storage options, the Duo is not positioned for the mainstream consumer replacement cycle.</p>
<p>In Everett Rogers’ classical <em>Diffusion of Innovations</em> framework, technology adoption follows a predictable curve:</p>
<table>
<thead>
<tr>
<th>Adoption Cohort</th>
<th>Share of Market</th>
<th>iPhone Duo Placement</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Innovators</strong></td>
<td>2.5%</td>
<td>Active (Tech executives and hardware enthusiasts)</td>
</tr>
<tr>
<td><strong>Early Adopters</strong></td>
<td>13.5%</td>
<td>Active (High-net-worth mobile professionals)</td>
</tr>
<tr>
<td><em>The Chasm</em></td>
<td><em>Adoption Barrier</em></td>
<td><em>Structural price and form-factor friction</em></td>
</tr>
<tr>
<td><strong>Early Majority</strong></td>
<td>34.0%</td>
<td>Inaccessible at &gt;$1,999 retail</td>
</tr>
<tr>
<td><strong>Late Majority</strong></td>
<td>34.0%</td>
<td>Inaccessible</td>
</tr>
<tr>
<td><strong>Laggards</strong></td>
<td>16.0%</td>
<td>Inaccessible</td>
</tr>
</tbody></table>
<p>At $2,000, the Duo sits squarely before Geoffrey Moore’s famous "chasm." It is a luxury showcase. Until manufacturing yields improve, component prices decline, and carrier trade-ins make it indistinguishable from standard monthly hardware financing, the device cannot reach mainstream users.</p>
<p>Building custom software for the Duo on Day One means expending engineering resources exclusively for a pre-chasm user cohort.</p>
<hr />
<h2>3. What Happens If You Do Nothing? (The Compatibility Reality)</h2>
<p>There is a pervasive fear among product teams that skipping launch-day support will result in broken user experiences. Apple’s official documentation confirms the opposite: unmodified legacy applications run completely safely.</p>
<p>According to Apple's Human Interface Guidelines on <em>Designing for iPhone Duo</em> and developer technical sessions, legacy iOS behavior follows clear rules:</p>
<ul>
<li><p><strong>Zero Crash Risk:</strong> Applications compiled against earlier SDKs execute within an automated system compatibility sandbox.</p>
</li>
<li><p><strong>Aspect Ratio Preservation:</strong> On the 7.6-inch inner display, older binaries run centered and letterboxed, maintaining their familiar mobile aspect ratio with inactive borders. Touch handling and coordinates remain intact.</p>
</li>
<li><p><strong>Outer Screen Parity:</strong> On the 5.4-inch outer cover screen, legacy applications report standard compact size classes and behave like standard single-screen iPhones.</p>
</li>
</ul>
<p>Your app will not crash, data flows will not break, and core customer flows remain functional. The only downside is visual letterboxing on the inner display when unfolded.</p>
<hr />
<h2>4. The "Few Hours" Myth: What Adapting Actually Demands</h2>
<p>Apple states in its developer sessions that updating an app can take "from a few hours to several days".</p>
<p>What that marketing statement leaves out is that the "few hours" path applies almost exclusively to teams that already maintain a modern, declarative iPadOS application with responsive size classes and multi-window scene delegates.</p>
<p>For the broad ecosystem of iPhone-first applications, Apple’s HIG and technical sessions reveal that adapting to iOS 27.1 requires refactoring fundamental mobile assumptions:</p>
<h3>Orientation Locks Are Ineffective</h3>
<p>On the 7.6-inch inner display, iOS 27.1 actively ignores app-level interface orientation locks for resizable software. If your codebase branches layout logic on <code>UIDevice.orientation</code> or <code>UIScreen.main.bounds</code>—practices present in thousands of legacy production codebases—the layout fails immediately. Supporting the Duo requires auditing view hierarchies to rely strictly on dynamic size classes and local coordinate environments.</p>
<h3>Lateral Navigation and Asymmetric Safe Areas</h3>
<p>On both the outer display and inner landscape mode, system navigation bars, tab bars, and toolbars shift from top and bottom edges to the lateral sides of the display to preserve vertical reading space.</p>
<p>This shift makes safe area insets asymmetric. In Split View, when an app shares the inner canvas with a secondary application, layout margins differ on every edge. Standalone UIKit toolbars cannot participate in this vertical layout; only system-managed container bars reposition automatically, requiring custom controls to undergo explicit vertical opt-ins and fixed-width compliance.</p>
<h3>Reserved Region Topography</h3>
<p>Codebases must dynamically track two system-level hardware obstructions:</p>
<ul>
<li><p><code>.division</code> — The physical folding crease. It splits the active layout into separate usable zones when partially folded, but has zero width when the device is laid flat.</p>
</li>
<li><p><code>.occlusion</code> — The under-display front camera. While invisible when inactive, it dynamically claims screen space when the sensor turns on, requiring the interface to yield.</p>
</li>
</ul>
<p>While continuous scrolling feeds may flow underneath the fold, primary interactive controls, action sheets, and modal forms must reposition outside these zones. Supporting dynamic poses requires adopting <code>ArrangementView</code> or <code>UIArrangementViewController</code>, restructuring view hierarchies into Split or Overlay configurations.</p>
<h3>Multi-Scene State Architecture</h3>
<p>The iPhone Duo is the first iPhone capable of running two concurrent windows of the same application side-by-side in Split View. If your app relies on singleton managers, global state objects, or single-window assumptions, running two concurrent instances introduces immediate state synchronization bugs.</p>
<h3>The Combinatorial QA Explosion</h3>
<p>Consider the impact on your testing pipeline. Testing an interactive flow is no longer a matter of checking portrait and landscape on fixed resolutions. You must validate:</p>
<ul>
<li><p>Closed portrait (compact phone profile)</p>
</li>
<li><p>Closed landscape (lateral toolbars)</p>
</li>
<li><p>Open vertical (tall, regular/regular canvas)</p>
</li>
<li><p>Open horizontal (wide, side-by-side arrangement)</p>
</li>
<li><p>Tabletop partial-fold (media above, controls below the crease)</p>
</li>
<li><p>Split View multi-window pairings (sharing screen width with another app)</p>
</li>
</ul>
<p>For an established engineering team, this represents <strong>6 to 8 engineer-weeks</strong> of refactoring, plus <strong>3 to 4 QA-weeks</strong> of test matrix verification. At standard tech compensation rates, that is an immediate expenditure of <strong>$60,000 to $95,000</strong>, with an ongoing 20% annual maintenance overhead across future OS updates.</p>
<hr />
<h2>5. Lessons from iPad and Vision Pro</h2>
<p>If you believe that hardware quality and brand reputation automatically generate software returns, recent platform history offers two cautionary precedents.</p>
<h3>The iPad Baseline: When 500 Million Devices Aren't Enough</h3>
<p>Apple has sold over 550 million iPads since 2010, maintaining an active installed base measured in the hundreds of millions. Yet for fifteen years, Meta’s Instagram famously refused to build a dedicated iPad application.</p>
<p>Instagram CEO Adam Mosseri repeatedly explained the decision through simple platform economics: the tablet audience was not large enough to justify the ongoing development overhead.</p>
<blockquote>
<p><em>“Each surface adds overhead; we support iOS, Android, www, and IG Lite... It's still just not a big enough group of people to be a priority. Right now we're very heads down on other things.”</em> — <strong>Adam Mosseri, Head of Instagram</strong></p>
</blockquote>
<p>Maintaining a dedicated tablet client meant diverting engineers from vertical video feeds, core messaging infrastructure, and monetization initiatives to serve users who were already using the app on their smartphones.</p>
<p>If an active installed base of 100 million tablets could not justify engineering priority for a major digital platform, 10 million Duo units will struggle to justify yours.</p>
<h3>The Vision Pro Freeze: The Sub-Critical Niche</h3>
<p>At the opposite extreme sits the Apple Vision Pro. Released in 2024 at $3,499, it achieved an estimated 390,000 shipments in its first year. By late 2025, quarterly shipments had fallen to roughly 45,000 units, production lines were idled, and marketing spend was curtailed.</p>
<p>The device experienced a coordination breakdown in multi-sided platform economics:</p>
<ul>
<li><p>Developers saw the small installed base and deferred building native applications (with major streaming platforms opting out of compatibility mode).</p>
</li>
<li><p>Consumers saw the lack of specialized software and delayed purchasing the hardware.</p>
</li>
<li><p>The ecosystem entered an adoption stall.</p>
</li>
</ul>
<p>While the iPhone Duo will achieve substantially more volume than the Vision Pro, the underlying economic principle holds: third-party software development cannot run on novelty alone.</p>
<h2>6. The Math: Calculating Your Developer Investment Threshold</h2>
<p>We can remove all guesswork by calculating your <strong>Developer Investment Threshold</strong>.</p>
<p>At a high level, the expected value of optimizing for the iPhone Duo must exceed the total engineering cost of building and maintaining it:</p>
<p>Expected Value = (Addressable Duo Users × Conversion Rate × Value Lift) + Strategic PR Value</p>
<p>Where:</p>
<ul>
<li><p><strong>Addressable Duo Users:</strong> The projected first-year hardware volume (approximately 10,000,000 units according to IDC forecasts).</p>
</li>
<li><p><strong>Conversion Rate:</strong> Your product's realistic penetration among early Duo buyers (accounting for the 1.5x to 2.5x higher purchasing propensity of early adopters).</p>
</li>
<li><p><strong>Value Lift (Incremental ARPU):</strong> The extra revenue an existing customer pays you <em>specifically because</em> you built custom dual-screen features.</p>
</li>
<li><p><strong>Strategic PR Value:</strong> The tangible monetary return of App Store editorial featuring or launch PR.</p>
</li>
</ul>
<p>To justify the project, your <strong>Expected Value</strong> must exceed your <strong>Total Project Cost</strong>:</p>
<p>Total Cost = Direct Engineering + QA Matrix Testing + Annual Maintenance + Opportunity Cost</p>
<p>If we solve for the minimum audience needed to break even:</p>
<p>Break-Even Users = (Total Cost - Strategic PR Value) / Incremental ARPU Lift</p>
<p>Let’s run the numbers for two standard mobile business models assuming a conservative <strong>$100,000 fully burdened project cost</strong>:</p>
<h3>Scenario A: The Ad-Supported Consumer Utility</h3>
<ul>
<li><p><strong>Monetization:</strong> Mobile advertising generating an average baseline ARPU of $1.50 per year.</p>
</li>
<li><p><strong>Marginal Lift:</strong> Dual-screen optimization increases session depth by 20%, yielding an incremental <strong>$0.30</strong> per user per year.</p>
</li>
<li><p><strong>Strategic PR Value:</strong> $0.</p>
</li>
</ul>
<p>Break-Even Audience = \(100,000 Dollars / \)0.30 Dollar = 333,333 active Duo users</p>
<p>To break even on an ad-supported utility, your app must capture <strong>over 333,000 active iPhone Duo users</strong>. In a 10-million-unit first-year hardware run, that requires capturing <strong>3.33% of every person who buys an iPhone Duo worldwide</strong>. For a utility application, that conversion rate is virtually impossible. The project represents an immediate loss.</p>
<h3>Scenario B: The Enterprise B2B SaaS Tool</h3>
<ul>
<li><p><strong>Monetization:</strong> Annual business subscription at $180/year.</p>
</li>
<li><p><strong>Marginal Lift:</strong> Tabletop review and side-by-side document comparison drive tier upgrades, yielding an incremental <strong>$40.00</strong> per subscriber.</p>
</li>
<li><p><strong>Strategic PR Value:</strong> $20,000 (estimated value of a Day-One App Store feature).</p>
</li>
</ul>
<p>Break-Even Audience = ($100,000 - $20,000) / $40.00 = 2,000 active Duo subscribers</p>
<p>To break even, this tool needs only <strong>2,000 subscribers</strong>, representing a modest <strong>0.02% penetration</strong> of early Duo buyers. Here, early optimization is economically rational.</p>
<table style="min-width:125px"><colgroup><col style="min-width:25px"></col><col style="min-width:25px"></col><col style="min-width:25px"></col><col style="min-width:25px"></col><col style="min-width:25px"></col></colgroup><tbody><tr><td><p><strong>Application Category</strong></p></td><td><p><strong>Primary Monetization Model</strong></p></td><td><p><strong>Typical Revenue Lift</strong></p></td><td><p><strong>Required Duo Users to Break Even</strong></p></td><td><p><strong>Strategic Recommendation</strong></p></td></tr><tr><td><p><strong>Consumer Utility</strong></p></td><td><p>Mobile Advertising</p></td><td><p>$0.20 – $0.40</p></td><td><p>250,000 – 500,000</p></td><td><p><strong>Pass (Rely on Compatibility Mode)</strong></p></td></tr><tr><td><p><strong>Casual Gaming</strong></p></td><td><p>Ads / Microtransactions</p></td><td><p>$0.50 – $1.00</p></td><td><p>100,000 – 200,000</p></td><td><p><strong>Pass (Letterboxed Display)</strong></p></td></tr><tr><td><p><strong>Social / Streaming</strong></p></td><td><p>Subscriptions / Ads</p></td><td><p>$1.00 – $3.00</p></td><td><p>33,000 – 100,000</p></td><td><p><strong>Defer (Wait for Platform Scale)</strong></p></td></tr><tr><td><p><strong>Pro Creative Suites</strong></p></td><td><p>Paid Pro Tier</p></td><td><p>$15.00 – $30.00</p></td><td><p>3,000 – 6,000</p></td><td><p><strong>Selective Support</strong></p></td></tr><tr><td><p><strong>Enterprise / FinTech</strong></p></td><td><p>High-AUM / B2B SaaS</p></td><td><p>$50.00 – $100.00</p></td><td><p>800 – 1,600</p></td><td><p><strong>Invest Early (Target Affluent Base)</strong></p></td></tr></tbody></table>

<hr />
<h2>7. The Decision Framework: When Is It Worth Building?</h2>
<p>Before committing engineering resources to iPhone Duo support, evaluate your product against five operational criteria:</p>
<ol>
<li><p><strong>Monetization Depth:</strong> Does your product generate high subscription ARPU, where converting a few thousand power users moves company metrics?</p>
</li>
<li><p><strong>Workflow Fit:</strong> Does your product genuinely benefit from side-by-side multitasking or tabletop media layouts, or are you forcing standard content into a split screen?</p>
</li>
<li><p><strong>Architectural Readiness:</strong> Does your codebase already use modern SwiftUI, adaptive size classes, and multi-window scene delegates, or will supporting Duo require unwinding years of UIKit orientation hacks?</p>
</li>
<li><p><strong>App Store Alignment:</strong> Does your team have the direct developer relations ties required to secure primary editorial featuring on launch day?</p>
</li>
<li><p><strong>Opportunity Cost:</strong> What critical roadmap feature—checkout speed, performance optimization, core reliability, or Android parity—are you willing to postpone to fund this work?</p>
</li>
</ol>
<p>If you cannot answer yes to at least four of these questions, allocating resources to custom Duo layouts is an inefficient use of engineering bandwidth.</p>
<hr />
<h2>The Takeaway: Protect Your Roadmap</h2>
<p>Every sprint your engineering organization spends managing dynamic safe areas, debugging hinge angle callbacks, and re-architecting navigation bars for iOS 27.1 is a sprint not spent on:</p>
<ul>
<li><p>Streamlining your purchase funnel.</p>
</li>
<li><p>Reducing app launch times on standard devices.</p>
</li>
<li><p>Resolving accessibility and compliance backlogs.</p>
</li>
<li><p>Reaching feature parity on Android.</p>
</li>
</ul>
<p>Supporting new hardware form factors is not a matter of engineering prestige; it is a capital allocation decision. Unless your business model directly capitalizes on a high-ARPU professional audience using side-by-side workflows, the pragmatic engineering strategy for the iPhone Duo at launch is straightforward:</p>
<p>Recompile against iOS 27.1 for basic runtime safety, allow system compatibility mode to handle window scaling, and let other organizations absorb the early adoption costs while your team ships features that move the needle for the other 99.6% of your users.</p>
<h3>UPDATE 20 Sep 2026:</h3>
<p>Following constructive peer review and a technical audit against Apple’s latest developer documentation, I want to clarify a few key assumptions, market data points, and historical examples cited in this post:</p>
<ul>
<li><strong>UI Compatibility Mode vs. Blanket Runtime Safety:</strong> In Section 3, I stated that unoptimized legacy apps run with "zero crash risk" and universal letterboxing. While Apple does provide a UI compatibility presentation mode via <a href="https://developer.apple.com/documentation/bundleresources/information-property-list/uidesignrequirescompatibility?utm_source=gemini"><code>UIDesignRequiresCompatibility</code></a> to center unadapted binaries, Apple’s official <a href="https://developer.apple.com/documentation/technologyoverviews/preparing-your-app-for-iphone-duo?utm_source=gemini">Preparing your app for iPhone Duo</a> guidance explicitly instructs teams to test their software across displays, poses, reserved regions, and scene lifecycles. Compatibility mode is an automatic presentation fallback, not a universal guarantee against input glitches, scene disconnects, or runtime edge cases.</li>
</ul>
<h3><strong>Source Attribution on Market Sizing:</strong></h3>
<ul>
<li><p>The <strong>10-million-unit</strong> first-year shipment estimate is an analyst forecast from <strong>IDC</strong> (<a href="https://www.bloomberg.com/news/articles/2026-08-26/apple-to-ship-10-million-foldable-iphones-in-year-one-idc-says?utm_source=gemini">reported by Bloomberg in August 2026</a>), not an official Apple shipment disclosure.</p>
</li>
<li><p>The <strong>2.5 billion active devices</strong> benchmark is an official milestone announced by Apple in their <a href="https://www.apple.com/newsroom/2026/01/apple-reports-first-quarter-results/?utm_source=gemini">January 2026 Q1 earnings release</a>.</p>
</li>
<li><p>The total foldable market is forecasted by <a href="https://www.idc.com/promo/smartphone-market-share/?utm_source=gemini">IDC</a> to reach <strong>22.9 million units in 2026</strong> (up 12.6% YoY). Comparing the Duo against this segment demonstrates that while 10 million units would capture nearly half of all global foldable volume, it still represents approximately 0.4% of Apple's broader active ecosystem.</p>
</li>
<li><p><strong>The Instagram on iPad Example:</strong> In Section 5, I cited Instagram’s long-standing refusal to build an iPad app. While Adam Mosseri’s famous 2022 remarks (<em>"still just not a big enough group of people to be a priority"</em>) remain a textbook case study in platform opportunity cost, Meta did eventually launch a dedicated Instagram iPad client in September 2025. The historical lesson remains valid—major complementors often wait over a decade until platform scale justifies surface overhead—but the example should not be read as current platform resistance.</p>
</li>
<li><p><strong>Vision Pro Sizing:</strong> The Vision Pro figures cited (390,000 units in 2024; ~45,000 in late 2025) are third-party IDC research estimates (<a href="https://www.macrumors.com/2026/01/02/vision-pro-still-failing-to-catch-on/?utm_source=gemini">reported via the Financial Times and MacRumors</a>), not Apple disclosures.</p>
</li>
<li><p><strong>Engineering Schedules and Financial Thresholds as a Decision Model:</strong> The schedules (6–8 engineer-weeks, 3–4 QA-weeks), the $100,000 fully burdened cost, and the ARPU marginal lifts are <strong>illustrative scenario models</strong>, not measured industry benchmarks. If calculating this for your own organization, adjust the break-even math strictly against your team’s actual loaded payroll and net contribution margins (e.g., at an unadjusted $100,000 cost, a $15–$30 pro creative lift breaks even at 3,333–6,667 users; a $50–$100 SaaS lift breaks even at 1,000–2,000 subscribers).</p>
</li>
</ul>
<p>The central thesis remains unchanged: <strong>unless you maintain a high-ARPU subscription workflow that natively benefits from side-by-side multitasking, building bespoke dual-screen interfaces at launch remains an inefficient allocation of finite engineering capital.</strong></p>
]]></content:encoded></item><item><title><![CDATA[Rethinking What Education Is For]]></title><description><![CDATA[Artificial Intelligence Has Not Changed the Purpose of Education; It Has Revealed It
For the greater part of human history, education was the vehicle for distributing what was scarce: knowledge. The t]]></description><link>https://erkek.in/rethinking-what-education-is-for</link><guid isPermaLink="true">https://erkek.in/rethinking-what-education-is-for</guid><dc:creator><![CDATA[Erk Ekin]]></dc:creator><pubDate>Tue, 18 Aug 2026 23:12:54 GMT</pubDate><content:encoded><![CDATA[<p><strong>Artificial Intelligence Has Not Changed the Purpose of Education; It Has Revealed It</strong></p>
<p>For the greater part of human history, education was the vehicle for distributing what was scarce: knowledge. The teacher knew; the student learned. Books were limited, experts were difficult to access, and knowledge itself was a precious resource. Today, for the first time, humanity faces the exact opposite problem. Knowledge is no longer scarce. Artificial intelligence can summarise millions of sources in seconds, write software, produce text, and answer questions with fluency. If knowledge is no longer scarce, what, then, is education for?</p>
<p>My contention is this: artificial intelligence has not changed the purpose of education; it has merely made it visible. The true role of education was never merely the transmission of information. Its deeper purpose was to equip human beings with the mental tools needed to establish a reliable relationship with reality. Logic, critical thinking, the scientific method, probabilistic reasoning, and ethical judgement. These should not be treated as "subsidiary subjects", but as the very foundation of all education.</p>
<p>This idea is not new. Socrates emphasised asking better questions rather than rote memorisation. Aristotle argued that reason and character must develop in tandem. The Stoics remind us to govern our judgements. Karl Popper demonstrated that science progresses through falsifiability. John Dewey maintained that genuine understanding does not arise from the passive reception of information, but through experience and inquiry. Daniel Kahneman's work demonstrates how easily our minds can err, turning rigorous reasoning skills from a luxury into a necessity. Even Asimov's fiction serves as an indirect guide here, as his stories are built far more on human responsibility than on technology.</p>
<p>The rise of large language models is more than just a technological advance. It represents a fundamental shift in humanity's cognitive environment. For the first time, many routine intellectual tasks can be delegated to machines. This should not diminish the value of human thought; rather, it should prompt us to question which forms of thinking are truly worth preserving. Perhaps the greatest challenge facing education is no longer teaching students what to think, but teaching them how to think in a world where information is limitless, certainty is rare, and persuasive illusions can easily pass for truth.</p>
<p>Future education systems must judge students not by how much information they can recall, but by their ability to distinguish evidence from opinion, identify flaws in reasoning, revise their beliefs in light of new evidence, and uphold intellectual integrity. Artificial intelligence has not rendered human intelligence obsolete. On the contrary, it has revealed that human judgement is more valuable than ever. In this new era, the education systems that thrive will not be those that produce the most information, but those that cultivate people who know how to reach the truth.</p>
]]></content:encoded></item><item><title><![CDATA[Type Safety in Swift: Haskell-style Newtypes with Phantom Types]]></title><description><![CDATA[In software engineering, "primitive obsession" describes a scenario where domain-specific concepts are represented by general types like strings, integers, or timestamps. This lack of specificity ofte]]></description><link>https://erkek.in/type-safety-in-swift-haskell-style-newtypes-with-phantom-types</link><guid isPermaLink="true">https://erkek.in/type-safety-in-swift-haskell-style-newtypes-with-phantom-types</guid><dc:creator><![CDATA[Erk Ekin]]></dc:creator><pubDate>Sun, 08 Mar 2026 20:23:36 GMT</pubDate><content:encoded><![CDATA[<p>In software engineering, "primitive obsession" describes a scenario where domain-specific concepts are represented by general types like strings, integers, or timestamps. This lack of specificity often leads to logic errors, for instance, accidentally passing an arrival timestamp into a function that expects a departure timestamp.</p>
<p>Haskell addresses this through the <code>newtype</code> construct, which creates a distinct type from an existing one with zero runtime cost. In Swift, we can replicate this rigorous type safety by combining <strong>Phantom Types</strong> with the <strong>RawRepresentable</strong> protocol.</p>
<h2>The Compile-Time Safety Guarantee</h2>
<p>The primary advantage of this pattern is that it shifts domain logic errors from runtime to compile time. If we define distinct types for our arrival and departure timestamps, the Swift compiler physically prevents us from swapping them.</p>
<p>Swift</p>
<pre><code class="language-plaintext">func createFlight(destination: String, arrival: NewType&lt;ArrivalTag&gt;, departure: NewType&lt;DepartureTag&gt;) -&gt; Flight {
    // ❌ Compiler Error: Cannot convert value of type 'NewType&lt;ArrivalTag&gt;' to expected argument type 'NewType&lt;DepartureTag&gt;'
    return Flight(arrival: departure, departure: arrival, destination: destination)
}
</code></pre>
<p>By explicitly tagging the types, the mistake of passing <code>departure</code> into the <code>arrival</code> parameter is caught immediately during development.</p>
<h2>The Implementation</h2>
<p>The following code demonstrates the generic <code>NewType</code> wrapper. By using empty enums as "Tags", we create distinct types for arrival and departure that the compiler will treat as strictly incompatible.</p>
<p>Swift</p>
<pre><code class="language-plaintext">import Foundation

struct NewType&lt;Tag&gt;: RawRepresentable, Codable {
    let rawValue: TimeInterval
}

extension NewType: Hashable  {}
extension NewType: Equatable {}

enum ArrivalTag {}
enum DepartureTag {}

struct Flight: Hashable, Codable {
    let arrival: NewType&lt;ArrivalTag&gt;
    let departure: NewType&lt;DepartureTag&gt;
    let destination: String
}

let flightJSON = """
{
    "destination": "London LHR",
    "arrival": 2345,
    "departure": 876867
}
"""

func decodeFlight(from jsonString: String) -&gt; Flight? {
    let decoder = JSONDecoder()
    
    guard let data = jsonString.data(using: .utf8) else { return nil }

    do {
        return try decoder.decode(Flight.self, from: data)
    } catch {
        print("❌ Decoding failed: \(error)")
        return nil
    }
}

if let flight = decodeFlight(from: flightJSON) {
    print("✅ Successfully decoded Flight:")
    print("   Destination: \(flight.destination)")
    print("   Arrival    : \(flight.arrival.rawValue)")
    print("   Departure  : \(flight.departure.rawValue)")
}
</code></pre>
<h2>Functional Programming and Category Theory Concepts</h2>
<h3>Phantom Types</h3>
<p>The <code>Tag</code> parameter in the <code>NewType</code> struct is a <strong>Phantom Type</strong>. It exists only in the type signature and does not appear in the stored properties of the struct. Its sole function is to serve as a marker for the compiler. This ensures that <code>NewType&lt;ArrivalTag&gt;</code> and <code>NewType&lt;DepartureTag&gt;</code> are seen as entirely different entities.</p>
<h3>Isomorphism</h3>
<p>From the perspective of category theory, the <code>NewType</code> struct is <strong>isomorphic</strong> to the underlying <code>TimeInterval</code>. This means there is a total, one-to-one mapping between a <code>TimeInterval</code> and a <code>NewType&lt;Tag&gt;</code> instance.</p>
<p>By utilising <code>RawRepresentable</code>, we preserve this isomorphism. We gain the semantic clarity of a custom type without losing the ability to access and manipulate the data in its original primitive form.</p>
<h2>The Advantage of RawRepresentable and Flat Data</h2>
<p>In Swift, defining a standard wrapper struct often results in nested JSON structures. For example, without specific conformance, a <code>NewType</code> might encode to a nested object like <code>{"arrival": {"rawValue": 2345}}</code>.</p>
<p>By adopting <code>RawRepresentable</code>, the Swift <code>Codable</code> system recognises the struct as a transparent wrapper around its <code>rawValue</code>. This enables <strong>flat data encoding</strong>: the JSON remains a clean, standard numeric timestamp, while the Swift domain model remains strictly typed. This provides the exact safety of Haskell's <code>newtype</code> while maintaining perfect compatibility with existing, flat API structures.</p>
<h2>Conclusion</h2>
<p>Utilising this pattern offers several critical benefits for architecture:</p>
<ul>
<li><p><strong>Strict Type Safety</strong>: Logic errors regarding parameter roles are caught at compile time.</p>
</li>
<li><p><strong>No Performance Overhead</strong>: Structs are value types that the Swift compiler can heavily optimise, treating them identically to the underlying primitive at runtime.</p>
</li>
<li><p><strong>Declarative Intent</strong>: The code explicitly documents the role of each piece of data through the type system rather than relying on variable naming conventions.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Reverse engineer a swift binary]]></title><description><![CDATA[When you compile your high-level Swift code, it gets turned into raw machine instructions. As developers, we rarely look at this level, but it's a fascinating world for anyone interested in security, performance, or just pure curiosity.
In this post,...]]></description><link>https://erkek.in/reverse-engineer-a-swift-binary</link><guid isPermaLink="true">https://erkek.in/reverse-engineer-a-swift-binary</guid><category><![CDATA[hexedit]]></category><category><![CDATA[disassemble]]></category><category><![CDATA[reverse engineering]]></category><category><![CDATA[objdump]]></category><category><![CDATA[Assembly]]></category><dc:creator><![CDATA[Erk Ekin]]></dc:creator><pubDate>Tue, 28 Oct 2025 21:07:31 GMT</pubDate><content:encoded><![CDATA[<p>When you compile your high-level Swift code, it gets turned into raw machine instructions. As developers, we rarely look at this level, but it's a fascinating world for anyone interested in security, performance, or just pure curiosity.</p>
<p>In this post, we are going to write a tiny "login" program in Swift, disassemble it, and find the <em>exact</em> instruction that decides if our password is right or wrong.</p>
<p>Then, we are going to change that instruction to let us in with <em>any</em> password.</p>
<h3 id="heading-part-1-the-swift-program">Part 1: The Swift Program</h3>
<p>First, our highly secure, un-hackable login program, <code>login.swift</code>:</p>
<pre><code class="lang-bash">// login.swift

// Our login <span class="hljs-keyword">function</span> with a very secret password
func login(pass: String) -&gt; Bool {
    pass == <span class="hljs-string">"right-password"</span>
}

// Get the password from the <span class="hljs-built_in">command</span> line
// Note: CommandLine.arguments[0] is the program name
// CommandLine.arguments[1] is our first argument
<span class="hljs-built_in">let</span> result = login(pass: CommandLine.arguments[1])

// Print success or failure
<span class="hljs-built_in">print</span>(result ? <span class="hljs-string">"success"</span> : <span class="hljs-string">"failure"</span>)
</code></pre>
<p>Let's compile it and run it to make sure it works as expected.</p>
<pre><code class="lang-bash">$ swiftc login.swift -o login

<span class="hljs-comment"># Test with the wrong password</span>
$ ./login wrong
failure

<span class="hljs-comment"># Test with the right password</span>
$ ./login right-password
success
</code></pre>
<p>It works perfectly. Now, let's break it.</p>
<h3 id="heading-part-2-disassembly">Part 2: Disassembly</h3>
<p>We will use <code>objdump</code> to disassemble the executable, turning its machine code back into (semi) readable ARM64 assembly.</p>
<pre><code class="lang-bash">$ objdump -D login
</code></pre>
<p>This produces a <em>lot</em> of output, which can be intimidating. But we are looking for two specific functions:</p>
<ol>
<li><p><code>_main</code>: This is the main entry point of our script.</p>
</li>
<li><p><code>_$s5loginAA4passSbSS_tF</code>: This is our <code>login(pass: String) -&gt; Bool</code> function after Swift "mangles" its name.</p>
</li>
</ol>
<h3 id="heading-part-3-finding-the-password-in-the-code">Part 3: Finding the Password in the Code</h3>
<p>Let's look at our <code>login</code> function's assembly first (<code>_$s5loginAA4passSbSS_tF</code>). Tucked inside, we see it loading some data:</p>
<pre><code class="lang-bash">00000001000007a4 &lt;_<span class="hljs-variable">$s5loginAA4passSbSS_tF</span>&gt;:
...
1000007c8: 90000000    adrp    x0, 0x100000000
1000007cc: 9124d000    add     x0, x0, <span class="hljs-comment">#0x934</span>
...
1000007f8: 94000036    bl      0x1000008d0 ; This calls the string comparison <span class="hljs-keyword">function</span>
...
100000810: 12000000    and     w0, w0, <span class="hljs-comment">#0x1 ; Result is 1 (true) or 0 (false)</span>
...
10000081c: d65f03c0    ret                  ; Return
</code></pre>
<p>The <code>add x0, x0, #0x934</code> instruction is loading the address of our hardcoded password. How do we know? If we look at the <code>__TEXT,__cstring</code> (string) section of the dump, we find this at that <em>exact</em> address:</p>
<pre><code class="lang-bash">0000000100000934 &lt;__cstring+0x10&gt;:
100000934: 68676972    ; <span class="hljs-string">"righ"</span>
100000938: 61702d74    ; <span class="hljs-string">"t-pa"</span>
10000093c: 6f777373    ; <span class="hljs-string">"sswo"</span>
100000940: 0a006472    ; <span class="hljs-string">"rd"</span>
</code></pre>
<p>The program literally has <code>"right-password"</code> stored inside it. This function compares the input password to this string and returns <code>1</code> (true) or <code>0</code> (false) in the <code>w0</code> register.</p>
<h3 id="heading-part-4-the-branch">Part 4: The Branch</h3>
<p>Now, let's look at <code>_main</code>. This is where the <em>decision</em> to print "success" or "failure" is made, based on the return value from our <code>login</code> function.</p>
<pre><code class="lang-bash">0000000100000628 &lt;_main&gt;:
...
100000668: 9400004f    bl    0x1000007a4 &lt;_<span class="hljs-variable">$s5loginAA4passSbSS_tF</span>&gt; ; CALLS OUR LOGIN FUNCTION
10000066c: aa0003e8    mov   x8, x0          ; Moves the 1 or 0 result into register x8 (w8)
...
1000006bc: 39400108    ldrb  w8, [x8]        ; Loads the boolean result (1 or 0) into w8
1000006c0: 36000188    tbz   w8, <span class="hljs-comment">#0x0, 0x1000006f0 &lt;_main+0xc8&gt;</span>
1000006c4: 14000001    b     0x1000006c8 &lt;_main+0xa0&gt;
</code></pre>
<p>This is it. This is the whole security model.</p>
<ul>
<li><code>1000006c0: 36000188 tbz w8, #0x0, 0x1000006f0</code></li>
</ul>
<p><code>tbz</code> stands for "Test Bit and Branch if Zero". This instruction does the following:</p>
<ol>
<li><p>Looks at register <code>w8</code> (which holds our <code>1</code> for success or <code>0</code> for failure).</p>
</li>
<li><p>Tests bit <code>#0</code> of that register.</p>
</li>
<li><p>If that bit is <strong>Zero</strong> (meaning the password was wrong), it branches (jumps) to address <code>0x1000006f0</code>.</p>
</li>
</ol>
<p>What is at <code>0x1000006f0</code>? It is the code that loads and prints "failure".</p>
<p>If the bit is <strong>Not Zero</strong> (i.e., it's <code>1</code>, meaning success), it <em>doesn't</em> branch. It just falls through to the next instruction at <code>0x1000006c4</code>, which takes it to the code that prints "success".</p>
<h3 id="heading-part-5-the-patch-bypassing-the-check">Part 5: The Patch: Bypassing the Check</h3>
<p>So, how do we bypass this? We just need to stop that conditional branch from <em>ever</em> happening. We can "neuter" the instruction by overwriting it with a <code>nop</code> (No-Operation) instruction.</p>
<p>The <code>nop</code> instruction in ARM64 is <code>d503201f</code>. It literally does nothing.</p>
<p>By replacing the <code>tbz</code> instruction with a <code>nop</code>, the code will <em>always</em> fall through to the "success" block, no matter what the password is.</p>
<p><strong>Here is our plan:</strong></p>
<ol>
<li><p><strong>Find the bytes:</strong> The <code>tbz</code> instruction is <code>36000188</code>. On a little-endian system (like Apple Silicon), this is stored in the file as <code>88 01 00 36</code>.</p>
</li>
<li><p><strong>Replace the bytes:</strong> The <code>nop</code> instruction is <code>d503201f</code>. This is stored as <code>1f 20 03 d5</code>.</p>
</li>
<li><p><strong>Use a hex editor</strong> (like <code>hexedit</code>) to find <code>88 01 00 36</code> and replace it with <code>1f 20 03 d5</code>.</p>
</li>
</ol>
<pre><code class="lang-bash"><span class="hljs-comment"># 1. Open the file in the hex editor</span>
$ hexedit login

<span class="hljs-comment"># 2. Press Ctrl+S to search.</span>
<span class="hljs-comment">#    Search for hex bytes: 88 01 00 36</span>

<span class="hljs-comment"># 3. Type over it with the new bytes: 1f 20 03 d5</span>
<span class="hljs-comment">#    (The text will turn red)</span>

<span class="hljs-comment"># 4. Press Ctrl+X to exit, and 'y' to save.</span>
</code></pre>
<h3 id="heading-part-6-the-final-step-macos-security">Part 6: The Final Step (macOS Security)</h3>
<p>If we try to run <code>./login</code> now, it will hang or crash. We have modified the binary, which breaks its "code signature". macOS sees the file has been tampered with and refuses to run it.</p>
<p>We need to apply a new, ad-hoc signature to tell macOS we trust this new version.</p>
<pre><code class="lang-bash">$ codesign -f -s - login
</code></pre>
<h3 id="heading-the-result">The Result</h3>
<p>Now, we run our patched app.</p>
<pre><code class="lang-bash">$ ./login anything-i-want
success

$ ./login blahblah
success

$ ./login 1234
success
</code></pre>
<p>We have successfully bypassed the password check by changing just 4 bytes in the compiled executable.</p>
<p>This is, of course, a very simple example, but it demonstrates the fundamentals of binary patching and reverse engineering. It also shows why hardcoding secrets in your client-side applications is never a good idea!</p>
]]></content:encoded></item><item><title><![CDATA[Beyond Public and Private: A Scale for Measuring Code Change Impact]]></title><description><![CDATA[We've all been there. You're staring at a line of code, your task is to change it, but a cold fear creeps in. You ask yourself: "What will break if I touch this?" 😨
In modern software development, this isn't a trivial question. The answer can range ...]]></description><link>https://erkek.in/beyond-public-and-private-a-scale-for-measuring-code-change-impact</link><guid isPermaLink="true">https://erkek.in/beyond-public-and-private-a-scale-for-measuring-code-change-impact</guid><category><![CDATA[CIS]]></category><category><![CDATA[code review]]></category><dc:creator><![CDATA[Erk Ekin]]></dc:creator><pubDate>Sun, 14 Sep 2025 11:51:04 GMT</pubDate><content:encoded><![CDATA[<p>We've all been there. You're staring at a line of code, your task is to change it, but a cold fear creeps in. You ask yourself: "What will break if I touch this?" 😨</p>
<p>In modern software development, this isn't a trivial question. The answer can range from "nothing" to "everything, everywhere, all at once." Our tools give us some clues, but they often don't tell the whole story. What we need is a shared vocabulary to talk about the risk of change.</p>
<p>Let's propose one: The <strong>Change Impact Scale (CIS)</strong>.</p>
<h2 id="heading-the-illusion-of-access-control">The Illusion of Access Control</h2>
<p>As programmers, our first line of defense against unintended consequences is <strong>access control</strong>. Every language has a system for defining a "blast radius" for our code.</p>
<ul>
<li>In <strong>Swift</strong>, we have a rich vocabulary: <code>private</code>, <code>fileprivate</code>, <code>internal</code>, <code>public</code>, and <code>open</code>. Each provides a clear, compiler-enforced boundary on what code can see and use other code.</li>
<li>In <strong>Go</strong>, the system is beautifully simple: identifiers starting with a capital letter are <strong>exported</strong> (public), while those starting with a lowercase letter are unexported (private to the package).</li>
<li><strong>Rust</strong> enforces strict privacy by default. Anything you want to expose outside of a module must be explicitly marked with the <code>pub</code> keyword.</li>
<li><strong>OCaml</strong> uses interface files (<code>.mli</code>) to explicitly declare the public API of a module (<code>.ml</code>), hiding implementation details by default.</li>
</ul>
<p>These tools are fundamental. They are the bedrock of encapsulation and good software architecture.</p>
<p><strong>But they are not enough.</strong></p>
<p>Access control tells you <em>who</em> can call your code, but it tells you nothing about <em>how</em> they depend on it. A <code>public</code> function might be safe to change, while a <code>private</code> string constant might be a time bomb. This is because of hidden, implicit contracts that the compiler can't see.</p>
<hr />
<h2 id="heading-introducing-the-change-impact-scale-cis">Introducing the Change Impact Scale (CIS)</h2>
<p>The CIS is a heuristic, a rule of thumb, designed to create a shared understanding of risk. It grades a change on a scale from 0 to 5, considering not just its visibility (Scope) but also the nature of its connections (Coupling Type).</p>
<h3 id="heading-cis-0-atomic-change-fully-isolated">CIS 0: Atomic Change (Fully Isolated)</h3>
<ul>
<li><strong>The Litmus Test:</strong> The change is completely invisible from the outside. No tests would need to be updated.</li>
<li><strong>Examples:</strong> Renaming a local variable inside a function, refactoring the internal logic of a <code>private</code> method without changing its behavior. This is the safest change you can make.</li>
</ul>
<h3 id="heading-cis-1-controlled-internal-change">CIS 1: Controlled Internal Change</h3>
<ul>
<li><strong>The Litmus Test:</strong> The change's impact is contained within a single module, and your compiler is your safety net.</li>
<li><strong>Examples:</strong> Adding a parameter to an <code>internal</code> (Swift) or package-private (Java/Go) function. The compiler will force you to fix every call site, leaving no room for runtime surprises.</li>
</ul>
<h3 id="heading-cis-2-brittle-internal-change-implicit-contracts">CIS 2: Brittle Internal Change (Implicit Contracts)</h3>
<ul>
<li><strong>The Litmus Test:</strong> The change breaks something within your own codebase, but the compiler <strong>cannot</strong> see the connection. This is the domain of "magic strings" and runtime failures.</li>
<li><strong>Examples:</strong><ul>
<li><strong>Configuration Keys:</strong> Renaming a key in a YAML file that is read as a string in your code.</li>
<li><strong>CSS Selectors:</strong> Renaming a CSS class in a stylesheet without updating the HTML/JSX that uses it.</li>
<li><strong>Reflection:</strong> Relying on the string name of a method or class, as is common in logging or some DI frameworks.</li>
</ul>
</li>
</ul>
<h3 id="heading-cis-3-cross-module-service-contract">CIS 3: Cross-Module / Service Contract</h3>
<ul>
<li><strong>The Litmus Test:</strong> The change breaks a contract with a "trusted peer" that you control, but which is deployed independently. Coordination is required.</li>
<li><strong>Examples:</strong><ul>
<li>Changing the JSON schema of an internal microservice API. The downstream service will fail to parse the response.</li>
<li>Modifying the schema of an event published to a message queue (Kafka, RabbitMQ), breaking consumer services.</li>
</ul>
</li>
</ul>
<h3 id="heading-cis-4-public-contract">CIS 4: Public Contract</h3>
<ul>
<li><strong>The Litmus Test:</strong> The change breaks a contract with "the outside world." You don't know all your consumers and must manage the change with versioning and deprecation policies.</li>
<li><strong>Examples:</strong><ul>
<li>Removing a field from a public v1 REST API.</li>
<li>Altering a <code>public</code> function in an open-source library you publish to npm or Maven.</li>
</ul>
</li>
</ul>
<h3 id="heading-cis-5-petrified-contract-immutable-reality">CIS 5: Petrified Contract (Immutable Reality)</h3>
<ul>
<li><strong>The Litmus Test:</strong> The contract is so foundational that changing it would cause a catastrophic cascade of failures across an ecosystem.</li>
<li><strong>Examples:</strong> The structure of the JPEG file format, the meaning of an HTTP status code, or the syntax of a core language keyword like <code>if</code>.</li>
</ul>
<hr />
<h2 id="heading-standing-on-the-shoulders-of-giants">Standing on the Shoulders of Giants</h2>
<p>The idea of classifying dependencies isn't new. The CIS is a practical heuristic inspired by more formal computer science concepts.</p>
<blockquote>
<p><strong>Connascence:</strong> A term coined by Meilir Page-Jones, connascence is a way to measure software dependency. It means "born together" and describes the degree to which two components must change together. There are many types, like <strong>Connascence of Name</strong> (when multiple components must agree on a name, like a function name as a string—a perfect fit for CIS 2) and <strong>Connascence of Position</strong> (when the order of values matters, like function arguments). CIS can be seen as a simplified, practical application of this powerful idea.</p>
<p><strong>Coupling and Cohesion:</strong> These are foundational principles of software engineering. CIS is, at its heart, a tool for identifying and communicating high-risk forms of <strong>coupling</strong>. A CIS 5 change represents the tightest possible coupling, while a CIS 0 change is completely decoupled.</p>
</blockquote>
<hr />
<h2 id="heading-how-to-use-the-cis-in-your-team">How to Use the CIS in Your Team</h2>
<p>The real power of the CIS is in communication.</p>
<ul>
<li><strong>During Code Reviews:</strong> Instead of a vague "this seems risky," you can say, "This looks like a <strong>CIS 3</strong> change because you're modifying a shared API model. Have you coordinated with the mobile team?"</li>
<li><strong>When Planning Refactoring:</strong> "Our logs show a lot of runtime errors from <strong>CIS 2</strong> changes. Let's prioritize refactoring these config strings into type-safe constants."</li>
<li><strong>In System Design:</strong> "Can we design this interaction to be <strong>CIS 1</strong> by using a compiled gRPC client instead of a <strong>CIS 3</strong> dynamic JSON contract?"</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Access controls are essential, but they only show us part of the picture. By also considering the <em>type</em> of dependency, the Change Impact Scale gives us a richer, more accurate way to assess risk.</p>
<p>It provides a shared vocabulary that allows us to move from fear and uncertainty to informed, confident decision-making. The next time you're about to make a change, ask yourself: <strong>What's the CIS level?</strong> 👍</p>
]]></content:encoded></item><item><title><![CDATA[Mastering Data Integrity with Merkle Trees in Swift]]></title><description><![CDATA[In the world of computer science, ensuring data integrity is paramount. Whether you're building a distributed system, a blockchain application, or simply need to verify that a large file hasn't been corrupted, you need a reliable way to detect any ch...]]></description><link>https://erkek.in/mastering-data-integrity-with-merkle-trees-in-swift</link><guid isPermaLink="true">https://erkek.in/mastering-data-integrity-with-merkle-trees-in-swift</guid><category><![CDATA[merkle tree]]></category><category><![CDATA[crypto]]></category><category><![CDATA[Hash]]></category><dc:creator><![CDATA[Erk Ekin]]></dc:creator><pubDate>Sat, 13 Sep 2025 16:48:55 GMT</pubDate><content:encoded><![CDATA[<p>In the world of computer science, ensuring data integrity is paramount. Whether you're building a distributed system, a blockchain application, or simply need to verify that a large file hasn't been corrupted, you need a reliable way to detect any changes to your data.</p>
<p>This is where Merkle Trees come in. A Merkle Tree is a powerful data structure that allows you to efficiently and securely verify the integrity of a large set of data. In this blog post, I'll explore the concept of Merkle Trees and how you can use them in your Swift projects with the help of my open-source <code>MerkleTree</code> library.</p>
<h2 id="heading-what-is-a-merkle-tree">What is a Merkle Tree?</h2>
<p>A Merkle Tree, also known as a hash tree, is a tree in which every leaf node is labelled with the hash of a data block, and every non-leaf node is labelled with the cryptographic hash of the labels of its child nodes.</p>
<p>The root of the tree, called the Merkle root, is a single hash that represents the entire set of data. If any part of the data changes, the Merkle root will also change, making it easy to detect any tampering.</p>
<h2 id="heading-why-use-a-merkle-tree">Why Use a Merkle Tree?</h2>
<p>Merkle Trees offer several benefits:</p>
<ul>
<li><strong>Efficiency:</strong> They allow you to verify the integrity of a large set of data without having to download or compare the entire dataset. You only need the Merkle root and a small amount of additional information, called an audit trail, to verify a specific piece of data.</li>
<li><strong>Security:</strong> They use cryptographic hash functions to ensure that the data cannot be tampered with without being detected.</li>
<li><strong>Decentralization:</strong> They are well-suited for use in distributed systems, where data is spread across multiple nodes. Each node can independently verify the integrity of the data it has, without having to trust a central authority.</li>
</ul>
<h2 id="heading-introducing-my-merkletree-swift-package">Introducing my <code>MerkleTree</code> Swift Package</h2>
<p>My <code>MerkleTree</code> Swift package is a lightweight, easy-to-use library that allows you to create and work with Merkle Trees in your Swift projects. It respects the Tree Hash EXchange format (THEX), which is designed for efficiently finding and transmitting differences between files.</p>
<h3 id="heading-features">Features</h3>
<ul>
<li>Builds a Merkle Tree from an array of <code>Data</code> blobs.</li>
<li>Handles both balanced and unbalanced trees.</li>
<li>Generates audit trails (proofs) for a given item.</li>
<li>Verifies audit trails.</li>
</ul>
<h2 id="heading-getting-started">Getting Started</h2>
<p>To use my <code>MerkleTree</code> package in your project, add it as a dependency in your <code>Package.swift</code> file:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">let</span> package = <span class="hljs-type">Package</span>(
    dependencies: [
        .package(url: <span class="hljs-string">"https://github.com/swift-tree/MerkleTree.git"</span>, from: <span class="hljs-string">"1.0.0"</span>)
    ]
)
</code></pre>
<h3 id="heading-creating-a-merkle-tree">Creating a Merkle Tree</h3>
<p>Creating a Merkle Tree is as simple as calling the <code>build(fromBlobs:)</code> static method with an array of <code>Data</code> blobs:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> MerkleTree
<span class="hljs-keyword">import</span> Foundation

<span class="hljs-keyword">let</span> dataBlobs = [
    <span class="hljs-string">"Hello"</span>.data(using: .utf8)!,
    <span class="hljs-string">"World"</span>.data(using: .utf8)!,
    <span class="hljs-string">"This"</span>.data(using: .utf8)!,
    <span class="hljs-string">"is"</span>.data(using: .utf8)!,
    <span class="hljs-string">"a"</span>.data(using: .utf8)!,
    <span class="hljs-string">"Merkle"</span>.data(using: .utf8)!,
    <span class="hljs-string">"Tree"</span>.data(using: .utf8)!,
]

<span class="hljs-keyword">let</span> merkleTree = <span class="hljs-type">MerkleTree</span>.build(fromBlobs: dataBlobs)

<span class="hljs-comment">// Get the root hash</span>
<span class="hljs-keyword">let</span> rootHash = merkleTree.value.hash
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Root Hash: \(rootHash)"</span>)
</code></pre>
<h3 id="heading-verifying-data-with-audit-trails">Verifying Data with Audit Trails</h3>
<p>An audit trail (or proof) allows you to verify that a specific piece of data is part of the Merkle Tree without having to reconstruct the entire tree. Here's how you can generate and verify an audit trail:</p>
<pre><code class="lang-swift"><span class="hljs-comment">// Create a leaves array to get the audit trail</span>
<span class="hljs-keyword">var</span> leaves = [<span class="hljs-type">MerkleTree</span>]()
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">getLeaves</span><span class="hljs-params">(tree: MerkleTree)</span></span> {
    <span class="hljs-keyword">if</span> tree.children.<span class="hljs-keyword">left</span> == <span class="hljs-literal">nil</span> &amp;&amp; tree.children.<span class="hljs-keyword">right</span> == <span class="hljs-literal">nil</span> {
        leaves.append(tree)
    }
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-keyword">left</span> = tree.children.<span class="hljs-keyword">left</span> {
        getLeaves(tree: <span class="hljs-keyword">left</span>)
    }
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-keyword">right</span> = tree.children.<span class="hljs-keyword">right</span> {
        getLeaves(tree: <span class="hljs-keyword">right</span>)
    }
}
getLeaves(tree: merkleTree)

<span class="hljs-comment">// Get the hash of the item you want to audit</span>
<span class="hljs-keyword">let</span> itemToAudit = <span class="hljs-string">"Hello"</span>.data(using: .utf8)!
<span class="hljs-keyword">let</span> itemHash = itemToAudit.doubleHashedHex

<span class="hljs-comment">// Get the audit trail</span>
<span class="hljs-keyword">let</span> auditTrail = merkleTree.getAuditTrail(<span class="hljs-keyword">for</span>: itemHash, leaves: leaves)

<span class="hljs-comment">// Verify the audit trail</span>
<span class="hljs-keyword">let</span> isValid = merkleTree.audit(itemHash: itemHash, auditTrail: auditTrail)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Audit trail is valid: \(isValid)"</span>) <span class="hljs-comment">// true</span>
</code></pre>
<h2 id="heading-use-cases-of-merkle-trees">Use Cases of Merkle Trees</h2>
<p>Merkle Trees are used in a wide variety of applications, including:</p>
<ul>
<li><strong>Blockchain and Cryptocurrencies:</strong> In cryptocurrencies like Bitcoin, Merkle Trees are used to summarize all the transactions in a block, allowing for efficient verification of transactions.</li>
<li><strong>Distributed Systems:</strong> In peer-to-peer networks like BitTorrent, Merkle Trees are used to verify the integrity of file pieces downloaded from different peers.</li>
<li><strong>Certificate Transparency:</strong> Google's Certificate Transparency project uses Merkle Trees to create a public log of all issued SSL/TLS certificates, allowing anyone to verify that a certificate is valid and has not been tampered with.</li>
<li><strong>Verifiable Databases:</strong> Merkle Trees can be used to create verifiable databases, where a user can query the database and receive a cryptographic proof that the returned data is correct.</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Merkle Trees are a powerful tool for ensuring data integrity in a wide range of applications. With my <code>MerkleTree</code> Swift package, you can easily incorporate this powerful data structure into your own projects.</p>
<p>I encourage you to check out my <a target="_blank" href="https://github.com/swift-tree/MerkleTree">project on GitHub</a> and try it out for yourself. I welcome any feedback or contributions!</p>
]]></content:encoded></item><item><title><![CDATA[My Journey to Build a Paradox: Implementing Gödel's Incompleteness in Swift]]></title><description><![CDATA[Some ideas in science and mathematics are so profound they change the way you see the world. For me, Kurt Gödel's First Incompleteness Theorem is one of those ideas. It's a statement about the absolute limits of logic and reason, a beautiful, paradox...]]></description><link>https://erkek.in/my-journey-to-build-a-paradox-implementing-godels-incompleteness-in-swift</link><guid isPermaLink="true">https://erkek.in/my-journey-to-build-a-paradox-implementing-godels-incompleteness-in-swift</guid><category><![CDATA[kurt]]></category><category><![CDATA[godel]]></category><category><![CDATA[proof]]></category><category><![CDATA[Maths]]></category><category><![CDATA[Swift]]></category><dc:creator><![CDATA[Erk Ekin]]></dc:creator><pubDate>Mon, 04 Aug 2025 21:10:38 GMT</pubDate><content:encoded><![CDATA[<p>Some ideas in science and mathematics are so profound they change the way you see the world. For me, Kurt Gödel's First Incompleteness Theorem is one of those ideas. It's a statement about the absolute limits of logic and reason, a beautiful, paradoxical truth that says any powerful system of logic will inevitably contain statements that are true but can never be proven within that system.</p>
<p>For years, this concept fascinated and eluded me. This is the story of that project—a journey to translate one of the most abstract theorems in history into <a target="_blank" href="https://github.com/erkekin/GodelNumbering">working Swift code</a>.</p>
<hr />
<h2 id="heading-the-winding-path-to-a-locked-room">The Winding Path to a Locked Room</h2>
<p>My journey didn't start with Gödel. In fact, it started with a very practical interest in <strong>functional programming</strong>. Writing cleaner, more predictable code led me down the rabbit hole of <strong>Type Theory</strong>, the mathematical foundation for languages like Haskell and Swift. I was captivated by the idea that a program's type could be a proof of its correctness.</p>
<p>This led me to <strong>Gentzen's Proof Theory</strong>, where I started to see proofs not as static walls of text but as dynamic, structured objects that could be manipulated and reasoned about. With every step, I was unknowingly gathering the tools I needed to tackle the big one.</p>
<p>Every path seemed to lead back to Gödel.</p>
<p>I was reminded of a book on his proof I had bought over a decade ago. On my first attempt to read it, I admit I understood almost nothing. It was a dense, impenetrable wall of symbols. But this time, after my detour through types and proofs, something clicked. The symbols weren't just abstract logic anymore; they were components of a system. And systems can be built.</p>
<p>This project is the result of that "aha!" moment—an attempt to demystify the theorem by building its machinery from the ground up.</p>
<hr />
<h2 id="heading-the-blueprint-how-do-you-build-a-theorem">The Blueprint: How Do You "Build" a Theorem?</h2>
<p>Before diving into code, we need to understand the core concepts we're trying to model.</p>
<h3 id="heading-1-formal-systems-a-game-of-logic">1. Formal Systems: A Game of Logic</h3>
<p>Imagine a simple board game. You have:</p>
<ul>
<li><p><strong>Axioms:</strong> The starting positions of your pieces. These are statements you accept as true without proof (e.g., "0 = 0").</p>
</li>
<li><p><strong>Rules of Inference:</strong> The legal moves you can make (e.g., if you have statements A and "A implies B", you can derive B).</p>
</li>
<li><p><strong>Theorems:</strong> All the possible board states you can reach from your starting position using only legal moves.</p>
</li>
</ul>
<p>A formal system is just this game of logic. The goal of the system is to generate all possible true theorems. Gödel's theorem asks: is it possible for a system to generate <em>all</em> true statements about numbers? His answer was a resounding <strong>no</strong>.</p>
<h3 id="heading-2-godel-numbering-turning-logic-into-math">2. Gödel Numbering: Turning Logic into Math</h3>
<p>Gödel's central genius was to find a way to make a formal system talk about itself. He did this by assigning a unique number (a <strong>Gödel Number</strong>) to every symbol, formula, and proof.</p>
<p>For this project, I assigned a number to each symbol in our simple logical language: <code>~</code> is 1, <code>∨</code> is 2, <code>(</code> is 8, <code>x</code> is 11, and so on. A formula is a sequence of these numbers. To get a <em>single</em> unique number for a whole formula, we use prime factorization:</p>
<blockquote>
<p>The formula <code>s1, s2, s3, ...</code> becomes <code>2^{g(s1)} x 3^{g(s2)} x 5^{g(s3)} x ...</code></p>
</blockquote>
<p>Suddenly, statements about logic become statements about numbers. "This formula is a theorem" becomes "This specific, massive number has a certain mathematical property."</p>
<hr />
<h2 id="heading-the-technical-challenge-an-impossible-number">The Technical Challenge: An Impossible Number</h2>
<p>A central challenge in implementing Gödel's proof is representing the "Gödel numbers" themselves. Let's take a simple 10-symbol formula from the project: <code>(∃x)(x=sy)</code>. While it looks small, its Gödel number is anything but. Through logarithmic calculation, we can determine that its value requires approximately <strong>287 bits</strong> (or 36 bytes) of storage.</p>
<p>For perspective, a standard 64-bit integer (<code>UInt64</code>), the largest in many languages, offers only <strong>64 bits</strong> (8 bytes). Our number isn't just a little bigger; it's more than four times the size and represents a value so vast it's in a completely different universe of magnitude. And this is for a trivial formula. A real proof would require thousands or millions of bits, making it computationally impossible to store these numbers directly.</p>
<p>So, how do you work with a number you can't compute? You work on it symbolically. For this, I used a library I had previously built called <a target="_blank" href="https://github.com/swift-tree/ExpressionTree"><code>ExpressionTree</code></a>. Instead of calculating the final number, <code>ExpressionTree</code> represents it as a tree of its prime factors. For example, <code>$2^8 x 3^4$</code> is stored <em>like</em> this:</p>
<p><code>* (^ 2 8) (^ 3 4)</code></p>
<p>This allows the program to reason about these immense numbers, compare them, and—most importantly—perform substitutions on them without ever needing to compute their final value.</p>
<hr />
<h2 id="heading-a-walk-through-the-code">A Walk Through the Code</h2>
<p>The project's structure directly mirrors the concepts above.</p>
<ul>
<li><p><code>Proposition.swift</code>: This is the alphabet of our formal language. It defines each logical symbol and assigns it a Gödel number.</p>
</li>
<li><p><code>GodelNumbering.swift</code>: This is our universal translator. It takes a formula string, converts it into a sequence of Gödel numbers, and then encodes that sequence into an <code>ExpressionTree</code>.</p>
</li>
<li><p><code>GStatement.swift</code>: The protagonist of our story. This struct represents the famous self-referential Gödel sentence, "G." Its one job is to enact the self-reference that creates the paradox.</p>
</li>
</ul>
<hr />
<h2 id="heading-the-climax-a-paradox-in-a-unit-test">The Climax: A Paradox in a Unit Test</h2>
<p>The entire logical argument of the theorem comes to life in a single unit test: <code>test_provable_leads_to_contradiction()</code>. Let's walk through it.</p>
<h3 id="heading-step-1-the-template-g">Step 1: The Template (<code>G</code>)</h3>
<p>First, we define our Gödel sentence, <code>G</code>. At this stage, it's just a template. It contains a placeholder variable, <code>y</code>, and it informally says:</p>
<blockquote>
<p>"The formula you get by substituting <code>y</code> in the formula with Gödel number <code>y</code> <strong>is not provable</strong>."</p>
</blockquote>
<h3 id="heading-step-2-the-self-reference-gg">Step 2: The Self-Reference (<code>G(g)</code>)</h3>
<p>This is the moment of magic. We perform a substitution on <code>G</code>, but we substitute the placeholder <code>y</code> with the Gödel number of <code>G</code> itself.</p>
<p>The formula is no longer a template. It's a concrete statement that now says:</p>
<blockquote>
<p>"<strong>This very statement</strong> is not provable."</p>
</blockquote>
<h3 id="heading-step-3-the-what-if-scenario">Step 3: The "What If" Scenario</h3>
<p>Now we test the limits of our system. We ask, "What if our formal system was powerful enough to prove this statement?" We simulate this by creating a <code>Proof</code> object where our self-referential statement is manually added to the list of official theorems.</p>
<h3 id="heading-step-4-the-contradiction">Step 4: The Contradiction</h3>
<p>The final line of the test asserts that <code>GStatement.isProvable()</code> should be <code>true</code>. And it is—because we put the proof in ourselves. But think about what we've just done.</p>
<p>Our system has successfully proven a statement that is explicitly defined as being "not provable." It has proven a falsehood. This is a fundamental contradiction, meaning any system capable of this feat is logically <strong>inconsistent</strong>.</p>
<p>And what if the test had failed? That would mean our system is <strong>consistent</strong>... but it would also mean it failed to prove a statement that we know to be true (the Gödel sentence itself). That would make the system <strong>incomplete</strong>.</p>
<p>This is the dilemma. <strong>Consistent or Complete. You can't have both.</strong></p>
<hr />
<h2 id="heading-conclusion-the-beauty-of-limits">Conclusion: The Beauty of Limits</h2>
<p>Gödel's theorem is often seen as a pessimistic result—a statement about what we can never know. But I see it differently. It's a beautiful map of the boundaries of formal reason. It shows that truth is a larger concept than provability.</p>
<p>For me, this project was the ultimate confirmation of a simple idea: the best way to understand a complex system is to build it. Going from an impenetrable book to a passing unit test transformed an abstract piece of mathematics into a tangible, working machine.</p>
]]></content:encoded></item><item><title><![CDATA[Faster Equatable and Hashable conformances]]></title><description><![CDATA[Compiler-synthesized conformance in Swift is a powerful tool that accelerates development by reducing boilerplate code. For protocols like Equatable and Hashable, the compiler can automatically generate the required implementations—a convenience that...]]></description><link>https://erkek.in/faster-equatable-and-hashable-conformances-with-identifiable</link><guid isPermaLink="true">https://erkek.in/faster-equatable-and-hashable-conformances-with-identifiable</guid><category><![CDATA[Swift]]></category><category><![CDATA[SwiftUI]]></category><category><![CDATA[performance]]></category><dc:creator><![CDATA[Erk Ekin]]></dc:creator><pubDate>Wed, 23 Jul 2025 20:59:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1753304211470/949930d3-b14c-42f8-857c-2a4b137bffe7.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Compiler-synthesized conformance in Swift is a powerful tool that accelerates development by reducing boilerplate code. For protocols like <code>Equatable</code> and <code>Hashable</code>, the compiler can automatically generate the required implementations—a convenience that is widely utilized. However, this automation, like any abstraction, comes with trade-offs. The default behavior can introduce significant and non-obvious performance bottlenecks, particularly in systems that rely on a clear concept of object identity.</p>
<p>This analysis examines the default synthesis mechanism, identifies its performance characteristics, and proposes an architectural pattern for specific use cases where performance is critical.</p>
<h3 id="heading-the-performance-cost-of-synthesized-conformance">The Performance Cost of Synthesized Conformance</h3>
<p>When the Swift compiler synthesizes conformance for <code>Equatable</code> and <code>Hashable</code>, it applies a simple, universal rule: every stored property of the type is included in the comparison or hashing logic. For a <code>struct</code> with a few value-type properties, the overhead is often negligible. However, for more complex data structures, the cost can become substantial.</p>
<p>Consider a model that includes a collection:</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Company</span> </span>{
    <span class="hljs-keyword">let</span> id: <span class="hljs-type">UUID</span>
    <span class="hljs-keyword">let</span> name: <span class="hljs-type">String</span>
    <span class="hljs-keyword">let</span> address: <span class="hljs-type">Address</span> <span class="hljs-comment">// Address might be a complex struct</span>
    <span class="hljs-keyword">let</span> employees: [<span class="hljs-type">User</span>] <span class="hljs-comment">// A potentially large collection</span>
}
</code></pre>
<p>If <code>Company</code> conforms to <code>Equatable</code>, a comparison between two instances (<code>companyA == companyB</code>) will trigger a member-wise comparison of all properties. This includes iterating through the entire <code>employees</code> array and comparing each <code>User</code> object. Such an operation's complexity is at least O(N), where N is the number of elements in the collection, and can be worse depending on the complexity of the elements themselves.</p>
<p>In performance-sensitive contexts, such as the diffing algorithms used to update SwiftUI views, these O(N) comparisons can become a significant performance hotspot, leading to dropped frames and unresponsive user interfaces. The same complexity issue applies to <code>Hashable</code> conformance, affecting the performance of <code>Set</code> and <code>Dictionary</code> operations.</p>
<h3 id="heading-the-identifiable-protocol-and-value-equality">The <code>Identifiable</code> Protocol and Value Equality</h3>
<p>Modern Swift development, particularly with frameworks like SwiftUI and Combine, heavily utilizes the <code>Identifiable</code> protocol.</p>
<pre><code class="lang-swift"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">Identifiable</span> </span>{
    <span class="hljs-keyword">associatedtype</span> <span class="hljs-type">ID</span>: <span class="hljs-type">Hashable</span>
    <span class="hljs-keyword">var</span> id: <span class="hljs-type">ID</span> { <span class="hljs-keyword">get</span> }
}
</code></pre>
<p>The <code>Identifiable</code> protocol asserts that a type has a single, canonical property representing its stable identity. If two instances share the same <code>id</code>, they represent the same conceptual entity, even if their other properties differ (e.g., a mutable state that has been updated).</p>
<p>The compiler's synthesized <code>Equatable</code> implementation, however, checks every property for <em>value equality</em>. This is distinct from identity. The default behavior is not wrong, but in cases where a full member-wise comparison isn't needed, it can be computationally expensive.</p>
<h3 id="heading-verifying-the-compilers-behavior">Verifying the Compiler's Behavior</h3>
<p>An analysis of the <a target="_blank" href="https://github.com/swiftlang/swift">Swift open-source repository</a> confirms that the derivation logic for <code>Equatable</code> and <code>Hashable</code> is generalized and does not account for the semantic meaning of <code>Identifiable</code>.</p>
<p>The relevant logic resides in the compiler source file <a target="_blank" href="https://github.com/swiftlang/swift/blob/main/lib/Sema/DerivedConformance/DerivedConformanceEquatableHashable.cpp"><code>DerivedConformanceEquatableHashable.cpp</code></a>. When synthesizing conformance, the compiler's primary concern is to verify that all of a type's stored properties themselves conform to <code>Hashable</code> before generating the combined hash.</p>
<pre><code class="lang-swift"><span class="hljs-comment">// Refuse to synthesize Hashable if type isn't a struct or enum, or if it</span>
<span class="hljs-comment">// has non-Hashable stored properties/associated values.</span>
auto hashableProto = <span class="hljs-type">Context</span>.getProtocol(<span class="hljs-type">KnownProtocolKind</span>::<span class="hljs-type">Hashable</span>);
<span class="hljs-keyword">if</span> (!canDeriveConformance(getConformanceContext(), <span class="hljs-type">Nominal</span>,
                          hashableProto)) {
  <span class="hljs-comment">// ...error handling...</span>
}
<span class="hljs-comment">// see: https://github.com/swiftlang/swift/blob/main/lib/Sema/DerivedConformanceEquatableHashable.cpp</span>
</code></pre>
<p>The same is true for <code>Equatable</code>. As the source demonstrates, the validation step is mechanical: it checks for protocol conformance on all members. It does not contain any special logic to detect if the type also conforms to <code>Identifiable</code> and, if so, use the <code>id</code> property as the sole source for hashing and equality.</p>
<p>The compiler will always default to its member-wise synthesis unless a more specific implementation—like the one provided by our extensions—is available to satisfy the protocol requirements first.</p>
<p><strong>A note on SwiftUI’s Equatable usage</strong></p>
<p>SwiftUI's rendering efficiency is deeply tied to the <code>Equatable</code> protocol. When your model or state type conforms to <code>Equatable</code>, SwiftUI can perform a quick check, <code>oldItem == newItem</code>, to determine if a value has actually changed. If items are equal, it smartly skips re-rendering the associated view, leading to significantly better performance by only updating what's absolutely necessary. Conversely, if your model does not conform to <code>Equatable</code>, SwiftUI lacks this comparison mechanism. In such types do not use this technique with Identifiable as changes on varying parameters won’t be reflected to view.</p>
<p>To ensure data consistency, SwiftUI must assume that the item might have changed and will consequently re-render the view for that item, or even the entire view hierarchy, every time, leading to unnecessary redraws and reduced performance. Therefore, understanding and leveraging <code>Equatable</code> correctly is crucial for building performant SwiftUI applications.</p>
<h3 id="heading-performance-benchmarks-the-numbers">Performance Benchmarks: The Numbers</h3>
<p>To quantify the performance characteristics of both approaches, I created a custom benchmarking suite implemented directly in <code>main.swift</code>. The tests were conducted on a <code>Company</code> model containing over 1,000 employees, each with nested profile information.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Test Case</td><td>Time (ms)</td></tr>
</thead>
<tbody>
<tr>
<td>1. Equatable w/ Identifiable</td><td>1.22</td></tr>
<tr>
<td>2. Equatable (Synthesized)</td><td>3.83</td></tr>
<tr>
<td>3. Equatable (Synthesized, Identical)</td><td>11.61</td></tr>
<tr>
<td>4. Hashable w/ Identifiable</td><td>6.44</td></tr>
<tr>
<td>5. Hashable (Synthesized)</td><td>195,413.01</td></tr>
</tbody>
</table>
</div><h3 id="heading-performance-comparison">Performance Comparison</h3>
<ul>
<li><p><strong>Synthesized Equatable is 3x slower</strong> than the <code>Identifiable</code>-based implementation.</p>
</li>
<li><p><strong>Synthesized Hashable is 30K times slower</strong> than the <code>Identifiable</code>-based implementation.</p>
</li>
</ul>
<h3 id="heading-test-configuration">Test Configuration:</h3>
<ul>
<li><p><strong>Test Object</strong>: <code>Company</code> with 1,000+ employees</p>
</li>
<li><p><strong>Nesting</strong>: 3 levels deep (<code>Company</code> → <code>Employees</code> → <code>Profile</code>)</p>
</li>
<li><p><strong>Iterations</strong>: 1,000,000 (for all tests)</p>
</li>
<li><p><strong>Environment</strong>: Release build, MacBook Air M4, macOS Tahoe 26.0</p>
</li>
<li><p><strong>Benchmark Code</strong>: <a target="_blank" href="https://github.com/erkekin/benchmark-equatable-and-hashable-with-id">https://github.com/erkekin/benchmark-equatable-and-hashable-with-id</a></p>
</li>
</ul>
<p>The results demonstrate that the <code>Identifiable</code>-based implementation maintains consistent O(1) performance by focusing solely on the <code>id</code> property, while the synthesized implementation processes every property in the object graph. This difference becomes particularly relevant in performance-sensitive contexts like SwiftUI's diffing algorithms or when working with large collections in <code>Set</code> or <code>Dictionary</code> types.</p>
<p><strong>References</strong></p>
<p><a target="_blank" href="https://github.com/swiftlang/swift-evolution/blob/main/proposals/0185-synthesize-equatable-hashable.md">https://github.com/swiftlang/swift-evolution/blob/main/proposals/0185-synthesize-equatable-hashable.md</a></p>
<p><a target="_blank" href="https://stackoverflow.com/questions/64279211/is-it-right-to-conform-hashable-by-only-taking-id-into-consideration">https://stackoverflow.com/questions/64279211/is-it-right-to-conform-hashable-by-only-taking-id-into-consideration</a></p>
]]></content:encoded></item><item><title><![CDATA[Use Touch ID for Terminal Passwords on macOS]]></title><description><![CDATA[As a software engineer, you likely spend a significant amount of time in the terminal, executing commands that often require sudo privileges. Typing your password repeatedly can become a tedious interruption to your flow. Did you know that you can le...]]></description><link>https://erkek.in/use-touch-id-for-terminal-passwords-on-macos</link><guid isPermaLink="true">https://erkek.in/use-touch-id-for-terminal-passwords-on-macos</guid><category><![CDATA[touch id]]></category><category><![CDATA[terminal]]></category><category><![CDATA[macOS]]></category><category><![CDATA[authentication]]></category><category><![CDATA[sudo]]></category><dc:creator><![CDATA[Erk Ekin]]></dc:creator><pubDate>Mon, 21 Apr 2025 16:24:46 GMT</pubDate><content:encoded><![CDATA[<p>As a software engineer, you likely spend a significant amount of time in the terminal, executing commands that often require <code>sudo</code> privileges. Typing your password repeatedly can become a tedious interruption to your flow. Did you know that you can leverage your Mac's Touch ID to authenticate these commands with just a touch of your finger?</p>
<p>This feature, when enabled, provides a more convenient and equally secure way to authorize actions in the terminal that require elevated permissions. Let's dive into how you can set this up on your macOS machine.</p>
<h2 id="heading-what-is-pam-and-why-do-we-modify-it">What is PAM and Why Do We Modify It?</h2>
<p>Before we get to the how, let's briefly touch upon the "why." macOS, like other Unix-like systems, uses a framework called PAM (Pluggable Authentication Modules) to handle authentication tasks. PAM acts as a layer between applications (like your terminal) and the actual authentication methods (like passwords, Touch ID, etc.).</p>
<p>PAM uses configuration files to determine how a user should be authenticated for a specific service. By modifying the PAM configuration file for <code>sudo</code>, we can instruct the system to accept Touch ID as a valid authentication method.</p>
<h2 id="heading-enabling-touch-id-for-sudo">Enabling Touch ID for <code>sudo</code></h2>
<p>The process involves editing a PAM configuration file. The specific file differs slightly depending on your macOS version.</p>
<p><strong>For macOS Sonoma (and later):</strong></p>
<p>macOS Sonoma introduced a change that makes this modification more persistent across system updates by using a dedicated local configuration file.</p>
<ol>
<li><p><strong>Open Terminal:</strong> Launch the Terminal application from your Applications &gt; Utilities folder or via Spotlight search.</p>
</li>
<li><p><strong>Copy the template file:</strong> We'll create a local configuration file based on a template. Run the following command and enter your administrator password when prompted:</p>
<pre><code class="lang-bash"> sudo cp /etc/pam.d/sudo_local.template /etc/pam.d/sudo_local
</code></pre>
</li>
<li><p><strong>Edit the configuration file:</strong> Now, open the copied file for editing using a command-line text editor like <code>nano</code>:</p>
<pre><code class="lang-bash"> sudo nano /etc/pam.d/sudo_local
</code></pre>
</li>
<li><p><strong>Uncomment the Touch ID line:</strong> Look for the line that starts with <code># auth sufficient pam_</code><a target="_blank" href="http://tid.so"><code>tid.so</code></a>. This line is commented out by default. Remove the <code>#</code> at the beginning of the line to uncomment it. The line should now look exactly like this:</p>
<pre><code class="lang-bash"> auth sufficient pam_tid.so
</code></pre>
</li>
<li><p><strong>Save and Exit:</strong> Save the changes to the file. If you're using <code>nano</code>, press <code>Control + O</code>, then hit <code>Enter</code> to confirm the filename, and finally press <code>Control + X</code> to exit the editor.</p>
</li>
</ol>
<p><strong>For macOS versions prior to Sonoma:</strong></p>
<p>If you are using an older version of macOS, you will directly edit the main <code>sudo</code> PAM configuration file. Be aware that this change <em>may</em> be reset after a macOS system update, requiring you to repeat these steps.</p>
<ol>
<li><p><strong>Open Terminal:</strong> Launch the Terminal application.</p>
</li>
<li><p><strong>Edit the sudo configuration file:</strong> Open the <code>sudo</code> PAM file for editing with <code>nano</code>:</p>
<pre><code class="lang-bash"> sudo nano /etc/pam.d/sudo
</code></pre>
</li>
<li><p><strong>Add the Touch ID line:</strong> Add the line <code>auth sufficient pam_</code><a target="_blank" href="http://tid.so"><code>tid.so</code></a> at the beginning of the file, just below any lines that start with a <code>#</code> (which are comments). It should look something like this:</p>
<pre><code class="lang-bash"> <span class="hljs-comment"># sudo: auth account password session</span>
 auth sufficient pam_tid.so
 auth include sudo_local
 <span class="hljs-comment"># ... rest of the file ...</span>
</code></pre>
<p> Make sure <code>auth sufficient pam_</code><a target="_blank" href="http://tid.so"><code>tid.so</code></a> is above <code>auth include sudo_local</code> if that line exists.</p>
</li>
<li><p><strong>Save and Exit:</strong> Save the file and exit the editor (using <code>Control + O</code>, <code>Enter</code>, and <code>Control + X</code> in <code>nano</code>).</p>
</li>
</ol>
<h2 id="heading-testing-your-setup">Testing Your Setup</h2>
<p>After making the changes and saving the file, close and reopen your Terminal application to ensure the new configuration is loaded.</p>
<p>Now, try running a command that requires <code>sudo</code>, such as:</p>
<pre><code class="lang-bash">sudo ls /private/var/root
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745252674262/2d1acfda-22e2-40c4-9e91-0339b44993c2.png" alt class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[Land Ahoy Method]]></title><description><![CDATA[This is the practice of following the changelogs for new versions of your dependencies and mimicking the new features yourself before you are able to actually update the dependency.
Note: I am gifting this method and its name to the community, but if...]]></description><link>https://erkek.in/land-ahoy-method</link><guid isPermaLink="true">https://erkek.in/land-ahoy-method</guid><category><![CDATA[dependency management]]></category><category><![CDATA[software migration]]></category><category><![CDATA[dependency update]]></category><dc:creator><![CDATA[Erk Ekin]]></dc:creator><pubDate>Wed, 10 Aug 2022 15:05:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660143331519/5JUAArRo_.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is the practice of following the changelogs for new versions of your dependencies and mimicking the new features yourself before you are able to actually update the dependency.</p>
<p><strong>Note:</strong> I am gifting this method and its name to the community, but if you use it anywhere, please provide a reference back to this page.</p>
<p><strong>Note:</strong> This article will be easier for Cocoa developers to understand, as the examples are from the iOS world. However, the principles can be understood and applied by all software engineers.</p>
<h3 id="heading-tldr-for-those-too-lazy-to-read">TL;DR (For those too lazy to read)</h3>
<p>If you can't update a dependency right away, take the time to thoroughly understand the changes in the new version. To prepare for these changes and ease the transition, implement the new functionalities yourself while still using the old version. Once you finally update the dependency, you can simply delete your custom implementation.</p>
<h3 id="heading-example-the-result-type">Example: The <code>Result</code> type</h3>
<p>The <code>Result</code> type was <a target="_blank" href="https://github.com/apple/swift-evolution/blob/master/proposals/0235-add-result.md">announced</a> in Swift 5. After downloading the beta version of Xcode and experimenting with this new feature, we implemented our own <code>Result</code> type in our app that still supported Swift 4. Later, as part of our Swift 5 migration tech debt, we deleted our custom version and adapted to the new native API at virtually zero cost.</p>
<h3 id="heading-example-swiftui">Example: SwiftUI</h3>
<p>SwiftUI was a revolution when it was introduced in 2019. Its philosophy shifted us from writing imperative code to declarative code. To put it simply, the UI and data are no longer on the same plane; the UI has become a direct function of the data (in truth, it always was, but now this relationship is enforced). SwiftUI has evolved significantly over the past few years; some APIs have been deprecated, and new ones have been added with each iOS release. It's now abundantly clear that SwiftUI will be an essential skill for teams wanting to develop UIs on Apple's platforms. It not only simplifies tasks that were once very difficult but also enables the creation of more functional, maintainable, and scalable code. Of course, it’s not without its problems, but I anticipate these will be ironed out, just as we've seen over the last few years.</p>
<p>So, how can we imitate SwiftUI within the framework of the Land Ahoy Method©? First, we need to understand the foundation of these changes. As mentioned, SwiftUI shifts the coding paradigm from imperative to declarative, forcing you into a different way of thinking. You can start by gradually applying this philosophy to your existing codebase, or at least writing new features with this declarative UI notion in mind. With this approach, you will learn by doing, understanding the direction the platform is heading, and dramatically reduce your team's future migration workload.</p>
<h3 id="heading-the-risk-factor-combine">The Risk Factor - <code>Combine</code></h3>
<p>There is a risky side to this method. For instance, the <code>Combine</code> framework, which was announced at WWDC 2019 and forms the backbone of SwiftUI, did not evolve as much as expected in the following years. At subsequent WWDCs, it received minor updates or sometimes none at all. If we had assumed in 2019 that Combine was the future and prepared for migration by rewriting everything with it, it would not have been a sound investment. We realised this when Apple began solving some of the same problems that Combine addressed with another API: <code>async/await</code>. The <code>async/await</code> syntax not only handles many of the use cases of Combine but also offers additional benefits for asynchronous code development, such as improved local reasoning and type-safety.</p>
<p>The <code>Combine</code> example serves as a cautionary tale. It's crucial to be prudent and not immediately adopt every new technology a platform releases. Instead, it's wise to closely monitor how much the community, and especially the platform provider itself, is investing in the new technology. Sometimes, they even declare their long-term intentions.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Migration is a form of tech debt that can consume a significant amount of a team's time. In the teams I've joined, the primary motivation for migration is often the desire to use new APIs, though sometimes a critical security patch or performance improvements can be the driving factor. Whatever the motivation, to ensure a smooth transition, I recommend this method, which <strong>you</strong> can start implementing today to establish it as a best practice within your team.</p>
<hr />
<p>Image taken at <a target="_blank" href="https://www.fitzmuseum.cam.ac.uk/">Fitzwilliam Museum</a> - Cambridge</p>
]]></content:encoded></item><item><title><![CDATA[Swift’te enum dizmece]]></title><description><![CDATA[Swift’ciler olarak ozellikle MVVM, Rx, Reducers gibi mimariler yaygınlaştıkça enum kullanımını artırdık. Objective C’den gelen kavimler olarak pek alışkın olmasak da Swift’teki kullanım kolaylığı ve yetenekleri bize enum’i sevdirdi. Dürüst olmak gere...]]></description><link>https://erkek.in/swiftte-enum-dizmece</link><guid isPermaLink="true">https://erkek.in/swiftte-enum-dizmece</guid><category><![CDATA[enum]]></category><category><![CDATA[Swift]]></category><category><![CDATA[swiftsyntax]]></category><dc:creator><![CDATA[Erk Ekin]]></dc:creator><pubDate>Sat, 18 Jun 2022 10:59:58 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1655549677117/iJEgC7J_H.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Swift’ciler olarak ozellikle MVVM, Rx, Reducers gibi mimariler yaygınlaştıkça enum kullanımını artırdık. Objective C’den gelen kavimler olarak pek alışkın olmasak da Swift’teki kullanım kolaylığı ve yetenekleri bize <code>enum</code>’i sevdirdi. Dürüst olmak gerekirse Objective C’deki kullanımı daha samimiydi. Çünkü enum, enumeration’in kısaltılışı ve türkçede <em>numaralandırma</em> demek. Objective C’den gelen dedeler olarak biliyoruz ki her <code>case</code>’in bir numarası vardir. Ancak Swift’teki liberallik sonucu görüyoruz ki bu yaklaşımın pek bir numarası yokmuş. İstersen bir case’i <code>String</code> olarak ifade edebiliyorsun. İstersen direk kendisi ile de ifade edebilirsin.</p>
<p>Biz impossible state’leri, <code>mesaj</code>, <code>action</code> ve <code>effect</code>leri <code>enum</code>larla ifade ediyoruz dolayılısyla codebase’de <code>enum</code>lar baya birinci sınıf vatandaş.</p>
<p>Bir de bunun küçük kardeşi var: <code>switch</code>. <code>guard let</code> case, <code>if let</code> hayrani değilseniz <code>switch</code> ile <code>case</code>’leri yürüyeceksiniz, çare yok.</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">T</span>, <span class="hljs-title">E</span>: <span class="hljs-title">Error</span>&gt; </span>{
    <span class="hljs-keyword">case</span> loaded(<span class="hljs-type">Result</span>&lt;<span class="hljs-type">T</span>, <span class="hljs-type">E</span>&gt;)
    <span class="hljs-keyword">case</span> loading
}
</code></pre>
<p>Adı <code>State</code>, generic <code>T</code> parametresi aliyor ve iki case’i var, biri associated value’su olmayan <code>loading</code> durumu, digeri ise Success tipi <code>T</code>, hata tipi <code>Error</code> protokolu olan loaded adında bir <code>case</code>. Hem associated values’lu hem de generic parametreli.</p>
<p>Durum her zaman böyle olmuyor. Uygulamanız karmaşıksa çok özellikli ekranlarda genellikle <a target="_blank" href="https://github.com/kickstarter/ios-oss/blob/master/Library/RefTag.swift">şunun</a> gibi <code>enum</code>’larla çalışırsınız.</p>
<p>Bu gibi uzun listeleri alfabetik dizmek gayet yararli, nereye bakacaginizi biliyorsunuz, case’leri hizlica bulabiliyorsunuz, ayni zamanda bizim gibi kalabalik bir ekibiniz varsa ortak bir dilde buluşmuş oluyorsunuz. Her case eşittir ama aralarında kavga çıkmasın diye alfabetik sıralıyoruz.
Bir de bu case’lerin uzerinde yuruyecek olan switch case’lerini hayal edin. Oradaki sikinti daha da büyük cunku her case’in altinda ilgili kod blogu da duruyor. Isler karmasiklasinca case’leri el ile sort etmek zorlaşıyor. Zaten çoğu zaman da hata yapıyoruz, PR çıkmışsak approval’lari kaybediyoruz, CI’i bekliyoruz vs.</p>
<p>Bu tip isleri yapmak icin SwiftFormat aracı var ancak henuz <code>sortedCases</code> adinda bir rule yok (güzel fırsat!). Ben de oturdum, SwiftFormat olmadan bu isi nasıl yaparım diye biraz kafa yordum. Size bu yolculugumu anlatacağım.
Ilk once bir Automator tool’u yazdım. Tek bir ogesi var, <code>Run Shell Script</code>. Orada da <code>sort</code> komut satırı aracını kullandım. En iyi sonucu <code>sort — ignore-leading-blanks -f</code> komutu verdi (Ayrıntılar icin <code>$ man sort</code>). Ancak bu aracın yeteneği çok sınırlı, körlemesine sıralıyor, ve case let ve case ayrımını anlayamadığı icin tum <code>case let</code>’leri grupluyor. Ayrıca <code>switch</code> kod bloklarinda iyice saçmalıyor.
Ardından farkettim ki, iyi bir alfabetik case siralayici yazabilmem icin biraz daha alt seviyeye, compiler seviyesine inmem gerekecek. Swift derleyicisi bizim kaynak kodumuzu alip AST’ye ceviriyor, yani kodu anlamlandirmadan onceki ilk adim, <code>tree</code> veri yapısına çevriliyor kaynak kodumuz. Şansıma da Apple SwiftSyntax adındaki aracı ile tam da bunu yapiyor ve bu arac GitHub’da acik kaynak kodlu olarak paylasiliyor. Bu araci kullanarak yapilmis su sitede ise canlı sekilde olusturulan AST’yi inceleyebilirsiniz.</p>
<p>Hemen yeni bir Swift paketi oluşturdum ve dependency olarak SwiftSyntax’i ekledim. Ardindan <code>import SwiftSyntax</code> diyerek <code>SwiftRewriter</code> class’ini inherit eden bir class actim, gerekli tokenlari manipule edecek olan metodlari override ettim. Boylece artik tokenize edilmis kaynak kodlari uzerinde modifikasyon yapabilir hale geldim.
SwiftSyntax öğrenirken, <em>iyi bir parser nasıl yazılır</em> konusu hakkında da gayet iyi iç görüler edindim, tavsiye ederim.</p>
<p>Kodu ne yazık ki paylasamayacagim ancak bahsetmemde bir kusur yok. Genel olarak kullandığım mantık su, amacım <code>enum</code> ve <code>switch case</code>’lerini alfabetik sıralamak olduğu icin case isimlerine ulaşmam gerekiyor (identifier token’i). Bunun icin <code>EnumDeclSyntax</code>, <code>SwitchStmtSyntax</code> gibi 4 adet token’in visit metodlarini override ettim ve ilgili <code>tree</code>’leri traverse edip case’lerin identifier’larini sort edip sonucu tekrar <code>standart output</code>’a yazdirdim.</p>
<p>Boylece Xcode içerisinde sort ettirmek istediğim kod blogunu seçip sag tıklayıp <code>Services</code>’ten ilgili automator scriptini çağırmam yeterli oluyor. Basitçe bu script, seçtiğim metni standard input bekleyen komut satiri aracıma gönderiyor, program islemleri yapıp sonucu output buffer’ına yazıyor ve kodum IDE uzerinde update oluyor.</p>
<h3 id="heading-referanslar">Referanslar</h3>
<p><a target="_blank" href="https://github.com/kickstarter/ios-oss">Kickstarter open source app</a></p>
<p><a target="_blank" href="[Link](https://nshipster.com/swiftsyntax/)">SwiftSyntax — NSHipster</a></p>
<p><a target="_blank" href="Link">https://medium.com/@lucianoalmeida1/an-overview-of-swiftsyntax-cf1ae6d53494</a></p>
]]></content:encoded></item><item><title><![CDATA[Swift: Unit test ile strong reference cycle yakalama]]></title><description><![CDATA[Burada strong reference cycle'ın ne olduğunu anlatmayacağım ama kısaca bahsedecek olursam, bir reference type instance'ınız olsun A, bunun stored property'lerinden en az birinin kendisi veya nested bir yapı düşünürseniz ağacın yapraklarından biri ger...]]></description><link>https://erkek.in/swift-unit-test-ile-strong-reference-cycle-yakalama</link><guid isPermaLink="true">https://erkek.in/swift-unit-test-ile-strong-reference-cycle-yakalama</guid><category><![CDATA[strong reference cycle]]></category><category><![CDATA[Memory Leak]]></category><category><![CDATA[unit test]]></category><category><![CDATA[autoreleasepool]]></category><dc:creator><![CDATA[Erk Ekin]]></dc:creator><pubDate>Tue, 14 Jun 2022 22:56:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/ZN5pxaoGm3E/upload/v1655213190993/xB2or64ZG.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Burada strong reference cycle'ın ne olduğunu anlatmayacağım ama kısaca bahsedecek olursam, bir reference type instance'ınız olsun <code>A</code>, bunun stored property'lerinden en az birinin kendisi veya nested bir yapı düşünürseniz ağacın yapraklarından biri geri dönüp tekrar <code>A</code>'ya strong reference tutuyorsa, <code>ARC</code> gidip de bu objelerin retain count'larını sıfırlayamıyor dolayısıyla bu objeler hiçbir zaman memory'den temizlenemiyorlar.</p>
<p>Bu durum crash log'lara çok yansımasa da uygulamanızın runtime'ında belirsiz davranışlara neden olacaktır. Örneğin bir view controller'ınız var diyelim, bu tip bir memory issue'sü yüzünden silinemiyorsa, gereksiz yere tekrar tekrar yaratılacaktır. Ek olarak diyelim ki bu controller'dan siz bazı analytics <code>event</code>'leri fırlatıyorsunuz. Aynı <code>event</code>i dashboard'unuzda tekrarlar biçimde göreceksiniz ama aslında user başına bir <code>event</code> bekliyordunuz. Ya da diğer bir senaryoda backend'e call yapıyorsunuz, bu call'lar tekrarlanacak ve boş yere sunuculara yük olacaktır. Yapacak en iyi şey bu cycle'ları tek tek temizlemektir fakat en iyisi en baştan hiç raise etmemek.</p>
<p>Bu yüzden potansiyel olarak reference cycle olabileceğini düşündüğünüz kısımları unit test ile cover etmek mantıklı. Bunun için çeşitli öneriler getirildi ancak ben <code>autoreleasepool</code> kullanan bir yardımcı method önereceğim.</p>
<pre><code class="lang-swift"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">AssertNoStrongReferenceCycle</span>&lt;T: AnyObject&gt;<span class="hljs-params">( // <span class="hljs-number">1</span>
   file: StaticString = #file, // <span class="hljs-number">2</span>
   line: UInt = #line, // <span class="hljs-number">3</span>
   act: <span class="hljs-params">()</span></span></span> <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">T</span> <span class="hljs-comment">// 4</span>
 ) <span class="hljs-keyword">rethrows</span> { <span class="hljs-comment">// 9</span>
   <span class="hljs-keyword">weak</span> <span class="hljs-keyword">var</span> system: <span class="hljs-type">T?</span> <span class="hljs-comment">// 5</span>
   <span class="hljs-keyword">try</span> autoreleasepool { <span class="hljs-comment">// 6</span>
     system = <span class="hljs-keyword">try</span> act() <span class="hljs-comment">// 7</span>
   } 

   <span class="hljs-type">XCTAssertNil</span>(system, <span class="hljs-string">"You've got a reference cycle in \(T.self)."</span>, file: file, line: line) <span class="hljs-comment">// 8</span>
 }
</code></pre>
<p><a target="_blank" href="https://developer.apple.com/documentation/foundation/nsautoreleasepool"><code>autoreleasepool</code></a> aslında objc zamanlarından elimize kalan kullanışlı bir kavram, onu da burada çok açıklamayacağım dileyen orijinal dökümantasyonuna ve Bruno Rocha'nun <a target="_blank" href="https://swiftrocks.com/autoreleasepool-in-swift">bloguna</a> göz atabilir. Çok kısaca yine özetlemem gerekirse <code>autoreleasepool</code> bir closure alıyor (bkz yorum 6), bu closure içinde allocate edilen objeler method sonunda release ediliyor (bkz yorum 7).</p>
<p>Bu bilgiyi nasıl kullanıyoruz? Eğer release edilen bir objeyi <code>weak</code> bir property'ye atamışsak (bkz yorum 5) sonucunda ilgili property'nin <code>nil</code> olmasını bekleriz (bkz yorum 8), olmuyorsa bir yerlerde birşeyler birbirini bırakmıyor demektir, yani strong reference cycle var ve kaldırmamız lazım.</p>
<p>Yardımcı methodun ayrıntılarına gelecek olursam, method <code>T</code> generic variable bekliyor ve bu type <code>T</code> <code>AnyObject</code>'e conform etmek zorunda (bkz yorum 1). <code>AnyObject</code> reference type tipini zorunlu kılan bir kısıt çünkü bildiğiniz gibi value type'larda reference tutulmadığı için memory leak problemi de yok. </p>
<p>Method üç adet girdi alıyor, <code>file</code> (bkz yorum 2), <code>line</code> (bkz yorum 3) ve asıl önemli kısım olan <code>act</code> (bkz yorum 4). <code>file</code> ve <code>line</code>'ı daha önce testler için yardımcı methodlar yazmış olanlar bilir, Xcode'un hata mesajlarını doğru satırda vermesini sağlayan ayrıntılar, <code>act</code> ise kodunuzun asıl test etmek istediğiniz şüpheli kısım. <code>act</code> closure'u bazen içinde <code>try</code> içerebilir, örneğin <code>XCTUnwrap</code> kullanılabilir bu yüzden <code>throws</code> ekledim (bkz yorum 4), fakat içermeyebilir de, bu iki durumu aynı anda sağlayabilmek için ana methoda <code>rethrows</code> ekledim (bkz yorum 9).</p>
<p>Şimdi bu methodun örnek kullanımına geçeyim. İki adet class'ımız olsun <code>A</code> ve <code>B</code>, bunların birbirlerine referansları olsun. Bir tanesininki <em>optional</em> olsun ki sıra sıra init edebilelim (bkz: yorum 11).</p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> XCTest

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">A</span> </span>{
    <span class="hljs-keyword">weak</span> <span class="hljs-keyword">var</span> b: <span class="hljs-type">B?</span>  <span class="hljs-comment">// 10</span>
    <span class="hljs-keyword">init</span>(b: <span class="hljs-type">B?</span>) {
        <span class="hljs-keyword">self</span>.b = b
    }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">B</span> </span>{
    <span class="hljs-keyword">let</span> a: <span class="hljs-type">A</span>
    <span class="hljs-keyword">init</span>(a: <span class="hljs-type">A</span>) {
        <span class="hljs-keyword">self</span>.a = a
    }
}

<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ReferenceTypeTests</span>: <span class="hljs-title">XCTestCase</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">testExample</span><span class="hljs-params">()</span></span> {
        <span class="hljs-type">AssertNoStrongReferenceCycle</span> { () -&gt; <span class="hljs-type">A</span> <span class="hljs-keyword">in</span>
            <span class="hljs-keyword">let</span> a = <span class="hljs-type">A</span>(b: <span class="hljs-literal">nil</span>) <span class="hljs-comment">// 11</span>
            <span class="hljs-keyword">let</span> b = <span class="hljs-type">B</span>(a: a)
            a.b = b
            <span class="hljs-keyword">return</span> a
        }
    }
}
</code></pre>
<p>Burada en bariz şekilde <code>A</code> ile <code>B</code> arasında strong reference cycle raise etmeyen bir ilişki kurdum, <em>etmeyen</em> dedim çünkü <code>A</code>'nın <code>B</code> ile olan ilişkisi <code>weak</code> modifier'ı ile işaretlenmiş (bkz: yorum 10). Eğer bu <code>weak</code>'i kaldırırsanız hata mesajı alacaksınız. Bu yöntemle karmaşık view controller'ları, reactive property'lerinizi <code>sink</code> ettiğiniz reference type'ları, ya da closure inject ettiğiniz herhangi bir class'ın strong reference raise edip etmediğini test edebilirsiniz.</p>
<h3 id="heading-referanslar">Referanslar</h3>
<p>https://www.hackingwithswift.com/example-code/language/how-to-use-the-rethrows-keyword</p>
<p>https://gist.github.com/erkekin/0f44eefb62f62e9f79b8f544ca2dd46e</p>
<p>https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html</p>
<p>https://developer.apple.com/documentation/foundation/nsautoreleasepool</p>
<p>https://swiftrocks.com/autoreleasepool-in-swift</p>
]]></content:encoded></item><item><title><![CDATA[iOS geliştiricisinin metamorfozu: tuist]]></title><description><![CDATA[Çalıştığım şirketlerde en çok dalga geçtiğimiz program bu çekiç logolu program oldu: internet 'meme'leri yaptık paylaştık, Slack'e garip ekran görüntüleri gönderdik, thread'lerde kah güldük kah eğlendik. Visual Studio'ya ve Intellij'ci Android ya da ...]]></description><link>https://erkek.in/ios-gelistiricisinin-metamorfozu-tuist</link><guid isPermaLink="true">https://erkek.in/ios-gelistiricisinin-metamorfozu-tuist</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Swift]]></category><category><![CDATA[iOS]]></category><category><![CDATA[mobile]]></category><dc:creator><![CDATA[Erk Ekin]]></dc:creator><pubDate>Sun, 22 May 2022 09:20:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1653167265987/FacgWCVE3.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Çalıştığım şirketlerde en çok dalga geçtiğimiz program bu çekiç logolu program oldu: internet 'meme'leri yaptık paylaştık, Slack'e garip ekran görüntüleri gönderdik, thread'lerde kah güldük kah eğlendik. Visual Studio'ya ve Intellij'ci Android ya da backend yazan iş arkadaşlarımızın bıyık altından bakışlarına henüz hiç geri vole vuramadık. Xcode update'lerinden umudu çoktan kestik, yeter ki daha çok bozulmasın, elimizdekinden olmayalım. Sürekli crash olan, kod kaybı yaşatan, devasa büyük, xib'ten unarchive etmesi bile en az yarım saatinizi alan hantal bir uygulama bu. Ha [AppCode](
AppCode: Smart Swift/Objective-C IDE for iOS &amp; macOS ...https://www.jetbrains.com › o...
) daha mı iyi? Yok onu da denedik gördük, aynısının başka bir rengi. Ne yapalım, zevk almaya bakacağız. En az zararla kodumuzu yazabilirsek, o da iyi.</p>
<p>Xcode bu kadar kötü olunca, Xcode projeleri, yani o çift tıklayıp açtığımız <code>.xcodeproj</code> ya da <code>.xcworkspace</code> klasörleri ve içindeki dosyaların yapıları da bir garip olmuş tabii ki. Android'çiler XML ile UI yapıyor diye dalga geçiyoruz, o zaman kendimize dönüp koskoca projeyi böyle standart olmayan bir formatta ayağa kaldırmak nedir demek, çuvaldızı biraz kendimize de batırmamız gerekir.</p>
<p>Aşağıda biraz sonra tuist ile oluşturacağım projenin <code>pbxproj</code> dosyasından bir örnek. 🤮</p>
<pre><code><span class="hljs-comment">/* Begin PBXBuildFile section */</span>
        1F44150A659AA0555426C718 <span class="hljs-comment">/* ProjemUI.framework in Frameworks */</span> <span class="hljs-operator">=</span> {isa <span class="hljs-operator">=</span> PBXBuildFile; fileRef <span class="hljs-operator">=</span> 70CE45157BA22C3F39682339 <span class="hljs-comment">/* ProjemUI.framework */</span>; };
        4117A4AB964C06CD27106B77 <span class="hljs-comment">/* ProjemTests.swift in Sources */</span> <span class="hljs-operator">=</span> {isa <span class="hljs-operator">=</span> PBXBuildFile; fileRef <span class="hljs-operator">=</span> 043163F177A27A253712F23C <span class="hljs-comment">/* ProjemTests.swift */</span>; };
        5193730CDE078B6405F2A976 <span class="hljs-comment">/* Assets+Projem.swift in Sources */</span> <span class="hljs-operator">=</span> {isa <span class="hljs-operator">=</span> PBXBuildFile; fileRef <span class="hljs-operator">=</span> 
{ ... }
{ ... }
        B2B4032F0086DB46D82FAAC3 <span class="hljs-comment">/* ProjemKitTests.swift in Sources */</span> <span class="hljs-operator">=</span> {isa <span class="hljs-operator">=</span> PBXBuildFile; fileRef <span class="hljs-operator">=</span> BEC96509FC38E0CCF15225BB <span class="hljs-comment">/* ProjemKitTests.swift */</span>; };
        C8FA70ECEE39C955495655C4 <span class="hljs-comment">/* Assets.xcassets in Resources */</span> <span class="hljs-operator">=</span> {isa <span class="hljs-operator">=</span> PBXBuildFile; fileRef <span class="hljs-operator">=</span> 01BD1BAB864AF3BE9C68D81D <span class="hljs-comment">/* Assets.xcassets */</span>; };
        FBB6AC91D22F871F1246666C <span class="hljs-comment">/* ProjemApp.swift in Sources */</span> <span class="hljs-operator">=</span> {isa <span class="hljs-operator">=</span> PBXBuildFile; fileRef <span class="hljs-operator">=</span> D786B0E0E85998BF253D881C <span class="hljs-comment">/* ProjemApp.swift */</span>; };
        FE4B22D8761B836D3CCF1B43 <span class="hljs-comment">/* ProjemUI.framework in Embed Frameworks */</span> <span class="hljs-operator">=</span> {isa <span class="hljs-operator">=</span> PBXBuildFile; fileRef <span class="hljs-operator">=</span> 70CE45157BA22C3F39682339 <span class="hljs-comment">/* ProjemUI.framework */</span>; settings <span class="hljs-operator">=</span> {ATTRIBUTES <span class="hljs-operator">=</span> (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
<span class="hljs-comment">/* End PBXBuildFile section */</span>
</code></pre><p>Bu tip tanınmayan bir formatla çalışmanın herhalde en kötü tarafı, merge conflict'ler ya da Code Review sürecinde gördüğümüz garip rastgele kod değişiklikleridir. İş arkadaşınızla proje dosyasının birbirine yakın yerlerini mi değiştiriyorsunuz, conflict çıkması çok olası ve bu conflict'i iyi çözemezsen proje açılmaz öyle kalırsın, al bakalım şimdi o merge işlemini geri, başka bir rastgele çözüm dene, bunları yaşadık gördük. Zaten bu yüzden vazgeçmedik mi yine XML formunda saklanan Storyboard'lardan, XIB'lerden?</p>
<p>2015'te, Apple, <a target="_blank" href="https://www.swift.org/package-manager/">Swift Package Manager</a> (SPM) projesini open source yapınca tüm iOS'çular kodu incelemeye başladık, en azından swift dilinin kendisi gibi <code>c++</code> ile değil <code>swift</code> ile yazıldığı için biraz anlıyorduk, anlamanın ötesine gidenler projeye katkı yaptılar, projedeki bazı inovasyonları kendi projelerinde kullandılar, o dönem bir SPM dependency kambriyen patlaması yaşandı, binlerce SPM projesi ortaya çıktı ve GitHub'da paylaşıldı. Bugün bu repolar bir <a target="_blank" href="https://swiftpackageindex.com/">index</a>'te toplanıyor.</p>
<p>Birkaç meraklı ekip SPM projesinin içerisinde, Xcode projesi oluşturan kısımla ilgilendi. Bildiğiniz gibi SPM package'ları bir Xcode projesine ihtiyaç duymuyor. Xcode'dan <code>Open Project</code> diyip SPM projenizin klasörünü gösterince proje olmasa da Xcode'da açabiliyorsunuz. Ancak siz yine de proje ile çalışacağım derseniz o zaman <code>--generate-project</code> diyip o berbat <code>pbxproj</code> dosyasını yaratıp Xcode'da eskisi gibi açabiliyorsunuz. </p>
<p>İşte bahsettiğim meraklı ekip open source SPM projesindeki bu generator kısmını aldı ve hızlıca yeni bir proje yarattılar. <a target="_blank" href="https://github.com/yonaskolb/XcodeGen">XcodeGen</a>, <a target="_blank" href="https://github.com/igor-makarov/xcake">xcake</a> ve konumuz <a target="_blank" href="https://github.com/tuist/tuist">tuist</a> böylece hayata gelmiş oldu.</p>
<p>Diğer alternatifleri geçiyorum, <code>tuist</code> kısaca, Cocoa projenizi Xcode projesi olmadan yönetmenizi sağlıyor. Bunu Android'çilerin yıllardır aşina olduğu tarzda bir manifest dosyası üzerinden yapıyor. Siz yalnızca root klasörünüzde bir <code>Project.swift</code> dosyası yaratıyorsunuz, bunun içerisinde Xcode projesinde yaptığınız ayarları giriyorsunuz ardından <code>$ tuist generate</code> komutu ile Xcode projeniz oluşturuluyor, zaten bundan sonrası aynı eskisi gibi, kodunuzu build ediyorsunuz ve aynen devam.</p>
<p>Bu durumda kar ediyorsunuz çünkü artık o bahsettiğimiz <code>.xcodeproj</code> ya da <code>.xcworkspace</code> klasörlerinizi <code>.gitignore</code>'a ekliyorsunuz, bu veda demek: artık code review sürecinde bu saçma dosyanın içeriğini görmeyeceksiniz demek, iş arkadaşınız hızlıca geçmek zorunda kalmayacak, yani artık baş ağrısı yok. Her proje ayarı, zaten aşina olduğumuz, ustası olduğumuz <code>swift</code> diliyle yazılıyor. Zaten bu yüzden <code>Cocoapods</code>'tan da SPM'e geçmiştik, <code>ruby</code> de neydi? İnsanın evi gibi yoktu.</p>
<p>Bu noktadan sonrası artık <em>yaparak öğrenme</em>. tuist'i kullanmanın birkaç yolu var, biz binary'sini repomuzda saklıyoruz, CI'da onu çalıştırıyoruz. Kodu clone'ladığımızda localimizde de bu binary'yi kullanıyoruz böylece büyük bir ekipte herkesin aynı versiyonda olduğundan ve aynı binary'yi kullandığından emin olabiliyoruz. Bu tip kısıtlar büyük bir ekipte çalışmanın en önemli şartı olabilir.</p>
<p>tuist'in nasıl kurulacağını burada yazmayacağım, çünkü zaten <a target="_blank" href="https://github.com/tuist/tuist/blob/main/README.md">README</a>'sinde yazıyor. Sadece ilk projenizi ayağa kaldırmanızı sağlayacağım, ardından aradan çekileceğim.</p>
<p>tuist'i lokal mac'inize kurduktan sonra, terminalinizden <code>Desktop</code>'a gidin ve aşağıdaki komutları çalıştırın.</p>
<pre><code class="lang-bash">mkdir Projem
<span class="hljs-built_in">cd</span> Projem
tuist init --platform ios --template swiftui
tuist generate --no-open
</code></pre>
<p>Bu komutlar taze <code>Projem</code> klasörünüzde bir Xcode projesi oluşturacaktır. Ben şu anda <code>3.4.0</code> versiyonundayım, bu versiyonda, <code>Projem</code> klasöründe aşağıdaki <code>Project.swift</code> dosyası oluştu (commentleri attım).</p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> ProjectDescription
<span class="hljs-keyword">import</span> ProjectDescriptionHelpers
<span class="hljs-keyword">import</span> MyPlugin

<span class="hljs-keyword">let</span> localHelper = <span class="hljs-type">LocalHelper</span>(name: <span class="hljs-string">"MyPlugin"</span>)
<span class="hljs-keyword">let</span> project = <span class="hljs-type">Project</span>.app(name: <span class="hljs-string">"Projem"</span>,
                          platform: .iOS,
                          additionalTargets: [<span class="hljs-string">"ProjemKit"</span>, <span class="hljs-string">"ProjemUI"</span>])
</code></pre>
<p>Farkedildiği üzere burada Projem adında bir iOS projesi yaratılıp altına ProjemKit ve ProjemUI adında iki adet target eklenmiş durumda. Yeni bir target eklemek için <code>["ProjemKit", "ProjemUI"]</code> array'ine birşey ekleyin ve <code>tuist generate</code> komutunu tekrar çalıştırın.</p>
<p>Burada biraz trick yapılmış, yani ilk kullanıcı etkilensin diye birçok şey abstract edilmiş. Asıl target'ların oluşturulduğu yere gidelim.</p>
<pre><code class="lang-bash">open Tuist/ProjectDescriptionHelpers/Project+Templates.swift
</code></pre>
<p>Buradaki commentleri ve kodu inceleyin, fikir verecektir.</p>
<p>Ekstralar: Herhangi bir tuist alt komutunun yanına <code>-h</code> yazarak alt komutla ilgili yardım alabilirsiniz.</p>
<pre><code class="lang-bash">tuist -h
tuist generate -h
tuist cache -h
</code></pre>
<h2 id="heading-halihazirdaki-bir-projeyi-tuiste-cevirmek">Halihazırdaki bir projeyi tuist’e çevirmek</h2>
<p>Birçoğunuzun senaryosu bu olacaktır. Bizim 30-40 feature’lü, 15 dk’da build olan projemizi tamamen tuist’e geçirmek birkaç ayımızı aldı. İşin güzel yanı proje modüler olunca modül modül ilerliyorsunuz bu da migration’ı kolaylaştırıyor.</p>
<p>Migration için en iyi başlangıç, Xcode’da el ile yaptığınız ayarları <code>xcconfig</code> dosyalarına çevirmek. <a target="_blank" href="https://docs.tuist.io/guides/adopting-tuist">Dökümantasyonda</a> bu işlem de anlatılmış, <code>tuist migration</code> tool’u gayet iş görüyor, yetmediği yerlerde iş başa düşüyor ama olacak o kadar.</p>
<h2 id="heading-tuist-icin-open-source-contributor-olmak">tuist için open source contributor olmak</h2>
<p>tuist open source projesinin <a target="_blank" href="https://github.com/tuist/tuist#contributors-">readme</a>'sini incelerseniz orada ismimi görürsünüz. Buraya girmek için gerçekten hiç çaba sarfetmedim. Çalıştığım şirketin projesine tuist'in <strong>cache</strong> ve <strong>test</strong> özelliklerini eklemek için uğraştığımda, bu komutların bizim projemizde çalışmadığını farkettim. Ardından araştırmaya başladım, ilk yaptığım iş tuist'i lokalime klonlamak ve aynı komutları çalıştırırken tuist'i debug etmek oldu. Bu aşamada birkaç bug buldum. Hemen GitHub issue'sü açmak yerine problemi çözmeye çalıştım. Birkaç tanesini çözünce bunları Pull Request'ler açıp ekiple paylaştım. <a target="_blank" href="https://join.slack.com/t/tuistapp/shared_invite/zt-g38gajhj-D6LLakrPnVCy4sLm24KxaQ">Slack grupları</a>na girdim tartıştım, derken birkaç PR'ım merge oldu ve beni eklediler.</p>
<p>Projeye katkı yaparken zorlandığım konu Xcode ve tuist versiyonları arasındaki uyumsuzluk oldu, ayrıca tuist'i debug modda ayağa kaldırmak için yine aynı ekip tarafından geliştirilip tuist repo içine koyulan <code>fourier</code> adlı ruby scriptini de çalıştırmak gerekti. Ruby'nin kendisini ve gem'lerini kurmak bir işkence zaten, özellikle proxy arkasındaysanız. Bunu da komuta <code>--http-proxy</code> <a target="_blank" href="https://stackoverflow.com/questions/4418/how-do-i-update-ruby-gems-from-behind-a-proxy-isa-ntlm">argümanıyla</a> ilgili proxy'nin linkini vererek çözdüm.</p>
<p>Bu yazıda yalnızca Xcode'un proje dosyasından dem vurarak sizi motive etmeye çalıştım ancak projeyi tuist'e taşımanın çok fazla artı tarafı var. Bunlardan benim için en önemlisi ise <code>tuist cache</code>. Eğer projenizi modülerleştirdiyseniz her modülü izole bir biçimde generate edebiliyorsunuz, bununla da kalmıyor bu modülün dependency'lerini cache'leyip build zamanlarını dramatik ölçüde kısaltabiliyorsunuz. Örneğin 10 dk süren bir incremental build oluyor sana 15 sn. Tabii deneyimli okuyucu Computer Science'ın en büyük iki probleminden ikincisinin cache yönetimi olduğunu biliyor olacaktır.</p>
<p>Diğeri <code>tuist test</code>. Bu da projedeki tüm testleri bir kez çalıştırdıktan sonra, eğer herhangi bir modülün içindeki dosyaya ve dependency'lerine dokunmadıysanız o modülün testlerini atlıyor böylece yine zaman tasarrufu sağlıyorsunuz.</p>
<p>Eğer mikro architecture (μFeature) yapısından yararlanıyorsanız, tuist yeni feature eklemeniz için <code>scaffold</code> özelliğiyle bir çok boilerplate kodu kendi yaratıyor size de sadece <code>Project.swift</code> dosyalarını modifiye etmek kalıyor. Bunu biz ekibimizde çok kullanıyoruz.</p>
<p>Bir başka baba özellik ise <code>tuist graph</code>. Projenin yapısını grafik halinde çıktı veriyor. Aşağıda benim <a target="_blank" href="https://apps.apple.com/gb/app/snake-track/id1622056207">oyunumun</a> çıktısından bir örnek.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653209244400/X8YxGf4jx.png" alt="graph.png" /></p>
<p>Bu kadar bilgiden sonra okuyucuyu tuist'in <a target="_blank" href="https://docs.tuist.io/tutorial/get-started">dökümantasyonuna</a> yönlendireceğim. Pek sağlam bir docs sayılmaz ancak baya fikir veriyor. Docs içinde özellikle <a target="_blank" href="https://docs.tuist.io/building-at-scale/microfeatures">μFeature kısmını</a> her iOS developer'a tavsiye ederim.</p>
<p>Sevgili dostlar, bu tip trendleri takip etmenin hep yararını gördüm. Belki bazınıza bunlar <em>overengineering</em> gibi gelecektir. Evet özellikle küçük bir projede gerçekten öyle ama işlerin büyüyeceği gün buna geçmeniz projenin sustainability'si açısından kesin yararlı olacaktır. Ben yeni projelerime size'larından bağımsız tuist ile başlıyorum, hem familiarity açısından hem de ekip en son hangi özelliği eklemiş öğrenmek açısından hep yararını gördüm. </p>
<p>Eğer kendi ekibinize tuist dersi vermemi isterseniz bana <a target="_blank" href="https://uk.linkedin.com/in/erkekin">LinkedIn</a>'den ulaşınız, yardım ederim.</p>
<h2 id="heading-referanslar">Referanslar</h2>
<ul>
<li>https://developers.soundcloud.com/blog/tuist-project-generation</li>
<li>https://sarunw.com/posts/getting-started-with-tuist/</li>
<li>https://www.raywenderlich.com/24508362-tuist-tutorial-for-xcode</li>
<li>https://antran.app/2022/tuist_getting_started/</li>
<li>https://docs.tuist.io/tutorial/get-started</li>
</ul>
]]></content:encoded></item></channel></rss>