<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Stewart&apos;s Blog | CreaTECH Solutions</title>
    <link>https://www.createchsol.com/blog.html</link>
    <description>I don’t blog very often, but that could change.  Stay tuned.</description>
    <language>en-us</language>
    <lastBuildDate>Tue, 28 Apr 2026 19:18:32 +0000</lastBuildDate>
    <atom:link xmlns:atom="http://www.w3.org/2005/Atom" href="https://www.createchsol.com/feed.xml" rel="self" type="application/rss+xml" />
  <item>
    <title>Appearance Mode Changer</title>
    <link>https://www.createchsol.com/blog/2026-04-28-appearance-mode-changer.html</link>
    <guid isPermaLink="true">https://www.createchsol.com/blog/2026-04-28-appearance-mode-changer.html</guid>
    <pubDate>Tue, 28 Apr 2026 19:18:32 +0000</pubDate>
    <description>Creating an Appearance Mode Picker in SwiftUI</description>
    <content:encoded><![CDATA[<h1>Creating an Appearance Mode Picker in SwiftUI</h1>
<p>Many apps give users a simple choice for how the interface should look:</p>
<ul><li>Follow the system appearance</li><li>Always use light mode</li><li>Always use dark mode</li></ul>
<p>SwiftUI makes this surprisingly easy. The trick is to model the choice once, save it with <code>@AppStorage</code>, and apply it at the root of your app with <code>preferredColorScheme</code>.</p>
<p>In this post, we will build an appearance mode setting that can be reused in almost any SwiftUI application.</p>
<h2>The Goal</h2>
<p>We want the user to choose an appearance mode from inside the app. Once selected, the app should update immediately and remember that choice the next time it launches.</p>
<p>The finished behavior looks like this:</p>
<ul><li><code>System</code> follows the device's current light or dark mode setting.</li><li><code>Light</code> forces the app to use light mode.</li><li><code>Dark</code> forces the app to use dark mode.</li><li>The selected value persists across launches.</li><li>Navigation views and modal sheets inherit the selected appearance.</li></ul>
<h2>Create the Appearance Mode Type</h2>
<p>Start by creating an enum to represent the three supported modes.</p>
<pre class="code-block" data-language="SWIFT"><code class="language-swift"><span class="code-keyword">import</span> <span class="code-type">SwiftUI</span>

<span class="code-keyword">enum</span> <span class="code-type">AppearanceMode</span><span class="code-punctuation">:</span> <span class="code-type">String</span><span class="code-punctuation">,</span> <span class="code-type">CaseIterable</span><span class="code-punctuation">,</span> <span class="code-type">Identifiable</span> <span class="code-punctuation">{</span>
    <span class="code-keyword">case</span> system
    <span class="code-keyword">case</span> light
    <span class="code-keyword">case</span> dark

    <span class="code-keyword">var</span> id<span class="code-punctuation">:</span> <span class="code-type">Self</span> <span class="code-punctuation">{</span> <span class="code-keyword">self</span> <span class="code-punctuation">}</span>

    <span class="code-keyword">var</span> title<span class="code-punctuation">:</span> <span class="code-type">String</span> <span class="code-punctuation">{</span>
        <span class="code-keyword">switch</span> <span class="code-keyword">self</span> <span class="code-punctuation">{</span>
        <span class="code-keyword">case</span> <span class="code-property">.system</span><span class="code-punctuation">:</span>
            <span class="code-string">"System"</span>
        <span class="code-keyword">case</span> <span class="code-property">.light</span><span class="code-punctuation">:</span>
            <span class="code-string">"Light"</span>
        <span class="code-keyword">case</span> <span class="code-property">.dark</span><span class="code-punctuation">:</span>
            <span class="code-string">"Dark"</span>
        <span class="code-punctuation">}</span>
    <span class="code-punctuation">}</span>

    <span class="code-keyword">var</span> iconName<span class="code-punctuation">:</span> <span class="code-type">String</span> <span class="code-punctuation">{</span>
        <span class="code-keyword">switch</span> <span class="code-keyword">self</span> <span class="code-punctuation">{</span>
        <span class="code-keyword">case</span> <span class="code-property">.system</span><span class="code-punctuation">:</span>
            <span class="code-string">"sun.righthalf.filled"</span>
        <span class="code-keyword">case</span> <span class="code-property">.light</span><span class="code-punctuation">:</span>
            <span class="code-string">"sun.max.fill"</span>
        <span class="code-keyword">case</span> <span class="code-property">.dark</span><span class="code-punctuation">:</span>
            <span class="code-string">"moon.fill"</span>
        <span class="code-punctuation">}</span>
    <span class="code-punctuation">}</span>

    <span class="code-keyword">var</span> colorScheme<span class="code-punctuation">:</span> <span class="code-type">ColorScheme</span><span class="code-punctuation">?</span> <span class="code-punctuation">{</span>
        <span class="code-keyword">switch</span> <span class="code-keyword">self</span> <span class="code-punctuation">{</span>
        <span class="code-keyword">case</span> <span class="code-property">.system</span><span class="code-punctuation">:</span>
            <span class="code-keyword">nil</span>
        <span class="code-keyword">case</span> <span class="code-property">.light</span><span class="code-punctuation">:</span>
            <span class="code-property">.light</span>
        <span class="code-keyword">case</span> <span class="code-property">.dark</span><span class="code-punctuation">:</span>
            <span class="code-property">.dark</span>
        <span class="code-punctuation">}</span>
    <span class="code-punctuation">}</span>
<span class="code-punctuation">}</span></code></pre>
<p>There are a few useful details here.</p>
<p><code>CaseIterable</code> lets us build a picker by looping over all cases. <code>Identifiable</code> makes the enum convenient to use in <code>ForEach</code>. The <code>title</code> and <code>iconName</code> properties keep display details close to the option they describe.</p>
<p>The most important property is <code>colorScheme</code>.</p>
<pre class="code-block" data-language="SWIFT"><code class="language-swift"><span class="code-keyword">var</span> colorScheme<span class="code-punctuation">:</span> <span class="code-type">ColorScheme</span><span class="code-punctuation">?</span> <span class="code-punctuation">{</span>
    <span class="code-keyword">switch</span> <span class="code-keyword">self</span> <span class="code-punctuation">{</span>
    <span class="code-keyword">case</span> <span class="code-property">.system</span><span class="code-punctuation">:</span>
        <span class="code-keyword">nil</span>
    <span class="code-keyword">case</span> <span class="code-property">.light</span><span class="code-punctuation">:</span>
        <span class="code-property">.light</span>
    <span class="code-keyword">case</span> <span class="code-property">.dark</span><span class="code-punctuation">:</span>
        <span class="code-property">.dark</span>
    <span class="code-punctuation">}</span>
<span class="code-punctuation">}</span></code></pre>
<p><code>preferredColorScheme</code> accepts an optional <code>ColorScheme</code>. Passing <code>.light</code> or <code>.dark</code> forces that appearance. Passing <code>nil</code> tells SwiftUI not to override the system appearance.</p>
<p>That makes <code>nil</code> the perfect value for the <code>System</code> option.</p>
<h2>Save the Selection with AppStorage</h2>
<p>Because <code>AppearanceMode</code> is backed by a <code>String</code> raw value, it works well with <code>@AppStorage</code>.</p>
<pre class="code-block" data-language="SWIFT"><code class="language-swift"><span class="code-attribute">@AppStorage</span><span class="code-punctuation">(</span><span class="code-string">"appearanceMode"</span><span class="code-punctuation">)</span> <span class="code-keyword">private</span> <span class="code-keyword">var</span> appearanceMode <span class="code-punctuation">=</span> <span class="code-type">AppearanceMode</span><span class="code-property">.system</span></code></pre>
<p>This stores the user's choice in <code>UserDefaults</code>. When the value changes, SwiftUI updates any view that depends on it. When the app launches again, the saved value is restored automatically.</p>
<p>You can use any key name you like, but keep it stable once your app ships. Changing the key later would make existing users lose their saved appearance preference.</p>
<h2>Apply the Appearance at the App Root</h2>
<p>The appearance mode should be applied as high in the view hierarchy as possible. In most apps, that means the <code>App</code> entry point.</p>
<pre class="code-block" data-language="SWIFT"><code class="language-swift"><span class="code-keyword">import</span> <span class="code-type">SwiftUI</span>

<span class="code-attribute">@main</span>
<span class="code-keyword">struct</span> <span class="code-type">MyApp</span><span class="code-punctuation">:</span> <span class="code-type">App</span> <span class="code-punctuation">{</span>
    <span class="code-attribute">@AppStorage</span><span class="code-punctuation">(</span><span class="code-string">"appearanceMode"</span><span class="code-punctuation">)</span> <span class="code-keyword">private</span> <span class="code-keyword">var</span> appearanceMode <span class="code-punctuation">=</span> <span class="code-type">AppearanceMode</span><span class="code-property">.system</span>

    <span class="code-keyword">var</span> body<span class="code-punctuation">:</span> <span class="code-keyword">some</span> <span class="code-type">Scene</span> <span class="code-punctuation">{</span>
        <span class="code-type">WindowGroup</span> <span class="code-punctuation">{</span>
            <span class="code-type">ContentView</span><span class="code-punctuation">(</span><span class="code-punctuation">)</span>
                <span class="code-property">.preferredColorScheme</span><span class="code-punctuation">(</span>appearanceMode<span class="code-property">.colorScheme</span><span class="code-punctuation">)</span>
        <span class="code-punctuation">}</span>
    <span class="code-punctuation">}</span>
<span class="code-punctuation">}</span></code></pre>
<p>Applying <code>preferredColorScheme</code> here means the choice affects the whole app, including pushed navigation destinations and presented sheets.</p>
<p>If you apply the modifier deeper in the hierarchy, you may end up with only part of the interface changing appearance. That can be useful for special cases, but for a user-facing app setting, the root is usually the right place.</p>
<h2>Add a Picker to the Interface</h2>
<p>Now you need a way for the user to change the value.</p>
<p>Here is a simple toolbar menu:</p>
<pre class="code-block" data-language="SWIFT"><code class="language-swift"><span class="code-keyword">struct</span> <span class="code-type">ContentView</span><span class="code-punctuation">:</span> <span class="code-type">View</span> <span class="code-punctuation">{</span>
    <span class="code-attribute">@AppStorage</span><span class="code-punctuation">(</span><span class="code-string">"appearanceMode"</span><span class="code-punctuation">)</span> <span class="code-keyword">private</span> <span class="code-keyword">var</span> appearanceMode <span class="code-punctuation">=</span> <span class="code-type">AppearanceMode</span><span class="code-property">.system</span>

    <span class="code-keyword">var</span> body<span class="code-punctuation">:</span> <span class="code-keyword">some</span> <span class="code-type">View</span> <span class="code-punctuation">{</span>
        <span class="code-type">NavigationStack</span> <span class="code-punctuation">{</span>
            <span class="code-type">VStack</span> <span class="code-punctuation">{</span>
                <span class="code-type">Image</span><span class="code-punctuation">(</span>systemName<span class="code-punctuation">:</span> <span class="code-string">"globe"</span><span class="code-punctuation">)</span>
                    <span class="code-property">.imageScale</span><span class="code-punctuation">(</span><span class="code-property">.large</span><span class="code-punctuation">)</span>
                    <span class="code-property">.foregroundStyle</span><span class="code-punctuation">(</span><span class="code-property">.tint</span><span class="code-punctuation">)</span>

                <span class="code-type">Text</span><span class="code-punctuation">(</span><span class="code-string">"Hello, world!"</span><span class="code-punctuation">)</span>
            <span class="code-punctuation">}</span>
            <span class="code-property">.padding</span><span class="code-punctuation">(</span><span class="code-punctuation">)</span>
            <span class="code-property">.navigationTitle</span><span class="code-punctuation">(</span><span class="code-string">"Mode Changer"</span><span class="code-punctuation">)</span>
            <span class="code-property">.toolbar</span> <span class="code-punctuation">{</span>
                <span class="code-type">ToolbarItem</span><span class="code-punctuation">(</span>placement<span class="code-punctuation">:</span> <span class="code-property">.bottomBar</span><span class="code-punctuation">)</span> <span class="code-punctuation">{</span>
                    <span class="code-type">Menu</span> <span class="code-punctuation">{</span>
                        <span class="code-type">Picker</span><span class="code-punctuation">(</span><span class="code-string">"Appearance"</span><span class="code-punctuation">,</span> selection<span class="code-punctuation">:</span> $appearanceMode<span class="code-punctuation">)</span> <span class="code-punctuation">{</span>
                            <span class="code-type">ForEach</span><span class="code-punctuation">(</span><span class="code-type">AppearanceMode</span><span class="code-property">.allCases</span><span class="code-punctuation">)</span> <span class="code-punctuation">{</span> mode <span class="code-keyword">in</span>
                                <span class="code-type">Label</span><span class="code-punctuation">(</span>mode<span class="code-property">.title</span><span class="code-punctuation">,</span> systemImage<span class="code-punctuation">:</span> mode<span class="code-property">.iconName</span><span class="code-punctuation">)</span>
                                    <span class="code-property">.tag</span><span class="code-punctuation">(</span>mode<span class="code-punctuation">)</span>
                            <span class="code-punctuation">}</span>
                        <span class="code-punctuation">}</span>
                    <span class="code-punctuation">}</span> label<span class="code-punctuation">:</span> <span class="code-punctuation">{</span>
                        <span class="code-type">Image</span><span class="code-punctuation">(</span>systemName<span class="code-punctuation">:</span> appearanceMode<span class="code-property">.iconName</span><span class="code-punctuation">)</span>
                    <span class="code-punctuation">}</span>
                <span class="code-punctuation">}</span>
            <span class="code-punctuation">}</span>
        <span class="code-punctuation">}</span>
    <span class="code-punctuation">}</span>
<span class="code-punctuation">}</span></code></pre>
<p>The picker writes directly to the same <code>@AppStorage</code> value. As soon as the selection changes, the app-level <code>preferredColorScheme</code> receives the new value and the interface updates.</p>
<h2>Why Use a Menu?</h2>
<p>A menu works well when the setting is available from a toolbar. It keeps the interface tidy while still making the choice easy to reach.</p>
<p>For a settings screen, you may prefer a segmented picker or a list row:</p>
<pre class="code-block" data-language="SWIFT"><code class="language-swift"><span class="code-type">Picker</span><span class="code-punctuation">(</span><span class="code-string">"Appearance"</span><span class="code-punctuation">,</span> selection<span class="code-punctuation">:</span> $appearanceMode<span class="code-punctuation">)</span> <span class="code-punctuation">{</span>
    <span class="code-type">ForEach</span><span class="code-punctuation">(</span><span class="code-type">AppearanceMode</span><span class="code-property">.allCases</span><span class="code-punctuation">)</span> <span class="code-punctuation">{</span> mode <span class="code-keyword">in</span>
        <span class="code-type">Label</span><span class="code-punctuation">(</span>mode<span class="code-property">.title</span><span class="code-punctuation">,</span> systemImage<span class="code-punctuation">:</span> mode<span class="code-property">.iconName</span><span class="code-punctuation">)</span>
            <span class="code-property">.tag</span><span class="code-punctuation">(</span>mode<span class="code-punctuation">)</span>
    <span class="code-punctuation">}</span>
<span class="code-punctuation">}</span></code></pre>
<p>The important part is not the picker style. The important part is that every picker binds to the same stored <code>appearanceMode</code> value.</p>
<h2>Make Sure Sheets and Navigation Inherit the Mode</h2>
<p>Because the color scheme is applied at the app root, child views do not need to know anything about the appearance setting.</p>
<pre class="code-block" data-language="SWIFT"><code class="language-swift"><span class="code-keyword">struct</span> <span class="code-type">NavigationStackView</span><span class="code-punctuation">:</span> <span class="code-type">View</span> <span class="code-punctuation">{</span>
    <span class="code-keyword">var</span> body<span class="code-punctuation">:</span> <span class="code-keyword">some</span> <span class="code-type">View</span> <span class="code-punctuation">{</span>
        <span class="code-type">VStack</span> <span class="code-punctuation">{</span>
            <span class="code-type">Text</span><span class="code-punctuation">(</span><span class="code-string">"This view was pushed onto the navigation stack."</span><span class="code-punctuation">)</span>
            <span class="code-type">Image</span><span class="code-punctuation">(</span>systemName<span class="code-punctuation">:</span> <span class="code-string">"star.fill"</span><span class="code-punctuation">)</span>
        <span class="code-punctuation">}</span>
        <span class="code-property">.font</span><span class="code-punctuation">(</span><span class="code-property">.largeTitle</span><span class="code-punctuation">)</span>
        <span class="code-property">.padding</span><span class="code-punctuation">(</span><span class="code-punctuation">)</span>
        <span class="code-property">.navigationTitle</span><span class="code-punctuation">(</span><span class="code-string">"Pushed View"</span><span class="code-punctuation">)</span>
    <span class="code-punctuation">}</span>
<span class="code-punctuation">}</span></code></pre>
<p>Presented sheets also inherit the selected mode:</p>
<pre class="code-block" data-language="SWIFT"><code class="language-swift"><span class="code-keyword">struct</span> <span class="code-type">ModalSheetView</span><span class="code-punctuation">:</span> <span class="code-type">View</span> <span class="code-punctuation">{</span>
    <span class="code-attribute">@Environment</span><span class="code-punctuation">(</span>\<span class="code-property">.dismiss</span><span class="code-punctuation">)</span> <span class="code-keyword">private</span> <span class="code-keyword">var</span> dismiss

    <span class="code-keyword">var</span> body<span class="code-punctuation">:</span> <span class="code-keyword">some</span> <span class="code-type">View</span> <span class="code-punctuation">{</span>
        <span class="code-type">NavigationStack</span> <span class="code-punctuation">{</span>
            <span class="code-type">VStack</span> <span class="code-punctuation">{</span>
                <span class="code-type">Text</span><span class="code-punctuation">(</span><span class="code-string">"Presented as a modal sheet."</span><span class="code-punctuation">)</span>
                <span class="code-type">Image</span><span class="code-punctuation">(</span>systemName<span class="code-punctuation">:</span> <span class="code-string">"apple.logo"</span><span class="code-punctuation">)</span>
            <span class="code-punctuation">}</span>
            <span class="code-property">.font</span><span class="code-punctuation">(</span><span class="code-property">.largeTitle</span><span class="code-punctuation">)</span>
            <span class="code-property">.padding</span><span class="code-punctuation">(</span><span class="code-punctuation">)</span>
            <span class="code-property">.toolbar</span> <span class="code-punctuation">{</span>
                <span class="code-type">Button</span><span class="code-punctuation">(</span>role<span class="code-punctuation">:</span> <span class="code-property">.close</span><span class="code-punctuation">)</span> <span class="code-punctuation">{</span>
                    dismiss<span class="code-punctuation">(</span><span class="code-punctuation">)</span>
                <span class="code-punctuation">}</span>
            <span class="code-punctuation">}</span>
        <span class="code-punctuation">}</span>
    <span class="code-punctuation">}</span>
<span class="code-punctuation">}</span></code></pre>
<p>These views stay clean because appearance mode is an app-level concern, not a screen-level concern.</p>
<h2>The Complete Pattern</h2>
<p>The reusable pattern is:</p>
<ol><li>Create an <code>AppearanceMode</code> enum.</li><li>Store the selected value with <code>@AppStorage</code>.</li><li>Apply <code>preferredColorScheme</code> at the <code>App</code> root.</li><li>Bind any picker or settings control to the same stored value.</li></ol>
<p>Once those pieces are in place, you can expose the setting anywhere in your app.</p>
<h2>Final Thoughts</h2>
<p>Appearance mode is a small feature, but it is one of those details that makes an app feel more considerate. Some users want their apps to follow the system. Others want a specific app to stay light or dark regardless of the device setting.</p>
<p>With SwiftUI, supporting all three options only takes a small enum, a stored preference, and one app-wide modifier.</p>
<p>That is a tidy little feature with a lot of user-facing polish.</p>
<p>If you want to download the code for a sample project using this technique you can find on my GitHub.</p>
<p><a href="https://github.com/StewartLynch/ModeChanger" target="_blank" rel="noreferrer">ModeChanger</a></p>]]></content:encoded>
  </item>
  <item>
    <title>Building my Own Static Website Builder</title>
    <link>https://www.createchsol.com/blog/2026-04-20-building-my-own-static-website-builder.html</link>
    <guid isPermaLink="true">https://www.createchsol.com/blog/2026-04-20-building-my-own-static-website-builder.html</guid>
    <pubDate>Mon, 20 Apr 2026 20:36:06 +0000</pubDate>
    <description>I have been wanting a better way to maintain the CreaTECH Solutions website.</description>
    <content:encoded><![CDATA[<p>I have been wanting a better way to maintain the CreaTECH Solutions website.</p>
<p>The site itself is not especially unusual. It has an About page, a YouTube tutorials page, an app portfolio, and a blog. But keeping those pages up to date can become surprisingly repetitive. Each new video needs a thumbnail, a title, a release date, and a link. Each app needs artwork, a description, a source, a download URL, and sometimes a separate app page. Blog entries need images and markdown content. Even simple site-wide details like the logo, header, footer, and social links need to stay consistent across every page.</p>
<p>So instead of continuing to hand-edit web pages, I decided to build a macOS app that could become the content management system for my own site.</p>
<p>The result is CTSiteBuilder, a SwiftUI app that lets me manage the website content in one place and export the whole thing as a static website.</p>
<h3>This site you are viewing this blog on was generated with this application.</h3>
<h2>Starting With the Content</h2>
<p>The first step was not the UI. It was understanding the shape of the website.</p>
<p>I listed the pages I needed:</p>
<ul><li>About</li><li>YouTube Tutorials</li><li>App Portfolio</li><li>Blog</li></ul>
<p>Then I broke those pages down into the actual pieces of data they needed. The About page needed a title, an about paragraph, a banner image, social links, a Ko-fi callout, and a newsletter callout. The Tutorials page needed playlist information, current video information, thumbnails, YouTube links, release dates, and a way to mark whether a video had been released. The Portfolio page needed app records grouped by source, such as App Store, Gumroad, and GitHub. The Blog page needed entries with dates, images, titles, and markdown content.</p>
<p>That became the foundation of the app. Rather than thinking of the site as a collection of HTML files, I treated it as structured data.</p>
<h2>Using SQLiteData as the Backbone</h2>
<p>For persistence I used SQLiteData. Each major part of the site has its own table-backed model: site configuration, about page, social links, tutorials page, playlists, videos, portfolio page, portfolio apps, blog page, and blog entries.</p>
<p>This gave the project a clean separation:</p>
<ul><li>SwiftUI is responsible for editing the content.</li><li>SQLiteData is responsible for storing it.</li><li>The exporter is responsible for turning it into web pages.</li></ul>
<p>That separation mattered. Once the data model was in place, it became much easier to add features without rethinking the entire app. For example, adding newsletter fields to the About page or adding a released flag to videos was handled as a database migration, not a rewrite.</p>
<p>The app also supports CloudKit synchronization through SQLiteData, so the content can be synced through iCloud. I added a manual "Force iCloud Resynchronization" action for the point where the CloudKit schema has been deployed and I want to make sure local website records are pushed again.</p>
<h2>Building the Editor</h2>
<p>The main app UI uses a SwiftUI <code>NavigationSplitView</code> with sections for Site Settings, About Page, YouTube Tutorials, App Portfolio, Blog, and Export.</p>
<p>Each section is an editor for a specific part of the website. Site Settings handles shared identity: the site name, logo, live URL, test URL, header title, header subtitle, and footer copyright. The page editors handle their own page title, page name, markdown content, images, and related child records.</p>
<p>The child records were important. Social links, playlists, videos, portfolio apps, and blog entries all need full create, edit, and delete support. Some also need ordering, so I added sort order support and drag-to-reorder behavior where it makes sense.</p>
<p>Images are stored with the records as data. The app supports choosing an image through a file picker or dragging an image directly into the editor. That may sound small, but it makes the app feel much more like a tool I would actually want to use.</p>
<h2>Markdown In, HTML Out</h2>
<p>I wanted writing content to remain comfortable, especially for about text and blog entries. Markdown was the obvious choice.</p>
<p>The app lets me write longer text in markdown, then converts it to HTML during export. That means I can keep the editing experience lightweight while still producing proper web pages. Blog entries become individual detail pages, and the blog index shows excerpts with "Read More" links.</p>
<p>This also kept the website flexible. I can write content naturally in the app without thinking too much about the final HTML structure.</p>
<h2>Exporting a Static Website</h2>
<p>The export process was the major turning point.</p>
<p>All the site data lives in SQLite, but the final output is a static website. When I click export, CTSiteBuilder creates the HTML files, writes image assets into an <code>images</code> folder, creates individual blog detail pages in a <code>blog</code> folder, and copies the shared stylesheet.</p>
<p>The exported pages are generated from templates bundled with the app. There are separate templates for About, Tutorials, Portfolio, Blog, and Blog Entry pages, plus a shared CSS file.</p>
<p>That template approach was intentional. I did not want the design to be buried entirely in Swift code. By keeping HTML templates and CSS as export assets, I can adjust the design of the website without changing the underlying database or editor logic.</p>
<p>The exported site supports:</p>
<ul><li>A shared header and footer.</li><li>Site logo and name on every page.</li><li>Navigation generated from the page records.</li><li>Responsive layout for mobile screens.</li><li>Light and dark mode styling.</li><li>Markdown-rendered content.</li><li>Image export for logos, thumbnails, banners, blog images, and social icons.</li></ul>
<p>The Tutorials page also exports an interactive video browser. Released videos are listed in reverse date order, and selecting a video displays the thumbnail, details, and an embedded player. The Portfolio page groups apps by source. The Blog page generates both the listing page and separate pages for each entry.</p>
<h2>Deployment Workflow</h2>
<p>Once the static site export was working, the next obvious step was deployment.</p>
<p>The app includes an export section where I can choose an export folder, generate the site, and then deploy it. I built in rsync support because it is a good fit for static websites: it transfers only changed files and works over SSH.</p>
<p>I also added dry-run support. That way I can preview what rsync would change before uploading anything. For safety, the rsync deployment avoids deleting unrelated files at the root of the remote site. It only prunes generated files inside the <code>blog</code> and <code>images</code> folders.</p>
<p>There is also FTP deployment support, including a remote manifest so previously generated files can be removed when they no longer exist locally. But rsync is the workflow I expect to lean on most.</p>
<h2>What I Like About This Approach</h2>
<p>The thing I like most is that the app is tailored to the actual website.</p>
<p>It is not a generic website builder. It does not try to solve every possible publishing problem. It solves my publishing problem: maintaining the CreaTECH Solutions website, keeping tutorials and apps current, writing blog posts in markdown, and exporting a clean static site when I am ready.</p>
<p>That made the project much more approachable. I could start with a concrete list of pages and fields, build a database around that structure, add editing screens, and then focus on generating exactly the website I needed.</p>
<p>It also shows why building small custom tools can be so satisfying. A lot of the time, the best app is not the most universal one. It is the one that understands your workflow.</p>
<h2>The Feature Set Today</h2>
<p>CTSiteBuilder now includes:</p>
<ul><li>Site-wide settings for branding, URLs, header text, and footer text.</li><li>Editors for About, Tutorials, Portfolio, and Blog pages.</li><li>CRUD support for social links, playlists, videos, portfolio apps, and blog entries.</li><li>Image import through file picking and drag and drop.</li><li>Markdown support for longer content.</li><li>SQLiteData persistence with database migrations.</li><li>CloudKit synchronization through SQLiteData.</li><li>Static HTML export from bundled templates.</li><li>Responsive CSS with light and dark mode support.</li><li>Generated image assets and blog detail pages.</li><li>FTP deployment support.</li><li>Rsync deployment with dry-run mode and selective pruning.</li></ul>
<h2>Final Thoughts</h2>
<p>This project started with a simple question: what would make updating my website easier?</p>
<p>The answer was not another admin panel or a full web CMS. It was a native Mac app built around the exact content I publish.</p>
<p>That is what I enjoy about this kind of project. SwiftUI gives me a fast way to build the editing experience, SQLiteData gives me a structured and syncable data layer, and a static export keeps the final website simple, fast, and easy to host.</p>
<p>The end result is both a tool and a workflow. I can manage the content locally, sync it through iCloud, export the finished pages, test the result, and deploy it when ready.</p>
<p>For me, that is the sweet spot: a custom app that removes friction from a real recurring task.</p>]]></content:encoded>
  </item>
  <item>
    <title>Can I call myself a YouTuber?</title>
    <link>https://www.createchsol.com/blog/2020-08-31-can-i-call-myself-a-youtuber.html</link>
    <guid isPermaLink="true">https://www.createchsol.com/blog/2020-08-31-can-i-call-myself-a-youtuber.html</guid>
    <pubDate>Mon, 31 Aug 2020 20:55:36 +0000</pubDate>
    <description>Some time ago I wrote a blog post titled &quot;Can I call myself a Software Developer&quot; where I mused about my path to becoming a software developer over the course of over 45 years. The jury is still out on that question but...</description>
    <content:encoded><![CDATA[<p>Some time ago I wrote a blog post titled "Can I call myself a Software Developer" where I mused about my path to becoming a software developer over the course of over 45 years. The jury is still out on that question but I have not stopped in my quest. The difference is that I really think that I am a far better developer two years later than I was back then. The difference? I became a YouTuber (I think). More on this in just a minute.</p>
<p>In June of 2019 I as interviewed by Sean Allen for his "Origin Stories" theme on his "iOS Dev Discussion" podcast. He felt, after reading my blog that I had a story to share so I jumped at the chance to build my network of fellow developers with the exposure the podcast would bring.</p>
<h4>Self Taught vs Self Directed</h4>
<p>I don't say that I am self taught. I say that I am a self directed learner because I don't believe anyone can teach themselves to be an iOS developer. We all learn from others whether that be through some kind of structured degree program or boot camp or by watching videos, reading other blog posts and asking questions on Twitter, a Facebook group or heaven forbid, Stack Overflow. </p>
<h4>Building A Network</h4>
<p>To be a self directed learner you have to be able to build your network of fellow developers or learners; preferably both. You learn from others who know more from you, but just as valuable is learning by sharing what you know with others who are at the same level or still catching up to you.</p>
<p>After Sean's interview, my network started to grow. I also decided through Patreon and GitHub to support some of the people I respect the most for sharing their content for free. Two key people for me at that time were Paul Hudson of Hacking With Swift and Mark Moeykens of Big Mountain Studio. Both offer quality content and at the time, everything they did was free. Both now offer other paid services like Paul's Hacking With Swift Plus and Mark's exceptional SwiftUI resources, which I am happy to support and pay for. You get what you pay for. I am only mentioning Sean, Paul and Mark here because they, unbeknownst to them, played a big part in my progress to become a YouTuber. There are too many to mention here and there are lots of bloggers and YouTubers who deserve credit, but that is not the point of this blog.</p>
<h4>In the beginning...</h4>
<p>It seems that my interview with Sean struck a chord with some people as my Twitter feed started to grow. Some people reached out to me to say that they are able to identify with my path and encouraged me to keep going. Last year, 2019, before Christmas, Paul Hudson started his 100 Days of SwiftUI. I had been learning SwiftUI from the start but was lacking a structured learning path so I started right at Day 1, committed to work through it to the end. I tweeted about my progress as did many others. One of my followers reached out to me who too was working through the course and had some questions and wondered if I could help him out. His name is Chris and we developed a bit of a rapport. I had more experience than he had and I found that I was able to offer him some suggestions. I have been at this a long time and made a lot of mistakes along the way. I have a background in education and so I know a thing or two about learning and teaching. The one thing I pride myself in is making sure that I do my homework. I want to make sure that the information that I am offering is accurate, structured, applicable and easy to understand. </p>
<p>I hesitate to say it, but I believe that I was a mentor to Chris, It made me feel good. But more than that, it made me analyze how I did things and I realized that in the past, I was not always adhering to best practice. </p>
<p>In my previous company, I was responsible for creating short product videos for YouTube that would show our users how to take advantage of certain features of the product. I created over 100 different videos for that channel so I had some experience with YouTube. That company's product also had a tool that allowed our customers to use a proprietary language based on BASIC to create additional tools that could be used to enhance the functionality of the product. I developed courses to teach those clients how to code those tools. I traveled across North America and parts of Europe teaching 3 day courses on the topic. Later on, the product evolved to a web based tool with an API so I transformed my focus into building web based tools. I continued, after leaving the company to consult and teach coding classes related to that product. Well, the product is basically grandfathered now and the consulting and demand for my services dried up. In my retirement, I needed something to keep my brain active and I realized that my self worth relied a lot on the positive feedback I was getting from my teaching. So, on Jan 12, 2020, I revised my only personal YouTube channel to start focusing on tutorials about coding in Swift.</p>
<h4>My YouTube Channel</h4>
<p>I wanted to focus on Swift and SwiftUI Fundamentals. In one of my earlier posts, I categorized the videos as being based on a line from an old song by Rod Stewart and the Faces. "I wish I knew what I know now, when I was younger." If only I knew what I know now about clean code when I first started out, then I would be a better coder now. I look at some of my old projects now and the code is atrocious. If I wanted to implement something I would watch a video, read a few blog posts and copy and paste someone else's code and tweak it until I got the results I wanted. Needless to say, there is no common architecture between apps and even within a single app there might be several different ones.</p>
<p>What I found was that when I was preparing for a video, I had to do a lot of research and make sure that what I wanted to cover followed best practices. This, more than anything has made me a much better developer. There is nothing better than to put yourself out there for the world to see and to comment or criticize on. It seems to work. With over 75 videos on my channel now, I have not yet had a negative review and the feedback has been really encouraging.</p>
<p>My channel is not really for beginners, nor it is for experienced developers though I think there is something there for those categories too. My focus is on people like me. People who have left the starting gate in their coding experience but constantly need reinforcement and review to stay on top of things and improve their skills in their quest to land a software development position.</p>
<p>My channel videos are focussed around 4 different themes. Focussed Playlists, Swift/SwiftUI Fundamentals, Techniques and Developer Tools. </p>
<p>I am guided by the following principals:</p>
<ul><li>Research carefully the topic I am about to present.</li><li>Don't ramble — stay on topic.</li><li>Provide practical examples.</li><li>Only publish quality videos that I am proud of.</li><li>Always credit other developers if I borrow and share something that they have published.</li><li>Publish at least one video a week every Sunday</li></ul>
<p>You will never see my face on a video. I don't think there is any need for a talking head in a YouTube video about coding. You may have a different opinion, but the focus is on the code, not me. Besides, I am afraid that if some of my younger audience actually saw how old I am they would be turned off and dismissive. There is definitely a prejudice with respect to the older generation and a failure to recognize that we have something to offer after all. </p>
<h4>The process</h4>
<p>With those principals in mind, this is how I go about creating my videos.</p>
<p>I start each new video idea by mulling the topic over in my mind and thinking about the best way to present the topic. I use Raindrop.io to gather resources. I often am thinking about many ideas at the same time. There is no fixed amount of time allocated to this task.&lt;br&gt;Once I have decide what the topic is for this week, I try to come up with a sample project or playground that I can work through during the video recording. This will go through many iterations until I am satisfied. Next, I loosely script out the step by step process for creating the project so that I can follow it when recording. Once the script is complete, I use Camtasia to record myself without audio. When the recording is complete, I create a Keynote presentation that I can use as an introduction and export it as a .mov and import it in as the beginning of the video. I then go through the video, editing out any false moves and corrections. The next step is to actually script the entire video, word for word. That's correct. I watch my own videos and write a transcript. When that is done, I use Audacity to record my voice over. I then edit the audio file by removing mistakes, unwanted noises and I normalize the volume. When I am satisfied, I export it as an MP3 and import it in to Camtasia. This is where the fun begins. I have to match the audio with the video. Sometimes I have to extend a frame and on other occasions I have to speed up the video. I seldom edit the audio once I have imported it. I know enough about how I recorded the video that I know when to pause and slow down during the recording of my voice over script. The final video production stage is to add annotations, call outs and zoom and transition effects so that it is clear what I am talking about and what I want the viewer to focus on. The final step is to upload to YouTube and set a date that I want to release it. When it comes to the release date, I post on Twitter, a number of iOS related YouTube groups, Reddit and LinkedIn and update my web site to point to the video. As you can see it is a lengthy, well thought out process and that is why I limit myself to one a week. It is not unusual for it to take 3 days to produce a 20 - 30 minute video. There are some downsides to this of course. My style is not for everyone. I have not had anyone tell me that the content is invalid, but I have had some comments about the lack of spontaneity and enthusiasm in my presentation style.</p>
<h4>How Am I Doing?</h4>
<p>I posted my first video on Jan 12, 2020 on creating and using a Swift Package with Swift Package Manager. The feedback I was positive and encouraging but not many viewers. Still, I hung in there. By May, I had reached 500 subscribers. By July I hit 1000 and by November I was at 2000 subscribers. I am averaging somewhere between 200 - 250 new subscribers a month now. Today, as I write this blog I have reached the YouTube threshold of a minimum 4000 watch hours over a 12 month period that entitles me to add ads to my videos. I am still undecided here in that regard. It would be nice to make some revenue after all, but I don't believe I have the numbers to really make anything worthwhile.</p>
<p>I am aways surprised by what content gets the most feedback. Some topics I am really excited about turn out to be not very interesting to others. One video though that really took off was one where I took a concept that I saw Paul Hudson do on his HWS+ feed that converts an SVG to a UIBezier Path in SwiftUI and animates it. I extended that thought in a video. Another time, Mark Moeykens mentioned my name and channel in one of his videos and that resulted in a number of new subscribers. Now it is mostly word of mouth and the fact that I now have over 70 videos in the channel and that they are more discoverable in a Google search.</p>
<p>I once put out a survey asking what kind of videos people wanted to see based on my categories of Fundamentals, Techniques and software development tools. The lowest was for videos on software development tools, yet by far the two most viewed videos I have the ones about Raidrop.io and Typora. I guess I appeal to more than one segment of the YouTube population.</p>
<p>Anyway, that is where I am today. Can I call myself a YouTuber? I think probably yes, but I am still looking to increase my viewing audience as I believe that there are many people who would benefit from what I have to offer. You can find my channel at <a href="https://youtube.com/StewartLynch" target="_blank" rel="noreferrer">https://youTube.com/StewartLynch</a>and feel free to reach out to me on Twitter @StewartLynch. You can also visit my web site at <a href="https://www.createchsol.com/" target="_blank" rel="noreferrer">https://www.createchsol.com</a> to find out about my apps and other things about me. I would love to hear from you and get your feedback on how I am doing.</p>]]></content:encoded>
  </item>
  <item>
    <title>Can I call myself a Software Developer?</title>
    <link>https://www.createchsol.com/blog/2020-08-06-can-i-call-myself-a-software-developer.html</link>
    <guid isPermaLink="true">https://www.createchsol.com/blog/2020-08-06-can-i-call-myself-a-software-developer.html</guid>
    <pubDate>Thu, 06 Aug 2020 20:56:15 +0000</pubDate>
    <description>This is a question I struggle to answer. Am I worthy? I have been involved in IT for most of my working life, but my interest preceded that. You see, I am a bit of a dinosaur. I started university in 1969 when there was...</description>
    <content:encoded><![CDATA[<p>This is a question I struggle to answer. Am I worthy? I have been involved in IT for most of my working life, but my interest preceded that. You see, I am a bit of a dinosaur. I started university in 1969 when there was no computer science department. Computer programming was part of the Math Department and I was studying for my BSc with a major in mathematics. I took every computer based course I could and learned how to code in FORTRAN and PL1 using punch cards and submitting my programs for batch processing on the IBM 360. It was frustrating and I certainly wouldn't call myself a programmer after that experience.</p>
<p>I ended up becoming a Math teacher and in 1980, an Apple II+ was introduced to my classroom. I started coding in BASIC, writing all sorts of little utilities that would help me explain concepts and give students a chance to build and review their skills. Still, I wasn't a 'software developer'. By 1986, computers were all throughout the school district but most teachers had no real idea what to do with them. I was seconded from the classroom to work with teachers and students to integrate the use of computers into the classroom. That ended my teaching career. For the next 20 years (with the exception of 3 years in hell as a High School Vice Principal), I worked my way up the chain in two different school districts, leaving in 2007 as the Director of Technology, responsible for IT in both the education and corporate sectors of one of the larger school districts in the Vancouver area.</p>
<p>During my career as an IT director, we purchased a very efficient and extremely flexible email/collaboration system that governed the way we communicated and shared information in our District. The software was flexible and customizable and allowed for the development of add-on tools that could enhance and extend the functionality of the product. I was in heaven. I got to develop all sorts of tools that allowed me to streamline the system administration and communicate with district SQL databases to give secure, real-time access to district data sources. Programming was a break from the endless, tedious meetings and I became quite competent. The district started marketing the tools I created and made a substantial profit from sales.</p>
<p>I began to establish a relationship with the software company and got certified as a trainer so I could share my skills with others all over the US and Canada. This brought in more money for the District. I became a very vocal supporter and evangelist of sorts for the software and eventually, they made me an offer that I couldn't refuse.</p>
<p>Not retiring, recareering!&lt;br&gt;I took the early retirement and went to work for this software company. But….. not as a 'software developer'. I was a "Senior Services Engineer". I did a fair amount of training, but also was able to continue developing tools as value-added products for our customers. Still…. it was made very clear to me that I was not a 'software developer'. I was a hacker. I was undisciplined and could never contribute to the core product. I was basically self taught and had a lot of very bad habits. If any of the real developers looked at my code, that would be very evident. Well, as often happens in the private sector, the division downsized and I was forced into another retirement.</p>
<p>A new beginning&lt;br&gt;Now what was I do do? This was 2014. What else happened in 2014? Swift, the new programming languge for iOS development was released. This fuelled my passion and set me in a new direction that consumes me to this day.</p>
<p>Like a lot of people, I started taking Udemy courses on how to develop apps for the iPhone using Swift. This got me started and I was able to launch my first app to the app store shortly afterwards, but in hindsight, I am not conviced that this was the best way to go.</p>
<p>A false start&lt;br&gt;The instructors advertised taking you from nowhere to being a full fledged iOS developer. Sure, I was able to create and publish an app on the App Store, but could I really call myself a software developer? These courses showed you how to create and utilize a lot of the APIs but lacked explanations. They did not follow best practice nor use recognized design patterns. My method for developing apps was basically copying code from someone else and substituting in my own values to fit my needs. I asked a lot of questions on Stack Overflow and accepted the abuse for asking questions that had already been answered. I lacked the ability to even frame a question correctly it seems. There was very little understanding of what I was actually doing. It worked, but when I had to go back to those apps a couple of years later to update and enhance, I had a really difficult time understanding and replicating what I did. There was a lot of redundant code and work arounds to get things working.</p>
<p>There is no easy route to being a software developer&lt;br&gt;That is when I decided that I needed to be more methodical in my learning and go back to square one. I wasn't able to attend any boot camps so I decided to invest my money in purchasing highly recommended resources that could teach me the right way to code and to be able to develop software that could stand the test of time. This also meant dedicating a lot of time reading, following tutorials and practising what I learned.</p>
<p>An ongoing adventure&lt;br&gt;My adventure continues. I have just published my 9th iPhone app. Three of these apps have Apple Watch companion applications. I learned how to use Swift to develop Mac apps and one of my iPhone apps has a Mac version on the Mac app store, I also studied Kotlin and recreated my first iPhone app as an Android app and published it on the Google Play Store.</p>
<p>Recommended Resources&lt;br&gt;So who did I rely on to reroute my learning? There are lots of good resources out there, but there are several that I would like to acknowledge as giving me ongoing inspiration and encouragement.</p>
<p>Ray Wendrerlich (https://www.raywenderlich.com/, @rwenderlich)&lt;br&gt;The resources and instructors available at raywenderlich.com are outstanding. I have purchased just about every book they have published and am an annual video subscriber. I look forward each week to see what new tutorials are published. I started with the Swift and iOS Apprentice books which provided me with a solid grounding in best practices.</p>
<p>Hacking with Swift (https://www.hackingwithswift.com/, @twoStraws)&lt;br&gt;Paul Hudson is a machine. Every one of his publications is so well laid out and presented, that I have to discipline myself to continue through to the end of each publication for fear of missing some key point. Again, I have purchased almost all of his books and it is just a matter of time before I purchase the remaining ones. He is a Swift Conference rock star and his presentation style is without compare.</p>
<p>YouTubers&lt;br&gt;There are a lot of YouTube series available as well that deserve mention. More than I have space for. There are three in particular that I would like to point out.</p>
<p>Mark Moeykens - Big Mountain Studio @bigmtnstudio&lt;br&gt;Mark has a special way of presenting material and is inspiring. He has a sense of design that appeals to me and if you look at my more recent apps you will definitely see his influence. I have appreciated his contributions so much that I have become a patron of his on Patreon.</p>
<p>Sean Allen - @seanallen_dev&lt;br&gt;Sean is another inspiring developer. He went from no programming experience to a full time developer in a very short period of time after enrolling in a developer Boot Camp. He shares his experience on his YouTube Channel and his weekly news updates on everything Swift. He has combined with Paul Hudson to present the Swift Over Coffee podcast. Sean is a breath of fresh air. He is not judgemental and always willing to offer assistance.</p>
<p>Code With Chris - @CodeWithChris&lt;br&gt;Chris is a long time YouTuber with an excellent set of YouTube videos. He also has a great FaceBook page where people can ask and answer questions. Perfect for the developer who is just starting out.</p>
<p>Twitter&lt;br&gt;Twitter is how I start my day. I have created my own twitter list of 70 individuals who live and breath iOS/Swift development. Every day the latest news and resources are highlighted and referenced in tweets. It is a fantastic resource. In addition to the above twitter accounts that I have already referenced, there are many more worth following. Here are just a few of the best: @swiftbysundell, @hanning_thomas, @DigitalLeaves, @objcio, @NatashaTheRobot, @johnsundell, @abargh, @ayabibagib, @mennenia, @davedelong, @harlanhaskinsm @subdigital, @erucasadun, @bendodson</p>
<p>Newsletters&lt;br&gt;I subscribe to several newsletters that publish weekly Swift updates and links to resources. Here at just a few of them</p>
<p>IOS Dev Weekly&lt;br&gt;Indie iOS Focus Weekly&lt;br&gt;Swift Developments&lt;br&gt;Use Your Loaf&lt;br&gt;Swift Weekly&lt;br&gt;iOS Dev Nuggets&lt;br&gt;So. Am I a Software Developer?&lt;br&gt;The question I guess is still open. Does it really matter? I am 6X years old and still going strong. I have no desire to get a job as a software developer. My age is against me, though I think that I am as qualified now as a lot of junior developers. I don't need a job. I just need something to keep my brain working and provide me with a little extra income to pay for my vacations. Don't count us old folks out, but also don't for a minute think that just because we are senior citizens we don't have something to contribute.</p>
<p>I don't go to conferences or meetups because I have experienced age discrimination in the past and don't need the aggravation. I communicate with a lot of people via email or online chats. When my age is unknown, what I have to offer is given more attention and my questions are more likely to be considered.</p>
<p>Still, I feel incredibly blessed to be able to come up with an idea and create a tool that can be useful to me and others. It is a great community to be in. Maybe one day I will be a software developer.</p>]]></content:encoded>
  </item>
  </channel>
</rss>