https://khanwinter.com/Khan's BlogKhan's Online Web Log (aka: my blog)!2026-02-06T18:24:20.610ZKhan Winterhttps://bsky.app/profile/khanwinter.comCopyright © Khan Winter 2025https://khanwinter.com/2026-02-06-OpenType-UIFontUsing OpenType Attributes with UIFont2026-02-06T06:00:00.000ZFonts come with numerous style settings that are difficult to use in UIKit and AppKit. Learn how to determine which ones are available, and how to use them.<h2 id="opentype-features"><a href="#opentype-features" class="header-link">OpenType Features</a></h2><p>Modern fonts come with hidden attributes that modify their look in subtle ways. Some examples are making the <code>4</code> glyph have an open back, making the <code>6</code> and <code>9</code> glyphs have straight tails rather than curved, and many more.</p><p>On the web, you can activate these features with well-defined, documented, CSS modifiers. It's very easy to load in a font, say you want the alternate G glyph, and move on! Here's the very <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Fonts/OpenType_fonts">lengthy documentation</a> from Mozilla for that.</p><p>I've used these features a couple of times previously, once for replicating the beautiful typography that Xcode uses for its line number gutter. A while ago, for CodeEdit, we were stumped as to how Xcode was making their line number font have open fours, straight tailed sixes and nines, until we found the following in a different open-source project.</p><pre><code class="language-swift">func rulerFont() -> NSFont {
/// Set the open four
let alt4: [NSFontDescriptor.FeatureKey: Int] = [
.selectorIdentifier: kStylisticAltOneOnSelector,
.typeIdentifier: kStylisticAlternativesType
]
/// Set alternate styling for 6 and 9
let alt6and9: [NSFontDescriptor.FeatureKey: Int] = [
.selectorIdentifier: kStylisticAltTwoOnSelector,
.typeIdentifier: kStylisticAlternativesType
]
let features = [alt4, alt6and9]
let descriptor = font.fontDescriptor.addingAttributes([.featureSettings: features, .fixedAdvance: fontAdvance])
return NSFont(descriptor: descriptor, size: 0) ?? font
}
</code></pre><p>We were not OpenType experts, so this tipped us off that we were missing something. This led us down a rabbit hole of figuring out exactly what the font was, including what features, attributes, and sizes it had until we had it <em>perfect</em>.</p><blockquote><p>Eventually we were able to perfectly replicate the Xcode font, it's hilariously specific but pixel perfect, <a href="https://github.com/CodeEditApp/CodeEditSourceEditor/blob/main/Sources/CodeEditSourceEditor/Extensions/NSFont/NSFont%2BRulerFont.swift">check it out</a>.</p></blockquote><p>This still left me wondering how I could find what features a font supported, or how in the world you could find those seemingly random C identifiers.</p><h2 id="finding-which-features-a-font-supports"><a href="#finding-which-features-a-font-supports" class="header-link">Finding Which Features a Font Supports</a></h2><p>I'm now working on a version 2.0 of my budgeting app <a href="https://openbudget.us">OpenBudget</a>, and while designing in Sketch I found that to make some small currency labels legible, I wanted three specific OpenType features:</p><ul><li>Open Currency.</li><li>Open Fours.</li><li>Straight Six and Nine.</li></ul><p>Sketch handily lets you view all the available options for a font in a nice menu with labels and checkboxes. However, if you try and google "UIFont OpenType Open Four" you'll be sorely disappointed. I knew that CoreText had constants somewhere for each feature, but looking up the <a href="https://github.com/bruce0505/ios7frameworks/blob/master/CoreText/SFNTLayoutTypes.h">CoreText headers</a> yields a list of undocumented constants. As it turns out Apple's CoreType predates OpenType, so the identifiers don't match to what OpenType calls them and Apple has no documentation for what each of them do.</p><p>Eventually I stumbled upon the CoreText documentation for font features and found the following magical CoreText function.</p><pre><code class="language-swift">func CTFontCopyFeatures(_ font: CTFont) -> CTArray?
</code></pre><p>Every font in Cocoa stores a list of <em>every</em> supported feature in their features array. If you pass a <code>UIFont</code> or <code>NSFont</code> to this function (thank goodness for class bridging) you'll find a map like the following.</p><pre><code class="language-bash">{
CTFeatureTypeIdentifier = 35;
CTFeatureTypeName = "Alternative Stylistic Sets";
CTFeatureTypeSelectors = (
{
CTFeatureOpenTypeTag = ss01;
CTFeatureOpenTypeValue = 1;
CTFeatureSelectorIdentifier = 2;
CTFeatureSelectorName = "Straight-sided six and nine";
},
{
CTFeatureOpenTypeTag = ss02;
CTFeatureOpenTypeValue = 1;
CTFeatureSelectorIdentifier = 4;
CTFeatureSelectorName = "Open four";
},
{
CTFeatureOpenTypeTag = ss03;
CTFeatureOpenTypeValue = 1;
CTFeatureSelectorIdentifier = 6;
CTFeatureSelectorName = "Vertically centered colon";
},
{
CTFeatureOpenTypeTag = ss04;
CTFeatureOpenTypeValue = 1;
CTFeatureSelectorIdentifier = 8;
CTFeatureSelectorName = "Open currencies";
},
);
},
</code></pre><p>You may note here, that these labels indicate these constants are exactly what I'm looking for.</p><h2 id="mapping-selectors-to-identifiers-to-uifont"><a href="#mapping-selectors-to-identifiers-to-uifont" class="header-link">Mapping Selectors To Identifiers To UIFont</a></h2><p>Now, if you take the map you printed from your font and try to put it into a features array, you'll quickly realize that you don't have a whole lot of information. Here's how to read that map to find exactly what to put in your Swift code.</p><p>For creating a <code>UIFont</code> or <code>NSFont</code>, we need to create an array of attribute dictionaries. Here's what they look like.</p><pre><code class="language-swift">let attribute: [UIFontDescriptor.FeatureKey: Int] = [
.type: // The type of attribute we're adding
.selector: // What option we're enabling (or disabling) on the type.
]
</code></pre><blockquote><p>For <code>NSFont</code>, the type and selectors are named <code>selectorIdentifier</code> and <code>typeIdentifier</code>.</p></blockquote><h3 id="features-with-unnamed-constants"><a href="#features-with-unnamed-constants" class="header-link">Features With Unnamed Constants</a></h3><p>You'll see the top-level dictionary here has a <code>CTFeatureTypeIdentifier</code> key. This key maps to the "Feature types" enum from those <a href="https://github.com/bruce0505/ios7frameworks/blob/master/CoreText/SFNTLayoutTypes.h">CoreText headers</a> I referenced earlier. That's the <code>.type</code> we're looking for. To find the selector, we'll take a look at the <code>CTFeatureOpenTypeTag</code>. The <code>CTFeatureOpenTypeTag</code> (if it's available on your feature) maps to a constant we can provide to CoreText to indicate if we want the feature on or off. That constant looks like:</p><pre><code class="language-swift">kStylisticAlt[Number][On/Off]Selector
</code></pre><p>If we look at the map from earlier, to enable "Open currencies" we have the dictionary:</p><pre><code>{
CTFeatureOpenTypeTag = ss04;
CTFeatureOpenTypeValue = 1;
CTFeatureSelectorIdentifier = 8;
CTFeatureSelectorName = "Open currencies";
},
</code></pre><p>So we'll want the fourth selector, and we'll want it on. So we get the constant.</p><pre><code class="language-swift">kStylisticAltFourOnSelector
</code></pre><p>And we already determined the type from the parent (number 35) which maps to the <code>kStylisticAlternativesType</code> constant in the CoreText headers. Combining this gives us the resulting Swift dictionary.</p><pre><code class="language-swift">let altCurrency: [UIFontDescriptor.FeatureKey: Int] = [
.selector: kStylisticAltFourOnSelector,
.type: kStylisticAlternativesType
]
</code></pre><h3 id="features-with-named-constants"><a href="#features-with-named-constants" class="header-link">Features With Named Constants</a></h3><p>There's a second case here, you'll likely see other font features that don't have the number-based constants. Instead, those dictionaries will look like:</p><pre><code class="language-swift">{
CTFeatureTypeExclusive = 1;
CTFeatureTypeIdentifier = 37;
CTFeatureTypeName = "Lower Case";
CTFeatureTypeSelectors = (
{
CTFeatureSelectorDefault = 1;
CTFeatureSelectorIdentifier = 0;
CTFeatureSelectorName = Default;
},
{
CTFeatureOpenTypeTag = smcp;
CTFeatureOpenTypeValue = 1;
CTFeatureSelectorIdentifier = 1;
CTFeatureSelectorName = "Small Capitals";
}
);
},
</code></pre><p>For these, head to the CoreText headers, find your type constant (37 here, <code>kLowerCaseType</code>) and search the headers for that constant. The options here will have their own enum. This is the case for all options that have <em>set</em> standard options. For the previous example some fonts may have different features enabled by different numbers there. Here, the options are known beforehand.</p><pre><code class="language-c">/*
* Summary:
* Selectors for feature type kLowerCaseType
*/
enum {
kDefaultLowerCaseSelector = 0,
kLowerCaseSmallCapsSelector = 1,
kLowerCasePetiteCapsSelector = 2
};
</code></pre><h2 id="conclusion"><a href="#conclusion" class="header-link">Conclusion</a></h2><p>After printing the font features and doing this investigating, I now have the following extension for <code>UIFont</code>.</p><pre><code class="language-swift">import UIKit
extension UIFont {
public func currency() -> UIFont {
/// Set the alt currency
let altCurrency: [UIFontDescriptor.FeatureKey: Int] = [
.selector: kStylisticAltFourOnSelector,
.type: kStylisticAlternativesType
]
/// Set the open four
let alt4: [UIFontDescriptor.FeatureKey: Int] = [
.selector: kStylisticAltOneOnSelector,
.type: kStylisticAlternativesType
]
/// Set alternate styling for 6 and 9
let alt6and9: [UIFontDescriptor.FeatureKey: Int] = [
.selector: kStylisticAltTwoOnSelector,
.type: kStylisticAlternativesType
]
let newDescriptor = fontDescriptor.addingAttributes([
.featureSettings: [alt4, alt6and9, altCurrency]
])
return UIFont(descriptor: newDescriptor, size: pointSize)
}
}
</code></pre><p>Which gives me this beautiful typography for my currency labels.</p><img src="./typography.png" alt="typograph-screenshot"/><p>This is one of the many changes I'm working on for version two of OpenBudget. Modifying fonts slightly for labels like this that are common throughout my app is essential for making everything legible.</p><p>It's a little annoying that CoreText doesn't map nicely to OpenType feature names, but honestly it's understandable. I think there's an opportunity here for some lookup tool that could select a font, open it up, and list all the available features with a preview. But honestly, now that I know how to find those features, it's such a small one-time task that I don't really mind doing it by hand.</p>https://khanwinter.com/2025-03-29-Cross-Compiling-SwiftCross Compiling Swift2025-03-29T05:00:00.000ZThere are multiple ways to cross-compile Swift on different platforms. I explored a few methods while building a Discord bot and a Bluesky bot.<p>I recently did a few small projects in Swift, and I wanted to run each of them on my home server running Gentoo Linux. I decided to do each of them in Swift, not because I thought it'd be easiest, but because I was curious about the challenge of compiling Swift from my Mac to my Linux machine. I'd heard lots recently about new <a href="https://www.swift.org/documentation/articles/static-linux-getting-started.html">static Linux Swift SDKs</a> (and the Swift SDK API being introduced), and have always had an interest in <a href="https://vapor.codes/">Vapor</a></p><h2 id="link-embed-bot"><a href="#link-embed-bot" class="header-link">Link Embed Bot</a></h2><p>The first project was a small link embedding bot for Discord. Some websites don't support Discord's embed UI when sending their links. One such website is Instagram. My friends often send memes and such from Instagram, and we'd used the <a href="https://github.com/Wikidepia/InstaFix">ddinstagram</a> URL previously to embed those videos in Discord messages, but that URL has become flaky and often just refuses to embed anything (it's a volunteer-run service and I don't want to bash on it, it works about 70% of the time).</p><p>So my goal was to create a Discord bot that would run 24/7 with an open WebSocket connection. That could get a little more expensive than I was willing to pay if run on a cloud provider (I was hoping to pay nothing). The bot would download images too, so it needed some memory. Thankfully, my home server meets those requirements perfectly. It's on all the time and has 8GB of RAM.</p><p>I used the <a href="https://github.com/DiscordBM/DiscordBM">DiscordBM</a> package (the same one that powers Vapor's Penny bot!) to implement it, and I won't go into the details of reverse engineering Instagram's API to download images, reels, and videos from their server and upload them to Discord, but it is really cool if I do say so myself.</p><video controls width="90%" style="margin-bottom: 1.5em;">
<source src="linkembedbot.mp4" type="video/mp4">
Your browser does not support the video tag.
</video><p>Anyways, what I ended up with was a working Swift executable package. When executed, it started the connection with Discord and began processing events. The issue was, I wanted to run this on my <a href="https://www.gentoo.org/">Gentoo</a> machine, but I had developed it on my Mac. In comes the power of static Linux Swift SDKs!</p><h2 id="method-1-static-linux-swift-sdk"><a href="#method-1-static-linux-swift-sdk" class="header-link">Method 1: Static Linux Swift SDK</a></h2><p>Swift introduced static Linux Swift SDKs a while ago to help meet the need to compile Swift programs for a platform other than the one it's being compiled on. A Swift SDK contains the information Swift needs to know to create a <em>statically linked executable</em>. This executable contains the Swift runtime, standard libraries, even libraries the standard library depends on. This executable will run on any Linux machine once compiled, with no dependencies.</p><p>The downside of this method is that we cannot make use of the fact that most distros ship with dynamically linkable libraries. We have to statically link the binary, which causes the resulting binary to be quite large. This isn't the biggest deal, but isn't very user-friendly if you were looking to ship a user application. However, for a server-based app like mine, it really doesn't matter.</p><h3 id="step-1-install-the-opensource-toolchain"><a href="#step-1-install-the-opensource-toolchain" class="header-link">Step 1: Install the Open-Source Toolchain</a></h3><p>This part is very important. You need to use the open source Swift toolchain from <a href="https://www.swift.org/install/macos/">Swift.org</a>. This is different from the Swift toolchain that is installed with Xcode, and includes support for features like Embedded Swift and Swift SDKs. Installing an open source Swift version is really straightforwards with the new <a href="https://www.swift.org/install/macos/swiftly/">Swiftly</a> tool.</p><h4 id="using-swiftly"><a href="#using-swiftly" class="header-link">Using Swiftly</a></h4><p>Follow the instructions on Swift.org to <a href="https://www.swift.org/install/macos/swiftly/">install Swiftly</a> and the latest version of Swift.</p><h4 id="using-an-installer"><a href="#using-an-installer" class="header-link">Using an Installer</a></h4><p>Download the install package from Swift.org and download the Package Installer .pkg file. Open the installer and it'll install a new <code>.xctoolchain</code> Xcode toolchain file on your system.</p><blockquote><p>This tripped me up for a while. When you run <code>swift</code> after installing a toolchain using this method, you'll still be using Xcode's version of Swift. You'll have to run the specific toolchain you just installed using <code>xcrun</code> like: <code></code>`bash xcrun --toolchain swift swift build -c release <code></code>` This may be an issue with my machine and my path settings. Having used <code>swiftly</code> since, it seems to work much better. I'd highly recommend using <code>swiftly</code>!</p></blockquote><h3 id="step-2-install-the-swift-sdk"><a href="#step-2-install-the-swift-sdk" class="header-link">Step 2: Install the Swift SDK</a></h3><p>Swift makes downloading and installing the Swift SDK itself very easy. Grab the URL and checksum from Swift's install page.</p><pre><code class="language-bash">swift sdk install <URL-or-filename-here> [--checksum <checksum-for-archive-URL>]
</code></pre><p>You can list installed Swift SDKs with <code>swift sdk list</code> and see more options with <code>swift sdk --help</code>. It doesn't matter which toolchain (Xcode or open source) you use to install the SDK, they'll install to the same spot.</p><blockquote><p>Quick note, I've been made aware that there's two closely named concepts that are very different. <em>SDK</em>s are an Xcode/Clang concept, but <em>Swift SDK</em>s are what we're working with. I've gone back and edited this (as of Sept 9th, 2025) to make sure I've referenced the correct SDK concept!</p></blockquote><h3 id="step-3-compile-using-the-swift-sdk"><a href="#step-3-compile-using-the-swift-sdk" class="header-link">Step 3: Compile using the Swift SDK</a></h3><p>Now that everything is installed, compiling the package is <em>very</em> easy. Make sure you're using the open source toolchain by running it using <code>xcrun</code> (yeah, I know that sentence didn't make sense. If someone has a suggestion for how I'm doing this wrong, <em>please</em> DM me).</p><p>Just run <code>swift build</code> with the specified Swift SDK and in release mode for performance.</p><pre><code class="language-bash">xcrun --toolchain swift swift build --swift-sdk x86_64-swift-linux-musl -c release
</code></pre><h2 id="captain-its-wednesday"><a href="#captain-its-wednesday" class="header-link">Captain, It's Wednesday</a></h2><p>The second project I did was the <a href="https://captainitswednesday.com">captainitswednesday.com</a> bot on <a href="https://bsky.app/profile/captainitswednesday.com">Bluesky</a>. This little app was <strong>much</strong> simpler. I used the <a href="https://github.com/MasterJ93/ATProtoKit/tree/0.25.0">ATProtoKit</a> Swift library by <a href="https://bsky.app/profile/cjrriley.com">Christopher Riley</a> to send posts for the Bluesky account and set up a really simple cron job to post every Wednesday at 9 a.m. The issue I had with this project was actually a compiler crash when using the previously discussed static Linux Swift SDKs. So I realized I'd have to go back to the original method for cross-compiling Swift, <em>Docker</em>.</p><h2 id="method-2-docker-containers"><a href="#method-2-docker-containers" class="header-link">Method 2: Docker Containers</a></h2><p><a href="https://docker.com">Docker</a> is a really cool tool. I've been digging into it recently, and I've known for a while that the Vapor community has had to use Docker to compile their applications for Linux cloud hosts before Swift really started building out tooling for both cross-compilation and even Linux as a target platform. There is actually a guide on Swift's website for <a href="https://www.swift.org/documentation/server/guides/packaging.html">packaging Swift projects</a>, which mentions being able to compile to Linux. However, I found that I still needed some more information after reading it, so I'll outline the steps I took here.</p><p>The Docker cross-compilation method uses a Docker container to compile your Swift project in the Docker container, then copies the executable file out of the container for use. It might be easier to just ship the Docker container with the compiled executable, but for my use case (and I'm sure others), I didn't want to run Docker; I just wanted an executable to kick off.</p><blockquote><p>A note about architecture: Each command listed in this section will contain a <code>--platform</code> flag to indicate the target architecture. Each example will compile to <code>x86</code> (<code>amd64</code>), swap that out with <code>arm64</code> to target an ARM CPU.</p></blockquote><h3 id="step-1-download-the-target-docker-container"><a href="#step-1-download-the-target-docker-container" class="header-link">Step 1: Download the target Docker container</a></h3><p>The official Swift guide mentions a <code>swift:bionic</code> container that I think is obsolete. I'd suggest downloading the <code>swift:latest</code> package, which is an Ubuntu container with everything you need to use Swift pre-installed. There are other Linux flavors you could use if necessary (bookworm, AmazonLinux2, Fedora, etc.), but in most cases, you'll be compiling against the Linux standard libraries and system calls, which should stay the same between most distributions. For instance, compiling in the Ubuntu container worked fine for running on my Gentoo server.</p><pre><code class="language-bash">docker pull swift:latest --platform linux/amd64
</code></pre><h3 id="step-2-compile-amp-copy-out"><a href="#step-2-compile-amp-copy-out" class="header-link">Step 2: Compile & Copy Out</a></h3><p>Now that we have the Swift container, Docker makes it really easy to spin up a container. This command will mount the current directory in the <code>/workspace</code> folder in the container and set it as the working directory.</p><p>We'll then run some bash commands in the container to: compile the Swift package in Release mode, delete the contents of the <code>.build/install</code> directory, and copy the compiled output to the <code>.build/install</code> directory.</p><pre><code class="language-bash">docker run --platform linux/amd64 \
--rm \
-v "$PWD:/workspace" \
-w /workspace \
swift:latest \
/bin/bash -cl '\
swift build -c release --static-swift-stdlib && \
rm -rf .build/install && \
mkdir -p .build/install && \
cp -P .build/release/CaptainItsWednesday .build/install/'
</code></pre><blockquote><p>Note: Change the path of the <code>cp</code> step to match your target's name. In my case it was <code>CaptainItsWednesday</code>. You could also copy the entire <code>release</code> directory here if you need.</p></blockquote><h2 id="step-3-profit"><a href="#step-3-profit" class="header-link">Step 3: Profit</a></h2><p>I have this whole step in a script named <code>deploy.sh</code> that then copies the resulting executable into my home server using <code>scp</code></p><pre><code class="language-bash">scp .build/install/CaptainItsWednesday khan@homeserver.local:/home/khan/
</code></pre><p>Pretty nice to have around. If you want to verify the resulting executable's target platform you can run <code>file</code> against it to check.</p><pre><code class="language-bash">file .build/release/CaptainItsWednesday
> .build/install/CaptainItsWednesday: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, with debug_info, not stripped
</code></pre><p>Another tip is to add a stripping step to remove all unnecessary code from the final executable. That reduced my resulting executable from 100MB to 64MB. Still large, but a significant improvement. My final deploy script looks like this, including the copy to my home server and strip steps.</p><pre><code class="language-bash">docker run --platform linux/amd64 \
--rm \
-v "$PWD:/workspace" \
-w /workspace \
swift:latest \
/bin/bash -cl '\
swift build -c release --static-swift-stdlib && \
strip --strip-unneeded .build/release/CaptainItsWednesday && \
rm -rf .build/install && \
mkdir -p .build/install && \
cp -P .build/release/CaptainItsWednesday .build/install/' \
&& scp .build/install/CaptainItsWednesday khan@homeserver.local:/home/khan
</code></pre><p>And it works on a test run!</p><img src="./screenshot.webp" alt="Captain its Wednesday screenshot"/><h2 id="final-thoughts"><a href="#final-thoughts" class="header-link">Final Thoughts</a></h2><p>Swift's cross-compilation capabilities have come a long way from only compiling using Docker. It's nice having these instructions down somewhere even as it continues to get better, and I expect I'll continue amending this post as the tooling gets better. Shoutout to the new Swiftly tool too, I got to try that out while I was writing this up and it's a huge improvement.</p><p>It's very exciting to see the Swift devs continue to hammer away at Linux and cross-compilation. I think my only remaining pain point is the confusion surrounding the toolchain, SDKs, and why (even using Swiftly) you can only run the open-source toolchain using <code>xcrun</code>.</p><p>I continue to have a blast programming in Swift. The language feels 'right' in a way other languages have not. I'm extremely grateful to be a part of this community and I'm excited for Swift's tooling to continue to improve. It's a long way from where it was when I started using Swift back in Swift 4. It made both of these afternoon projects super fun and exciting.</p>https://khanwinter.com/2024-11-02-New-BlogNew Blog, New Me2024-11-03T05:00:00.000ZI redesigned my blog, in Swift.<h2 id="why-restart"><a href="#why-restart" class="header-link">Why Restart?</a></h2><p>Previously, my blog was published using the popular <a href="https://jekyllrb.com/">Jekyll</a> static site generator. While Jekyll worked well for a long time, it has became a serious pain to maintain.</p><p>I often have long periods of time where I don't post. Ideally when I come back after a year of not posting I'd like my tools to work just as well as they did previously. Jekyll did not meet this requirement well. With Ruby increasingly losing popularity and Gems requiring some serious finagling, I often had to completely reinstall the entire tool and all dependencies every time I wanted to write a new post. This was not working well.</p><p>Therefore, my new system had the following requirements:</p><ul><li><strong>No or few outside dependencies.</strong> Whatever dependencies are used need to be well known to me and well known what they do.</li><li><strong>Familiar tools.</strong> I need whatever this is built on to be entirely familiar to me. At this point (2024) this likely means <strong>Swift and JavaScript</strong>.</li><li><strong>Static HTML.</strong> I dislike overuse of JS libraries. This means no TailwindCSS, no React, only CSS, JS and HTML.</li></ul><h2 id="existing-tools"><a href="#existing-tools" class="header-link">Existing Tools</a></h2><p>If you're familiar with the Swift ecosystem, you may have heard of the tool <a href="https://github.com/JohnSundell/Publish?tab=readme-ov-file">Publish</a> by <a href="https://www.swiftbysundell.com/">John Sundell</a>. This looks like it was going to be a perfect tool! It is actually a really nice tool. It's well built, entirely Swift, and fairly extendable. However, while it likely works for a lot of developers and blogs, mine needed some customizations that the library did not support.</p><p>Publish's markdown rendering engine <a href="https://github.com/JohnSundell/Ink/tree/master">Ink</a> though, turned out to be perfect. It did require some modification to support LaTeX, but it's very fast and I was very surprised to find that it didn't rely on any C libraries like cmark. This ended up being one of two dependencies I allowed and I'm very thankful for the work John has done on this package.</p><h2 id="swift-result-builders"><a href="#swift-result-builders" class="header-link">Swift Result Builders</a></h2><p>Swift seems the obvious choice for building my own blog. I've been in love with the language for years now, and it continues to improve. One of the features that I've been eyeing for a long time but hadn't been able to dig into myself is <a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/advancedoperators/#Result-Builders">Result Builders</a>.</p><p>Using result builders for HTML has been well documented, and I think it's perfect for this job. My goal was to make some Swift syntax that can compile into HTML. Ideally it should look kind of like SwiftUI, and it shouldn't be a hinderance to maintaining or building the website itself.</p><p>The core of my website is the following protocol:</p><pre><code class="language-swift">protocol Component {
associatedtype Body: Component
@HTMLBuilder
var body: Self.Body { get }
var html: String { get }
}
extension Component {
var html: String { body.html }
}
</code></pre><p>This allows me to define components that can optionally provide HTML contents. Similar to SwiftUI, I defined a <code>TupleComponent</code> for the result builder to use. This one is fun because it uses Swift's new parameter packs to take multiple types of components in the initializer. I did have to use a <code>[any Component]</code> in one spot, and I'd very much like to not have to do that but I'll revisit that in the future.</p><pre><code class="language-swift">struct TupleComponent: Component {
init<each Content>(_ content: repeat each Content) where repeat each Content: Component {
func buildHTML<T: Component>(_ item: T, html: inout String) {
if T.self != EmptyComponent.self {
html += item.html
}
}
var value = ""
repeat buildHTML(each content, html: &value)
self.html = value
}
init(_ components: [any Component]) {
self.html = components.filter { type(of: $0) != EmptyComponent.self }.map { $0.html }.joined()
}
var body: some Component {
fatalError("")
}
var html: String
}
</code></pre><p>Also similar to SwiftUI, some components need to not have their children evaluated. In the previous example, <code>TupleComponent</code>'s <code>body</code> compiles to <code>Never</code> but still produces HTML using it's children. To get around this I used Swift's typing system to extend <code>Never</code> to be a component. While I was at it I made an <code>EmptyComponent</code> similar to SwiftUI's <code>EmptyView</code> to help myself avoid <code>fatalError</code>s.</p><pre><code class="language-swift">extension Never: Component {
var body: some Component { self }
var html: String { "" }
}
struct EmptyComponent: Component {
var body: some Component { fatalError() }
var html: String { fatalError() }
}
</code></pre><p>This component type made simple HTML tags easy to make Swift-y syntax for, and allowed for nesting content easily.</p><pre><code class="language-swift">struct A<Content: Component>: Component {
let url: String
let content: () -> Content
init(_ url: String, @HTMLBuilder content: @escaping () -> Content) {
self.url = url
self.content = content
}
var body: some Component {
content()
}
var html: String {
return "<a href=\"\(url.path()\">" + content().html + "</a>"
}
}
</code></pre><p>Then, because I was using a protocol as my base type, I was able to extend some standard lib types for my HTML builder. One key thing is Strings. Again, I used that <code>Never</code> extension to make this possible, as Strings won't ever have HTML children to evaluate.</p><pre><code class="language-swift">extension String: Component {
var body: some Component { fatalError() }
var html: String { self }
}
</code></pre><p>Finally, using that I was able to define a result builder that combines components and allows for the syntax I was aiming for.</p><pre><code class="language-swift">@resultBuilder
enum HTMLBuilder {
static func buildBlock<each Content>(_ content: repeat each Content) -> TupleComponent where repeat each Content: Component {
TupleComponent(repeat each content)
}
static func buildOptional(_ component: TupleComponent?) -> TupleComponent {
if let component {
component
} else {
TupleComponent([EmptyComponent()])
}
}
static func buildEither<Content: Component>(first component: Content) -> Content {
component
}
static func buildEither<Content: Component>(second component: Content) -> Content {
component
}
static func buildArray(_ components: [any Component]) -> TupleComponent {
TupleComponent(components)
}
}
</code></pre><p>To actually generate the HTML document I just added a small function that uses the <code>.html</code> property on the <code>Component</code> type. This also adds a few common sense things for an HTML document like the language, HTML5 support, etc.</p><pre><code class="language-swift">enum HTMLRenderer {
static func render<Content: Component>(@HTMLBuilder page: () -> Content) -> String {
"<!DOCTYPE html><html lang=\"en\" data-theme=\"light\">" + page().html + "</html>"
}
}
</code></pre><p>This is slightly different from other methods I've seen around. The primary difference is the use of a root protocol type for each component, rather than inheritance from a class. I also wanted a system very similar to SwiftUI, in that there is no <code>children</code> array on any components. Instead, each component is a distinct element in the hierarchy of the page.</p><h2 id="but-why"><a href="#but-why" class="header-link">But Why?</a></h2><p>A good question to ask is is this effort worth it? I certainly think so. This resulted in a syntax that lets me express the HTML content of the webpage, while mixing in Swift logic that is never exposed to the browser.</p><p>For example, after a little more work the entire home page is represented using just this component.</p><pre><code class="language-swift">struct HomePage: Component {
var body: some Component {
let posts = /* Get All Posts */
Page(title: "Posts", description: "Khan Winter's Blog Posts", path: "index.html", loadCodeStyles: false) {
Tag("div", ["class": "home"]) {
Tag("div", ["class": "home-header"]) {
Img(resourceName: "avatar-large.webp", alt: "Avatar", size: (128, 128))
P { "Khan's Online Web Log (aka: my blog)!" }
}
Tag("ul") {
for (post, path) in posts {
let url = path.deletingPathExtension().appendingPathExtension("html").path()
Tag("li") {
A(url) {
P { post.createdAt.formatted() }
H(3) { post.title }
P { post.excerpt }
}
}
}
}
}
}
}
}
</code></pre><p>The largest benefit being able to use logic like for loops, variables, and branches directly in the page's code.</p><pre><code class="language-swift">for (post, path) in posts {
// ...
}
</code></pre><p>To me, this is very appealing. This is written in raw Swift, meaning as long as I have a Swift compiler on my machine it'll work. It also generates static HTML, with dynamic structure like loops and branches performed without JavaScript or an ugly syntax like Liquid. It meets the last goal I set for myself by not having any unecessary dependencies (Ink is pure Swift, <em>doesn't even need Foundation</em>).</p><p>So yeah, for my use case and my blog it was very worth the effort. On top of that, I got to explore Result Builders! You can check out the entire site on <a href="https://github.com/thecoolwinter">my GitHub</a> if you'd like to check out more.</p><h2 id="embunemdling"><a href="#embunemdling" class="header-link"><em>Bun</em>dling</a></h2><p>The last thing I wanted was to minify my website's assets. There's a few lines of JS and CSS for highlighting code, displaying Math, etc. I've liked <a href="https://esbuild.github.io/">esbuild</a> in the past so I decided to run esbuild on each file using <a href="https://bun.sh/">Bun</a> (instead of NodeJS). The speed Bun provided is awesome. It's so fast that the script I made to minify all the JS and CSS files was moved to be in the main loop when generating the site rather than something that happens only when I deploy. Serious props to the team at Bun. Figured I'd share that while I was at it.</p><h2 id="cloudflare"><a href="#cloudflare" class="header-link">CloudFlare</a></h2><p>I'm not only moving domains and site generators, but also my hosting provider. I've used Github pages and Netlify in the past, and while I don't have any issue with them in particular they have some rough edges a few years in. Netlify is nice, but they've clearly adjusted their target audience to larger businesses. Most of the features they provide just don't apply to a little site like mine (beyond the CDN). Github Pages is pretty much perfect, but not transparent enough for my liking. I prefer to know when my content will be live after deploying it.</p><p>I initially was going to just host my static files in R2, CloudFlare's block storage offering. While setting that up I noticed they offer a <a href="https://pages.cloudflare.com/">static site hosting</a> which I'm trying out. So far, I've loved it. My site's download speed is faster than either Github or Netlify. They offer unlimited bandwidth, and DNS setup with them was faster than any other provider I've worked with. I don't usually give shoutouts for things like this but CloudFlare has been awesome to work with so far.</p><h2 id="last-thing--page-preloading"><a href="#last-thing--page-preloading" class="header-link">Last Thing - Page Preloading</a></h2><p>After all this work, all of the HTML, JS, and CSS for my site (not including images and videos) is only 0.33MB on disk uncompressed (<strong>97KB compressed</strong> using ZSTD, CloudFlare's default compression mechanism). Each new page is roughly 18KB uncompressed, which is nothing to download for most internet connections. Well, HTML has this nice feature that allows you to <a href="https://developer.mozilla.org/en-US/docs/Web/Performance/Speculative_loading#link_reldns-prefetch">pre-fetch pages for navigation</a>. Inspired in part by this <a href="https://www.youtube.com/watch?v=-Ln-8QM8KhQ">Youtube Video</a>, I decided to preload the entire blog on each page.</p><p>Give it a try yourself! If you've made it this far. Clicking the "Random" navigation button a few times really shows off how zippy it is. Ironically, that's what powers that "Random" nav button. It uses the cached pages as the list to choose randomly from!</p><p>Is this tidbit useful anywhere else in any other context? No. But it's really funny to me to make every visitor download and cache the entire blog just to save a few milliseconds loading the post they're looking for 😉.</p><h2 id="conclusion"><a href="#conclusion" class="header-link">Conclusion</a></h2><p>So that's the post. New domain, new blog, new me! This has mostly been my rambling about some cool Swift features and a few things I found while rewriting this website. I hope it was worth the read!</p>https://khanwinter.com/2024-11-01-Hide-Desktop-ShortcutHide Desktop macOS Shortcut2024-11-01T05:00:00.000ZA quick Automator script I use almost daily to toggle my desktop contents. Updated for desktop widgets.<p>I often find myself needing to hide my desktop icons. Whether for a demo for <a href="https://codeedit.app">CodeEdit</a>, sharing my screen for work, or any other task that requires sharing my screen, I prefer to have my desktop contents hidden.</p><p>So here's a really simple Automator script to toggle your desktop. To show your desktop items again just run the script! Note: it kills Finder, so any finder panels you have open will be closed when this is run.</p><p>Throw this in a <code>Run Shell Script</code> action in a new automation:</p><pre><code class="language-bash">previous_val=$(defaults read com.apple.finder CreateDesktop)
if [ "$previous_val" = "true" ]
then
defaults write com.apple.finder CreateDesktop 'false'
defaults write com.apple.WindowManager StandardHideWidgets -int 1
else
defaults write com.apple.finder CreateDesktop 'true'
defaults write com.apple.WindowManager StandardHideWidgets -int 0
fi
killall Finder
</code></pre><img src="./automator.webp" alt="Automator Screenshot"/><p>Then I like to export it to an application so I can run it easily from Spotlight</p><img src="./application.webp" alt="example application icon in finder"/><p>Demo Video:</p><video controls width="90%" style="margin-bottom: 1.5em;">
<source src="demo.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>https://khanwinter.com/2021-08-07-Sort-List-Of-ObjectsSort a List of Objects2021-08-07T05:00:00.000ZLearn how to store a sort order on a list of items saved in a database.<h2 id="sorting-objects-in-a-db"><a href="#sorting-objects-in-a-db" class="header-link">Sorting Objects In A DB</a></h2><p>Lists of objects can often be sorted by a variable, eg calendar events are often sorted by the date they are for. Like so:</p><pre><code class="language-plaintext">Event 1: 10:00 AM
Event 2: 11:00 AM
Event 3: 12:00 PM
</code></pre><p>Sometimes though, we need to sort objects in a list that don't have an easy way to sort, or we need to allow users to sort the objects to their desire. The user will most likely expect his/her sort order to be kept, and we also can't just store an Array of objects in a db.</p><p>The other problem is if we give all the objects a number value to sort by, we can't update <u>every single object</u> in the database with a new sort number. That's just impractical and a waste of compute time and energy.</p><p>The solution is to sort the items by a <code>Double</code> variable. The objects will look basically like this at first:</p><pre><code class="language-plaintext">Item 1: 10.0
Item 2: 20.0
Item 3: 30.0
</code></pre><p>When the user moves Item 3 up a slot, we just give it a number between items 1 and 2, like this:</p><pre><code class="language-plaintext">Item 1: 10.0
Item 3: 25.0 <- Inserted here
Item 2: 20.0
</code></pre><p>If a user moves Item 3 back down, we just add 10.0 to the biggest item's order, and get the list we started out with:</p><pre><code class="language-plaintext">Item 1: 10.0
Item 2: 20.0
Item 3: 30.0
</code></pre><p>Finally, if the user moves Item 3 to the top of the list, we find a number between <code>0.0</code> and Item 1's order, and give it to Item 3:</p><pre><code class="language-plaintext">Item 3: 5.0 <-- Halfway between 0 and 10
Item 1: 10.0
Item 2: 20.0
</code></pre><p>Pretty simple! We don't have to update every single item in the list, and <code>Double</code>s can be divided for a long time, so it will be rare to run out of space between items in the list.</p><p>This article will give an example of how to give users the ability to sort items and keep the sort order using Core Data in SwiftUI. This same method applies to any database/UI framework that allows you to sort your items by a <code>Double</code> type.</p><blockquote><p>There's also a fully functional example project <a href="https://github.com/thecoolwinter/SortDBExample">available for download here</a> that implements the ideas talked about.</p></blockquote><h2 id="order-variable"><a href="#order-variable" class="header-link">Order Variable</a></h2><p>The main idea behind this solution is having a variable on all our objects in the list that's a <code>Double</code> type. For this example, say we have a list of items that we're going to allow the user to sort at will. The <code>Item</code> object may look something like this:</p><pre><code class="language-swift">struct Item {
var id: UUID = UUID()
var label: String
var order: Double
}
</code></pre><p>Notice that we're adding an <code>order</code> variable as type <code>Double</code> to this object. In Core Data, this can be done super easily by just adding a variable of type <code>Double</code> to your .xcdatamodeld file.</p><p>When we go to fetch the <code>Todo</code>s we'll just sort them by the <code>order</code> variable. For Core Data this looks like this:</p><pre><code class="language-swift">@FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Item.order, ascending: true)], animation: .default)
private var items: FetchedResults<Item>
</code></pre><h2 id="updating-the-order"><a href="#updating-the-order" class="header-link">Updating The Order</a></h2><p>There are 2 pieces of code we need to modify to make this actually work:</p><ol><li>When an <code>Item</code> is added</li><li>When an <code>Item</code> is moved</li></ol><h3 id="added"><a href="#added" class="header-link">Added</a></h3><p>When an <code>Item</code> is added, we need to give it an initial order value. The code may look something like below, where I'm just getting the biggest order value, and adding more onto it for the new object. If there aren't any <code>Item</code>s I just give it a value of <code>100.0</code></p><pre><code class="language-swift">func addItem() {
withAnimation {
let newItem = Item(context: viewContext)
newItem.label = "New Item!"
if items.count > 0 { // Check if there are items
newItem.order = items.last!.order + 25.0 // Add some more to the order
} else {
newItem.order = 100.0 // Give some padding from 0.0 for later
}
try! viewContext.save()
}
}
</code></pre><p>Awesome, now when items are added they will automatically go to the bottom of the list. If there are no items, we'll give ourselves some padding numerically for the next case.</p><h3 id="moved"><a href="#moved" class="header-link">Moved</a></h3><p>When an item is moved, things get a little more tricky. There are 3 paths here to handle:</p><ol><li>The item is moved to the top of the list</li><li>The item is moved to the bottom of the list</li><li>The item is put somewhere in the middle</li></ol><p>We'll always need to find an order value between two values to insert the item somewhere.</p><p>For the three cases the <code>upper</code> and <code>lower</code> order values will be between:</p><ol><li>0.0 -> next list item</li><li>last list item -> last list item + 100.0</li><li>Next destination list item -> destination list item</li></ol><p>In swift this looks like the following code</p><pre><code class="language-swift">var upper: Double
var lower: Double
if destination == items.count {
print("Appending to the end of the list")
lower = items.last!.order
upper = items.last!.order + 100.0
} else if destination == 0 {
print("Inserting into the begining")
lower = 0.0
upper = items.first?.order ?? 100.0
} else {
print("Inserting into the middle of the list")
// Find the upper and lower sort around the destination and make some sort orders
upper = items[destination - 1].order
lower = items[destination].order
}
</code></pre><p>Then, we can get numbers between the upper and lower limits like</p><pre><code class="language-swift">var newOrders: [Double] = stride(from: lower, to: upper, by: (upper - lower)/Double(sourceItems.count + 1)).map { $0 }
newOrders.remove(at: 0)
</code></pre><blockquote><p>We're handling the case where we're moving more than one item too. So if two items are inserted between 10.0 and 20.0 we should generate 12.5 and 17.5 to insert both the items in the correct spot.</p></blockquote><p>Then, its a simple matter of updating the sort order of the objects!</p><pre><code class="language-swift">var i = 0
source.forEach { index in
items[index].order = newOrders[i]
i += 1
}
try! viewContext.save()
</code></pre><p>Done! 🎉</p><p>Here's the code from the example project in action.</p><video width="400" controls>
<source src="video.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>https://khanwinter.com/2021-05-08-Implementing-SinImplementing Sin(x)2021-05-08T05:00:00.000ZWe have a basic sin function, now we need to calculate more than π/2, and do it fast.<h2 id="previously-on"><a href="#previously-on" class="header-link">Previously on...</a></h2><p>In my last post, I used Taylor Series to come up with a function to estimate <span class="math-inline">$sin(x)$</span> for any number between <span class="math-inline">$0$</span> and <span class="math-inline">$\frac{\pi}{2}$</span>. While this is cool, it's rather useless. But, thankfully, I can use the numbers we can already to calculate any number of <span class="math-inline">$sin(x)$</span>.</p><blockquote><p>If you haven't read my previous post, it's a good read and will definitely make this post make much more sense. <a href="https://windchillblog.com/2021/03/17/How-Computers-Calculate-Sin.html">You can read that post here</a>.</p></blockquote><h2 id="sin-repeats"><a href="#sin-repeats" class="header-link">Sin Repeats</a></h2><span class="math-block">$$sin(x)$$</span><p>has an awesome property that I'm going to take complete advantage of for this implementation. <strong>Sin is the exact same from <span class="math-inline">$0$</span> to <span class="math-inline">$\frac{\pi}{2}$</span> everywhere else on the number line.</strong> All the rest of the numbers are flipped horizontally, or vertically, or both.</p><p>It's easier to see this with the graph of <span class="math-inline">$sin(x)$</span>.</p><img src="sin.png" alt="sin(x) with points 0, pi/2, 3pi/2, pi and 2pi highlighted"/><p>It's pretty easy if you can rotate your phone/laptop to see that the curve from <span class="math-inline">$0$</span> to <span class="math-inline">$\frac{\pi}{2}$</span> is the flipped horizontally between <span class="math-inline">$\frac{\pi}{2}$</span> and <span class="math-inline">$\pi$</span>, then horizontally and vertically between <span class="math-inline">$\pi$</span> and <span class="math-inline">$\frac{3\pi}{2}$</span>, and finally flipped vertially from <span class="math-inline">$\frac{3\pi}{2}$</span> to <span class="math-inline">$2\pi$</span>.</p><h2 id="flip-flops-"><a href="#flip-flops-" class="header-link">Flip Flops 🩴</a></h2><p>Since we can already calculate all the numbers from <span class="math-inline">$0$</span> to <span class="math-inline">$\frac{\pi}{2}$</span> in <span class="math-inline">$sin$</span>, all we've gotta do is figure out some swift functions to flip flop around our existing results.</p><p>Here's what we're starting with, with each quadrant that we need to flip or flop in a different color.</p><img src="orig.png" alt="orig"/><ol><li>Vertical Flips</li></ol><p>It's pretty easy to flip a function vertically. In math it's called the <em>reflection</em> of a function. All we have to do is make the entire function negative and it'll be flipped vertically.</p><img src="vert.gif"/><blockquote><p>Source: https://mathbitsnotebook.com/Algebra1/FunctionGraphs/FNGTransformationFunctions.html</p></blockquote><p>The only areas we need to vertically flip <span class="math-inline">$sin(x)$</span> are between <span class="math-inline">$\pi$</span> and <span class="math-inline">$2\pi$</span>, where <span class="math-inline">$sin(x)$</span> is negative.</p><p>After applying the vertical flops, our graph looks like this</p><img src="vert-flip.png" alt="vert-flip"/><ol start="2"><li>Horizontal Flops</li></ol><p>Now all we have to do is flop the blue and purple lines backwards. We can do this by just shifting the <span class="math-inline">$x$</span> value <span class="math-inline">$\frac{\pi}{2}$</span> to the left.</p><p>After the horizontal flop we end up with this graph.</p><img src="hor-flip.png" alt="hor-flip"/><p>Sweet! Now we can use these flip flops in our sin function in swift to calculate any value for <span class="math-inline">$sin(x)$</span>.</p><h2 id="swift-flip-flops-"><a href="#swift-flip-flops-" class="header-link">Swift Flip Flops 🐥🩴</a></h2><p>Now to implement these stylish shoes in Swift.</p><ol><li>Vertical Flips</li></ol><p>As said before, all we have to do to flip a function vertically is make sin negative. That's super easy in swift, so here's the vertical flip function.</p><pre><code class="language-swift">sin(-input)
</code></pre><ol start="2"><li>Horizontal Flops</li></ol><p>All we need to do here is subtract <span class="math-inline">$\frac{\pi}{2}$</span> from the <code>input</code> variable and we're good to go!</p><pre><code class="language-swift">sin(input - (Double.pi/2))
</code></pre><h2 id="finally"><a href="#finally" class="header-link">Finally...</a></h2><p>Yay! Now all we have to do is</p><ol><li>Reduce <span class="math-inline">$x$</span> to be between <span class="math-inline">$0$</span> and <span class="math-inline">$2\pi$</span>.</li><li>Flip flop our way to our result!</li></ol><p>Here are the few rules that we'll need to follow to figure out which flip or flop to use.</p><ol><li>If <span class="math-inline">$x$</span> is between <span class="math-inline">$\pi$</span> and <span class="math-inline">$2\pi$</span>, flip it <u>vertically</u>.</li><li>If <span class="math-inline">$x$</span> is between <span class="math-inline">$\frac{\pi}{2}$</span> and <span class="math-inline">$\pi$</span> <em>or</em> <span class="math-inline">$\frac{3\pi}{2}$</span> and <span class="math-inline">$2\pi$</span>, flip it <u>horizontally</u>.</li></ol><p>With these rules in mind, we almost have the swift code written for us! I'll just take those "If <span class="math-inline">$x$</span> is..." and turn them into <code>if</code> statements.</p><p>Here's the final swift implementation of <span class="math-inline">$sin(x)$</span>.</p><pre><code class="language-swift">let S1 = -1.66666666666666324348e-01; // -1/(3!)
let S2 = 8.3333333332248946124e-03; // 1/(5!)
let S3 = -1.98412698298579493134e-04; // -1/(7!)
let S4 = 2.75573137070700676789e-06; // 1/(9!)
let S5 = -2.50507602534068634195e-08; // -1/(11!)
let S6 = 1.58969099521155010221e-10; // 1/(13!)
func usersin(_ xOrig: Double) -> Double {
// Reduce x to be between 0 and 2pi
var x = xOrig.truncatingRemainder(dividingBy: 2 * Double.pi)
// If it's between pi/2 => pi or 3pi/2 => 2pi. Flip it horizontally
if (x > Double.pi/2 && x < Double.pi) || (x > (3*Double.pi)/2 && x < 2*Double.pi) {
x = x - (Double.pi/2)
}
let z = x*x // x^2
let v = z*x // x^3
let r = S2+z*(S3+z*(S4+z*(S5+z*S6))) // Taylor function part 1/2
// Flip the function if needed, eg between pi and 2pi
if x > Double.pi && x < Double.pi * 2 {
return -x+v*(S1+z*r) // Taylor function part 2/2 (but negative)
} else {
// No need for vertical flip
return x+v*(S1+z*r) // Taylor function part 2/2
}
}
</code></pre><h2 id="benchmark"><a href="#benchmark" class="header-link">Benchmark</a></h2><p>I decided to test the speed of this implementation, and the benchmarks look pretty good. For 1,000,000 <span class="math-inline">$sin(x)$</span> operations, the results look like:</p><pre><code>Time elapsed for User Sin: 0.7528519630432129 s.
Time elapsed for Normal Sin: 0.6716610193252563 s.
</code></pre><p>So not too far off between them. The code I used to benchmark is below.</p><pre><code class="language-swift">#!/usr/bin/swift
import Foundation
... User sin defined up here ...
func printTimeElapsedWhenRunning(title: String, operation: (() -> Void)) {
let startTime = CFAbsoluteTimeGetCurrent()
operation()
let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
print("Time elapsed for \(title): \(timeElapsed) s.")
}
printTimeElapsedWhenRunning(title: "User Sin") {
for i in 0..<1000000 {
let _ = usersin(Double(i))
}
}
printTimeElapsedWhenRunning(title: "Normal Sin") {
for i in 0..<1000000 {
let _ = sin(Double(i))
}
}
</code></pre><blockquote><p>After running this code ~10 times the results were within 0.001s of each other, so the error from this test is negligible.</p></blockquote><h2 id="conclusion"><a href="#conclusion" class="header-link">Conclusion</a></h2><p>While it's fun to figure out and define our own implementation of <span class="math-inline">$sin(x)$</span>, it's not super practical. Other programers have already figured out this math, and have figured out ways of making it slightly faster than my own. So, is it practical? No. But it was fun to do!</p>https://khanwinter.com/2021-03-17-How-Computers-Calculate-SinHow Computers Calculate Sine2021-03-17T05:00:00.000ZSine is infinite, so how can a computer calculate an infinite function?<h2 id="computers-arent-infinite"><a href="#computers-arent-infinite" class="header-link">Computers aren't infinite.</a></h2><p>How can computers compute <em>(no pun intented)</em> infinite functions like sin so fast? Take video games for instance, that need to compute trig functions incredibly fast 100's of times a second to render frames. If sine and cosine took long at all, we would never have video games.</p><p>How do computers do it?</p><hr><p>Historically computer scientists have two ways of getting around this problem.</p><ol><li>Store a table of sine and cosine values, then reference that table whenever you need. Java does this, and it works fairly well until you need a value of sine that isn't in the table</li><li>Estimate <span class="math-inline">$sin(x)$</span> using another function.</li></ol><p>Here I'll show you how computers estimate sin for any value.</p><h2 id="sine-and-derivatives"><a href="#sine-and-derivatives" class="header-link">Sine and derivatives</a></h2><p><strong>Before we take a deeper dive into the math, there's two concepts we need to understand.</strong></p><hr><h3 id="sine"><a href="#sine" class="header-link">Sine</a></h3><p>For a little background I'll start by explaining two important concepts. First, <span class="math-inline">$sin(x)$</span>. You may remember this fun function from a high school algebra class. <span class="math-inline">$sin(x)$</span> has these important properties.</p><ol><li>Sine is infinite, meaning it'll have a value at any number you put into it.</li><li>It's also periodic, which means that it repeats itself over and over again (like a wave).</li></ol><p>Here's a graph of <em>y=sin(x)</em>.</p><img src="1.png" alt="y=sin(x)"/><p>As you can see, the value of <em>sin(x)</em> goes up and down between <span class="math-inline">$-1$</span> and <span class="math-inline">$1$</span>.</p><p>You can also tell that it's not coming to an end. It'll go on forever from −∞ all the way to +∞.</p><p><strong>One more important thing about sine is that the curve 0 to <span class="math-inline">$\frac{\pi}{2}$</span> is repeated on the entire rest of sine. The curve might be backwards or upside down, but it's always exactly the same.</strong></p><p><strong>That means if we can figure out values for any point in <span class="math-inline">$0$</span> to <span class="math-inline">$\frac{\pi}{2}$</span>, we can figure out points for anywhere on sine.</strong></p><hr><h3 id="derivatives"><a href="#derivatives" class="header-link">Derivatives</a></h3><p>A derivative is the core concept of Calculus. Basically, when you take a derivative of a function, you create another function that measures the <em>rate of change</em> of the original function. It can be confusing, but a really good example of this comes from physics.</p><p>If we have a particle that's moving according to the position function</p><span class="math-block">$$
x=vt+\frac{1}{2}at^2
$$</span><p>We can take the <strong><em>Derivative</em></strong> of that funciton, to get</p><span class="math-block">$$
v=at
$$</span><p><strong><em>Velocity</em></strong> is the derivative of the position function. It's literally, <em>the change in position over time</em>. We can even take the derivative of Velocity to get</p><span class="math-block">$$
a=\left(some\:number\right)
$$</span><p><strong><em>Acceleration</em></strong>. Acceleration is <em>the change in velocity over time</em>.</p><p>Here's a graph of all these functions over time. You'll notice the velocity is going up at a constant rate <em>a</em>, and the position is going up exponentially. If we dropped a ball off a cliff. The ball would fall faster over time, so the velocity would steadily increase, but the acceleration would always be <em><span class="math-inline">$9.81\frac{m}{s^2}$</span></em>.</p><img src="derivatives.png" alt="Derivatives"/><p>Finally, derivatives are shown using this syntax <span class="math-inline">$
derivative\:of\:f\left(x\right)=f'\left(x\right)
$</span> So from now on, if you see <em>f'(x)</em> that's the derivative of the function <em>f</em>. If we add more <em>'</em> to the function if grows in derivatives. So the second derivative of <em>f</em> is <span class="math-inline">$
second\:derivative\:of\:f\left(x\right)=f''\left(x\right)second\:derivative\:of\:f\left(x\right)=f''\left(x\right)
$</span></p><hr><h2 id="how-do-we-use-derivatives-to-calculate-sine"><a href="#how-do-we-use-derivatives-to-calculate-sine" class="header-link">How do we use derivatives to calculate sine?</a></h2><h3 id="a-taylor-series"><a href="#a-taylor-series" class="header-link">A: Taylor Series</a></h3><p>Taylor series estimate a function. Basically, a mathemetician named Brook Taylor realized that any function is equal to this sum</p><span class="math-block">$$
f\left(x\right)=f\left(0\right)+\frac{f'\left(0\right)}{1!}x+\frac{f''\left(0\right)}{2!}x^2+...
$$</span><p>Each part that we're adding up is the <span class="math-inline">$nth$</span> derivative of <em>f</em> divided by the number <em>n!</em> it's at, times <span class="math-inline">$x^n$</span>. If we add up infinity of these functions, this Taylor series will be perfectly equal to <span class="math-inline">$f(x)$</span>.</p><blockquote><p>Each time we add one of the parts above, we're adding a <em>degree</em> to the Taylor series. So the above function is to the 3rd degree.</p></blockquote><p>Taylor series work on the idea that the function is related to it's derivatives somehow. So we can get the initial value of <span class="math-inline">$f(x)$</span> at <span class="math-inline">$f(0)$</span>, and the next derivative <span class="math-inline">$f'(x)$</span> gives us the direction it should go next. Each derivative we add in this way points the Taylor series in the correct direction more and more until it's exactly equal to the entire original function.</p><hr><p>But, we don't always have to add up to infinity. In fact, the series is very close to <span class="math-inline">$f(x)$</span> for a short bit after only a few sums of the Taylor series.</p><p>This comes in handy if say, we only need to know values for a specific part of the function say <em>*ahem*</em> 0 to <span class="math-inline">$\frac{\pi }{2}$</span>.</p><p>In fact, this is the Taylor series to the 7th degree, which happens to equal <span class="math-inline">$sin(x)$</span> nearly exactly from <span class="math-inline">$0$</span> to <span class="math-inline">$\frac{\pi }{2}$</span>.</p><span class="math-block">$$
sin(x)=x-\frac{x^3}{3!}+\frac{x^5}{5!}-\frac{x^7}{7!}+\frac{x^{11}}{11!}-\frac{x^{13}}{13!}+\frac{x^{15}}{15!}
$$</span><img src="taylor-sin.png" alt="Taylor Series of Sin"/><p>The graph above shows that the Taylor series is equal to <span class="math-inline">$sin(x)$</span> until <span class="math-inline">$\frac{\pi}{4}$</span>, where it starts to wander.</p><h2 id="computer-time"><a href="#computer-time" class="header-link">Computer time!</a></h2><p>Now we have a function we can use to estimate <span class="math-inline">$sin(x)$</span> anywhere along <span class="math-inline">$sin(x)$</span>.</p><p>Since the function isn't infinite, we can translate it to a computer function very easily. Heres <code>sin(x)</code> in Swift.</p><pre><code class="language-swift">let S1 = -1.66666666666666324348e-01; // -1/(3!)
let S2 = 8.3333333332248946124e-03; // 1/(5!)
let S3 = -1.98412698298579493134e-04; // -1/(7!)
let S4 = 2.75573137070700676789e-06; // 1/(9!)
let S5 = -2.50507602534068634195e-08; // -1/(11!)
let S6 = 1.58969099521155010221e-10; // 1/(13!)
func usersin(_ x: Double) -> Double {
let z = x*x // x^2
let v = z*x // x^3
let r = S2+z*(S3+z*(S4+z*(S5+z*S6))) // Taylor function part 1/2
return x+v*(S1+z*r) // Taylor function part 2/2
}
</code></pre><p>Here's how this works.</p><ol><li>First, we're storing the larger numbers at the top. These numbers are constants, so we can set them statically outside the function.</li><li>The variables <code>z</code>, and <code>v</code> are placeholder variables for powers of <code>x</code>, they're there mostly for readability</li><li><code>r</code> is the first part of the actual Taylor function. I found that if I kept the entire polynomial in one declaration it messed up the final result.</li><li>We return the final Taylor function.</li></ol><p>To test this implementation, we can import <code>Foundation</code> and compare it to Swift's built-in <code>sin</code>.</p><pre><code class="language-swift">#!/usr/bin/swift
import Foundation
let S1 = -1.66666666666666324348e-01; // -1/(3!)
let S2 = 8.3333333332248946124e-03; // 1/(5!)
let S3 = -1.98412698298579493134e-04; // -1/(7!)
let S4 = 2.75573137070700676789e-06; // 1/(9!)
let S5 = -2.50507602534068634195e-08; // -1/(11!)
let S6 = 1.58969099521155010221e-10; // 1/(13!)
func usersin(_ x: Double) -> Double {
let z = x*x // x^2
let v = z*x // x^3
let r = S2+z*(S3+z*(S4+z*(S5+z*S6))) // Taylor function part 1/2
return x+v*(S1+z*r) // Taylor function part 2/2
}
func printUserSin(_ input: Double, name: String) {
print("x\t=\t\(name)")
print("usersin(x):\t\(usersin(input))")
print("sin(x):\t\t\(sin(input))")
print("")
}
printUserSin(Double.pi/6, name: "Pi/6")
printUserSin(Double.pi/8, name: "Pi/8")
printUserSin(Double.pi/21, name: "Pi/21")
printUserSin(Double.pi/2, name: "Pi/2")
printUserSin(Double.pi/2 - 0.01, name: "Pi/2 - 0.01")
</code></pre><p>Which spits out:</p><pre><code class="language-bash">x = Pi/6
usersin(x): 0.4999999999999961
sin(x): 0.49999999999999994
x = Pi/8
usersin(x): 0.38268343236508884
sin(x): 0.3826834323650898
x = Pi/21
usersin(x): 0.14904226617617444
sin(x): 0.14904226617617444
x = Pi/2
usersin(x): 1.000000000251294
sin(x): 1.0
x = Pi/2 - 0.01
usersin(x): 0.9999500006418387
sin(x): 0.9999500004166653
</code></pre><p>Which is <strong>nearly</strong> completely accurate for all the values we gave it, plenty close for any calculation we may need, and plenty fast as well!</p><h2 id="conclusion"><a href="#conclusion" class="header-link">Conclusion</a></h2><p>I've shown a very naive implementation of <span class="math-inline">$sin(x)$</span>. It doesn't take into account values outside of <span class="math-inline">$0$</span> to <span class="math-inline">$\frac{\pi}{2}$</span> and doesn't account for negative values. In fact, this is the bare minimum you would want from a <code>sin</code> function.</p><p>In a future post I'll go over some of the tricks computer scientists have used to get around those problems, and compare speeds of this function with C and C++.</p><blockquote><p>If you'd like to run the code above on your own, create a file called <code>main.swift</code> and copy-paste the code into that file. Then run<code>chmod +x main.swift && ./main.swift</code> to run the script.</p></blockquote>https://khanwinter.com/2021-02-02-The-Search-Engine-MatrixThe Search Engine Matrix2021-02-03T06:00:00.000ZOne way search engines like google rank page poplarity using matrices.<h2 id="search-engines-rank-pages-based-on-how-likely-you-are-to-visit-them"><a href="#search-engines-rank-pages-based-on-how-likely-you-are-to-visit-them" class="header-link">Search engines rank pages based on how likely you are to visit them</a></h2><p>For a person, this might be a simple thing to understand. You would probably think the search engine should probably rank websites that are more popular higher on the list. However, computers don't understand popularity inherently. We have to convert a human problem, "How popular is this website" into a math problem. How does one do that?</p><p>The answer: <strong>Matrices</strong></p><h2 id="a-bit-of-a-primer"><a href="#a-bit-of-a-primer" class="header-link">A bit of a primer</a></h2><p>If you haven't taken Linear Algebra, there's a good chance you have never heard of matrices. So here's a brief explaination.</p><p><strong>Matrices</strong> hold numbers in a grid, sort of like a Sudoku board. Below is a matrix called the <em>Identity Matrix</em> You can think of it as the 1 of matrices.</p><span class="math-block">$$
I=\begin{pmatrix}1&0&0\\ 0&1&0\\ 0&0&1\end{pmatrix}
$$</span><p>You can use matrices like other numbers, adding, subtracting, multiplying, dividing etc. For instance if we multiply <em>I</em> by 2 we get</p><span class="math-block">$$
I\cdot 2=\begin{pmatrix}2&0&0\\ 0&2&0\\ 0&0&2\end{pmatrix}
$$</span><p>We can also put numbers in where we currently have 0 like below, as well as have different numbers of rows and columns.</p><span class="math-block">$$
I_2=\begin{pmatrix}1\\ 0\\ 0\end{pmatrix},\:I_3=\begin{pmatrix}1&\frac{1}{2}&\infty \\ sin\left(\theta \right)&\pi &\log \left(20\right)\\ -1&2&\frac{3}{4}\end{pmatrix}
$$</span><p>If you multiply a narrow matrix by a wider matrix with the same number of rows, you'll end up with a narrow matrix. This will come in handy later when calculating probabilities.</p><span class="math-block">$$
I_2\cdot I=\begin{pmatrix}1&0&0\\ 0&1&0\\ 0&0&1\end{pmatrix}\begin{pmatrix}1\\ 0\\ 0\end{pmatrix}=\begin{pmatrix}1\\ 0\\ 0\end{pmatrix}
$$</span><h2 id="turning-the-emhumanem-problem-into-a-emcomputerem-problem"><a href="#turning-the-emhumanem-problem-into-a-emcomputerem-problem" class="header-link">Turning the <em>human</em> problem into a <em>computer</em> problem</a></h2><p>Say, for instance, we have a group of 3 websites which are linked in a graph like this:</p><img src="1.png" alt="Website Graph"/><p>In this graph, website 1 links only to website 2, website 2 links to both 1 and 3, and website 3 links to both 1 and 2.</p><p>For each website we want to know how it links to the other websites <strong>mathematically</strong>. So, for website 2, we'll make a single-column matrix like this: <img src="2.png" alt="Single-column matrix"/></p><p>This matrix only has one column, which adds up to 1. We got the numbers for the matrix by adding up all the links that that website links to, and dividing one by that sum, eg: <span class="math-inline">$\frac{1}{Number\:Of\:Links}$</span>. We'll now create this matrix for each website, and combine them into one, larger matrix which we'll label <span class="math-inline">$A$</span>.</p><span class="math-block">$$
A=\:\begin{pmatrix}0&\frac{1}{2}&\frac{1}{2}\\ 1&0&\frac{1}{2}\\ 0&\frac{1}{2}&0\end{pmatrix}
$$</span><p>Each column in this matrix represents how one of the three websites links to the other two.</p><h2 id="follow-the-links"><a href="#follow-the-links" class="header-link">Follow the links</a></h2><p>Now that we have a matrix representing the website graph, we can start following it around to different websites. We'll start at website 3. We've got a 50/50 chance of going to either website 1 or website 2. I'll roll a dice and it landed on website 2, so we'll go there.</p><p>Now that we're at website two, we have a 50/50 chance of going to either website 3, or website 1. The dice picks website 1.</p><p>Website 1 is different, it only links to website 2. So I don't even have to roll a die, I can only go to website 2.</p><p>This is a lot of writing, so I'll convert this into a formula.</p><hr><p>We'll start our walk through the links at page one. This is step one, so we'll name this variable <span class="math-inline">$p_0$</span>. <span class="math-inline">$p_0$</span> is going to be a matrix representing the probability of us being on any page after we've taken 0 steps. We're starting at website 1, so we have a 100% probability in the next step of going to website 2, meaning the matrix looks like this.</p><span class="math-block">$$
p_0=\begin{pmatrix}0\\ 1\\ 0\end{pmatrix}
$$</span><p>If I multiply <span class="math-inline">$p_0$</span> by <span class="math-inline">$A$</span> (which we created earlier) we'll end up with the probability of being at any of the resulting websites after one step (<span class="math-inline">$p_1$</span>).</p><span class="math-block">$$
p_1=A\cdot \:p_0=\:\begin{pmatrix}0&\frac{1}{2}&\frac{1}{2}\\ 1&0&\frac{1}{2}\\ 0&\frac{1}{2}&0\end{pmatrix}\cdot \begin{pmatrix}1\\ 0\\ 0\end{pmatrix}=\begin{pmatrix}0\\ 1\\ 0\end{pmatrix}
$$</span><p>The resulting matrix tells us that we have a 100% probability of being at website 2 after one step. This makes sence, since website 1 only links to website 2.</p><p>To find the next step, we just multiply <span class="math-inline">$p_1$</span> by <span class="math-inline">$A$</span> to get <span class="math-inline">$p_2$</span>.</p><span class="math-block">$$
p_2=A\cdot \:p_1=A\cdot A\cdot p_0=A^2p_0=\:\begin{pmatrix}0&\frac{1}{2}&\frac{1}{2}\\ \:1&0&\frac{1}{2}\\ \:0&\frac{1}{2}&0\end{pmatrix}\cdot \begin{pmatrix}0\\ \:1\\ \:0\end{pmatrix}=\begin{pmatrix}\frac{1}{2}\\ 0\\ \frac{1}{2}\end{pmatrix}
$$</span><p>I'm starting to see a patern. For each step we take, we're really just adding one power to <span class="math-inline">$A$</span>. On step one, we found <span class="math-inline">$A^1*p_0$</span>, on step two we found <span class="math-inline">$A^2*p_0$</span>, and so on. Because of that, we can say the following formula is true.</p><span class="math-block">$$
p_n=Ap_n=A^np_0
$$</span><p><strong>If we take a larger number of steps, we'll find the probability of being at any website in the graph.</strong></p><p>What if we find <span class="math-inline">$p_{15}$</span>? <em>(by the way I'm using <a href="https://www.symbolab.com/solver/matrix-calculator">symbolab</a> for these smaller computations)</em></p><span class="math-block">$$
p_{15}=\:\begin{pmatrix}0&\frac{1}{2}&\frac{1}{2}\\ 1&0&\frac{1}{2}\\ 0&\frac{1}{2}&0\end{pmatrix}^{15}\begin{pmatrix}1\\ 0\\ 0\end{pmatrix}=\begin{pmatrix}0.333313\\ 0.444763\\ 0.221924\end{pmatrix}
$$</span><p>This means after 15 steps through each website, <strong>we've got the highest probability of landing on website 2, then website 1, then website 3.</strong></p><p><strong>Congrats! You've found how likely a user is to navigate to each of these three sites!</strong></p><h2 id="this-seems-really-workintensive"><a href="#this-seems-really-workintensive" class="header-link">This seems really work-intensive</a></h2><p>It is tough to calculate <span class="math-inline">$p$</span> for each step, what if we had a <strong>huge</strong> graph of websites. We wouldn't know if we needed to stop walking the graph at 15 steps or 500! So how can we solve this problem?</p><hr><p><em>Side note: I'm going to redefine <span class="math-inline">$A$</span> from now on, so we're working with 5 websites.</em></p><span class="math-block">$$
A=\begin{pmatrix}0&\frac{1}{3}&\frac{1}{2}&\frac{1}{2}&0\\ \frac{1}{2}&0&0&0&0\\ 0&\frac{1}{3}&0&0&0\\ 0&\frac{1}{3}&\frac{1}{2}&0&1\\ \frac{1}{2}&0&0&\frac{1}{2}&0\end{pmatrix}
$$</span><hr><p>For starters lets define the actual formula we're working with here (<span class="math-inline">$n$</span> is the number of steps we take).</p><span class="math-block">$$
p_{n+1}=Ap_n
$$</span><p>If we take <span class="math-inline">$\infty$</span> steps, this equation becomes</p><span class="math-block">$$
p_\infty =Ap_\infty
$$</span><p>Which is equal to a proof meaning that <span class="math-inline">$p$</span> is actually something called an <em>eigenvector</em> which corrensponds to <span class="math-inline">$A$</span> and an <em>eigenvalue</em> of 1 since <span class="math-inline">$p_n$</span> will always add up to 1. (<a href="https://www.khanacademy.org/math/linear-algebra/alternate-bases/eigen-everything/v/linear-algebra-introduction-to-eigenvalues-and-eigenvectors">More about eigenvectors and eigenvalues</a>)</p><p>Using this proof we can use the calculation below to find an eigenvector of <span class="math-inline">$A$</span> which I'm going to call <span class="math-inline">$R$</span>. <span class="math-inline">$I$</span> is the identity matrix. <em>(now I'm using Matlab for these calculations since it's easier to work with matrices, I'll include the code at the bottom of this page)</em></p><span class="math-block">$$
I=\begin{pmatrix}1&0&0&0&0\\ \:\:0&1&0&0&0\\ \:\:0&0&1&0&0\\ \:\:0&0&0&1&0\\ \:\:0&0&0&0&1\end{pmatrix},\:R=rref\left(A-I\right)
$$</span><p>After this computation, <span class="math-inline">$R$</span> equals</p><span class="math-block">$$
R=\begin{pmatrix}1&0&0&0&-0.8\\ 0&1&0&0&-0.4\\ 0&0&1&0&-1.33\\ 0&0&0&1&-1.2\\ 0&0&0&0&0\end{pmatrix}
$$</span><p>From which we can draw the eigenvector <span class="math-inline">$v$</span> and the probability matrix <span class="math-inline">$p$</span>. <em>The first equation takes only the left column of <span class="math-inline">$R$</span> and then sets the 5th value to 1. Since <span class="math-inline">$p$</span> is a probability matrix, it always needs to add up to 1, so we divide it by it's sum to reduce it down to 1.</em></p><span class="math-block">$$
v=-R\left(:,5\right),\:v\left(5\right)=1
$$</span><span class="math-block">$$
p=\frac{v}{sum\left(v\right)}
$$</span><p>At this point, <span class="math-inline">$p$</span> equals</p><span class="math-block">$$
p=\begin{pmatrix}0.2264\\ 0.1132\\ 0.0377\\ 0.3396\\ 0.2830\end{pmatrix}
$$</span><p>Which is the final probability matrix.</p><h2 id="conclusion"><a href="#conclusion" class="header-link">Conclusion</a></h2><p>Using some fun math we're able to turn a human problem (<em>what's the most popular page</em>) into a computer problem (<em>what's the probability of reaching any page</em>). This is the job of computer scientists, to be able to break down problems in our world into their mathematical components, and tell a computer how to solve them.</p><p>KhanAcademy has some great resources on Matrices, Eigenvectors and Eigenvalues if you'd like to learn more here -> <a href="https://www.khanacademy.org/math/linear-algebra">https://www.khanacademy.org/math/linear-algebra</a></p><p><em>If you have access to Matlab, and want to check my work or mess around on your own, here's the Matlab code I used for the second example.</em></p><pre><code class="language-matlab">>> A = [0 1/3 1/2 1/2 0; 1/2 0 0 0 0; 0 1/3 0 0 0; 0 1/3 1/2 0 1; 1/2 0 0 1/2 0];
>> I = eye(5); R = rref(A-I);
>> v=-R(:,5); v(5)=1; p=v/sum(v);
>> p
p =
0.2264
0.1132
0.0377
0.3396
0.2830
</code></pre>https://khanwinter.com/2021-01-07-Save-Codable-Dates-In-FirestoreSave Codable Dates In Firestore2021-01-07T06:00:00.000ZAvoid JSON decoding errors when using Dates with Firestore and Codable.<h2 id="lets-say-we-have-a-codable-object-we-want-to-save-in-firestore"><a href="#lets-say-we-have-a-codable-object-we-want-to-save-in-firestore" class="header-link">Lets say we have a Codable object we want to save in Firestore</a></h2><p>This object has <code>name</code>, and <code>dateUpdated</code> properties. The <code>dateUpdated</code> property is a <code>Date</code> object. The implementation looks like this:</p><pre><code class="language-swift">class Name: Codable {
var name: String
var dateUpdated: Date
}
</code></pre><p>To make my life easier, I've added a custom init to this object to convert it from <code>[String: Any]</code> to a <code>Name</code> object.</p><pre><code class="language-swift">extension Name {
init(dictionary: [String: Any]) throws {
let data = try JSONSerialization.data(withJSONObject: dictionary, options: .prettyPrinted)
self = try decoder.decode(Self.self, from: data)
}
}
</code></pre><p>Pretty easy! Now when I get a response from Firestore I can just convert it to my object like this.</p><pre><code class="language-swift">if let document = snapshot.documents.first {
let data = document.data()
let name: Name? = try? Name(data)
}
</code></pre><p>And I have a function that converts the object into a dictionary to save it to firestore.</p><pre><code class="language-swift">func dictionary() throws -> [String: Any] {
let data = try JSONEncoder().encode(self)
guard let dictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] else {
throw NSError()
}
return dictionary
}
</code></pre><h2 id="whats-the-problem"><a href="#whats-the-problem" class="header-link">What's the problem?</a></h2><p>If you try saving an object with a <code>Date</code> property to Firestore, Firestore automatically changes the <code>Date</code> to a <code>Timestamp</code>. This probably wouldn't be a problem, unless you're trying to decode that same object where you'll get the error:</p><pre><code class="language-swift">"Invalid type in JSON write (FIRTimestamp)"
</code></pre><p>This is being caused because Swift's Foundation doesn't know how to decode Firestore's <code>Timestamp</code> type from JSON.</p><h2 id="how-do-you-fix-it"><a href="#how-do-you-fix-it" class="header-link">How do you fix it?</a></h2><p>It's pretty simple actually, we just tell the <code>JSONDecoder</code> and <code>JSONEncoder</code> to use a special way of encoding/decoding dates. It's just a few line's change for each of the methods in the class.</p><pre><code class="language-swift">extension Name {
init(dictionary: [String: Any]) throws {
let data = try JSONSerialization.data(withJSONObject: dictionary, options: .prettyPrinted)
let decoder = JSONDecoder() // Change
decoder.dateDecodingStrategy = .secondsSince1970 // Change
self = try decoder.decode(Self.self, from: data) // Change
}
func dictionary() throws -> [String: Any] {
let encoder = JSONEncoder() // Change
encoder.dateEncodingStrategy = .secondsSince1970 // Change
let data = try encoder.encode(self)
guard let dictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] else {
throw NSError()
}
return dictionary
}
}
</code></pre><p>All this is doing is telling swift to save our <code>dateUpdated</code> date as a number instead of an actual date. This way, Firestore doesn't try to change any <code>Date</code> types to <code>Timestamps</code> and we don't get the aforementioned error.</p><p>The change made was instead of using a bare <code>JSONEncoder</code> or <code>JSONDecoder</code>, I changed it to use this:</p><pre><code class="language-swift">let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .secondsSince1970
</code></pre><p>This is applicable to other environments too. If you're building a server using swift, you can use those custom <code>JSONEncoder</code>/<code>JSONDecoder</code> date strategies to encode date types so you know what they'll look like on the server side. The other options for date strategies can be found in the <a href="https://developer.apple.com/documentation/foundation/jsondecoder/2895216-datedecodingstrategy">documentation</a>.</p>https://khanwinter.com/2020-05-5-Scripting-With-SwiftScripting With Swift2020-11-04T06:00:00.000ZMake fast, simple scripts with my favorite language.<h2 id="create-the-script"><a href="#create-the-script" class="header-link">Create the script</a></h2><p>Launch the terminal and <code>cd</code> into the directory you want your script to be. Eg: <code>cd Desktop/Scripts</code>.</p><p>Enter <code>touch main.swift</code> to create a new swift file.</p><p>Then, to make it executable change the chmod on the file with <code>chmod +x main.swift</code>. This makes it so your computer recognizes the file as something that can be run. Instead of something to read.</p><p>Launch Xcode (or another editor) and open your new swift file. Then add this bit to the top:</p><pre><code class="language-swift">#!/usr/bin/swift // This is crucial. Without it your script will not run.
print("Hello World")
</code></pre><p>Now, go back to your terminal and run the script using <code>./main.swift</code>. It should read something like</p><pre><code class="language-bash">user@Computer ~ % ./main.swift
Hello World
user@Computer ~ %
</code></pre><h3 id="one-more-thing-to-note"><a href="#one-more-thing-to-note" class="header-link">One more thing to note</a></h3><p>Xcode's autocomplete will not work for scripts. So what I do is create a playground, write and debug the script and then when it's ready copy-paste it into the script file you just made. Then, you have autocomplete while writing the script.</p>https://khanwinter.com/2020-02-24-Programatic-UI-In-UIKit-Without-StoryboardsProgramatic UI in UIKit without Storyboards2020-11-04T06:00:00.000ZUse simple functions and layout anchors to create a simple UI without storyboards.<blockquote><p>This doesn't reflect my opinions circa 2024, however I'm leaving this public cause removing media is always bad. This was written while I was in high school, so take it with a large grain of salt and don't judge my writing abilities 😅.</p></blockquote><h2 id="why-are-storyboards-all-that-bad"><a href="#why-are-storyboards-all-that-bad" class="header-link">Why are storyboards all that bad?</a></h2><h3 id="answer-theyre-not"><a href="#answer-theyre-not" class="header-link">Answer: They're not</a></h3><p>Really, storyboards are super good at helping create UI. They make it easy to see exactly what you're building and work with constraints on each view. Sometimes though, it's easier to not use them. For more repetitive views, using storyboards will be a pain, having to link every single input and output to your <code>UIViewController</code> file, copy-pasting the whole thing. It leads to bugs and spaghetti code.</p><p>Sometimes though, in an app with many views storyboards get messy and overloaded. It then becomes easier to use programmatic views and try not to use storyboards. Otherwise there's no disadvantage to using them.</p><h2 id="the-best-way-that-ive-found-to-create-view-controllers-programmatically-is-this"><a href="#the-best-way-that-ive-found-to-create-view-controllers-programmatically-is-this" class="header-link">The best way that I've found to create View Controllers programmatically is this.</a></h2><p><strong>We'll be making a simple UI with a label, image and button as an example.</strong></p><p>First, create your <code>UIViewController</code>. We'll call it <code>SampleViewController</code></p><pre><code class="language-swift">import UIKit
class SampleViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
</code></pre><p>There. Now lets add a few methods underneath the <code>viewDidLoad()</code> function.</p><pre><code class="language-swift">private func setUpViews() {
}
private func setUpConstraints() {
}
</code></pre><p>These methods will hold our UI setup code. Let's add some variables to the top of this class to reference the 3 UI item's we're going to add</p><pre><code class="language-swift">class SampleViewController: UIViewController {
var label: UILabel?
var button: UIButton?
var image: UIImage?
...
</code></pre><h2 id="great-now-lets-populate-those-variables"><a href="#great-now-lets-populate-those-variables" class="header-link">Great! Now lets populate those variables.</a></h2><p>In the <code>setUpViews()</code> function put this code in.</p><pre><code class="language-swift">private func setUpViews() {
label = UILabel()
label?.text = "Hello World!"
label?.translatesAutoresizingMaskToConstraints = false //Important to do with all views. If you don't set this to false, iOS will break all the constraints you will set.
button = UIButton()
button?.titleLabel.text = "Press Me"
button?.tintColor = .systemBlue
button?.translatesAutoresizingMaskToConstraints = false //Important
button?.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside) //Will return an error right now, ignore it as we haven't added the target function yet.
image = UIImage()
image?.image = UIImage(systemName: "gamecontroller")
image?.translatesAutoresizingMaskToConstraints = false //Important
//Add the views we just created to the view hierarchy
view.addSubview(label!) //we can force-unwrap these because we know we just made them and they won't be nil.
view.addSubview(button!)
view.addSubview(image!)
}
</code></pre><p>Awesome! It's easy right? You have complete access to each view you're adding right here in this function. You modify it and add targets and images cleanly and knowing exactly what each one is going to be.</p><h2 id="now-lets-add-some-constraints-and-lay-out-our-ui"><a href="#now-lets-add-some-constraints-and-lay-out-our-ui" class="header-link">Now lets add some constraints and lay out our UI.</a></h2><p>We'll be using layout anchors to layout our views. They're much simpler to deal with than constraints.</p><p>In the <code>setUpConstraints()</code> function we defined before enter this.</p><pre><code class="language-swift">private func setUpConstraints() {
NSLayoutConstriant.activate([
// Place the label in the top
label.topAnchor.constraint(equalTo: view.topAnchor),
label.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 16),
label.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -16),
// put the button centered below the label
button.topAnchor.constraint(equalTo: label.bottomAnchor),
button.widthAnchor.constraint(equalToConstant: 250),
button.heightAnchor.constraint(equalToConstant: 100),
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
//put the image below the button
image.topAnchor.constraint(equalTo: button.bottomAnchor),
image.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 16),
image.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -16)
])
}
</code></pre><p>The function <code>NSLayoutConstraint.activate([])</code> activates multiple constraints at once. So we use it here to activate all of ours at once.</p><p><strong>Make sure to add the <code>setUpViews()</code> and <code>setUpConstraints</code> to your <code>viewDidLoad()</code> function. Otherwise our code won't ever be executed :).</strong></p><p>Let's also add the button target code at the end of the file:</p><pre><code class="language-swift">@objc func buttonPressed() {
print("You Pressed the Button!")
}
</code></pre><h2 id="sweet-youre-all-done-youve-made-a-uiviewcontroller-with-a-ui-without-any-storyboards"><a href="#sweet-youre-all-done-youve-made-a-uiviewcontroller-with-a-ui-without-any-storyboards" class="header-link">Sweet! You're all done! You've made a UIViewController with a UI without any storyboards.</a></h2><hr><p>Here's the full code:</p><pre><code class="language-swift">import UIKit
class SampleViewController: UIViewController {
var label: UILabel?
var button: UIButton?
var image: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
setUpViews()
setUpConstraints()
}
private func setUpViews() {
label = UILabel()
label?.text = "Hello World!"
label?.translatesAutoresizingMaskToConstraints = false //Important to do with all views. If you don't set this to false, iOS will break all the constraints you will set.
button = UIButton()
button?.titleLabel.text = "Press Me"
button?.tintColor = .systemBlue
button?.translatesAutoresizingMaskToConstraints = false //Important
button?.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside) //Will return an error right now, ignore it as we haven't added the target function yet.
image = UIImage()
image?.image = UIImage(systemName: "gamecontroller")
image?.translatesAutoresizingMaskToConstraints = false //Important
//Add the views we just created to the view hierarchy
view.addSubview(label!) //we can force-unwrap these because we know we just made them and they won't be nil.
view.addSubview(button!)
view.addSubview(image!)
}
private func setUpConstraints() {
NSLayoutConstriant.activate([
// Place the label in the top
label.topAnchor.constraint(equalTo: view.topAnchor),
label.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 16),
label.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -16),
// put the button centered below the label
button.topAnchor.constraint(equalTo: label.bottomAnchor),
button.widthAnchor.constraint(equalToConstant: 250),
button.heightAnchor.constraint(equalToConstant: 100),
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
//put the image below the button
image.topAnchor.constraint(equalTo: button.bottomAnchor),
image.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 16),
image.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -16)
])
}
@objc func buttonPressed() {
print("You Pressed the Button!")
}
}
</code></pre>https://khanwinter.com/2020-11-04-Getting-Started-With-Vapor-4Getting Started With Vapor 4.02020-11-04T06:00:00.000ZBuilding a small Toggl Clone using Vapor.<h2 id="what-is-vapor"><a href="#what-is-vapor" class="header-link">What is Vapor?</a></h2><p><a href="https://vapor.codes/">Vapor</a> is a modern server-side swift framework. It's community built , with tons of support for databases, hosting and user management. It's built on my favorite language <strong>Swift</strong>, meaning it's easy to learn, and has type safety built in.</p><p>I'll be building a small time tracking API similar to <a href="https://toggl.com/">Toggl</a> over the course of a few posts, this is the first in this series.</p><h2 id="get-started--installation"><a href="#get-started--installation" class="header-link">Get Started - Installation</a></h2><h3 id="install-swift"><a href="#install-swift" class="header-link">Install Swift</a></h3><p>If you're running a Mac, and already have Xcode installed. Great, you can skip this step. If not, go to the <a href="https://apps.apple.com/us/app/xcode/id497799835?mt=12">App Store</a> and download it.</p><p>If you're running a Linux computer, you'll have to go over to the <a href="https://swift.org/download/#using-downloads">Swift Downloads</a> page, and follow the instructions there for your distribution.</p><h3 id="install-vapor"><a href="#install-vapor" class="header-link">Install Vapor</a></h3><p>We're going to need the vapor toolbox to run any project we make.</p><p>To install it on mac, you'll need <a href="https://brew.sh/">homebrew</a> installed. Once installed, run <code>brew install vapor</code> and homebrew will install it all for you. Then when it's done, verify your installation by running <code>vapor -help</code>. You should see a list of available commands.</p><p>If you're on Linux, you'll have to build vapor from source. Just run these commands in a terminal.</p><pre><code class="language-shell">git clone https://github.com/vapor/toolbox.git
cd toolbox
git checkout <desired version>
make install
</code></pre><p>Then verify it's installed correctly by running <code>vapor -help</code>.</p><h2 id="creating-a-project"><a href="#creating-a-project" class="header-link">Creating a project</a></h2><p>Now, we're going to set up our working directory. I'm going to build a time logging API, so I'll name my project TimeLog.</p><p>We'll just run <code>vapor new TimeLog</code> and follow the prompts given to you. You'll probably want to answer no to all the questions and get a bare-bones template to get started. For that, you can add a <code>-n</code> flag to the end of the prevous command like this <code>vapor new TimeLog -n</code>.</p><p>Sweet! Now you have an empty vapor project!</p><h2 id="get-a-database-running"><a href="#get-a-database-running" class="header-link">Get a Database Running</a></h2><p>I'm going to be using the Postgres database, as it's the one I have the most experience with. Vapor <a href="https://github.com/vapor/sqlite-nio">supports</a> <a href="https://github.com/vapor/fluent-mongo-driver">tons</a> <a href="https://github.com/vapor/mysql-nio">of</a> <a href="https://github.com/vapor/postgres-nio">databases</a>. So you should be able to follow along with this tutorial no matter which one you choose.</p><p>If you don't have any databases installed, I'd recommend Postgres. <a href="https://gist.github.com/ibraheem4/ce5ccd3e4d7a65589ce84f2a3b7c23a3">This gist</a> helped me a lot when I first installed it.</p><p>Since I'm using Postgres, I've created a database named <code>time-table</code> and opened the database to the url <code>postgres://<Admin-Name>:@localhost:5432/time-table</code>. Since this isn't goint to be a public project quite yet, I left off giving the database a password. If you're planning on releasing this server to the world, <em>you need a password</em>.</p><p>Once you have the url of your database, make a <code>.env</code> file in the root directory of your project. Then, insert the url you just made like this <code>DB_URL=postgres://<Admin-Name>:@localhost:5432/time-table</code> on the first line. Then you're all set with your database.</p><h2 id="finally-swift"><a href="#finally-swift" class="header-link">Finally, Swift</a></h2><h3 id="dependencies"><a href="#dependencies" class="header-link">Dependencies</a></h3><p>Make an Xcode project by running <code>vapor xcode</code> in your project's directory.</p><p>For this series, I'm going to be using a few SPM packages. If you're following along, install them by opening the <code>Package.swift</code> and adding these three dependencies below <code>.product(name: "Vapor", package: "vapor"),</code>:</p><pre><code class="language-swift">.product(name: "Fluent", package: "fluent"),
.product(name: "FluentPostgresDriver", package: "fluent-postgres-driver"),
.product(name: "JWT", package: "jwt")
</code></pre><p>Then, below where it says <code>.package(url: "https://github.com/vapor/vapor.git", from: "4.0.0"),</code> add these lines.</p><pre><code class="language-swift">.package(url: "https://github.com/vapor/fluent.git", from: "4.0.0"),
.package(url: "https://github.com/vapor/fluent-postgres-driver.git", from: "2.0.0"),
.package(url: "https://github.com/vapor/jwt.git", from: "4.0.0-rc.1"),
</code></pre><p>This will install Fluent, Vapor's database framework, JWT, which we'll use for user authentication and the Fluent Postgres Driver so we can use our Postgres database.</p><h3 id="connect-the-database"><a href="#connect-the-database" class="header-link">Connect the database</a></h3><p>In your <code>configure.swift</code> file under Sources > App add the following extension to the top of the file.</p><pre><code class="language-swift">extension Application {
static let databaseURL = URL(string: Environment.get("DB_URL")!)!
}
</code></pre><p>This uses the <code>.env</code> file we made earlier to get the database url. You can use <code>.env</code> files for other things like admin keys and passwords you don't want plain in your code for anyone to see.</p><p>Now lets use that url in the <code>configure(_ )</code> function add the following line to the top.</p><pre><code class="language-swift">try app.databases.use(.postgres(url: Application.databaseURL), as: .psql)
</code></pre><p>Right now, Xcode will throw an error, so import the required dependencies at the top of the file.</p><pre><code class="language-swift">import Fluent
import FluentPostgresDriver
import JWT
</code></pre><h2 id="conclusion"><a href="#conclusion" class="header-link">Conclusion</a></h2><p>There we go! You now have an empty vapor project with an empty database and all the dependencies installed. I'll be making a part 2 soon for this series, when it's up it'll be linked right here!</p>https://khanwinter.com/2019-12-17-Save-Array-Of-Any-Object-In-RealmSave Arrays of Objects in Realm2020-05-14T05:00:00.000ZCreate a mapping field on a realm object to easily save and retrieve arrays of anything from a realm object.<h2 id="the-problem-realm-only-allows-you-to-save-lists-of-objects-like-arrays-in-the-codelistlttgtcode-format"><a href="#the-problem-realm-only-allows-you-to-save-lists-of-objects-like-arrays-in-the-codelistlttgtcode-format" class="header-link">The problem: Realm only allows you to save lists of objects (like arrays) in the <code>List<T></code> format.</a></h2><p>For instance we'll take this dog object. The dog can have multiple Collars, but you have to store them in a list like so.</p><pre><code class="language-swift">class Dog: Object {
@objc dynamic var name = ""
@objc dynamic var age = 0
let collars = List<Collar>()
}
class Collar: Object {
@objc dynamic var color = "Blue"
@objc dynamic var favorite = false
}
</code></pre><p>This makes for slightly confusing code when trying to retrieve the collars from the dog and do other operations. However, we can abstract the list and make it an array property by adding one propery to the Dog object</p><pre><code class="language-swift">class Dog: Object {
@objc dynamic var name = ""
@objc dynamic var age = 0
var collars: [Collar] {
get {
return _collars.map{ $0 }
}
set {
_collars.removeAll()
_collars.append(objectsIn: newValue)
}
}
// The original property is here, we simply renamed it and added the above collar variable.
let _collars = List<Collar>()
}
</code></pre><p>We change the <code>collars</code> property to <code>_collars</code>, then make a new variable <code>collars</code> that realm wont track.</p><p>This variable has two key parts: a getter, and a setter.</p><p>The getter simply maps the <code>_collars</code> object to an array and returns the array.</p><pre><code class="language-swift">get {
return _collars.map{ $0 }
}
</code></pre><p>The setter removes all the values using <code>.removeAll()</code> and then sets the <code>_collars</code> list from the given array.</p><p>Now we can use the <code>collars</code> property just like any other array. Just make sure to use it in a write block when writing the object to storage like so:</p><pre><code class="language-swift">try! realm.write {
dog.collars = [Collar(color: "Red", favorite: false), Collar(color: "Blue", favorite: true)]
}
</code></pre><p>This method makes it easier to work with the <code>List<></code> object and allows for simpler, easier to read code in the rest of your app.</p>https://khanwinter.com/2020-05-11-Subclassing-Sprites-In-SpriteKitSubclassing Sprites in SpriteKit2020-05-11T05:00:00.000ZSubclass a SKSpriteNode to make multiple types of the same sprite to save time and energy.<h2 id="subclassing"><a href="#subclassing" class="header-link">Subclassing</a></h2><p>Subclassing <code>SKSpriteNode</code>s makes it easy to have multiple classes of a similar sprite. One use case for this would be in a breakout-like game where there are multiple kinds of blocks that can do different things.</p><p>In this example we'll start with a normal <code>BlockNode</code>. Then, we'll subclass it to create a new type of block called <code>ExplosionNode</code>.</p><h2 id="block-node"><a href="#block-node" class="header-link">Block Node</a></h2><p>The code for the original block node looks like this.</p><pre><code class="language-swift">class BlockNode: SKSpriteNode {
var background: SKShapeNode?
convenience init(size: CGSize) {
self.init(texture: nil, color: .clear, size: size)
background = SKShapeNode(rectOf: size, cornerRadius: 5)
background!.fillColor = .darkGray
background!.strokeColor = .lightGray
addChild(background!)
}
override init(texture: SKTexture?, color: UIColor, size: CGSize) {
super.init(texture: texture, color: color, size: size)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func endBlock() {
guard let scene = self.scene as? GameScene else { self.removeFromParent(); return }
removeFromParent()
}
}
</code></pre><p>This simple SKSpriteNode class creates a block with a grey background. It can be created using <code>BlockNode(size: CGSize())</code>.</p><p>In the game when the ball touches the block, <code>endBlock()</code> is called to remove the block.</p><p>Now that we have a normal block lets make another, more fun, block.</p><h2 id="explosion-block"><a href="#explosion-block" class="header-link">Explosion Block</a></h2><p>We can easily subclass the <code>BlockNode</code> by adding it to the class definition of <code>ExplosionBlockNode</code> like so.</p><pre><code class="language-swift">class ExplosionBlockNode: BlockNode {
...
}
</code></pre><p>Now lets override the init function that makes the block grey, and instead make it red.</p><pre><code class="language-swift"> convenience init(size: CGSize) {
self.init(texture: nil, color: .clear, size: size)
background = SKShapeNode(rectOf: size, cornerRadius: 5)
background!.fillColor = .red
background!.strokeColor = .darkGray
addChild(background!)
}
</code></pre><p>Now the block can be created using the same method as the normal block, except now using <code>ExplosionBlockNode(size: CGSize())</code>. This initializer will create a red block instead of a grey one.</p><p>We can also change the <code>endBlock()</code> function to being special for the explosion block by simply adding it to the <code>ExplosionBlockNode</code> like this.</p><pre><code class="language-swift"> override public func endBlock() {
guard let scene = self.scene as? GameScene else { self.removeFromParent(); return }
let scoreLabel = SKLabelNode(text: "Boom")
scoreLabel.color = .red
scoreLabel.position = self.position
scoreLabel.fontSize = 24
scene.addChild(scoreLabel)
scoreLabel.run(.sequence([.group([.moveTo(y: self.position.y + 50, duration: 0.4), .fadeOut(withDuration: 0.3)]), .removeFromParent()]))
scene.addChild(field)
field.run(SKAction.sequence([SKAction.wait(forDuration: 0.05),SKAction.removeFromParent()]))
removeFromParent()
}
</code></pre><p>Now when we call the <code>endBlock</code> method on the explosion block it'll add a little label saying "Boom" that fades quickly and animates up.</p><p>The final explosion block code looks like this.</p><pre><code class="language-swift">class ExplosionBlockNode: BlockNode {
convenience init(size: CGSize) {
self.init(texture: nil, color: .clear, size: size)
background = SKShapeNode(rectOf: size, cornerRadius: 5)
background!.fillColor = .red
background!.strokeColor = .darkGray
addChild(background!)
}
override public func endBlock() {
guard let scene = self.scene as? GameScene else { self.removeFromParent(); return }
let scoreLabel = SKLabelNode(text: "Boom")
scoreLabel.color = .red
scoreLabel.position = self.position
scoreLabel.fontSize = 24
scene.addChild(scoreLabel)
scoreLabel.run(.sequence([.group([.moveTo(y: self.position.y + 50, duration: 0.4), .fadeOut(withDuration: 0.3)]), .removeFromParent()]))
scene.addChild(field)
field.run(SKAction.sequence([SKAction.wait(forDuration: 0.05),SKAction.removeFromParent()]))
removeFromParent()
}
}
</code></pre>https://khanwinter.com/2020-05-5-Using-Dependencies-In-Swift-ScriptsUsing Dependencies in Swift Scripts2020-05-05T05:00:00.000ZUse swift-sh to import any SPM module into your scripts.<h2 id="swiftsh"><a href="#swiftsh" class="header-link">Swift-sh</a></h2><p>Swift-sh is an amazing dependency package from <a href="https://github.com/mxcl/swift-sh">Max Howel</a>. The library makes it easy to use any Swift Package Manager (SPM) package into your Swift scripts. (<a href="https://windchillblog.com/snippets/2020/05/05/scripting-with-swift">more on writing Swift scripts</a>)</p><p>In this example I'll be importing the <a href="https://github.com/paulot/Colors">Colors library by paulot</a> and the <a href="https://github.com/jkandzi/Progress.swift">Progress library from jkandzi</a> . A mixture of these two libraries makes for fun, colorful script output like this.</p><img src="Screen Shot 2020-05-05 at 2.10.01 PM.png" alt="cool terminal example"/><p>Which I think is fun. We'll be importing both libraries using Swift-sh.</p><h2 id="install-swiftsh"><a href="#install-swiftsh" class="header-link">Install Swift-sh</a></h2><p>To install swift-sh you'll need <a href="https://docs.brew.sh/Installation">homebrew</a> installed on your mac.</p><p>Then, it's just a simple command to install</p><p><code>brew install mxcl/made/swift-sh</code></p><small>Go check out [Max's GitHub](https://github.com/mxcl) and [Patreon](https://www.patreon.com/mxcl). He's an amazing programmer and has built tons of important open source tools</small><h2 id="create-the-script"><a href="#create-the-script" class="header-link">Create The Script</a></h2><p><code>cd</code> into the directory you'll run your script in and run <code>touch main.swift</code> to create an empty script. Then make it executable using <code>chmod +x main.swift</code>.</p><p>Now open the file using any editor and enter this on the first line.</p><pre><code class="language-swift">#!/usr/bin/swift sh
</code></pre><p>This tells the terminal to use <code>swift sh</code> to execute our script, which is exactly what we want.</p><h2 id="import-progress"><a href="#import-progress" class="header-link">Import Progress</a></h2><p>Next, in the <code>main.swift</code> file do a normal import like you would in any other Swift project.</p><pre><code class="language-swift">import Foundation
import Progress
import Colors
</code></pre><p>If you run this code right now you'll get an error because there aren't any packages linked to your script. Let's import Progress with swift-sh like this.</p><pre><code class="language-swift">import Progress // jkandzi/Progress.swift
</code></pre><p>The comment after the import statement tells swift-sh to retreive the SPM package Progress.swift from GitHub. You tell swift-sh where the package is by giving it the path to the repository on GitHub.</p><h2 id="import-colors"><a href="#import-colors" class="header-link">Import Colors</a></h2><p>Now that Progress is imported successfully lets import Colors.</p><p>Colors doesn't have SPM support naturally, so I'm going to give it one. I've downloaded the source code from here and grabbed all the files in the folder Colors. I've deleted the files <code>Colors.o</code><code>Info.plist</code> and <code>ANSITextColorCode.o</code> as the script doesn't need them.</p><p>Now go back to the directory your script is in and run <code>mkdir Colors</code>. <code>cd</code> into the new directory and run <code>mkdir Sources</code>.</p><p>Next, drag and drop the downloaded Colors folder into the Sources folder you just made.</p><p>The last thing we need to make is a <code>Package.swift</code> file. This tells SPM what code is available for this package. Make one using <code>touch Package.swift</code></p><p>Your file structure should look like this.</p><img src="Screen Shot 2020-05-05 at 2.31.22 PM.png" alt="file structure"/><p>Open Package.swift and copy-paste the following into the file.</p><pre><code class="language-swift">// swift-tools-version:5.1
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "Colors",
products: [
.library(
name: "Colors",
targets: ["Colors"]),
],
targets: [
.target(
name: "Colors",
dependencies: [])
]
)
</code></pre><p>Save it, and you now have a SPM package!</p><p>Lets go back to the <code>main.swift</code> file and import Colors using swift-sh like this.</p><pre><code class="language-swift">import Colors // ./Colors/
</code></pre><h2 id="run-the-script"><a href="#run-the-script" class="header-link">Run The Script</a></h2><p>Now that all the dependencies are working, your script should look like this.</p><pre><code class="language-swift">#!/usr/bin/swift sh
import Foundation
import Colors // ./Colors/
import Progress // jkandzi/Progress.swift
</code></pre><p>Add this code to give some fun output when you run the script.</p><pre><code class="language-swift">#!/usr/bin/swift sh
import Foundation
import Colors // ./Colors/
import Progress // jkandzi/Progress.swift
print(Colors.Red("The Script is Running, Downloading Important Data..."))
for _ in Progress(0...100) {
sleep(1)
}
print(Colors.Green("Script Complete!"))
</code></pre><p>To run the script run <code>./main.swift</code> in the same directory as the script. If you get an error saying <code>zsh: permission denied: ./main.swift</code> run <code>chmod +x main.swift</code> to fix it.</p><p>Your terminal should now be counting down from 101, nice job!</p><img src="Screen Shot 2020-05-05 at 2.40.38 PM.png" alt="finished script"/><p>Full usage documentation for swift-sh can be found <a href="https://github.com/mxcl/swift-sh#usage">here</a>. I've only covered two ways of using this library but there's much more you can do with it.</p>https://khanwinter.com/2020-02-21-Easy-Container-Controllers-in-SwiftEasy Container Controllers in Swift2020-02-23T06:00:00.000ZCreate a container controller to separate and simplify your UIKit views.<h2 id="what-are-container-controllers"><a href="#what-are-container-controllers" class="header-link">What are container controllers?</a></h2><p>Container controllers are like a larger, older brother to the UIViewController. They come in extremely handy when dealing with things like Auth state and UIViewController transitions. UIKit already comes with a few container controllers that you most likely know very well. Some examples of these are the <code>UINavigationController</code>, or the <code>UITabViewController</code>. Each of those controllers have one or more sub-controllers and help with things like transitions or UI that needs to stay similar between each of the sub-controllers they contain.</p><h2 id="why-should-i-spend-the-time-to-make-one"><a href="#why-should-i-spend-the-time-to-make-one" class="header-link">Why should I spend the time to make one?</a></h2><p>Container controllers do a few things that make life as a developer much easier. First, they naturally separate your UI code. Instead of having your <code>Auth.onSignOut()</code> function in your AppDelegate (which can get very messy, very quickly), you have the <code>Auth.onSignOut()</code> happen in a separated container that can directly interact with it's sub-controllers without having to use any <code>window.controllers.first</code> junk.</p><h2 id="first-we-need-a-few-extensions"><a href="#first-we-need-a-few-extensions" class="header-link">First we need a few extensions.</a></h2><p>Make a new file for this extension and put the following code in.</p><pre><code class="language-swift">import UIKit
extension UIViewController {
/*
This method gives us a function to quickly add a child view controller,
it takes care of adding the child, adding the view as a subview and then
telling the subview that it's moved to a new parent.
*/
func add(_ child: UIViewController) {
addChild(child)
view.addSubview(child.view)
child.didMove(toParent: self)
}
/*
This method makes it simpler to remove a child view controller.
*/
func remove() {
// First we check if the parent view controller exists,
// if not we can stop the whole thing right now.
guard parent != nil else { return }
// Here we enumerate through each subview telling them that their
// going to be removed and then removing them.
children.forEach({
$0.willMove(toParent: nil)
$0.view.removeFromSuperview()
$0.removeFromParent()
})
// These tell this view controller that it will be moving to nil parent
// and then removes it from the parent view controller.
willMove(toParent: nil)
view.removeFromSuperview()
removeFromParent()
}
}
</code></pre><p>Read through the comments above for more detail on this peice of code. Basically this extension makes it easier to add and remove child view controllers.</p><h2 id="implementing-the-container-controller"><a href="#implementing-the-container-controller" class="header-link">Implementing the Container Controller.</a></h2><p>Here's the main code of the controller.</p><pre><code class="language-swift">import UIKit
class ContainerController: UIViewController {
static let shared = ContainerController()
var displayedView: UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
if displayedView == nil {
displayedView = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()
add(displayedView!)
}
}
public func transitionTo(_ view: UIViewController) {
if displayedView != nil{
displayedView!.remove()
}
add(view)
displayedView = view
}
}
</code></pre><p>Simple. In it we give a simple UIViewController with one method called <code>transitionTo(_ view:)</code> which can be called by outside controllers to change what view is displayed.</p><p>We also added a default view to be displayed in the <code>viewDidLoad()</code>. It just makes sure that when we initialize this class it automatically populates the <code>displayedView</code> variable. In this example I intitialize the root controller from the <code>main</code> storyboard, but you can replace it with any view controller or even omit this code entirely.</p><p>Also! I created a singleton by adding a <code>static let shared = ContainerController()</code> variable. You can read more about singletons <a href="https://cocoacasts.com/what-is-a-singleton-and-how-to-create-one-in-swift">here</a> but they basically make it so we can keep track of one container controller for the entire app.</p><small>TL;DR: Some people dislike singletons. However, in this case the ability to access the container controller from anywhere in your app without having to implement a delegate method or callback into every single view controller greatly outweighs the tiny bit of overhead a singleton creates. </small><h2 id="sweet-how-do-i-use-it"><a href="#sweet-how-do-i-use-it" class="header-link">Sweet. How do I use it?</a></h2><p>Really easily. The main way I use it is in my AppDelegate (or SceneManager) like so:</p><pre><code class="language-swift">import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
var controller: UIViewController!
let container = ContainerController.shared
// In this example I'm using FirebaseAuth for authentication.
// I check if a user is logged in, and if they are I send them
// to the "Admin" view. Otherwise they go to the "Auth" view.
if Auth.auth().currentUser != nil {
let storyboard = UIStoryboard(name: "Admin", bundle: nil)
controller = storyboard.instantiateInitialViewController()!
} else {
let storyboard = UIStoryboard(name: "Auth", bundle: nil)
controller = storyboard.instantiateInitialViewController()!
}
// Make the container the root view controller.
self.window?.rootViewController = container
self.window?.makeKeyAndVisible()
// Transition to the pre-defined controller
container.transitionTo(controller)
return true
}
}
</code></pre><p>Really fairly simple to use. It can also be used from a view controller after the app is already running. To access the container at the root one can simply use <code>ContainerController.shared</code>.</p><hr><p>This post was heavily inspired by John Sundell's post <a href="https://www.swiftbysundell.com/articles/custom-container-view-controllers-in-swift/">here</a>. Go check it out, he's an amazing writer and developer.</p>https://khanwinter.com/2019-12-19-Firebase-Auth-State-Listeners-iOSFirebase Auth State Listeners iOS2019-12-19T06:00:00.000ZReceive notification when firebase users auth state changes on iOS.<h2 id="firebase-auth-lets-you-pass-a-handler-function-when-the-auth-state-changes"><a href="#firebase-auth-lets-you-pass-a-handler-function-when-the-auth-state-changes" class="header-link">Firebase auth lets you pass a handler function when the auth state changes.</a></h2><p>In my use case I need to receive state change alerts. My user might be disabled by an admin or a bot, or the account is stopped for security reasons. But, I need to update user interfaces and data code to match that the user is logged out.</p><h2 id="fortunately-its-easy-with-firebase"><a href="#fortunately-its-easy-with-firebase" class="header-link">Fortunately its easy with firebase</a></h2><p>Somewhere in your startup code -I use a container view for all my views, but the AppDelegate or SceneManager works well here- put this snippet in.</p><pre><code class="language-swift">func setAuthListener() {
Auth.auth().addStateDidChangeListener { (auth, user) in
guard let user else {
// The user is either logged out, or something went wrong.
// You can use the Auth.auth().currentUser.reload() function
// to try to find the error using the FIRErrorCode enum
return
}
// Otherwise, we now have a signed in user. Usually that just means
// we carry on, but you could put some custom handler here too.
}
}
</code></pre><p>Then just fire that function somewhere at the start of your app, or in a container controller like we made in <a href="https://windchillblog.com/2020/2/21/easy-container-controllers-in-swift">this</a> article.</p>