<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Utku Y.</title>
    <description>The latest articles on DEV Community by Utku Y. (@uy).</description>
    <link>https://dev.to/uy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F437121%2Fa2840beb-0a77-45b8-b461-175dfbbfcfa8.png</url>
      <title>DEV Community: Utku Y.</title>
      <link>https://dev.to/uy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/uy"/>
    <language>en</language>
    <item>
      <title>I Built a macOS App to Automate the Most Boring Part of Shipping an App</title>
      <dc:creator>Utku Y.</dc:creator>
      <pubDate>Thu, 03 Sep 2026 22:01:52 +0000</pubDate>
      <link>https://dev.to/uy/i-built-a-macos-app-to-automate-the-most-boring-part-of-shipping-an-app-3a6h</link>
      <guid>https://dev.to/uy/i-built-a-macos-app-to-automate-the-most-boring-part-of-shipping-an-app-3a6h</guid>
      <description>&lt;p&gt;Shipping an app globally is exciting. Managing its App Store metadata in 28 languages is not.&lt;/p&gt;

&lt;p&gt;Every time I pushed a new version of my app, I had to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open App Store Connect in the browser&lt;/li&gt;
&lt;li&gt;Click through every single locale&lt;/li&gt;
&lt;li&gt;Copy-paste "What's New" text into each one&lt;/li&gt;
&lt;li&gt;Write the same description in English, then somehow handle German, Japanese, Arabic…&lt;/li&gt;
&lt;li&gt;Generate screenshots for 5 device sizes × 28 locales = 140 images&lt;/li&gt;
&lt;li&gt;Upload all of them manually&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'm one person. This was eating 4–6 hours per release. So I built a tool to fix it.&lt;/p&gt;

&lt;p&gt;What is Lookalization?&lt;/p&gt;

&lt;p&gt;Lookalization is a macOS app for indie developers that handles two things:&lt;/p&gt;

&lt;p&gt;• xcstrings management — browse, edit, and AI-translate all your in-app localization strings directly, without touching Xcode&lt;br&gt;
• App Store Connect metadata — edit descriptions, keywords, what's new, and promo text for every locale, all from one place&lt;/p&gt;

&lt;p&gt;Version 1.6 just shipped, and it adds the piece I was missing most: full App Store Connect integration with AI translation and automated screenshot upload.&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;p&gt;What's new in v1.6&lt;/p&gt;

&lt;p&gt;App Store Connect sidebar&lt;/p&gt;

&lt;p&gt;Connect once with your ASC API key (stored securely in Keychain). Your apps and versions load into the sidebar. Pick a version, and all your localizations are right there — editable, with live character counts and limit warnings.&lt;/p&gt;

&lt;p&gt;No more browser tabs. No more copy-pasting between windows.&lt;/p&gt;

&lt;p&gt;One-click AI translation&lt;/p&gt;

&lt;p&gt;Each metadata field (description, keywords, what's new, promotional text) has a ✨ button next to it. Press it, and Lookalization translates that field from your source locale to every other locale simultaneously — using Claude via AIProxy under the hood.&lt;/p&gt;

&lt;p&gt;It runs in parallel, so 28 locales take roughly the same time as 1.&lt;/p&gt;

&lt;p&gt;Screenshot generator + batch upload&lt;/p&gt;

&lt;p&gt;This is the part that took the most time to get right.&lt;/p&gt;

&lt;p&gt;The flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write a headline and subtitle in your language&lt;/li&gt;
&lt;li&gt;Pick a device size (iPhone 6.9", 6.5", iPad 13" Pro, etc.)&lt;/li&gt;
&lt;li&gt;Hit Generate &amp;amp; Upload All&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Lookalization then:&lt;br&gt;
• Translates the caption to all your ASC locales in one API call&lt;br&gt;
• Renders a PNG for each locale using ImageRenderer&lt;br&gt;
• Uploads each PNG to ASC via the screenshot upload API (reserve → chunk upload → MD5 commit)&lt;br&gt;
• Shows live progress: Translating… → Uploading 4/28&lt;/p&gt;

&lt;p&gt;What used to take an afternoon now takes about 90 seconds.&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;p&gt;The technical bits (for the curious)&lt;/p&gt;

&lt;p&gt;The screenshot upload to ASC is a 5-step pipeline that isn't well documented:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;fetchScreenshotSets(localizationId)   → find or create a set for the display type&lt;/li&gt;
&lt;li&gt;reserveScreenshot(setId, name, size)  → get upload operations + screenshot ID&lt;/li&gt;
&lt;li&gt;uploadScreenshotChunks(data, ops)     → PUT each chunk per the operations list&lt;/li&gt;
&lt;li&gt;commitScreenshot(id, md5)             → PATCH with uploaded: true + MD5 checksum&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The MD5 is computed with CryptoKit.Insecure.MD5 and sent as a hex string. Without it, the commit silently fails — took me a while to figure that out.&lt;/p&gt;

&lt;p&gt;For rendering, SwiftUI.ImageRenderer at @MainActor runs synchronously, which is exactly what you want when generating 28 images in a loop without any async confusion.&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;p&gt;Who it's for&lt;/p&gt;

&lt;p&gt;If you're a solo developer or small team shipping on the App Store with multiple language targets, the ROI on this tool kicks in within the first release.&lt;/p&gt;

&lt;p&gt;It's available on the Mac App Store, free to try with a one-time Pro unlock.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apps.apple.com/app/apple-store/id6748926206?pt=125700389&amp;amp;ct=devto&amp;amp;mt=8" rel="noopener noreferrer"&gt;Download Lookalization&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Would love any feedback — especially from devs who've built similar pipelines or have ASC API war stories.&lt;/p&gt;

</description>
      <category>indiedev</category>
      <category>appstore</category>
      <category>macos</category>
      <category>swift</category>
    </item>
    <item>
      <title>Turning SwiftUI Views into PDFs: A Quick How-To</title>
      <dc:creator>Utku Y.</dc:creator>
      <pubDate>Tue, 22 Apr 2025 19:46:37 +0000</pubDate>
      <link>https://dev.to/uy/turning-swiftui-views-into-pdfs-a-quick-how-to-388n</link>
      <guid>https://dev.to/uy/turning-swiftui-views-into-pdfs-a-quick-how-to-388n</guid>
      <description>&lt;p&gt;Need to generate a PDF from a SwiftUI view? Whether it's for sharing a report, creating a simple document, or any other purpose, SwiftUI's ImageRenderer offers a straightforward way to achieve this.&lt;/p&gt;

&lt;p&gt;Here's a handy function that takes a SwiftUI view (ViewForPDFOutput in this example) and returns the URL of the generated PDF file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;VStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"demo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toolbar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;buildShareButton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;buildShareButton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;ToolbarContent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;ToolbarItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;placement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;topBarTrailing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;ShareLink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getPdfUrl&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;systemName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"square.and.arrow.up"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;getPdfUrl&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;URL&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;renderer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ImageRenderer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ViewForPDFOutput&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;documentsDirectory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appending&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"swiftui_output.pdf"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;renderer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;box&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CGRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;guard&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;pdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CGContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;CFURL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;mediaBox&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beginPDFPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endPDFPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;closePDF&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: It's important to give width to view which you want to make pdf.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>swiftui</category>
      <category>swift</category>
      <category>ios</category>
      <category>pdf</category>
    </item>
    <item>
      <title>SwiftData: MigrationStage.custom</title>
      <dc:creator>Utku Y.</dc:creator>
      <pubDate>Thu, 26 Sep 2024 00:24:23 +0000</pubDate>
      <link>https://dev.to/uy/swiftdata-migrationstagecustom-138p</link>
      <guid>https://dev.to/uy/swiftdata-migrationstagecustom-138p</guid>
      <description>&lt;p&gt;Hi,&lt;/p&gt;

&lt;p&gt;I just wasted the whole day on &lt;code&gt;MigrationStage&lt;/code&gt;'s custom option.&lt;/p&gt;

&lt;p&gt;My first advice is: &lt;code&gt;Just care which document are you reading.&lt;/code&gt;. It can lead you in the wrong direction if document is missing, not totally correct, etc.&lt;/p&gt;

&lt;p&gt;My app has one model and I call it with Story.&lt;br&gt;
and it look likes this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="kt"&gt;StorySchema_Vx_x_x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;VersionedSchema&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;versionIdentifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;models&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;any&lt;/span&gt; &lt;span class="kt"&gt;PersistentModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Story&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;@Model&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;Story&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;story&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I have a new feature on app. However, due to the new design requirements I need to store &lt;code&gt;Story.story&lt;/code&gt; as below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;StoryPage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Codable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;story&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the problem: &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;em&gt;How should I store story on last database design?&lt;/em&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Replace &lt;code&gt;var story: String&lt;/code&gt; with &lt;code&gt;var storiesAsPart: [StoryPage]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Just add new parameter as &lt;code&gt;var storiesAsPart: [StoryPage]?&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  First approach
&lt;/h3&gt;

&lt;p&gt;Of course my first decision is first one! Because I think it's cleaner as code, easier to understand. There are no any other distracting variables. Bla, bla, bla...&lt;/p&gt;

&lt;p&gt;Ta-daa, That's why I wasted my whole day.&lt;/p&gt;

&lt;p&gt;I couldn't find proper way to achieve first option.&lt;br&gt;
Every development I tried, came with another issue...&lt;/p&gt;
&lt;h3&gt;
  
  
  Second approach
&lt;/h3&gt;

&lt;p&gt;I stopped to waste my time. Because this feature need to be release ASAP due to market-trending.&lt;/p&gt;

&lt;p&gt;But still got errors. WHY!?&lt;/p&gt;

&lt;p&gt;Because wrong usage of parameters...&lt;/p&gt;
&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;Do you remember what I wrote earlier?&lt;br&gt;
You have to check if it is correct; what you read, what you watch, what you listen...&lt;/p&gt;

&lt;p&gt;I just found a video(1) and realize how should I use &lt;code&gt;MigrationStage.custom&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's see how achieve to this migration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="kt"&gt;StoryMigrationPlan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;SchemaMigrationPlan&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;any&lt;/span&gt; &lt;span class="kt"&gt;VersionedSchema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="kt"&gt;StorySchema_Vprevious_one&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="kt"&gt;StorySchema_Vlatest_one&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;stages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;MigrationStage&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;migrate_Vprevious_one_TO_Vlatest_one&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;migrate_Vprevious_one_TO_Vlatest_one&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;MigrationStage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nv"&gt;fromVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;StorySchema_Vprevious_one&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;toVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;StorySchema_Vlatest_one&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;willMigrate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;didMigrate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;stories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;StorySchema_Vlatest_one&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Story&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

            &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;stories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;FetchDescriptor&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;StorySchema_Vlatest_one&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Story&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;fatalError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to fetch stories: &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="n"&gt;stories&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forEach&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;story&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
                &lt;span class="n"&gt;story&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;storiesAsPart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="kt"&gt;StoryPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;story&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;story&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;story&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;fatalError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to save migration: &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have two additional parameters compared to MigrationStage.lightweight: the willMigrate and didMigrate closures.&lt;/p&gt;

&lt;p&gt;I was already familiar with these closures, having read a lot about them, but as I mentioned before, &lt;em&gt;it's important to know if the documentation is correct&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;On that video (1), I got that the meaning of those closures.&lt;br&gt;
&lt;strong&gt;willMigrate&lt;/strong&gt;: Is commonly used for clean up data, prepare data to migrate etc.&lt;br&gt;
&lt;strong&gt;didMigrate&lt;/strong&gt;: Update new model variables as you wish.&lt;/p&gt;

&lt;p&gt;In my case, I used &lt;code&gt;didMigrate&lt;/code&gt; because I just wanted to fill new variable (because I know that's enough and it's okay).&lt;/p&gt;

&lt;h2&gt;
  
  
  Last words
&lt;/h2&gt;

&lt;p&gt;I didn't waste anymore time on this feature. So, that's why I chose the quicker solution from my perspective.&lt;/p&gt;

&lt;h3&gt;
  
  
  I still believe that it's possible to achieve the first approach.
&lt;/h3&gt;

</description>
      <category>ios</category>
      <category>swiftdata</category>
      <category>swiftui</category>
    </item>
    <item>
      <title>Need advice ~ Solo Dev. vs Design UI&amp;UX</title>
      <dc:creator>Utku Y.</dc:creator>
      <pubDate>Fri, 13 Oct 2023 19:22:13 +0000</pubDate>
      <link>https://dev.to/uy/need-advice-solo-dev-vs-design-uiux-mg3</link>
      <guid>https://dev.to/uy/need-advice-solo-dev-vs-design-uiux-mg3</guid>
      <description>&lt;p&gt;Hi guys,&lt;/p&gt;

&lt;p&gt;I'm just a solo developer and I have some ideas as side projects.&lt;br&gt;
Some of 'em should run with team, some of 'em can be done as solo. &lt;br&gt;
In anyway I need to design apps. Maybe as MVP I could go with only UX but I even need some advice about it.&lt;/p&gt;

&lt;p&gt;Here's the point of this post, how could I get over &lt;em&gt;design&lt;/em&gt; problem? I'm widely open for any tips.&lt;/p&gt;

</description>
      <category>solo</category>
      <category>developer</category>
      <category>ui</category>
      <category>ux</category>
    </item>
    <item>
      <title>XCode Code Snippet</title>
      <dc:creator>Utku Y.</dc:creator>
      <pubDate>Mon, 05 Sep 2022 20:32:10 +0000</pubDate>
      <link>https://dev.to/uy/xcode-code-snippet-56g4</link>
      <guid>https://dev.to/uy/xcode-code-snippet-56g4</guid>
      <description>&lt;p&gt;Hi friends,&lt;/p&gt;

&lt;p&gt;Here is a little trick for XCode beginners.&lt;/p&gt;

&lt;p&gt;Have you ever wonder how you can improve your coding speed?&lt;br&gt;
Here is a basic trick for speed up, code snippet.&lt;/p&gt;

&lt;p&gt;For example you are developing an app with SwiftUI, actually it doesn't matter SwiftUI, Swift, Obj-c, etc. You can even use snippets as creating comment blocks.&lt;/p&gt;

&lt;p&gt;Here are steps for creating snippets.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write block which will use as snippet
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;CodeSnippetDemo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, World!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;@ViewBuilder&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;#func&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;CodeSnippetDemo_Previews&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PreviewProvider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;previews&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;CodeSnippetDemo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfm0y7by4ngq34t03vuo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfm0y7by4ngq34t03vuo.png" alt="screen shot of above code block" width="704" height="806"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select whole block&lt;/li&gt;
&lt;li&gt;Then use menu as "Editor -&amp;gt; Create Code Snippet"&lt;/li&gt;
&lt;li&gt;You can change information as you want. Top block field is name of snippet. Below block is holding platform informations. Trick point is "Completion" field. Because when you type what you used in completion field, XCode will ask you to complete with "that" snippet.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy57m1vtu8k11yj0bsvdy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy57m1vtu8k11yj0bsvdy.png" alt="snippet creation view" width="800" height="580"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F26xxuzxipwqk6125jkho.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F26xxuzxipwqk6125jkho.png" alt="snippet completion example" width="762" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>swift</category>
      <category>xcode</category>
      <category>snippet</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
