r/SwiftUI

▲ 43 r/SwiftUI+2 crossposts

GlowEffectKit for SwiftUI

GlowEffectKit is a SwiftUI SDK for adding animated glow effect to iOS and macOS components, powered by a bundled Metal shader. More compact version: SwiftUI SDK for animated grow and glow effects powered by bundled Metal shaders.

https://github.com/didisouzacosta/GlowEffectKit

u/Grand_Gur_2800 — 18 hours ago

How can I make this window match macOS 26’s default window corner radius?

I'm working on the "About the app" window of my app on macOS 26 with SwiftUI. Is there any way that could make its corner radius match with macOS 26's default?

u/It_is_Sean — 14 hours ago
▲ 0 r/SwiftUI+1 crossposts

SwiftUI 3D is Flat (visionOS is not)

Short version: on iOS/macOS, SwiftUI's rotation3DEffect / projectionEffect give you per-view 2D projection of one rendered rectangle — not membership in a shared 3D scene. So you can flip one card convincingly, but a cube fails: with six faces there's no shared depth space and no per-frame depth sort.

The tell: render a pure-SwiftUI cube and sweep yaw. At 180° the front face is pointed away, but it still paints on top (declared-last wins), while a Core Animation cube shows the true back face. Painter's order vs depth sort.

The missing primitive has a name: CATransformLayer (non-flattening container) plus the parent sublayerTransform as a shared camera. Apple did build the richer model — transform3DEffect, Rotation3D, perspectiveRotationEffect — but shipped them visionOS-only. The boundary looks deliberate, not an oversight.

Full writeup with the CA-input → SwiftUI-output filter table, rendered witnesses, and a repo with positive (UIKit/AppKit + CA) and negative (pure SwiftUI) controls you can run:

https://aleahim.com/blog/swiftui-3d-is-flat/

u/Super-Storage6685 — 1 day ago
▲ 2 r/SwiftUI+1 crossposts

How do you manage translation JSON files for i18n?

Title: How do you manage translation JSON files? (built a tool for my own pain points, curious if others hit the same walls)

I've been building a multilingual vocabulary app (iOS/macOS, SwiftUI + SwiftData) that ships in 30+ languages, and managing the translation JSON has been way more painful than I expected. Curious how other devs handle this, and whether the specific problems I ran into are universal or just my own workflow being weird.

The problems I kept hitting:

  • Structural drift: With thousands of repeated objects like {"id": "...", "translations": {"en": "...", "ko": "...", ...}}, it's easy for one entry somewhere in the middle to silently lose a field, get a typo'd language key, or mismatch nesting — and standard JSON validators don't catch this, because the JSON is technically still valid, just structurally inconsistent with the rest of the file.
  • Missing/empty language values: Standard validation says the file is fine even if half your language keys are empty strings. You don't find out until QA or a user report.
  • Truncated files: If you're generating translations with an LLM and the output gets cut off mid-response, you end up with a JSON tail that's broken in very specific, recoverable ways — but a normal validator just gives you "unexpected end of input" with no path to fix it.
  • camelCase ↔ snake_case round-tripping: If your app code uses camelCase but your pipeline/backend expects snake_case, converting back and forth loses information — going from snake_case back to camelCase is ambiguous (is it "userID" or "userId"?). I needed something that remembers the original casing per-project so round-trips are lossless.
  • Merging new translations back in without breaking things: getting updated translations back — from a translator, an LLM, or myself editing a spreadsheet — and merging them into the existing file without silently clobbering good data or creating duplicate entries turned out to be its own problem.

What I built:

A macOS app that treats JSON translation files as a first-class problem instead of "just JSON":

  • A syntax validator that recovers from smart quotes, markdown code fences, trailing commas — the common copy-paste mistakes when dealing with LLM output
  • A "pattern stamp" engine that learns the structure of your repeated objects and flags entries that deviate, without caring about the actual values — just the shape
  • Language coverage checking that detects missing keys and can auto-suggest which language a stray value probably belongs to
  • A tail-repair engine specifically for truncated LLM output
  • camelCase/snake_case conversion with per-project memory of original casing
  • A paste-merge tool with conflict detection when combining translation batches

Why this changes how you use AI for translation (and saves tokens):

Most people I see doing this either paste the whole JSON file into an Xcode-integrated AI assistant, or hand-copy fragments into ChatGPT and hope the formatting survives the round trip. Both are wasteful in different ways.

Because this tool can isolate exactly the slice you need — a single language column, a single entry, a single group of entries missing a specific key — you don't have to send your whole dataset to any AI to get one thing translated or fixed. You extract just the piece you actually need, send that to whatever model you want (doesn't have to be the one tied into your IDE), and paste the result back. The merge engine handles conflict detection so pasting back in isn't a manual find-and-replace exercise — it tells you if something you're about to overwrite already differs from what's incoming, instead of silently clobbering it.

This matters more at maintenance time than at initial translation time. Say you're at 10 languages and you add 10 more. You don't need to regenerate all 20 — you extract the existing 10-language values as reference/context, send only the request for the 10 new languages, and merge the result back into the same objects without touching what's already correct. Later, if one specific language needs a wording fix across the whole file (a tone change, a terminology correction, whatever), you can pull out just that one language's values across every entry, hand that single column to an AI, and merge the corrected column back in — instead of re-sending 20 languages' worth of context just to fix one.

The net effect: the LLM call gets smaller and cheaper because you're not sending 19 languages of context to fix the 20th, and the merge step means you're not manually reconciling what came back against what you had.

Who I think this is for:

  • Indie/solo devs doing i18n for their own app who don't want a full localization platform built for teams/CI pipelines (Lokalise, Localazy, etc.) when what they actually have is "a JSON file and a growing list of languages"
  • Anyone generating or maintaining translations via LLM, since most of these failure modes (truncated output, inconsistent formatting, one language quietly missing, wasted context on unchanged languages) are specifically artifacts of an LLM-in-the-loop workflow
  • Anyone who designed their own JSON schema for translations rather than using a framework's built-in i18n format (i18next, .strings, .arb, etc.)

Question for the thread: does this match how you handle i18n JSON and AI-assisted translation, or do you use a completely different structure/approach where these problems don't come up? Trying to figure out if I've been solving a self-inflicted problem or something more people run into.

The philosophy behind the app:
Ultimately, I wanted to build an app that gracefully handles almost every mistake and repetitive task that a beginner or indie developer encounters.
Sure, if you dig deep enough into VS Code, you can technically find all these features. But honestly, what did you think when you first opened VS Code? I remember looking at it and thinking, "Piloting a spaceship would probably be easier than navigating this." I wanted to build something that bypasses that friction entirely.

Please share your thoughts or experiences

reddit.com
u/Content_Day8 — 2 days ago
▲ 193 r/SwiftUI

I open-sourced 112 dot-matrix loading animations for SwiftUI — no images, no dependencies

Dot. Dot. Dooot. 🟦

We're building Mana (an AI-first creation studio for iOS) and wanted the chat's "thinking" indicator to feel alive instead of a stock spinner. We found zzzzshawn's "matrix" (a React/CSS dot-matrix loader collection), loved it, and ported the whole thing to SwiftUI — 112 loaders across square / circular / hex / triangle / 3×3, plus some "fun" silhouettes (heart, arrow, snake) and an icon.

How it works:

  • Zero image assets, zero dependencies. Every loader is animated Circles driven by a single TimelineView.
  • Each loader is a per-cell opacity resolver ported ~1:1 from the upstream CSS keyframes + JS math (spiral snakes, ring waves, a literal heartbeat curve).
  • Deterministic: the same key always maps to the same loader, so they don't reshuffle as SwiftUI rebuilds on scroll. Reduce Motion aware too.

Two ways to use it:

DotmSquare3(size: 28)           // named component, 1:1 with the upstream API
MatrixLoader(.hex(3), size: 28) // by shape id, when the choice is data-driven

There's an interactive gallery + a runnable Swift Playgrounds example in the repo.

It's a derivative port, published with the original author's explicit permission (attribution + link-back throughout). Only the "fun" family is ours. iOS 18+.

Repo: https://github.com/mana-am/matrix-swift

Which one's your favorite? I keep flip-flopping between the hex ripple and the heartbeat.

u/Codenter — 3 days ago
▲ 21 r/SwiftUI+1 crossposts

I’m so sick of SwiftUI LiquidGlass bugs. Just wasted an entire night discovering that @FocusState breaks completely... unless you put a button before the TextField.

I just spent an entire night tearing my hair out over this. I hit a really strange focus bug on iOS 26 and want to share it, both as a warning and in case someone knows what's actually going on under the hood.

Setup: a bottom "add item" bar living in .safeAreaBar(edge: .bottom), inside a NavigationStack that sits in a TabView. The bar shows a suggestions row above the text field, but only while the field has focus:

struct AddItemBar: View {
     var text: String
     private var isFocused: Bool

    var body: some View {
        VStack {
            if isFocused {
                SuggestionsView(...)   // horizontal ScrollView, .glassEffect()
            }

            HStack(spacing: 0) {
                TextField("1 kg tomatoes", text: $text)
                    .focused($isFocused)
                    .padding(.vertical, 12)
                    .padding(.horizontal)
                    .glassEffect()

                Button("Add", systemImage: "plus") { ... }
                    .buttonStyle(.glassProminent)
                    .buttonBorderShape(.circle)
            }
        }
        .animation(.default, value: isFocused)
    }
}

The bug: tap the field → keyboard comes up, typing works fine, but isFocused never becomes true. I put a debug Text(isFocused ? "FOCUS" : "NO FOCUS") in the bar: it says NO FOCUS the whole time the keyboard is up. So anything gated on the focus state (my suggestions row) simply never appears. No warnings, no console output, nothing.

What did NOT fix it:

  • GlassEffectContainer + glassEffectID on every glass element (this is supposed to be the blessed way to handle dynamic glass shape sets). Bonus weirdness: with the container active, the text you type became invisible inside the field.
  • Moving the glass off the field onto a background shape: .background { Capsule().glassEffect() }. Binding still never fires.
  • Making the conditionally-inserted suggestions view non-glass (material background instead). Irrelevant, because the binding never fires in the first place; the conditional view isn't even inserted yet.

What DOES fix it (all verified on device):

  • Putting any glass button before the TextField in the HStack. Moving my Add button to the left of the field: focus binding works instantly.
  • Applying .glassEffect() to the whole HStack instead of the field itself (single capsule, compose-bar style): works.
  • Funny enough, I only discovered the bug because I removed a glass Menu that used to sit left of the field. It had been masking the bug the entire time.

So the failing configuration is specifically: TextField as the first/leftmost glass element in a .safeAreaBar (under TabView), followed by a glass button. Reorder the elements and \@FocusState`` works; keep the field first and the binding is just dead, even though the keyboard and typing work normally.

I ended up shipping the "button on the left" layout. Has anyone else run into this? I'd love to understand the actual mechanism. My best guess is something about how Liquid Glass captures/hosts the field's content, but the container + glassEffectID route failing makes me think it's just a plain UIKit-bridging bug. Filing a Feedback either way.

Xcode 26.3, iOS 26.5, device

reddit.com
u/oneness33 — 3 days ago
▲ 27 r/SwiftUI

Anyone know how to create this type of menu opening animation?

u/_7down — 3 days ago

How do apps like Instagram/X keep post state (e.g. likes) synchronized across different feeds in SwiftUI?

I'm trying to understand the correct architecture for shared post state in SwiftUI using the new Observation framework.

I have a simple example where both ContentView and SavedView display PostView.

Observable class Post {
    var id: Int
    var content: String
    var title: String
    var isLiked: Bool = false

    init(id: Int, content: String, title: String, isLiked: Bool) {
        self.id = id
        self.content = content
        self.title = title
        self.isLiked = isLiked
    }

    func toggleLike() {
        isLiked.toggle()
    }
}


struct PostView: View {
     var post: Post

    var body: some View {
        HStack {
            Text(post.title)
            Text(post.content)
            Spacer()
            Button(post.isLiked ? "Liked" : "Like") {
                post.toggleLike()
            }
        }
    }
}

ContentView

struct ContentView: View {
    @State private var posts : [Post] = [] 

    var body: some View {
        List(posts, id: \.id) { post in
            PostView(post: post)
        }
        .task {
            if posts.isEmpty {
                let examplePost = Post(
                    id: 1,
                    content: "examplecontent",
                    title: "exampletitle",
                    isLiked: false
                )
                posts.append(examplePost)
            }
        }
    }
}

SavedView

struct SavedView: View {
    @State private var posts: [Post] = []

    var body: some View {
        List(posts, id: \.id) { post in
            PostView(post: post)
        }
        .task {
            if posts.isEmpty {
                let examplePost = Post(
                    id: 1,
                    content: "examplecontent",
                    title: "exampletitle",
                    isLiked: false
                )
                posts.append(examplePost)
            }
        }
    }
}

If I like the post in ContentView, the same post in SavedView is still shown as not liked.

I understand that in this example each view creates its own Post instance, so they aren't actually sharing the same object.

My question is more about architecture:

  • How is this typically solved in real apps?
  • Do apps like Instagram or X (Twitter) maintain a single shared source of truth for every post?
  • Is the common approach to have a central store/repository (similar to Redux/TCA) where every screen references the same Post object by ID?
  • Or do they simply refetch the data whenever a user navigates between screens?
  • With SwiftUI's new Observation framework, what would be considered the idiomatic architecture?

I'm interested in how large social media apps handle this problem rather than just fixing this sample.

reddit.com
u/erkanunluturk — 4 days ago
▲ 5 r/SwiftUI+2 crossposts

How can WidgetKit display the latest HealthKit steps without opening the app?

I'm building a Flutter health app that tracks steps and sleep using HealthKit. I've also created WidgetKit widgets so users can view their latest step count and sleep data without opening the app.

On Android, I use WorkManager to update the widget approximately every 45 minutes, and that works well.

On iOS, however, once the app is terminated, background work is no longer reliable, so I can't periodically fetch HealthKit data and update the widget.

My main question is about live HealthKit data:

  • How do health apps keep their step-count widgets updated while the app has been terminated for days?
  • Can HKObserverQuery with HealthKit background delivery wake the app after it has been terminated, or does it stop working until the user launches the app again?
  • Is there an Apple-recommended architecture for keeping HealthKit widgets reasonably up to date?
  • Are BGTaskScheduler, WidgetKit timelines, or App Intents sufficient for this use case, or are there limitations that make truly live updates impossible?
  • If you've built a HealthKit-based widget before, how do you keep the widget displaying the latest step count without requiring the user to open the app?

I'm looking for Apple's recommended approach and any real-world implementation experience. Any advice or links to relevant documentation would be greatly appreciated.

reddit.com
u/Own-Rough3783 — 3 days ago
▲ 69 r/SwiftUI+1 crossposts

The hidden O(n²) in your SwiftUI List: that per-row .contains is scanning the whole array every redraw

Ran into this in a code review recently and it turned into a rabbit hole I think is worth sharing, because the code looks completely reasonable and the cost is invisible until the list grows.

The setup: a List of posts, and for each row you check wheteher it's favorited.

List(posts) { post in
    PostRow(
        post: post,
        isFavorite: favorites.contains(where: { $0.id == post.id })
    )
}

Nothing here looks wrong. But .contains(where:) scans up to every favorite, for every row. That's O(n × m), and it re-runs on every body evaluation, which during a scroll is a lot. With a few hundred posts and a few hundred favorites you're doing tens of thousands of comparisons per frame. It shows up as scroll jank you can't easily place, because no single line looks expensive.

The fix is to build a Set of the favorite IDs once, above the List, so each row becomes an O(1) lookup:

let favoriteIDs = Set(favorites.map(\.id))

List(posts) { post in
    PostRow(post: post, isFavorite: favoriteIDs.contains(post.id))
}

O(n × m) becomes O(n + m). The part that bit me: it has to be built above the List, not inside the row closure. Build it inside and you're rebuilding the Set every row, which is worse than where you started.

Two things I'm curious about from people who've shipped more of this than I have:

Do you reach for this proactively, or only after Instruments points at it? I've gone back and forth on whether pre-optimizing membership checks is worth the readability cost on small lists.

And is there a cleaner pattern than a manually-built ID Set for this, something with diffable data sources or a computed lookup, that holds up in production?

(I've been making short videos connecting interview algorithms to real iOS code, and this one came from that. Happy to link it if it's useful, but the discussion is the part I actually care about.)

reddit.com
u/singhraman4282 — 5 days ago
▲ 7 r/SwiftUI+2 crossposts

Clawd - Notch Usage Tracker

I got inspired by a post i saw in the r/SwiftUI thread by u/Impossible_Step6452 who showed a claude implementation into the notch showcasing claudes actual process.

I got inspired to get rid of an app I had in the Mac Menu Bar that shows usage to use the notch instead for it. It shows your 5h and weekly usage, resets and your available credits. It estimates your generated today's cost, so you can continue maxing your usage and show off the bills.

It's open source, signed and you can either get the app or build yourself. If it is of use for any of you, nice enjoy. If not, lets go back to watch the world cup.

Link: https://github.com/stevemcqueenz/claude-notch-tracker

u/stanizzle — 3 days ago
▲ 15 r/SwiftUI+4 crossposts

hotstash clipboard

I'm the solo dev. I've been building Hotstash, a menu-bar clipboard manager for macOS, and just shipped a big 4.0 update. Would love

honest feedback — good or brutal.

The basics

- 📋 Clipboard history — automatically keeps everything you copy: text, links, code, images. Unlimited and instantly searchable.

- ⌨️ Fast access — global hotkey opens your history anywhere; ⌘1–⌘9 to paste any item by position; multi-paste to combine several clips into one

paste (inline or on new lines). All shortcuts are rebindable.

- 📌 Pin & reorder the clips you reuse.

The part I'm most excited about

- 🧩 Transforms — reshape text before you paste: UPPERCASE, lowercase, slugify, format/minify JSON, Base64/URL encode, JWT decode, trim whitespace,

strip HTML, extract URLs, word count, wrap in quotes/brackets… plus image ops (resize, grayscale, rotate, flip, PNG/WebP).

- 🛒 Transforms Marketplace — build your own transforms (small JavaScript for text, a visual step pipeline for images), test them live, and share

them. Install ones other people made. It's the "make it yours" layer.

Other 4.0 stuff

- ☁️ iCloud sync — history + installed transforms follow you across all your Macs. No account needed, just iCloud. Everything stays in your private

iCloud — nothing on my servers.

- 🔒 Privacy-first — copies from password managers (concealed content) are never captured.

- 📱Mobile app — in order to paste with transformers on the mobile and get all mac features here not just the sync

Honest disclosure: it's a paid app (one-time, with a free trial 30 days, no subscription). Marketplace sign-in is optional and only for publishing/rating.

What I'm asking:

  1. Try it and tell me what's missing or annoying — I genuinely want the criticism.
  2. If you like it, an honest App Store review would help a tiny indie app a lot. 🙏

🎁 Giveaway: I've got 20 Pro codes to hand out.

App Store → https://apps.apple.com/eg/app/hotstash/id6771842605?mt=12

or search "Hotstash clipboard"

Thanks for reading 🙏

u/zeyadamer — 5 days ago
▲ 22 r/SwiftUI

The one architecture rule that keeps my solo iOS apps from rotting

I ship iOS apps solo. No code reviewer, no teammate catching me cut corners at 1am. The codebase only stays sane if I enforce structure on myself, because nobody else is going to.

The rule that saves me: views do not touch data.

My basic split:

  • Views handle layout and taps. If a view is deciding how data gets saved, I moved too much into the view.
  • ViewModels own screen state. I keep them @Observable and @MainActor, because SwiftUI already gives me enough ways to hurt myself.
  • Services do the work: SwiftData, network calls, file access, anything that changes the outside world.
  • Models stay boring. Data only.

I use it as a fence. Future me needs fewer places to look.

When a screen breaks, I know where to start. When I change storage, the UI does not need surgery. And when I let an AI agent work on a service, it has less room to wander into seven other files.

I learned this by doing the opposite. I let views query SwiftData directly in an early app and it turned into spaghetti. Worked fine until it didn't. Then every small change felt like pulling a wire out of a wall and hoping the lights stayed on.

Curious how other solo SwiftUI devs handle this. Do you keep the layers this strict, go heavier with something like TCA, or let small apps stay messy.

reddit.com
u/VictorBuildsApps — 6 days ago
▲ 4 r/SwiftUI+1 crossposts

Thoughts on TCA for a New Project

I work as an iOS developer in a team of 8 developers. We are starting a brand new greenfield project in a financial sector. Few of our team members suggested that we should use TCA for iOS SwiftUI application. I personally have no experience with TCA.

What are your thoughts and recommendations?

reddit.com
u/tonycliftondev — 8 days ago
▲ 52 r/SwiftUI+1 crossposts

Building a translucent notch overlay that shows your wallpaper why .glassEffect() doesn’t work, and what does

I spent weeks trying to build a floating “Dynamic Island”-style panel under the Mac notch — one where the desktop wallpaper shows through the bottom. Here’s what I learned, because almost every SwiftUI-first approach fails the same way.
1. .glassEffect() is the wrong tool. It only refracts content inside your own window. It never samples the desktop behind a floating, transparent window. So if your goal is “blur what’s behind MY window” (the wallpaper), it can’t do it.
2. The real recipe is AppKit. Use an NSVisualEffectView with blendingMode = .behindWindow, inside a transparent NSWindow. Set state = .active so it keeps rendering even when the window isn’t key (a floating overlay never becomes key), and a dark material like .hudWindow.
3. Do the shape in AppKit, not SwiftUI. This is the trap that cost me the most: any SwiftUI .clipShape / .cornerRadius / .mask / .shadow on the hosting view forces offscreen rendering → the blur stops sampling the desktop and turns into flat opaque black. Round the corners and draw the bottom fade with an AppKit maskImage (rounded rect + alpha gradient) on the visual effect view instead.
4. Split it into two windows. One opaque black strip that masks the physical notch, and one transparent, centered island (not full width, so the wallpaper stays visible on both sides) that holds the single NSVisualEffectView.
That’s the core of the rendering. Happy to go deeper on any part in the comments.

u/sakaax — 7 days ago
▲ 57 r/SwiftUI

I'm building a daily quiz game for iOS/Swift devs — ~1,700 questions across 17 topics. Would you actually use this?

Hey all, solo dev here. In the age of AI I got a bit worried about my own brain going lazy — autocompleting instead of thinking — so in my free time I've been building a small game to keep Swift skills sharp.

It's a bite-sized quiz with a bit of game on top: a few minutes a day of "what does this print?", spot-the-bug, fill-in-the-blank and concept questions, wrapped in a world map with streaks / XP / levels.

Launching with 17 topics across the Swift stack — Swift Basics, Protocols & Generics, Concurrency, SwiftUI, Networking & Codable, Memory Management, Testing, App Architecture and more — roughly 1,700 questions to start.
One thing I'd really like to do: let people submit their own questions to be added, since the best ones come from real experience.

No link and nothing to sell yet — I genuinely just want to know if this is useful to anyone besides me. 50-second clip below.

Honest questions:
Would you actually play a couple of minutes a day?
Which topic would you want to be drilled on most?
What would make this an instant "no" for you?
Brutal feedback welcome — thanks 🙏

u/vadimkrutov — 8 days ago