-- Leo's gemini proxy

-- Connecting to gemini.circumlunar.space:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini

Creating a gemini browser with Swift UI


When I first read about the Gemini protocol on Hacker News, I immediately wanted to try it out. Never content to do things the easy way, I decided the best way to proceed would be to write my own client. After all, the FAQ says it can be done over the weekend in 100 lines of code. Should be an easy project, right?


Well, two months and 984 lines of code later, I finally have a mostly complete Gemini client! I chose to implement it in Swift because


There wasn't another Swift client.

I'd never written anything in Swift, but I'd heard good things about it on Hacker News.

My primary development environment is a Mac.


From doing some reading about the Apple ecosystem, there are two main application frameworks in use - the Cocoa framework used on MacOS, and the UIKit for iOS and tvOS. SwiftUI is a new player released by Apple in 2019 that tries to unify development across all their platforms. After working with it for a while, it feels similar to Facebook's React-Native, which I use at work.


The traditional way to develop an application for iOS is to use a WYSIWYG system integrated with XCode called Interface Builder. With Interface Builder, you lay out a separate window on a huge canvas for every screen of your app, and drag arrows between them to represent transitions between screens. Into each of these window you drag user interface components, which you can then link to your actual code. It's a system that works well for prototyping, and can be quite effective for apps with a lot of static screens. For components like lists you can create a representative element that gets used as a model for any list element you instantiate at runtime. But for apps with more complicated screens you often end up generating a lot of content dynamically, which somewhat defeats the purpose of using Interface Builder.


SwiftUI is a new way of doings things. Instead of laying things out visually, you create views in code, building them up hierarchically from simpler views. Apple provides a number of basic components you can build from, like "Text" and "Button". By way of example, have a look at the higest level view from Sputnik:


    var body: some View {
        VStack {
            HStack {
                Button( action: {
                    self.document.back()
                } ) {
                    Text("←")
                }
                .padding(.leading, 6)
                Button( action: {
                    self.document.forward()
                } ) {
                    Text("→")
                }
                TextField("Gemini Site", text: $document.navBarUrl, onCommit: browse)
            }

            GeminiDocumentView(document: document)
        }
    }

You can see that the whole document is laid out vertically (in a VStack), and contains two subviews: The URL bar in an HStack with the navigation buttons and search field, and the document itself - GeminiDocumentView.


SwiftUI keeps track of all the views in your app and updates them dynamically when values change. For instance, the dollar sign in "$document.navBarUrl" above marks this value as a two-way binding. When the user types an URL into the TextField, the navBarUrl automatically updates, and likewise if navBarUrl is changed in the code, the changes will be reflected in the navigation bar.


Using SwiftUI for Sputnik means that it will be easier to convert to iOS later if I want to create on iPhone version. There are some drawbacks though. Since it was only recently released, the built-in set of components is still pretty basic. There's no built in way, for instance, to display the loading spinner that's ubiquitous on iPhone. If you want to show a waiting animation, you have to do something complicated with shapes or images.


Even though SwiftUI is designed to be cross-platform, it's clear from the available components that Apple's focus is on the iOS ecosystem. Something that was really lacking for this app was the ability to select and copy text from a Text view. This is really important for a desktop app, but something you don't do often on Mobile. As a result, you can't select text in Sputnik. Hopefully Apple will add this to a later release.


It was an interesting experiment learning Swift and SwiftUI. I'm not sure if I'll ever get the chance to use it for work - most of our mobile development needs to work on both Android and iOS, and we use cross-platform tools for that. There's something satisfying about getting this done, though. Making a Gemini client (or, I imagine, a server) is the perfect scope for a hobby project. It's a great way to learn about protocols and basic application-level networking without getting lost in a mess of details. Just like how a common way to learn about emulation is to create a Chip8 emulator, I expect the creation of Gemini-related software will become a common learning opportunity for a lot of people going forward.


Back

-- Response ended

-- Page fetched on Wed Apr 24 20:46:47 2024