[SC]()

iOS. Apple. Indies. Plus Things.

Thread's iOS Interface in SwiftUI

// Written by Jordan Morgan // Jul 19th, 2023 // Read it in about 3 minutes // RE: SwiftUI

This post is brought to you by Emerge Tools, the best way to build on mobile.

In my previous snip, I used a cheeky Threads knockoff interface to demonstrate the subject matter. It occurred to me how crazy it is that I can even do this. SwiftUI has done away with so much work that used to be pervasive in interface programming, interface builder or not. Again, look at this UI:

SwiftUI version of Threads

I realize that lines of code is rarely a good measurement of just about any relevant programming metric (looking at you, productivity). That said, the fact that I could create this entire interface in 148 lines of code (and 18 of it is variable declarations) is mind boggling:

struct ContextMenuPreviewView: View {
    private let symbols: [String] = [
        "square.and.pencil.circle.fill",
        "calendar.badge.checkmark",
        "square.and.arrow.up.circle.fill",
        "book.pages.fill",
        "pencil.tip.crop.circle.badge.plus",
        "apple.terminal.on.rectangle.fill",
        "network.slash",
        "snowboard.fill",
        "sun.snow.circle.fill",
        "circle.lefthalf.striped.horizontal",
        "lock.app.dashed",
        "staroflife.shield.fill"
    ]
    
    private let columnLayout = Array(repeating: GridItem(), count: 3)

    var body: some View {
        VStack {
            Group {
                HStack {
                    Image(systemName: "globe")
                        .fontWeight(.semibold)
                    Spacer()
                    Image(systemName: "plus.circle")
                        .fontWeight(.semibold)
                }
                .padding(.bottom, 32)
                HStack {
                    VStack(alignment: .leading, spacing: 8) {
                        Text("Jordan Morgan")
                            .font(.system(size: 24, weight: .bold, design: .default))
                        Text("lookitsjordanmorgan")
                            .font(.system(size: 16))
                    }
                    Spacer()
                    Image("Me")
                        .resizable()
                        .frame(width: 64, height: 64)
                        .clipShape(Circle())
                        .contextMenu {
                            Button("Change Picture") {
                                
                            }
                        }
                }
                .padding(.bottom, 16)
                Text("🧑🏻‍💻 Writing bugs since iOS 4\n📍 Missouri\n🏀 New app announcing soon!")
                    .font(.system(size: 14))
                    .multilineTextAlignment(.leading)
                    .lineSpacing(10)
                    .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
                    .padding(.bottom, 16)
                HStack(spacing: 0) {
                    ForEach(["JM", "BM", "RM"], id:\.self) { item in
                        Text(item)
                            .foregroundStyle(Color.white)
                            .fontWeight(.bold)
                            .padding(6)
                            .background {
                                Circle()
                                    .fill(Color.blue)
                                    .shadow(radius: 5)
                            }
                            .offset(x: item == "BM" ? -10 : 0)
                            .offset(x: item == "RM" ? -20 : 0)
                    }
                    Text("1.2 mil followers · rickroll.net/about")
                        .fontWeight(.medium)
                        .foregroundStyle(Color(uiColor: .secondaryLabel))
                        .offset(x: -10)
                    Spacer()
                }
                .font(.system(size: 12))
                .padding(.bottom, 16)
                HStack {
                    Text("Edit profile")
                        .frame(width: 160, height: 44)
                        .background {
                            RoundedRectangle(cornerRadius: 8)
                                .strokeBorder(Color(uiColor: .systemGray5), lineWidth: 1)
                        }
                    Spacer()
                    Text("Share profile")
                        .frame(width: 160, height: 44)
                        .background {
                            RoundedRectangle(cornerRadius: 8)
                                .strokeBorder(Color(uiColor: .systemGray5), lineWidth: 1)
                        }
                }
                .font(.system(size: 16))
                .fontWeight(.semibold)
                .padding(.bottom, 16)
            }
            .padding(.horizontal, 16)
            HStack {
                Text("Favorites")
                    .frame(maxWidth: .infinity)
                    .padding(.bottom, 16)
                    .overlay(alignment: .bottom) {
                        Rectangle()
                            .frame(height: 1)
                        
                    }
                Text("Replies")
                    .frame(maxWidth: .infinity)
                    .foregroundStyle(Color(uiColor: .tertiaryLabel))
            }
            .font(.system(size: 16))
            .fontWeight(.semibold)
            .padding(.bottom, 16)
            ScrollView {
                LazyVGrid(columns: columnLayout) {
                    ForEach(symbols, id: \.self) { index in
                        Button {

                        } label: {
                            Image(systemName: index)
                                .font(.largeTitle)
                                .padding()
                        }
                        .buttonStyle(.plain)
                        .contentShape(ContentShapeKinds.contextMenuPreview, Circle())
                        .contextMenu {
                            Button {
                                
                            } label: {
                                Label("Share", systemImage: "square.and.arrow.up")
                            }

                        }
                    }
                }
            }
            .padding(.horizontal, 16)
        }
        .background(Color.white)
    }
}

I get it, too. A lot more code is in store to make anything usable for something as complex as a social networking app. Or, well, any app. That was always my first thought when SwiftUI first hit the general public, and immediately designers kept tweeting those oh-so-retweetable “speed run” posts.

If I’m being honest, my initial reaction to those tweets was one of…a slight annoyance, maybe? “What’s the point, that’s not even a real app - or close to it. It takes so much more”, I would think. I felt as though it undermined what it actually took to make these kind of apps. A few years removed, though, I think I was missing the point: SwiftUI has made iOS infinitely more accessible, it’s made things that were an encumbrance easy flying, interface code vastly more understandable, and has shortened general development life cycles by several orders of magnitude. I feel confident saying that now that I’ve done heavy production work with it.

Look, there are frustrating bugs. But, there always will be in literally all the software that graces God’s green earth. SwiftUI is so much easier to stand up compared to UIKit, I can’t imagine building an entire app without it anymore. I’ll drop down where I need to (e.g. is there seriously no band selection in SwiftUI still? Why can’t I get insane performance on scrolling like I can with UIKit?), but on the whole?

SwiftUI is a cheat code. It’s a 120 FPS 8K television after you just upgraded from a CRT box. It’s taking a quiz when the smartest kid in class slipped you the answers. It’s Twitter before crypto. It’s playing Contra with the Konami code. It’s pizza without pineapples (come at me).

I take the wins when they show up, and SwiftUI is a win for the whole Apple ecosystem.

Until next time ✌️

···

Spot an issue, anything to add?

Reach Out.