<?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" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Jakub Milcarz on Medium]]></title>
        <description><![CDATA[Stories by Jakub Milcarz on Medium]]></description>
        <link>https://medium.com/@kubamilcarz?source=rss-b30973e2bd56------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*mwdZenCCtEp5GOqZWB6NgQ.jpeg</url>
            <title>Stories by Jakub Milcarz on Medium</title>
            <link>https://medium.com/@kubamilcarz?source=rss-b30973e2bd56------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 24 Apr 2026 05:58:31 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@kubamilcarz/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[I Refactored 3 Apps in a Year. Here’s What I Actually Learned.]]></title>
            <link>https://kubamilcarz.medium.com/i-refactored-3-apps-in-a-year-heres-what-i-actually-learned-bc519ba33bb1?source=rss-b30973e2bd56------2</link>
            <guid isPermaLink="false">https://medium.com/p/bc519ba33bb1</guid>
            <category><![CDATA[clean-architecture]]></category>
            <category><![CDATA[ios-development]]></category>
            <category><![CDATA[swiftui]]></category>
            <dc:creator><![CDATA[Jakub Milcarz]]></dc:creator>
            <pubDate>Thu, 19 Mar 2026 17:06:26 GMT</pubDate>
            <atom:updated>2026-03-19T17:06:26.931Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*g7N8MhOYWAkNDgrepMbNJg.jpeg" /></figure><p>13 months ago I opened a codebase I hadn’t touched in about six months and just… stalled. Not because it was huge. Not even because it was badly written. It just didn’t feel like mine anymore, even though as an indie developer I was the only one working on it. I had to spend the first hour untangling how things connected before I could even think about moving forward. For a solo developer, that’s a brutal way to start a session.</p><p>And the thing is, it happened every single time. This event prompted me to reminisce about the past, and look at that: every single time I would come back to a codebase I hadn’t touched for some time I had to spend the first few hours reading it to get back to speed. That’s what got me thinking seriously about refactoring.</p><p>Over the past year I went through three pretty different refactors across my three apps — Coffee Note, Bookie, and Memorize. And I kept expecting to land on the same target: clean principle I could apply every time. PS: I talk about my approach to architecting SwiftUI apps going forward in <a href="https://medium.com/@kubamilcarz/syntax-is-cheap-swiftui-architecture-ad5f752af1bf">this article</a>.</p><h3>A bit of context</h3><p>I’m an indie developer and a UX designer. Coffee Note is for home baristas, Bookie is a reading tracker, Memorize is a flashcard app, and while working solo across multiple projects, one thing becomes obvious fast: <em>consistency matters more than cleverness</em>.</p><p>You can’t afford to be clever in ways only you understand. If you come back to something after two months and have to reverse-engineer your own thinking, you’ve already lost time you didn’t have. And that’s especially true in the AI era.</p><p>That realization is what kept pushing me back into these codebases, even when it wasn’t fun.</p><h3>Coffee Note: all in, all at once</h3><p>Coffee Note was the most chaotic of the three. It was the first one to go, and so my approach wasn’t that structured yet. I refactored the architecture, redesigned the UI, and added new features more or less simultaneously. I also centralized Core Data management and leaned into a more VIPER-inspired separation of concerns throughout.</p><p>It was a lot. And honestly, not the smoothest experience.</p><p>But the app needed it. I wasn’t trying to tidy things up, I was trying to change the direction of the whole product. Doing it incrementally would’ve felt like fixing one room in a house that needed a new floor plan. So I just started somewhere in the middle and worked outward.</p><p>I wouldn’t recommend this as a default approach. But for where Coffee Note was at, it made sense. Over the past 3 years, the idea of Coffee Note transformed in my mind several times. As the target user changed, I could not constrain the changes to the UI anymore. The update hasn’t been released yet, but I’m planning to finish my work by the end of this year.</p><h3>Bookie: slow down, start with logic</h3><p>Bookie was almost the opposite. For the first four months I barely touched the view. I focused almost entirely on centralizing business logic — cleaning up how things actually worked before worrying about how they looked. Stronger APIs, less behavior leaking into places it didn’t belong, laying the groundwork for Swift 6.</p><p>Before, a lot of the domain logic lived directly in view models — things like filtering, sorting, and updating reading progress were computed on the spot, inline, repeated across screens. The first step was pulling all of that into proper repositories and service layers, giving each piece of behavior one clear home. Then the view models became thin — they stopped doing the thinking and started just passing things along. Only after that felt stable did I start pulling the UI layer along with it, slowly. One domain model after another. One component after another.</p><p>The thing I keep thinking about from Bookie: the best refactor is the one your users barely notice. From the outside, nothing changed. Internally, a lot did. And because of that, everything that came after: new UI work, new personal theming feature, felt less risky.</p><p>Bookie taught me that restraint is actually a strategy, and also showed hidden benefits of this approach: clear performance gains from day one.</p><h3>Memorize: UI first, mess later</h3><p>Memorize has been the most backwards of the three, and I did it on purpose.</p><p>I started at the UI layer — pulling business logic out of views to make the interface cleaner and easier to work with. Which worked. But it also meant I pushed that complexity up a level rather than actually resolving it. The repositories and surrounding layers are still not fully clean. That’s just where things are right now.</p><p>The reason I made that call: priorities. It’s March 2026 and I still haven’t released Liquid Glass update, Memorize strongly needs. That made the interface the most strategically important layer to get right first. So I started there, knowing I’d have to come back and clean up underneath later.</p><p>It’s not ideal. But it’s the right order for what this app needs. However, all 3 approaches are in some sense not ideal. Each has its benefits and downsides, more on which later.</p><h3>So why was every approach different?</h3><p>Looking back at it: Coffee Note was somewhere in the middle — architecture, UI, data, all at once. Bookie was bottom-up: logic first, UI followed. Memorize is top-down: UI first, logic cleanup still coming. Same long-term goal across all three. Completely different entry points.</p><p>And the reason was never really about architecture. It was always about what the product needed next. Coffee Note needed a new foundation. Bookie needed its internals stabilized. Memorize needed its interface to be ready for a design shift.</p><p>That’s the thing I’d push back on in most refactoring conversations: they tend to focus on what’s theoretically cleanest. But real apps exist inside constraints. Roadmaps, release timelines, technical debt, user expectations. The right approach is usually just the one that supports where the product is actually going.</p><h3>The thing nobody talks about enough</h3><p>I want to say something honest here. I liked doing these refactors. There’s something genuinely satisfying about making a tangled codebase make sense again. It made me think more clearly about both code and product decisions.</p><p>But every single approach eventually hits the same wall: the repetitiveness. Move this, rename that, rebuild the boundary, reconnect the flow, re-test, repeat. Doesn’t matter where you start. At some point you’re just grinding through the same motions over and over. And that’s not a complexity problem, it’s a patience problem. Nobody really talks about that part. But in my experience, it’s the hardest part.</p><h3>Conclusions</h3><p>Next time I’d have to refactor something, I wouldn’t ask “what’s the right approach?” that question doesn’t go anywhere useful. Instead, I’ll ask “what does this project need next?”</p><p>If you’re missing a design system and planning to expand to new platforms, maybe the UI layer is where to start. If your logic is scattered and your behavior is unpredictable, go there first. If the app is young enough to absorb big changes, maybe you go all in. If it’s mature and users are active, gradual is probably a better fit — feature by feature.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bc519ba33bb1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Syntax is Cheap, Systems are Expensive. On Architecting SwiftUI.]]></title>
            <link>https://kubamilcarz.medium.com/syntax-is-cheap-swiftui-architecture-ad5f752af1bf?source=rss-b30973e2bd56------2</link>
            <guid isPermaLink="false">https://medium.com/p/ad5f752af1bf</guid>
            <dc:creator><![CDATA[Jakub Milcarz]]></dc:creator>
            <pubDate>Mon, 26 Jan 2026 16:22:41 GMT</pubDate>
            <atom:updated>2026-01-26T16:22:41.334Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pbRRD3lF2V8-eyPliOS0iA.png" /></figure><p>A few years ago, being “a good developer” meant you could ship code. You knew your frameworks, you had your patterns, you could lose your hair on fighting with Xcode, and you could grind out features on demand.</p><p>Now AI can ship code too.</p><p>That’s genuinely exciting, don’t get me wrong, but up to the point you realize AI doesn’t just generate <em>code</em>. It generates <strong>too much code</strong>. Surely something can go off track… and it will, unless your system has rails to hold on to.</p><p>As Krzysztof Zabłocki recently noted, the role of the developer is evolving [<a href="https://merowing.info/posts/the-age-of-micro-entrepreneurs/">The Age of Micro-Entrepreneur</a>]. We are moving from <strong>syntax-first programming</strong> to <strong>systems-first programming.</strong> Not because syntax no longer matters, but because the bottleneck moved. In this new reality, our job isn’t to type code, but rather to design the systems that guide code — including AI-generated code — toward the right outcome.</p><p>If you want your apps to scale, survive years on the App Store, and actually benefit from AI instead of being destroyed by it, we need to talk about structure. And I learned that the hard way while refactoring my reading-tracker app, <em>Bookie</em>.</p><h3>When MVVM Becomes the New MVC</h3><p>We all remember the “Massive View Controller” trauma of the UIKit era. The View Controller in itself wasn’t that bad, it just quickly became one-man orchestra allowed to do everything — networking, caching, routing, validation, analytics, plus the obvious — state management and UI rendering.</p><p>MVVM offered a great alternative — finally views are views and business logic is outsourced to ViewModel. As a result, views got thinner, logic got pretty testable, and we were happy we achieved “clear separation of concerns”. But then again, did anyone tell us what should be placed in the view, and what in the ViewModel? Same case as with MVC, ViewModel slowly but surely became the new “just put it here” zone.</p><p>Interestingly enough even when you put analytics, networking, caching, and routing to a given ViewModel you still don’t get the same feeling as with the Massive View Controller, since the view is still separated, right? Thanks to SwiftUI everything <em>looks</em> clean. From the outside. Your views are pretty, your ViewModel is pure Swift, everything compiles, you ship, and move on onto the next feature.</p><p>And then one day you open a ViewModel and it’s like:</p><ol><li>it fetches the book,</li><li>decides whether cache is “fresh enough”,</li><li>merges remote changes with local progress,</li><li>updates stats and streaks,</li><li>schedules notifications,</li><li>retries on network,</li><li>logs analytics,</li><li>and navigates when something succeeds.</li></ol><p>And that’s how MVVM becomes the new MVC — not because the pattern is wrong, but because the boundaries are missing. Let me ask you a question, what happens if you need to perform a multi-step operation such as login in 2 places in the app: one from login view and one from register view? Do you duplicate same blocks of code? What happens when you plug AI into the mix and it goes off the rails and generates 3 more login implementations across 5 screens. Everything looks fine locally, but globally your app starts living in multiple realities. It’s easy to lose sight of things and even an essential step such as validation becomes a chore — you are adding same piece of code so it’s better just to skip it.</p><p>That’s why I believe MVVM never answered the hard questions:</p><ol><li>Where does the data actually come from? And who decides cache vs online vs offline?</li><li>Who owns the side effects?</li><li>How do we share business logic between five different screens without copy-pasting and slowly diverging?</li></ol><p>Architecture isn’t about boilerplate. It’s about constraints. Just like any other system — structure offers a framework to work inside. Architecture starts when you decide what a ViewModel — which should be responsible primarily for state management — is <em>not</em> allowed to know. Because the moment a ViewModel is allowed to own “just a bit” of data strategy or workflow coordination, it expands like gas, poisoning all parts of your codebase. Especially when shipping speed goes up, and in 2026, thanks to AI, it definitely will go up.</p><h3>The Three-Layer Foundation</h3><p>To create something that scales (and stays sane), can be maintained, and built upon in the future, we have to stop thinking in terms of files and start thinking in terms of layers.</p><p><strong>1) Data Layer</strong></p><p>This is the part of your app that sees the real world and interacts with data sources outside your control. It’s full of Codable objects, API clients, Core Data/Swift Data schemes, or cache.</p><p>The one and only job of the Data layer is to <strong>translate the outside world</strong> into something our app can use. <em>Services</em> are where you should decode JSON into Swift Codable models. Services deal with endpoints, DTOs, network error codes, and mismatched JSON keys.</p><p>Beneath them we have <em>Repositories</em>, which actually own the data. And here let’s answer the question: what does it mean? If you have a list of books you need to display on 5 different screens, you would put that into a repository. Repositories decide whether to refresh local cache from remote, they offer an offline fallback by translating error codes from the service layer above.</p><p>The point here I want to make is: ViewModels do not get to invent data policy. If you are in the situation where all ViewModels try to decide on their own to re-sync with remote, delete local cache as it’s probably stale, and another one falls back on offline copy; you can easily lose sight of the true state of your app.</p><p><strong>2) The Domain Layer (Glue)</strong></p><p>The Domain is what’s left when you remove infrastructure. Since all data-related operations happen in repositories and services, the Domain deals with things you would use in the UI. The idea is to have the UI layer clearly separated from the Data. You can check that by answering 2 questions:</p><ol><li>What happens if you delete your UI? Does your business logic survive?</li><li>What happens if you delete your APIs or local database? Is UI still intact and do interfaces still make sense?</li></ol><p>If the answer to any of both questions is no, your logic is coupled. And coupling is exactly what makes refactors and long-term maintenance painful. What’s more if you have either business logic or UI strictly coupled, introducing changes to any one of them poses a threat to the other.</p><p>Domain models are not DTOs, they’re not database entities. They focus on the UI and what’s actually useful to the end user. Now a ReadingSession isn’t whatever the API returned and my properties on top, but its a concept focused mainly around the design and user stories. It’s a concept your app cares about, with rules that should stay stable even if your backend changes next month.</p><p>This layer is also where shared logic belongs — the stuff you don’t want duplicated across Book Details, Currently Reading, Stats, and Reading Challenge screens. If you can’t point to one place where the rules live, they should probably be moved here. (Spoiler alert: you would put Use Cases in this layer too!)</p><p><strong>3) The Presentation (UI) Layer</strong></p><p>This is where SwiftUI lives. And once Data and Domain are doing the heavy lifting, the Presentation layer can finally breathe. The ViewModel becomes what it was born to be — a state manager.</p><p>Finally, ViewModel doesn’t fetch data directly. It doesn’t care about caching strategy. It doesn’t merge remote and local differences. It doesn’t coordinate multi-step workflows. It asks the system for what it needs, and it translates results into UI state: loading, loaded, error, and empty. (More on which I wrote <a href="https://medium.com/@kubamilcarz/building-predictable-swiftui-screens-with-viewconfig-viewmodel-and-viewstate-654c7679a73c">here</a>.)</p><p>This is the point where MVVM becomes powerful again, because the ViewModel is no longer the anything goes here place. It’s the interface between your product’s meaning and what the user sees. So it’s not longer a battle between clean and polished, logic-free SwiftUI views and messy ViewModels.</p><p>And if the wiring feels boring? Good. Boring wiring means clear ownership, and clear ownership is what lets your app scale, and lets AI help without quietly destroying consistency. Just like in investing, the boring is good. Yes, hooking up dozens of screens to the architecture may seem daunting, but so rewarding as soon as you’re finished. Adding new features gets easier too— you no longer worry “how do I fit this part in here?” or “maybe I should break down this ViewModel into two smaller ones?”</p><h3>Coordinating ViewModel Problem</h3><p>At some point though, you will open up Xcode and see your ViewModel. Turns out, that the once simple NotesListViewModel fetches data, handles sorting, debounce search, and filters. Often at once.</p><p>It doesn’t start that way. You think “I will just put it in here”. Totally reasonable. The UI needs data, so the ViewModel pulls it together. Then the UI needs more than data — it needs a workflow. And workflows are where ViewModels quietly become monstrosities we’ve never wanted in the first place.</p><p>To illustrate that a little cleaner. NotesListViewModel ends up:</p><ol><li>calling 3 repositories, because well each note has relationships and we want to show these on screen,</li><li>coordinating a multi step sequence (search → sort → refresh → add a new note and see it nicely show up on screen → filter → swipe to delete),</li><li>handling errors across steps (and deciding which ones are fatal vs ignorable),</li><li>retrying, debouncing, cancelling, and timing out concurrent tasks,</li><li>firing analytics events (”Notes_Refresh_Started” or “Notes_Search_Debounce_Success”),</li><li>and then navigating to Note Details screen.</li></ol><p>This is the moment when our architecture and MVVM makes the full circle. Not because our system is bad, but because this coordination is deliberate business logic pretending to be UI (”don’t look at me, I’m just controlling the UI”).</p><p>And the symptoms of this happening are always the same: you can’t reuse logic without copy-pasting major parts of the whole ViewModel, a few screens do essentially “the same thing” but behave slightly differently, bug fixes take forever because no one is sure which part is the correct rule, and which part is “oh this is a special case”. Testing too becomes a struggle, where a state manager now touches everything.</p><p>In Bookie this shows up fast. “Start reading” button in Book Details screen sounds like a button tap, but it’s actually a hidden workflow: create a reading and initial book update entry, update the book’s status, bump a streak, record stats, schedule a reminder, update the widget, and sync with Currently Reading section. If that logic sits inside a ViewModel, it will eventually get duplicated, drift apart, and god I’m nervous just thinking about it.</p><p>But there’s a solution…</p><h3>Use Cases (Business Actions)</h3><p>Use Cases are the simplest things that work: verbs. They describe what the business is doing, not what the UI is doing. A ViewModel’s method could be called onLoginButtonTapped, while Use Case would be called LoginAndSyncUserData, StartReadingSession, SyncLibrary.</p><p>“Start reading” or “Login” button taps are not UI events, but rather <strong>business actions</strong>. And business operations deserve a home that isn’t tied to any screen. Its no longer UI related.</p><p>A Use Case is basically a named box that says: “This is what must happen, in what order, with what rules”. It can call repositories, coordinate steps, decide retry policies, and return a clean result a ViewModel can continue working with — in terms of state! The UI doesn’t need to know the messy details — it just needs to know what state to show.</p><p>When, on the one side, we have ViewModels and on the other Repositories, I like to think about Use Cases as smart aggregates of actionable operations. For instance, “Start reading” step requires Book Repository, UpdateEntry Repository, and Streak Repository — all of which our Book Details screen doesn’t use, won’t use, and should never care about. Use Case wraps around this logic and enables us to replace the complex ViewModel operations with just a simple call.</p><p>When you move workflows into Use Cases, a few really practical things happen:</p><ol><li>Your ViewModels get smaller and focused — they finally stop being the place where decisions are made. They become state managers (and I know I’m repeating myself, but this time this is their final form).</li><li>Your logic becomes reusable without copy-paste — “Start reading” doesn’t belong to Book Details only. It belongs to the app and it can be used from anywhere: a widget, different screen, or a future watch app.</li><li>Your app gains vocabulary — once you have verbs, your team (or future you) stops speaking in screens (”Notes List screen does this”) and starts speaking in product terms (”let’s login”, “let’s start reading”).</li><li>AI becomes dramatically safer and more useful — “Implement StartReadingSession” is a contained problem with a clear intent. In comparison, “implement reading logic inside this ViewModel that also does navigation, caching, analytics, and error handling” is how you get chaos with nice formatting.</li></ol><p>Use Cases are basically buttons on the console ViewModels tap. You click, some action happens, and soon after there’s its effect. Use Cases give you a spine that survives UI refactors, even those touching ViewModels.</p><h3>Common Questions</h3><p><strong>1) “Isn’t This Overkill?”</strong></p><p>Fair question, especially if you’re an indie developer and shipping fast. This sort of structure can feel too much: more files, more types, more “architecture”, less features shipped. But that’s short sighted, because yet you will ship less however you don’t take into account what “shipping” really means. We often don’t think about it, but when you ship a feature it means you’ll need to upkeep it for the foreseeable future and with 90% certainty release bug fixes.</p><p>Architecture may seem like a chore at first, but sooner than later you start to notice its benefits, and time to market really goes down, as well as number of bugs. Building systems is not about adding layers, its about reducing decision repetition. If you don’t set the rules once, you’ll end up adding them in five places, slightly differently. And that cost compounds behind the scenes.</p><p><strong>2) “What if my ViewModel needs 10 repositories?”</strong></p><p>Let’s take Settings View, you will work with ten different properties, each from a different part of the app. And I’ve got bad news for you — the architecture probably isn’t your problem. Screen design is.</p><p>A ViewModel pulling from 10 sources is usually a signal that the screen is trying to be too many things at once. And it doesn’t mean your code is inherently bad, it’s just that something may be off with your product — too many responsibilities and too many states.</p><p>Sometimes the fix is to split the screen. Sometimes it’s to introduce a Use Case that returns a single model the UI can render and work with. Sometimes it’s to rethink what the screen is for. But if your first instinct with a large ViewModel is “I’ll just inject more repos”, you’re not solving the problem, but trying to remedy.</p><p><strong>3) “Why wrap third party packages?”</strong></p><p>Firstly let’s set the scene. You’re building an app, maybe adding a backend provider, maybe hooking up analytics service — these things are not part of your app — they are connections to the real world. Let’s imagine you use Firebase, maybe in the future you plan to switch to CloudKit, maybe you use TelemetryDeck for analytics and plan to switch to Firebase Analytics.</p><p>Libraries get deprecated. APIs change. Pricing changes. Product requirements change. A package you love today becomes a tech debt tomorrow. And your app, if it’s successful, will outlive most of its dependencies.</p><p>We can fight this harsh reality by wrapping a third-party library in your own interface. If your app talks directly to a third-party package across 50 call sites, you don’t have a dependency, but a coupling. A pretty binding relationship which will be a pain to break.</p><h3>The One Ugly Place</h3><p>Every app eventually hits the same awkward phase: not a “business logic” problem, a wiring problem.</p><p>At the start, wiring feels harmless. Your BookDetailsViewModel needs a BooksRepository, so you pass it in. Clean. You feel responsible. You can still trace every dependency with your eyeballs. Then the app grows.</p><p>Now “Book Details” isn’t just “load a book.” It’s also progress updates, offline behavior, cover loading, syncing, and “start reading.” Suddenly that ViewModel needs a repository, a use case, a settings page, and one more thing you swear is the last one. Then the “Library” screen needs a similar set, but not identical. Then your widget needs a stripped-down version. Then SwiftUI previews need mocks. Then you want a dev build that launches straight into a feature without tapping through onboarding.</p><p>This is why I’m a big believer in <strong>one Composition Root</strong>: one deliberately “ugly” place where object creation is allowed to happen, openly. A place where you can answer, without guessing, “where is this dependency made?” and “what version of the system does this screen actually run?”</p><p>This idea clicked even harder for me after Simon B. Støvring’s talk at <strong>Mobile Warsaw #100</strong>, <em>“Achieving Loose Coupling with Pure Dependency Injection and the Composition Root Pattern.” </em>(available <a href="https://youtu.be/mNNHx7mMgVM?si=6KFLGWTqqk2y2Ayk">here</a>).<em> </em>It’s also where dev apps and feature-only builds become easy, because you can compose a small, focused slice of your app without dragging the whole universe along with it.</p><p>In practice, for a reading tracker like <em>Bookie</em>, Composition Root becomes the one place where you decide things that should never be decided ad hoc inside screens:</p><ul><li>When do we refresh progress from remote?</li><li>Which analytics pipeline is active in this build?</li><li>What gets mocked in previews? What gets mocked in tests? What gets mocked in dev builds?</li></ul><p>If these system decisions are scattered across ViewModels, your app will behave differently depending on where the user entered a flow. If they’re centralized, your app behaves like one coherent product.</p><p>Yes, it’s boring. It should be boring like I said earlier. Wiring is not where you want creativity. Wiring is where you want predictability and coherency. Once you have one Composition Root, your architecture finally, at last, becomes glanceable, easy to follow and easy to understand. Interestingly this is exactly this setting where new team members grasp the idea of the new environment they are in the quickest. You can look at it and understand it. You can build a feature in isolation. You can swap a dependency without performing a project-wide refactor. Your app stops feeling like a collection of screens and starts feeling like a system you can reason about.</p><h3>AI Twist</h3><p>Here’s the deal: writing code is no longer the hard part and 2025 proved this well enough. App Store changed its regulations to limit AI copycat apps, which can be vibe coded in a matter of days. AI can generate code quickly. It can refactor quickly. It can scaffold a new feature in minutes. It can write “reasonable” implementations all day long.</p><p>But AI does not understand intent. And intent is where software lives. AI doesn’t know what must never depend on what. That’s you.</p><p>It doesn’t know which decisions must be consistent across the entire product. That’s you.</p><p>It doesn’t know what you want to keep stable for five years. That’s you.</p><p>So what happens in a typical MVVM codebase when you add AI to the mix? Your once simple and perfect app, day by day loses track. AI agent you didn’t prompt well enough in one session changes your policy on cache, another one pushes its agenda on refresh mechanism, and a few days later one logs analytics on success only, because who cares about failed states. And look, none of these choices are “wrong” per-se, which is exactly why AI will happily produce them.</p><p>The problem is that your app becomes a bundle of slightly different ideas and approaches. And don’t get me wrong it’s all AI. Humans can make this mistake too if a particular logic is scattered around a few places and its side effects are duplicated.</p><p>This is why systems-first thinking matters now. Architecture is how you encode intent into a shape that even fast, automated code generation can’t accidentally violate. It’s how you take a decision that used to be made 100 times (“where does this logic go?” “who owns this side effect?”) and make it once.</p><p>The developer skill that scales in the AI era isn’t typing. It’s designing constraints that make correctness the default. Be it through systems or agent prompting, but that’s a totally different story.</p><h3>Final Thought</h3><p>If you want your apps to scale, survive years on the App Store, and actually benefit from AI instead of being slowly corrupted by it, don’t try to out-type the machine. Instead of memorizing APIs by heart, focus on the architecture. Build a system that makes the right thing easy and the wrong thing hard.</p><p>Because in 2026, AI will happily produce code at unlimited speed.</p><p>Your job is to steer that current so it flows through the right channels — through clear layers, interfaces and a system that preserves intent.</p><p>Syntax is cheap. Systems are expensive. So build systems. And be expensive.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ad5f752af1bf" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building Predictable SwiftUI Screens with ViewConfig, ViewModel, and ViewState]]></title>
            <link>https://kubamilcarz.medium.com/building-predictable-swiftui-screens-with-viewconfig-viewmodel-and-viewstate-654c7679a73c?source=rss-b30973e2bd56------2</link>
            <guid isPermaLink="false">https://medium.com/p/654c7679a73c</guid>
            <category><![CDATA[swiftui]]></category>
            <category><![CDATA[mvvm]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[software-architecture]]></category>
            <dc:creator><![CDATA[Jakub Milcarz]]></dc:creator>
            <pubDate>Sun, 09 Nov 2025 11:17:58 GMT</pubDate>
            <atom:updated>2025-11-09T13:48:05.504Z</atom:updated>
            <content:encoded><![CDATA[<p>When a SwiftUI app grows, screens evolve, and can only get more complex. A view that once displayed static data now fetches from a remote source, supports multiple states, or presents several variants of itself depending on context. Without a consistent structure, initialization logic spreads across different places, parameters become unpredictable, and it’s no longer obvious where data comes from or when it loads.</p><p>Over time, I’ve found that most of these issues trace back to two core problems: <strong>unclear inputs</strong> and <strong>muddled lifecycle handling</strong>. The solution that has kept my projects stable and predictable is separating three core layers:</p><ul><li><strong>ViewConfig</strong>: Defines the explicit inputs and setup.</li><li><strong>ViewModel</strong>: Manages state, business logic, and async work.</li><li><strong>View</strong>: Renders the UI based on a strongly-typed ViewState.</li></ul><h3>Modeling State Explicitly</h3><p>Before diving into structure, it’s important to handle how each screen <em>behaves over time</em>. Every view transitions through phases — initial, loading, loaded, or failed — and modeling those transitions explicitly through a ViewState type makes initialization and data flow far more predictable.</p><p>Instead of juggling ambiguous boolean flags, we use a simple state enum:</p><pre>enum ViewState {<br>  case empty<br>  case initial<br>  case loading<br>  case loaded<br>  case failed(error: Error)<br>}</pre><p>This immediately avoids those “impossible” UI states, like when isLoading is true but hasError is <em>also</em> true. The UI doesn&#39;t know what to do, and frankly, neither do we. This enum ensures each state transition is explicit and the UI only ever renders one, valid state.</p><p>SwiftUI in iOS 17 introduced the @Observable macro, which is a fantastic simplification. However, it can also make it easier to lose track of <em>when</em> and <em>how</em> a view is initialized. Too much &quot;magic&quot; can lead to confusion.</p><p>I’ve found it essential to make screen creation <strong>fully deterministic</strong>. Each view in the app should have a clear initializer with explicitly defined parameters.</p><pre>struct CoffeeDetailView: View {<br>  let viewConfig: CoffeeDetailViewConfig<br>  @State var viewModel: CoffeeDetailViewModel<br>	<br>  var body: some View { /* ... */ }<br>}</pre><p>There’s nothing implicit here — no dependency on environment values or type-based injection <em>during initialization</em>. The goal is to make each screen’s creation <strong>readable at the call site</strong>, so you always know which variant you’re presenting and with which data.</p><h3>Define What the View Needs</h3><p>The ViewConfig struct is the view&#39;s &quot;input contract.&quot; It describes <em>only</em> what’s needed to build the screen and nothing more. It shouldn&#39;t include a screen ID or anything tied to SwiftUI identity management. Instead, it represents a clean, Sendable contract of parameters, flags, and callbacks.</p><pre>struct CoffeeDetailViewConfig: Sendable {<br>  let coffeeId: UUID<br>  let allowsEditing: Bool<br>  let onDismiss: (() -&gt; Void)?<br>}</pre><p>This allows for lightweight, testable, and incredibly clear initializers:</p><pre>CoffeeDetailView(<br>  viewConfig: .init(<br>    coffeeId: id,<br>    allowsEditing: true,<br>    onDismiss: { dismiss() }<br>  ),<br>  viewModel: .init(...)<br>)</pre><p>The important part is <strong>clarity</strong>, thanks to which every required parameter is visible where the view is created, and optional behaviors (like onDismiss) are contained within the config. What’s more, when you introduce a new parameter, you update it in a single place instead of adjusting initializers across multiple views. This alone makes maintaining and extending screens significantly easier.</p><h3>ViewModel — Business Logic and Controlled State</h3><p>The <strong>ViewModel</strong> layer is where business logic lives. It manages state transitions, coordinates async work, and interacts with repositories or data sources. By keeping it isolated from SwiftUI’s lifecycle, the ViewModel remains predictable, testable, and independent of how often the view itself gets recreated.</p><pre>@Observable<br>final class CoffeeDetailViewModel {<br>  private let coffeeRepo: CoffeeRepository<br>	<br>  init(coffeeRepo: CoffeeRepository) {<br>    self.coffeeRepo = coffeeRepo<br>  }<br>    <br>  var state: ViewState = .initial<br>  private(set) var coffee: CoffeeModel = .empty<br>  <br>  // The View tells the ViewModel when to start work.<br>  func onViewAppear(viewConfig: CoffeeDetailViewConfig) async {<br>    await load(coffeeId: viewConfig.coffeeId)<br>  }<br><br>  private func load(coffeeId: UUID?) async {<br>    guard let coffeeId else { return }<br>    <br>    state = .loading<br>    <br>    do {<br>      let response = try await coffeeRepo.fetchCoffee(id: coffeeId)<br>     <br>      coffee = response<br>      state = .loaded // Set success state<br>     } catch {<br>       state = .failed(error) // Set failure state<br>     }<br>  }<br>}</pre><p>By keeping the ViewModel focused purely on <strong>state and actions</strong>, it stays easy to test, reason about, and evolve. There are no assumptions about SwiftUI’s identity, no side effects triggered during initialization, and no view logic leaking in.</p><h3>Connecting It All Together in View</h3><p>In this structure, the <strong>View</strong> owns its ViewModel using @State rather than @StateObject. It allows the View to manage the model’s lifecycle directly and re-create it when needed, ensuring that initialization remains predictable and explicitly tied to the View&#39;s own lifecycle.</p><pre>struct CoffeeDetailView: View {<br>  let viewConfig: CoffeeDetailViewConfig<br>  @State var viewModel: CoffeeDetailViewModel<br><br>  var body: some View {<br>    content<br>      // 1. Kick off async work when the view appears<br>     .task { await viewModel.onViewAppear(viewConfig: viewConfig) }<br>  }<br> <br>  @ViewBuilder<br>  private var content: some View {<br>    switch viewModel.state {<br>    case .empty, .initial:<br>      Text(&quot;No coffee selected.&quot;)<br>    case .loading:<br>      ProgressView()<br>    case .loaded(let book):<br>      CoffeeDetailsContent(book: book, onDismiss: config.onDismiss)<br>    case .failed(let error):<br>      ErrorView(error: error)<br>    }<br>  }<br>}</pre><p>This pattern keeps the view lifecycle simple and ensures every instance starts from a known configuration and state. ViewModel is created in init, async work is started in .task, and state transitions are represented through ViewState.</p><h3>Testing and Scaling</h3><p>With explicit configurations and clearly modeled states, testing becomes a natural part of the process rather than an afterthought. You can inject mock services into the ViewModel, observe how the state transitions under various scenarios, and preview the view in each state using static data. As an added bonus, all without touching production code!</p><pre>#Preview(&quot;Loaded&quot;) {<br>  CoffeeDetail(<br>    viewConfig: .init(coffeeId: CoffeeModel.mock.id, allowsEditing: false),<br>    viewModel: .init(<br>      coffeeRepo: MockCoffeeRepository(coffees: [CoffeeModel.mock])<br>      )<br>    )	<br>}<br><br>#Preview(&quot;Delay&quot;) { /* ... */ }<br>#Preview(&quot;Error&quot;) { /* ... */ }<br>#Preview(&quot;Empty&quot;) { /* ... */ }</pre><p>This consistency makes large projects easier to reason about and maintain over time. Scaling the pattern across an app is simple because every screen follows the same lifecycle:</p><ol><li>Define inputs (ViewConfig),</li><li>Manage async state (ViewModel),</li><li>Render predictable UI (View + ViewState).</li></ol><h3>Conclusion</h3><p>A clean SwiftUI architecture isn’t about frameworks or clever patterns — it’s about <strong>consistency</strong>.</p><p>When each view follows the same interface and lifecycle, you eliminate ambiguity about where data comes from, when it’s loaded, and how it transitions between states.</p><p>By defining:</p><ul><li>ViewConfig as the contract of inputs,</li><li>ViewModel as the handler of state and logic, and</li><li>ViewState as the explicit reflection of progress,</li></ul><p>your screens remain predictable, composable, and easy to extend.</p><p>The real payoff is cohesion — a codebase that grows in a controlled, maintainable way. When the structure stays consistent, adding a new screen or feature feels familiar instead of fragile, and the entire project scales naturally.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=654c7679a73c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Indie iOS Developer Insights from WWDC25]]></title>
            <link>https://kubamilcarz.medium.com/indie-ios-developer-insights-from-wwdc25-8a9a34a0501c?source=rss-b30973e2bd56------2</link>
            <guid isPermaLink="false">https://medium.com/p/8a9a34a0501c</guid>
            <category><![CDATA[swiftui]]></category>
            <category><![CDATA[wwdc25]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[wwdc]]></category>
            <category><![CDATA[ios-development]]></category>
            <dc:creator><![CDATA[Jakub Milcarz]]></dc:creator>
            <pubDate>Thu, 12 Jun 2025 15:27:55 GMT</pubDate>
            <atom:updated>2025-06-12T15:27:55.378Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uJk47ihs5sOwiDSZ_jhZWg.png" /><figcaption>Source: Apple (<a href="https://developer.apple.com/wwdc25/">https://developer.apple.com/wwdc25/</a>)</figcaption></figure><p>Last week, I had the chance to attend WWDC25 — and let me tell you, if you’re an indie dev working in the Apple ecosystem, it’s something you shouldn’t miss. The conference is completely free to attend, and if you have a developer account, you can even sign up for exclusive one-on-one labs with real Apple engineers. I was lucky enough to score three of those sessions, where we went deep on topics like Core Data performance tuning, Xcode Cloud’s shared environment values, and the powerful new Foundation Models API. Here’s my take on how iOS 26 is set to impact indie development in a big way.</p><h4><strong>Foundation Models API</strong></h4><p>Apple’s announcement of the Foundation Models framework was easily the biggest moment of the event. These powerful AI language models now run directly on-device — meaning they’re private, offline-capable, and free to use. For larger or more complex requests, Apple’s concept of Private Cloud Compute steps in, allowing those tasks to be handled on remote servers while still maintaining the same high standard of security and user privacy. That’s a huge win for indie developers who’ve previously hesitated to build AI-powered features due to cost or privacy concerns.</p><p>Apps showcased, like Kahoot (turning handwritten notes into quizzes) and AllTrails (providing real-time, chat-based hiking suggestions), show just how versatile these tools can be. With only a few lines of Swift, we can now build AI-powered features that deliver structured outputs, stream responses as they’re generated, and leverage Apple’s new Macros API for defining clear, predictable behavior. This plays especially well with the existing Translations API, giving us more ways than ever to create intelligent, real-time experiences that feel both native and useful. Foundation Models are truly leveling the playing field — giving indie developers the kind of powerful, privacy-preserving AI capabilities that used to require a cloud backend and a big budget.</p><h4><strong>Liquid Glass</strong></h4><p>WWDC25 introduced us to Liquid Glass — a vibrant, dynamic material that elegantly adapts to different contexts, backgrounds, and lighting. Apple’s new design ethos emphasizes fluidity and natural interactions, encouraging us to build UIs that feel more alive. However, this year’s Developer Beta rollout sparked a wave of backlash from early testers online, especially on platforms like Twitter and TikTok. It’s frustrating, because these builds aren’t meant for daily drivers — they’re dev betas. And yet Apple now allows anyone to install them, no developer license required. Sure, this helps Apple gather more feedback and analytics, but if most of that feedback turns into performative complaints from users who don’t even know Feedback Assistant exists, it doesn’t help anyone — least of all the devs trying to ship meaningful features. We’ve got to remind ourselves (and others) that these early versions exist to help refine the experience, not to be judged as final products.</p><p>iOS 26 brings a whole new layer of polish to UI design. Tools like .glassEffect(), along with updated navigation and tab bar APIs, allow us to create smoother, more responsive interfaces — but they also raise the bar. The design-related sessions this year were genuinely some of the most valuable content at WWDC, and I highly recommend watching all of them. My biggest takeaways include: using three distinct corner radii for different design intents, building with Liquid Glass in a way that maintains structure and cohesion, and leaning into the system’s new sense of fluidity and interaction expectations. These updates aren’t just cosmetic — they influence how we design user flows, handle transitions, and create an overall experience that feels deeply native to iOS 26.</p><h4><strong>Useful Features</strong></h4><p>Apple’s rollout of Apple Intelligence sparked mixed reactions — and understandably so. The feature set is limited at launch, and much of the promised functionality won’t arrive until later. Still, I give Apple credit: they’re shipping what’s ready and doing it in a way that reflects their values. I actually agree with their position that not everything needs to be branded as AI. In fact, most of what we call AI today is really just well-trained machine learning — and that’s okay. What matters is how it improves everyday life.</p><p>iOS 26 delivers that. Features like live translation in Messages and FaceTime, real-time call screening and hold assist in the Phone app, and context-aware Visual Intelligence on the screen itself are practical upgrades that make a difference day-to-day. developers, we can tap into this power through App Intents, making our apps smarter, more useful, and more tightly integrated into the broader iOS experience.</p><p>App Intents has been quietly evolving for a few years now, and this year just confirms it’s become essential. I had already been thinking about creating a separate communication layer for my apps last year — and iOS 26 makes it clear this is the right direction. Think of App Intents as a new interface between your app and the system, or even the user themselves. Whether it’s Siri, Spotlight, or now Visual Intelligence, this is how iOS expects apps to communicate moving forward. If you haven’t started exploring it, now’s the time — it’s absolutely worth implementing.</p><h4><strong>Xcode 26</strong></h4><p>The integration of AI directly into Xcode 26 is transformative. With built-in chat-based coding assistants powered by ChatGPT, coding feels more intuitive and productive. My sessions with Apple engineers highlighted new debugging tools, automated UI test creation (simply by screen recording!), and streamlined cloud collaboration via Xcode Cloud.</p><p>The addition of the icon composer app — letting us easily create layered, visually striking icons that adapt to different device sizes and contexts — is a huge win for developers. I’ve already created two app icons using it — one for Bookie and another for Coffee Note — and the results are honestly stunning. Much like SF Symbols, it’s one of those tools that instantly levels up the visual quality of your app. It saves time, encourages consistency, and gives indie devs a shot at studio-level polish without the overhead.</p><h4><strong>Unified Experiences</strong></h4><p>WWDC25 wasn’t just about iOS — it underscored Apple’s commitment to delivering a unified, intelligent experience across the entire ecosystem, including macOS Tahoe, watchOS 26, tvOS 26, visionOS 26, and especially iPadOS 26. The macOS-style multitasking coming to iPad is a massive leap forward: you now get full window management, hover-activated menu bars, and the ability to move between apps in a much more fluid, desktop-like way. For years, I’ve wanted to make my iPad my daily driver, and with these updates, we’re finally getting closer. But to fully cross that line, Xcode has to come to iPad. There’s no way around it. The hardware is more than capable, the UI is ready, and developers are hungry for it. iPadOS 26 lays the groundwork beautifully — now it’s time for the tools to follow.</p><p>These cross-platform developments mean our apps can seamlessly deliver consistent, high-quality experiences across Apple’s ecosystem, boosting user engagement and satisfaction.</p><h4><strong>Personal Highlights</strong></h4><p>My one-on-one labs with Apple engineers gave me real, hands-on insight into the direction Apple is heading — and more importantly, how to take advantage of it as an indie dev. I came away with a deeper understanding of how to design responsibly with Liquid Glass, how iPadOS multitasking is reshaping the expectations for pro apps, and how Apple’s developer tools are evolving to meet those needs. Xcode 26, especially with its built-in AI and tighter cloud integrations, feels like a serious productivity upgrade. The icon composer tool was another highlight. And of course, the Foundation Models framework has endless potential — not just technically, but creatively. It opens new doors for intelligent, privacy-respecting features that would’ve been unthinkable a year ago.</p><p>iOS 26 beta drops in July, with full releases expected in September 2025. Now is the perfect time to dive into Foundation Models, App Intents, and the new design tools. The doors Apple has opened for indie devs are wide, offering unprecedented opportunities to innovate and excel. Now, back to work — and see you in September with a feature-rich, iOS 26-ready release!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8a9a34a0501c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Swift Measurement API: Building Inclusive Mobile Apps]]></title>
            <link>https://kubamilcarz.medium.com/swift-measurement-api-building-inclusive-mobile-apps-ab98973a3169?source=rss-b30973e2bd56------2</link>
            <guid isPermaLink="false">https://medium.com/p/ab98973a3169</guid>
            <category><![CDATA[swiftui]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[inclusive-design]]></category>
            <category><![CDATA[accessibility]]></category>
            <dc:creator><![CDATA[Jakub Milcarz]]></dc:creator>
            <pubDate>Mon, 02 Jun 2025 19:03:37 GMT</pubDate>
            <atom:updated>2025-06-02T19:03:37.449Z</atom:updated>
            <content:encoded><![CDATA[<p><em>Part 1 of the “Inclusive iOS Development” series — Making apps that users actually love</em></p><p>I’m currently knee-deep in refactoring <a href="https://apps.apple.com/pl/app/coffee-note-brew-taste/id6447321924">Coffee Note</a> (my caffeine tracking app) from a monolithic architecture into VIPER and RIBs. You know how it is — what starts as a simple refactor quickly becomes a complete rethinking of your app’s foundations. As I was working through the caffeine tracking feature (which handles everything from mg measurements to “cups of coffee”), I stumbled upon something that made me stop and think: my app was handling units like ml/fl oz and g/lbs all over the place, but in the most inefficient way possible.</p><p>That’s when I discovered Swift’s Measurement API — and honestly, it changed everything about how I approach user-centric design. Picture this: your American users are confused by “250ml” while your European users scratch their heads at “8.5 fl oz.” Sound familiar? As indie developers, we often focus on features first and user experience second. But here’s the thing — true accessibility isn’t just about VoiceOver support (though that’s crucial too). It’s about making every user feel like your app was built specifically for them.</p><h4><strong>What Exactly Is a Measurement?</strong></h4><p>At its core, Apple’s <a href="https://developer.apple.com/documentation/foundation/measurement">Measurement</a> is beautifully simple — it’s an object that combines a value with its unit. Think of it as giving context to raw numbers:</p><pre>let weight: Double = 0.7 // Just a number — 0.7 what?<br>let bagWeight = Measurement(value: 0.7, unit: UnitMass.kilograms) // Now we&#39;re talking!</pre><p>The magic happens with the conversion methods:</p><ul><li>func convert(to: UnitType) — Modifies the measurement in place</li><li>func converted(to: UnitType) -&gt; Measurement&lt;UnitType&gt; — Returns a new converted measurement</li></ul><p>But why should you care about this seemingly simple concept?</p><h4>The Real-World Problem We’re Solving</h4><p>Here’s where things get interesting from both a technical and business perspective:</p><p><strong>1. Global Accessibility Matters</strong></p><p>Your app isn’t just competing locally anymore — it’s competing globally. When users see measurements they don’t understand, they don’t just get confused; they leave. We’re not just talking about metric vs. imperial systems. Consider:</p><ul><li>A fitness app showing weight in pounds to a European user</li><li>A recipe app displaying temperatures in Celsius to an American cook</li><li>A travel app showing distances in miles to someone who thinks in kilometers</li></ul><p><strong>2. The Database Dilemma</strong></p><p>Traditionally, you might store different units in your database: some records in kg, others in lbs. But what happens when a user switches preferences? Suddenly, that “60” in your database could mean 60kg (132 lbs) or 60 lbs (27kg) — quite a difference!</p><p>The elegant solution? Store everything in a base unit (like kilograms for weight) and let the Measurement API handle display conversion. Your data stays consistent, your users stay happy.</p><p><strong>3. Code Readability Is King</strong></p><p>Sure, you could write conversion functions yourself. But Swift is designed to read like English, and the Measurement API embraces this philosophy:</p><pre>// The old way — error-prone and unclear<br>let poundsToKg = pounds * 0.453592<br>let kgToPounds = kg * 2.20462<br><br>// The Swift way - crystal clear intent<br>let weightInPounds = weightInKg.converted(to: .pounds)</pre><p>Which version would your future self thank you for?</p><h4><strong>How It All Works in Practice</strong></h4><p>Let’s see this in action with some real-world examples from my coffee tracking app:</p><p><strong>Basic Unit Conversion</strong></p><pre>let weight: Double = 0.7<br>let bagWeight = Measurement(value: weight, unit: UnitMass.kilograms)<br>// Display with SwiftUI - automatically respects locale of user from the environment<br><br>Text(bagWeight, format: .measurement(width: .abbreviated, usage: .general))<br>// Shows &quot;0.7 kg&quot; or &quot;1.5 lbs&quot; depending on user&#39;s region settings</pre><p><strong>Temperature Conversion (Perfect for Coffee Brewing!)</strong></p><pre>let brewTemp = Measurement(value: 195, unit: UnitTemperature.fahrenheit)<br>let celsiusTemp = brewTemp.converted(to: .celsius)<br>// Converts 195°F to 90.6°C automatically</pre><p><strong>Volume Measurements (Essential for Coffee Ratios)</strong></p><pre>let coffeeAmount = Measurement(value: 250, unit: UnitVolume.milliliters)<br>let fluidOunces = coffeeAmount.converted(to: .fluidOunces)<br>// Perfect for displaying &quot;250ml (8.5 fl oz)&quot; to satisfy everyone</pre><h4><strong>The Business Impact</strong></h4><p>Here’s what I have learned from user feedback I got. It’s not a secret that most of the users come from the US, and some of them really wanted a clean and simple way for showing what they need in a format they understand. As my app grows and more features are to come, it’s important to focus on the foundation and core business logic. The robust measurement conversion layer will bring stability and certainty my users need. That’s the power of thoughtful accessibility — it doesn’t just make your app usable; it makes it preferable.</p><h4><strong>What’s Next in This Series?</strong></h4><p>This is just the beginning of our journey into inclusive iOS development. In upcoming articles, we’ll explore:</p><ul><li>Dynamic Type and how to make your layouts truly adaptive</li><li>VoiceOver optimization that goes beyond basic accessibility</li><li>Localization strategies that indie developers actually have time to implement</li><li>Color accessibility and designing for different types of color vision</li></ul><h4><strong>Your Turn to Experiment</strong></h4><p>The beauty of the Measurement API is how quickly you can start using it. Whether you’re building a fitness app, a cooking companion, or even a travel planner, measurements are everywhere in our apps.</p><p>Try implementing basic unit conversion in your current project. Start small — maybe just weight or temperature — and see how it feels. I guarantee you’ll start noticing opportunities everywhere once you begin thinking in measurements rather than raw numbers.</p><p>Building apps that truly serve everyone isn’t just good karma — it’s good business. Follow along as we explore more ways to make iOS development both more inclusive and more profitable for indie developers.</p><p><strong>What unit conversion challenges have you faced in your apps? Drop a comment below — I’d love to hear about your experiences and maybe feature them in future articles!</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ab98973a3169" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Mocking Core Data for SwiftUI Previews]]></title>
            <link>https://kubamilcarz.medium.com/mocking-core-data-for-swiftui-previews-my-two-year-journey-bbeae673ea86?source=rss-b30973e2bd56------2</link>
            <guid isPermaLink="false">https://medium.com/p/bbeae673ea86</guid>
            <category><![CDATA[core-data]]></category>
            <category><![CDATA[swiftui]]></category>
            <category><![CDATA[mvvm]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[Jakub Milcarz]]></dc:creator>
            <pubDate>Sun, 30 Mar 2025 21:59:11 GMT</pubDate>
            <atom:updated>2025-06-06T08:41:56.882Z</atom:updated>
            <content:encoded><![CDATA[<p>After avoiding SwiftUI previews for two years while building my reading tracker app, I finally cracked the code. Here’s how I went from preview skeptic to preview evangelist, and the exact solution that transformed my development workflow.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0I4uVx3cDNWRZVbWH8nkew.png" /></figure><p><strong>Why I Avoided SwiftUI Previews</strong></p><p>Let’s be honest: Core Data and SwiftUI previews don’t play nicely together out of the box. For the first two years of building my app, I simply gave up on previews entirely.</p><p>The core problem was frustratingly simple but seemingly impossible to solve: to create or display any Core Data object in a preview, I needed a valid NSManagedObjectContext. But how do you reliably inject that into a preview? And where would the logic for creating realistic mock data live?</p><p>So I did what many of us do — I relied on the simulator for all my UI iterating — and let me tell you how much time I spent on hitting cmd+r every couple of secons. As my app grew more complex, this meant increasingly long build-and-run cycles for even minor UI changes. Not ideal.</p><p><strong>The Architectural Foundation</strong></p><p>My path to a solution began when I started diving deeper into architectural patterns. I learned about the importance of separating my code into distinct layers — data, domain, and presentation. This wasn’t just theoretical knowledge; it became the foundation for rebuilding significant parts of my app with testing and mocking in mind.</p><p>I started creating mock implementations for most of my dependencies, but Core Data remained the stubborn holdout. I initially thought, “I’ll just create an extension to my entity models — something like .mockBook — and be done with it.” But I kept running into the same context problem.</p><p><strong>My Breakthrough Solution</strong></p><p>After months of frustration, I finally developed a solution that works reliably. It centers around two key components:</p><ol><li>An in-memory Core Data stack specifically designed for previews</li><li>Extension methods that create fully configured mock entities using this preview context</li></ol><p>Here’s how I implemented it:</p><p><strong>1. The In-Memory Preview Container</strong></p><p>First, I created a lightweight, in-memory Core Data container that wouldn’t persist anything:</p><pre>extension PersistenceController {<br>    lazy var previewInMemory: NSPersistentContainer = {<br>        let container = NSPersistentContainer(name: &quot;YourModelName&quot;)<br>        container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: &quot;/dev/null&quot;)<br>        container.loadPersistentStores(completionHandler: { _, error in<br>            if let error = error as NSError? {<br>                fatalError(&quot;Unresolved error \(error), \(error.userInfo)&quot;)<br>            }<br>        })<br>        <br>        return container<br>    }()<br>}</pre><p>The clever trick here is setting the store URL to <strong>/dev/null </strong>— this creates a true in-memory store that’s perfect for previews.</p><p><strong>2. Mock Entity Creators</strong></p><p>Then I added extension methods to my PersistenceController for creating realistic mock entities:</p><pre>extension PersistenceController {<br>    func mockBook(<br>        title: String = &quot;The Great Gatsby&quot;,<br>        author: String = &quot;F. Scott Fitzgerald&quot;,<br>        pageCount: Int = Int.random(in: 200...400)<br>    ) -&gt; Book {<br>        let context = Self.shared.previewInMemory.viewContext<br>        let book = Book(context: context)<br>        book.id = UUID()<br>        book.dateAdded = Date.now<br>        book.title = title<br>        book.author = author<br>        book.pageCount = pageCount<br>        return book<br>    }<br>    <br>    var mockBooks: [Book] {<br>        [<br>            mockBook(pageCount: 673),<br>            mockBook(title: &quot;The Lord of the Rings&quot;, author: &quot;J.R.R. Tolkien&quot;, pageCount: 542),<br>            mockBook(title: &quot;Sapiens&quot;, author: &quot;Yuval Noah Harari&quot;, pageCount: 382),<br>            mockBook(title: &quot;1984&quot;, author: &quot;George Orwell&quot;, pageCount: 290)<br>        ]<br>    }<br>}</pre><p>The key insight was using Self.shared.previewInMemory.viewContext to get a valid context for these mock objects.</p><p><strong>3. The Complete Preview Environment</strong></p><p>To make my previews even more powerful, I created a View extension that configures my entire app environment with a single line:</p><pre>extension View {<br>    func previewEnvironment(<br>        isConnected: Bool = true,<br>        inMemory: Bool = true,<br>        isPremium: Bool = false<br>    ) -&gt; some View {<br>        let persistence = PersistenceController.shared.previewInMemory<br>        <br>        return self<br>            // Managers with mock services<br>            .environment(BookManager(service: MockBookService()))<br>            // Network, purchases, etc.<br>            .environment(NetworkManager(service: MockNetworkService(isConnected: isConnected)))<br>            .environment(\.managedObjectContext, persistence.viewContext)<br>    }<br>}</pre><p>This approach has been transformative because it lets me test different app states by toggling network connectivity, premium features, and other variables — all without rebuilding.</p><p><strong>Using It In Practice</strong></p><p>With this infrastructure in place, creating previews became dead simple:</p><pre>#Preview {<br>    BookDetailView(book: PersistenceController.shared.mockBook())<br>        .previewEnvironment()<br>}</pre><p>Need to see how a view handles multiple books? No problem:</p><pre>#Preview {<br>    BookshelfView(books: PersistenceController.shared.mockBooks)<br>        .previewEnvironment(isPremium: true)<br>}</pre><p><strong>The Power of Multiple Previews</strong></p><p>Once I had my mocking system in place, I discovered the real power of previews: testing multiple states of the same component simultaneously. This has been a game-changer for my development process:</p><pre>#Preview(&quot;Empty State&quot;) {<br>    BookListView(books: [])<br>        .previewEnvironment()<br>}<br><br>#Preview(&quot;Single Item&quot;) {<br>    BookListView(books: [PersistenceController.shared.mockBook()])<br>        .previewEnvironment()<br>}<br><br>#Preview(&quot;Multiple Items&quot;) {<br>    BookListView(books: PersistenceController.shared.mockBooks(count: 10))<br>        .previewEnvironment()<br>}<br><br>#Preview(&quot;Premium User&quot;) {<br>    BookListView(books: PersistenceController.shared.mockBooks)<br>        .previewEnvironment(isPremium: true)<br>}</pre><p>Or for individual UI components, seeing different styling options side by side:</p><pre>#Preview(&quot;Default&quot;) {<br>    let book = PersistenceController.shared.mockBook()<br>    <br>    BookCoverCell(<br>        title: book.title,<br>        author: book.author,<br>        coverImage: book.coverImage<br>    )<br>    .padding()<br>}<br><br>#Preview(&quot;Compact&quot;) {<br>    let book = PersistenceController.shared.mockBook()<br>    <br>    BookCoverCell(<br>        title: book.title,<br>        author: book.author,<br>        coverImage: book.coverImage,<br>        style: .compact<br>    )<br>    .padding()<br>}<br><br>#Preview(&quot;Featured&quot;) {<br>    let book = PersistenceController.shared.mockBook(<br>        title: &quot;The Design of Everyday Things&quot;,<br>        author: &quot;Don Norman&quot;<br>    )<br>    <br>    BookCoverCell(<br>        title: book.title,<br>        author: book.author,<br>        coverImage: book.coverImage,<br>        style: .featured<br>    )<br>    .background(.secondary.opacity(0.1))<br>    .padding()<br>}</pre><p>This approach has completely transformed how I design and refine my UI components. I can instantly see how my views respond to different data conditions, styling options, and environment configurations. It’s like having dozens of mini-apps running simultaneously, each showing a different state or configuration.</p><p><strong>Was It Worth It?</strong></p><p>I’ll be honest — implementing this solution took me a solid 2–3 months. It meant revisiting my architecture, creating mock services, and refactoring significant portions of my codebase.</p><p>But the results have been worth every minute:</p><ul><li>My code quality has improved astronomically</li><li>UI iteration is dramatically faster</li><li>I can test edge cases without complex setup</li><li>I finally enjoy using SwiftUI previews as they were meant to be used</li></ul><p><strong>Generating Dynamic Test Data</strong></p><p>One particularly useful pattern I implemented was creating methods to generate variable quantities of mock entities. For instance, sometimes you need just a single book for a detail view, while other times you need a whole collection for testing list displays or search functions:</p><pre>extension PersistenceController {<br>    func mockBooks(count: Int) -&gt; [Book] {<br>        var books: [Book] = []<br>        for _ in 0..&lt;count {<br>            books.append(mockBook())<br>        }<br>        return books<br>    }<br>}</pre><p>This gives me tremendous flexibility in testing different states. Need to see how your UI handles 20 books? Just callmockBooks(count: 20).</p><p><strong>The Value of Slowing Down</strong></p><p>Perhaps the most important lesson from this journey has nothing to do with the technical implementation. It’s about the value of slowing down.</p><p>As developers, we often get caught up in the cycle of iterate, release, repeat. We focus on shipping features and fixing bugs, and our code quality suffers. Technical debt accumulates. Architecture becomes an afterthought.</p><p>Taking a step back to really polish and hone your craft — giving extra attention to the stuff you don’t see — is what ultimately gives you a headstart and an extra push. It’s counterintuitive, but slowing down actually made me faster in the long run.</p><p>Those 2–3 months I spent refactoring my codebase and implementing this preview system felt like lost time initially. But the payoff has been exponential in terms of development speed, code quality, and honestly, my enjoyment of the development process.</p><p><strong>What I’d Tell My Past Self</strong></p><p>If I could go back in time, I’d tell myself to invest in this approach from day one. The upfront cost in time and effort pays massive dividends in development speed and code quality.</p><p>The key insights were:</p><ol><li>Separate your concerns properly — data, domain, presentation</li><li>Create a dedicated in-memory Core Data stack for previews</li><li>Build extension methods that generate realistic mock entities</li><li>Make a comprehensive environment setup for different app states</li><li>Don’t be afraid to slow down and focus on the foundations</li></ol><p>I spent two years avoiding previews because of Core Data complexity, but it turns out the solution wasn’t that complicated — it just required thinking differently about my app’s architecture.</p><p>If you’re struggling with the same issues, I hope my journey saves you some time. You don’t have to choose between Core Data and SwiftUI previews — with the right approach, you can have the best of both worlds.</p><p>If you liked this post, consider supporting my work here, on LinkedIn, Threads, or YouTube where I talk all about juggling indie dev side hustle and full time job. Find me everywhere at @kubamilcarz!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bbeae673ea86" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Impact of Reading on Mental Health]]></title>
            <link>https://kubamilcarz.medium.com/the-impact-of-reading-on-mental-health-4f16db10426a?source=rss-b30973e2bd56------2</link>
            <guid isPermaLink="false">https://medium.com/p/4f16db10426a</guid>
            <category><![CDATA[books]]></category>
            <category><![CDATA[reading]]></category>
            <category><![CDATA[focus]]></category>
            <category><![CDATA[mental-health]]></category>
            <category><![CDATA[health]]></category>
            <dc:creator><![CDATA[Jakub Milcarz]]></dc:creator>
            <pubDate>Wed, 22 Mar 2023 13:12:14 GMT</pubDate>
            <atom:updated>2023-03-22T13:12:14.823Z</atom:updated>
            <content:encoded><![CDATA[<p>Reading is a popular hobby that has been enjoyed by people for centuries. Not only is it an enjoyable way to spend time, but it can also have a significant positive impact on mental health. In this article, we’ll explore the ways in which reading can benefit mental health and recommend some books that you might find helpful.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2A76gV3VGatI6qhnvYro1g.png" /><figcaption>Image by <a href="https://pixabay.com/users/stocksnap-894430/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=2619909">StockSnap</a> from <a href="https://pixabay.com//?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=2619909">Pixabay</a></figcaption></figure><p>Reading has been shown to have many benefits for mental health. It can help to reduce stress and anxiety, improve cognitive function, increase empathy, and provide a sense of comfort and escape from daily life. Additionally, reading can be a valuable tool for self-discovery and personal growth. Let’s explore some of the ways in which reading can be beneficial for mental health and recommend some books that you might find helpful.</p><p><strong>Reducing Stress and Anxiety</strong></p><p>Research’s shown that reading can lower the heart rate, ease muscle tension, and reduce feelings of stress and anxiety. When we read, we can escape our own lives and immerse ourselves in a different world, which can be a welcome break from the pressures of daily life.</p><p>Books like “The Alchemist” by Paulo Coelho and “The Hitchhiker’s Guide to the Galaxy” by Douglas Adams can be great choices for reducing stress and anxiety. “The Alchemist” is a timeless story about following your dreams and discovering your own path in life. “The Hitchhiker’s Guide to the Galaxy” is a lighthearted and humorous tale about the absurdity of the universe, which can be a great way to escape from everyday stressors.</p><p>But why? When we read, our brains are able to focus on the story instead on the world around us. The act of reading itself can be a relaxing and soothing activity, especially if we’re reading in a quiet and comfortable environment.</p><p>“The Alchemist” can be comforting and inspiring for readers who may be feeling lost or uncertain about their own life journey. Additionally, the story has a hopeful and uplifting tone. Similarly, “The Hitchhiker’s Guide to the Galaxy” is a humorous and absurd tale that can be a great way to escape from the stressors of daily life. The humor and silliness of the latter story and the inspiring journey in “The Alchemist” can be welcome distractions from everyday worries and can help to reduce feelings of stress and anxiety.</p><p><strong>Improving Cognitive Function</strong></p><p>Reading can also be beneficial for cognitive function. It can improve vocabulary, memory, and concentration. Additionally, reading can challenge our brains and encourage us to think critically and creatively. This can help to improve our problem-solving skills and overall cognitive function.</p><p>Books like “Quiet” by Susan Cain can be great choices for improving cognitive function. “Quiet” is a thought-provoking book that encourages readers to think critically and challenge their own beliefs. One of the key themes of the book is the value of introverted traits, which are often overlooked or undervalued in a world that values extroverted behavior.</p><p>When we challenge our own beliefs and assumptions, we are forced to think more deeply and consider alternative perspectives. This can lead to improved cognitive function and increased mental flexibility. Additionally, “Quiet” can be a great way to learn more about oneself and one’s own strengths and weaknesses. By reading about introverted traits and how they can be leveraged, readers may gain a better understanding of their own personalities.</p><p><strong>Increasing Empathy</strong></p><p>Reading can also increase empathy and understanding. When we read about different experiences and perspectives, we can develop a greater sense of empathy and compassion for others. This can help to improve our relationships with others and our overall sense of connectedness.</p><p>Books like “The Subtle Art of Not Giving a F*ck” by Mark Manson can be great choices for increasing empathy. While the title may suggest otherwise, this book is actually a thoughtful exploration of how we can cultivate a sense of purpose and meaning in our lives. It encourages readers to focus on what truly matters and to develop a greater sense of empathy and understanding for others.</p><p><strong>Closing Thoughts</strong></p><p>In conclusion, reading can be a valuable tool for improving mental health. It can reduce stress and anxiety, improve cognitive function, increase empathy, and provide a sense of comfort and escape from daily life. Whether you’re looking for a lighthearted escape or a thought-provoking exploration of the human experience, there’s a book out there that can benefit your mental health. So go ahead, grab a book, and start reading!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4f16db10426a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Finding Your Zen: The Power of Music for Boosting Productivity and Creativity]]></title>
            <link>https://kubamilcarz.medium.com/finding-your-zen-the-power-of-music-for-boosting-productivity-and-creativity-88907db2d1ca?source=rss-b30973e2bd56------2</link>
            <guid isPermaLink="false">https://medium.com/p/88907db2d1ca</guid>
            <category><![CDATA[productivity]]></category>
            <category><![CDATA[creativity]]></category>
            <dc:creator><![CDATA[Jakub Milcarz]]></dc:creator>
            <pubDate>Wed, 15 Mar 2023 15:35:05 GMT</pubDate>
            <atom:updated>2023-03-15T15:35:05.964Z</atom:updated>
            <content:encoded><![CDATA[<p>Are you feeling stressed out and overwhelmed after a long day of work or school? Don’t worry, we’ve all been there. Luckily, there are some simple things you can do to help you relax and unwind so that you can recharge and get ready to take on the next day.</p><h4>1. Take a hot bath or shower</h4><p>There’s nothing quite like soaking in a hot bath or taking a long, steamy shower to help you relax and let go of the stresses of the day. Plus, the warm water can help soothe sore muscles and promote better sleep. Add some bubbles or essential oils to make it even more luxurious!</p><h4>2. Practice deep breathing</h4><p>Deep breathing exercises can help slow down your heart rate and calm your mind, making it easier to relax and let go of stress. Try inhaling deeply for 4 seconds, holding your breath for 4 seconds, and exhaling slowly for 8 seconds. Repeat this a few times, and you’ll be feeling more relaxed in no time.</p><h4>3. Listen to some music</h4><p>Music has the power to soothe the soul and help you relax. Put on some calming tunes or your favorite playlist and let the music wash over you. You can even try some guided meditation tracks or ASMR videos if you’re really looking to chill out.</p><h4>4. Treat yourself to a little indulgence</h4><p>Sometimes, a little indulgence can go a long way in helping you relax and unwind. Treat yourself to a piece of your favorite chocolate, a glass of wine, or your favorite takeout meal. Just remember to enjoy it mindfully and in moderation.</p><h4>5. Spend time with loved ones</h4><p>Spending time with loved ones can help you feel more connected and supported, which can in turn help you relax and feel more at ease. Whether it’s snuggling up with your pet, calling a friend, or spending time with your family, make sure you’re making time for the people who matter most to you.</p><p>In conclusion, taking time to relax and unwind after a long day is essential for your physical and mental well-being. By taking a hot bath or shower, practicing deep breathing, listening to music, treating yourself to a little indulgence, and spending time with loved ones, you can help yourself feel more relaxed and rejuvenated. So go ahead, kick off your shoes, and take some time to chillax!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=88907db2d1ca" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Transforming Your Success, Work, and Life: 3 Books to Read in 2023]]></title>
            <link>https://kubamilcarz.medium.com/transforming-your-success-work-and-life-3-books-to-read-in-2023-c6fad8d73de3?source=rss-b30973e2bd56------2</link>
            <guid isPermaLink="false">https://medium.com/p/c6fad8d73de3</guid>
            <category><![CDATA[reading]]></category>
            <category><![CDATA[books]]></category>
            <category><![CDATA[must-read]]></category>
            <category><![CDATA[2023-reading-list]]></category>
            <dc:creator><![CDATA[Jakub Milcarz]]></dc:creator>
            <pubDate>Thu, 09 Feb 2023 23:32:38 GMT</pubDate>
            <atom:updated>2023-02-09T23:32:38.988Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*f6Nk6pnGSIekDMSl" /><figcaption>Photo by <a href="https://unsplash.com/@alexandrajf?utm_source=medium&amp;utm_medium=referral">Alexandra Fuller</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>February is here. I hope you still stick to the New Year’s resolution! Don’t give up.</p><p>In 2022 I read 70 books from all sorts of genres, and honestly it’s been a hell of a ride. I have never expected to go through so many books in just 52 weeks, and more importantly learn so much!</p><p>70 is a big number for sure, but here I composed a list of just three books you just <em>must</em> read this year. These are the books I’ve learned the most from in the past year and which helped me become a better person.</p><p>And no, you’re not gonna find <em>Atomic Habits</em> or other weird psuedo-productivity “books” (or rather just lengthly articles).</p><p>Here we go…</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kz-kG0VU8E3qLucUJy4rkg.jpeg" /><figcaption>Outliers (source: <a href="https://yourteenmag.com/wp-content/uploads/2016/01/outliers-11.jpg">Your Teen Magazine</a>)</figcaption></figure><h4><em>1) ‘Outliers’</em> by Malcolm Gladwell</h4><p>Listen, I don’t care if 10 000 hours rule is fake. Let’s leave all controversies aside and focus. <em>Outliers</em> is a phenomenal book that delves deep into what contributes to success, and what we call ‘luck’ is much more complex.</p><p>Talent is one thing, practice is another, and yet another is environment. You have no control over your talent, but you do over time and how you decide to spend it. Research shows that talent doesn’t influence how well you perform. That means, everything is possible. It only takes time.</p><blockquote>“If you work hard enough, assert yourself, and use your mind and imagination, you can shape the world to your desires.”</blockquote><p>And a second qoute:</p><blockquote>“When and where you are born, what your parents did for a living, and what the circumstances of your upbringing were make a significant difference in how well you do in the world.”</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*iV5GGfd7ODgxNlii" /><figcaption>Photo by <a href="https://unsplash.com/@sworupimages?utm_source=medium&amp;utm_medium=referral">Saurav Thapa Shrestha</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h4><em>2) ‘Deep Work’ </em>by Cal Newport</h4><p>This one has revolutionized the way I work and study. No kidding. Deep work means simply the ability to focus and do work at top gear.</p><p>If you want to stand out in the crowd, get that pay rise, or learn anything more efficiently this is the book for you! And what’s cool, according to Cal, deep work can be done anytime regardless of your schedule.</p><p>Why does ‘deep work’ matter? Well, especially in 2023, because of this:</p><blockquote>“Two Core Abilities for Thriving in the New Economy 1. The ability to quickly master hard things. 2. The ability to produce at an elite level, in terms of both quality and speed.”</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZxprcGadSyiWqc1AIrVUrg.jpeg" /><figcaption>Stillness is the Key (source: <a href="https://www.google.com/url?sa=i&amp;url=https%3A%2F%2Fwww.jonglat.com%2Fhome%2Fstillness-is-the-key&amp;psig=AOvVaw2hWUZtm2Aee6l00sgI5C_1&amp;ust=1675190943447000&amp;source=images&amp;cd=vfe&amp;ved=0CAMQjB1qFwoTCLjB29r67_wCFQAAAAAdAAAAABAE">Jon Glatfelter</a>)</figcaption></figure><h4>3) ‘Stillness is the Key’ by Ryan Holiday</h4><p>I’ve read that book in April last year and even after finishing it I thought it wasn’t that big of a deal. But now, looking back, I truly believe this short book has influenced me greatly.</p><p>Ryan Holiday is a strong proponent of Stoicism, has a YouTube channel with a total of almost 1 million subscribers, and has written countless books on stoicism. Stillness refers to one of the fundamentals of stoic philosophy.</p><p>It’s hard to sell someone on stoicism, but if you ever feel like living in a constant hurry and like the world doesn’t let you take a breath; you should try delving into stoicism in 2023. <em>Stillness is the Key</em> is a good entry point to this ancient, though timeless philosophy.</p><blockquote>“We can’t be afraid of silence, as it has much to teach us. Seek it. The ticking of the hands of your watch is telling you how time is passing away, never to return. Listen to it.”</blockquote><p>So there you have it, these are 3 books I believe will transform the way you look at success, work, and live. Happy reading!</p><p>If you want to read more books in 2023, but have troubles tracking progress, or sticking to your goals, try Bookie! Bookie is a free app available to download on App Store that motivates you to read more every day!</p><p>Make it yours by clicking the link down below:</p><p><a href="https://apps.apple.com/us/app/bookie-book-tracker/id6443825869">https://apps.apple.com/us/app/bookie-book-tracker/id6443825869</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c6fad8d73de3" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Craft — Home For My Notes]]></title>
            <link>https://kubamilcarz.medium.com/craft-home-for-my-notes-5e25d351a669?source=rss-b30973e2bd56------2</link>
            <guid isPermaLink="false">https://medium.com/p/5e25d351a669</guid>
            <category><![CDATA[apps]]></category>
            <category><![CDATA[productivity]]></category>
            <category><![CDATA[notes]]></category>
            <category><![CDATA[ui]]></category>
            <dc:creator><![CDATA[Jakub Milcarz]]></dc:creator>
            <pubDate>Wed, 01 Feb 2023 19:04:20 GMT</pubDate>
            <atom:updated>2023-02-01T19:04:20.579Z</atom:updated>
            <content:encoded><![CDATA[<h3>Craft — Home For My Notes</h3><p>I love writing. It can be literally anything and chances are I’d probably like the experience. I’m also a huge fan of productivity and productivity tools. And for the last 4 years I’ve been constantly switching note-taking apps until a year ago I found Craft and soon fell in love with it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AROOshsHQewpBuTUOPqzNw.jpeg" /><figcaption>Craft Documents Overview (source: <a href="https://www.google.com/url?sa=i&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DqD665WBmGhs&amp;psig=AOvVaw2qLVnAonBGDW4yFLy9236P&amp;ust=1675190588841000&amp;source=images&amp;cd=vfe&amp;ved=0CBEQjhxqFwoTCLCJvrH57_wCFQAAAAAdAAAAABAE">Introducing Craft for iPhone, iPad, and Mac</a>)</figcaption></figure><h4>Just a Tool</h4><p>In terms of the number of features, Notion is hands-down superior. I believe less is more, and whether you use Notion, Apple Notes, or Craft… they’re just tools. And let’s not get ahead of ourselves. These are tools you use to do amazing things.</p><h4>Perfect Balance</h4><p>I’ve used countless apps but for this article let’s focus on the main 3 — Apple Notes, Notion, and Craft. Unless you lived under a rock you heard of the first 2 of them, so where does Craft lie?</p><p>Apple Notes is the basic app everyone has on their phones, Notion is this all-in-one advanced tool, and Craft… well it is something in-between.</p><h4>Hiding Complexity</h4><p>Under the hood, Craft is quite complex as well, but thanks to its inviting UI and top-notch User Experience you won’t probably notice that. Beautiful design is a pleasure to look at, and honestly, as an amateur Software Developer I like nice things and this might have been why I chose Craft over everything I’ve ever seen.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bPRc3FlMs6CbYh_RwitUBg.png" /><figcaption>Craft (source: <a href="https://www.google.com/url?sa=i&amp;url=https%3A%2F%2Fwww.macstories.net%2Freviews%2Fcraft-review-a-powerful-native-notes-and-collaboration-app%2F&amp;psig=AOvVaw2qLVnAonBGDW4yFLy9236P&amp;ust=1675190588841000&amp;source=images&amp;cd=vfe&amp;ved=0CBAQjRxqFwoTCLCJvrH57_wCFQAAAAAdAAAAABAJ">MacRumors</a>)</figcaption></figure><h4>Versatility</h4><p>Like Notion, Craft can be used for virtually anything and everything at once. All documents can be categorized into folders and a powerful search will help you find everything you need in a few seconds.</p><p>In my case, I use it for personal note-taking, writing articles, coding, learning languages, and studying purposes. I keep there cooking recipes, and privacy policies for my published and unpublished apps. And somehow even though it all happens in the same app I have never once had any issues.</p><p>On top of that, a very cool feature is Daily Note. The idea is you open Craft every day and write down your tasks. Simple as that. But the integration with the rest of the app made it also my to-do app of choice in 2022.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BMtWGARA4jF5HorJfouL_Q.png" /><figcaption>Daily Note in Craft (source: <a href="https://www.google.com/url?sa=i&amp;url=https%3A%2F%2Fthesweetsetup.com%2Fa-beginners-guide-to-craft-daily-notes%2F&amp;psig=AOvVaw3aza1CXqEkcMbSHLguFlXy&amp;ust=1675190643764000&amp;source=images&amp;cd=vfe&amp;ved=0CBAQjRxqFwoTCODE1Mv57_wCFQAAAAAdAAAAABAE">Sweet Setup</a>)</figcaption></figure><h4>What doesn’t work</h4><p>In my opinion, I noticed Craft kind of lacks features or just isn’t suitable for project management tasks, which I prefer to carry out on paper or in Notion. Also on the iPad handwriting experience isn’t great. Besides these two the app has been perfect for me, and I’m not planning on switching any time soon.</p><h4>Conclusion</h4><p>If you seek an app that’s simple yet not as basic as Apple Notes, Craft is the app for you. It perfectly balances complexity by keeping the design clean and elegant. Plus, the writing experience is just next level!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5e25d351a669" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>