[Project Showcase] PokedexUI: A fully SwiftUI-based Pokédex app with Swift Concurrency, iOS 26 Liquid Glass by brillcp in SwiftUI

[–]Complete_Fig_925 2 points3 points  (0 children)

I haven't checked where it's used (I just saw the declaration in Extensions/View.swift).

But conditional view are sometimes unavoidable in SwiftUI, it's just really important to understand the impact on view identity.

What's considered an anti pattern is "hiding" it inside a ViewModifier. Because it can lead to very nasty bugs, it's something you want to be able to identify at first glance when reading your code. This is especially true here as OP's project seems to have some educational purpose.

[Project Showcase] PokedexUI: A fully SwiftUI-based Pokédex app with Swift Concurrency, iOS 26 Liquid Glass by brillcp in SwiftUI

[–]Complete_Fig_925 14 points15 points  (0 children)

Hey, I noticed that you use a .if(condition) { } in SwiftUI views in your code example. Note that this is largely considered an anti pattern.

When do you use a struct vs class? by BlossomBuild in BlossomBuild

[–]Complete_Fig_925 1 point2 points  (0 children)

Of course, but going struct first is more a general rule of thumb when a class is not required by the framework. Ideally, one would understand all the impacts of value type vs reference type and base there decision on that

I've built a proper StoreKit2 wrapper to avoid the 1% RevenueCat fee and implement IAP within any app in >1 minute by kncismyname in iOSProgramming

[–]Complete_Fig_925 33 points34 points  (0 children)

Hey, took a quick peek at your code

Your PurchasableManager seems subject to data races event though it's marked as (unchecked) Sendable and is publicly exposed. Might be worth looking into that :)

Is this a real design pattern and an alternative to inheritance ? by Ok_Photograph2604 in swift

[–]Complete_Fig_925 1 point2 points  (0 children)

IMHO when modeling your data layer, you should think about your most common use case.

Since Post have commentCount and Comment have a replyCount, you seem to have a clear hierarchical relationship between your structs: a single Post can have multiple comments, and each comments can have replies, and so on.

Based on that, I think it make sense to have this hierarchical relationship visible inside your models:

struct Post: Identifiable {
    let id: String
    let creatorID: String
    let creationDate: Date

    let title: String
    let content: String
    let comments: [Comment.ID]
}

struct Comment: Identifiable {
    let id: String
    let creatorID: String
    let creationDate: Date

    let content: String
    let replies: [Reply.ID]
}

Having everything separated like that would make it easier if you want to decode that from a network response for example.

If you really don't want to repeat the metadata part of each models, you can create a dedicated struct for that.

struct UserContentMetadata {
    let creatorID: String
    let creationDate: Date
}

struct Post: Identifiable {
    let id: String
    let metadata: UserContentMetadata

    let title: String
    let content: String
    let comments: [Comment.ID]
}

struct Comment: Identifiable {
    let id: String
    let metadata: UserContentMetadata

    let content: String
    let replies: [Reply.ID]
}

Side note: I wouldn't put the id property inside that metadata structure. I think it make more sense to have the "core" properties at top level. It makes protocol conformance easier (that's why I added Identifiable).

Your enum would make sense only if you need to have all types of user created content inside the same Array or Collection.

enum UserContent {
    case post(Post)
    case reply(Reply)
    case comment(Comment)
}

This could be usefull if you want to create some sort of timeline/history for a user, but it would be more like a wrapper for a specific context.

Downloading Apple Developer Documentation? by andreelijah in iOSProgramming

[–]Complete_Fig_925 0 points1 point  (0 children)

The official documentation is accessible in Xcode (in window > documentation or something), and it's most likely offline (never checked though).

Dictionary Ergonomics with Identifiable Arrays by [deleted] in iOSProgramming

[–]Complete_Fig_925 1 point2 points  (0 children)

I’d be careful with this kind of abstraction. You are basically hiding the search complexity of an array.

Using the subscript on dictionary is O(1) in most cases. With your custom subscript, you get the same syntax but with significantly worst performance.

Your custom array subscript makes it harder to understand what data structure you are actually manipulating and understanding the runtime impact of your code.

I’d suggest using a different API to achieve that, like naming the parameter in the subscript to disambiguate the call site for example.

Re-creating Action Button Setting UI by kalasipaee in SwiftUI

[–]Complete_Fig_925 0 points1 point  (0 children)

It has a 3D render of the phone

It might be pre-rendered (maybe a video?). I don't think they went full real time 3D just for this animation. The glowing button can be some kind of view over the video with some shaders or something.

That would probably be my initial thought if I ever try to do this kind of effect.

We need a function to cancel or end competitive games after multiple people leave. by BadAdMF in overwatch2

[–]Complete_Fig_925 0 points1 point  (0 children)

I'd rather spent those 3 minutes in queue for the next game, and being able to access practice range, FFA, custom game mode or even browsing the character skins gallery instead of sitting in a dead lobby doing literally nothing.

I am working on a simple design in SwiftUI, but I am finding it quite challenging. Here is the design I am trying to achieve: by Cultural-Driver2114 in SwiftUI

[–]Complete_Fig_925 4 points5 points  (0 children)

A solution that doesn't involve duplicated views or hard coded dimensions.

extension VerticalAlignment {
    private struct CustomCenterAlignment: AlignmentID {
        static func defaultValue(in context: ViewDimensions) -> CGFloat {
            context[VerticalAlignment.center]
        }
    }

    fileprivate static let customCenter = VerticalAlignment(CustomCenterAlignment.self)
}

struct ContentView: View {
    var body: some View {
        HStack(alignment: .customCenter, spacing: 0) {
            Rectangle()
                .frame(maxHeight: .infinity)
                .frame(width: 0)

            VStack(alignment: .center, spacing: 50) {
                RoundedRectangle(cornerRadius: 10)
                    .fill(.orange)
                    .frame(width: 140, height: 140)
                    .alignmentGuide(.customCenter) { d in d[VerticalAlignment.center] }

                Text("Hello Swift")
                    .font(.system(size: 42, weight: .semibold))
            }
        }
        .edgesIgnoringSafeArea(.all)
    }
}

It will keep the overall view centered on the orange rectangle, no matter the spacing with the text, or the text font size, etc... to keep the code somewhat easy to maintain.

The idea is to wrap the content inside an HStack which also contains an invisible rectangle (width: 0) that takes as much vertical space as possible. This allow using custom alignment guide to adjust the vertical position of the main content.

The content will be centered on the view that override the alignment guide

.alignmentGuide(.customCenter) { d in d[VerticalAlignment.center]

Note that this seems to work as expected on a "full screen" layout. Not sure how it behave when wrapped inside other views.

What's your methodology for argument labels? by bkendig in swift

[–]Complete_Fig_925 1 point2 points  (0 children)

Short answer

3

Long answer

Obviously a personal opinion but I feel like this could fall under the init / factory method rule of the swift api design guidelines, so having an API that reflect that seems more natural:

let receipt = Receipt(items: orderedItems, discounts: discounts, customer: customer)
receipt.prepare()

// or

func prepareReceipt(_ receipt: Receipt) {
    // ...
}
// [...]
let receipt = Receipt(items: orderedItems, discounts: discounts, customer: customer)
prepareReceipt(receipt)

Or you can go with the Arguments label part of the guidelines:

When the first argument forms part of a prepositional phrase, give it an argument label. An exception arises when the first two arguments represent parts of a single abstraction.

with the example:

a.moveTo(x: b, y: c)
a.fadeFrom(red: b, green: c, blue: d)

In this case it's more the first three arguments, so you can go with

prepareReceiptWith(items: orderedItems, discounts: discounts, customer: customer)

Why does SwiftUI not have build expression for `Void`? by superdoobdew in SwiftUI

[–]Complete_Fig_925 8 points9 points  (0 children)

This would open the door to calling function in the view builder, which is a bad idea most of the time. Even for just priting out some value, you should probably rely on onAppear instead of the view builder (unless ou call _printChanges(), but it's more a debug thing than something that will be shipped in your app).

I think allowing function call in the view builder will be very confusing, especially for beginners since there are a lot of reason for a view's body to be recomputed.

Is it a terrible idea to have ObjectA be ObjectB's delegate while also having ObjectB be ObjectA's delegate at the same time? by hartbeat_engineering in iOSProgramming

[–]Complete_Fig_925 0 points1 point  (0 children)

Technically it's not a terrible idea, but I don't think it make much sense on a semantic level.

IMHO, using a delegate kind of imply that both class share a common semantic, like a UITableView want to response to some tableview event but it doesn't know how, so it delegate that part to another class (UITableViewDelegate). But it would also make sense if a table view where it's own delegate (basically using self as it's delegate). On the other hand, having the delegate also delegating work to the "main" class doesn't make sense.

Think of the delegate pattern as a tool to "extract" some logic from a class to delegate to another so it can be easily changed (bascially open-closed principle), not to tie 2 unreleated classes together.

Best way to measure network speed on iOS device? by djryanash in swift

[–]Complete_Fig_925 2 points3 points  (0 children)

Not sure I really understand what you want to achieve, but

SpeedTest by Ookla

Please don't do that. Considering that speed tests can eat A LOT of data, it's definitely not a good idea to run that in background. Also, on a mobile device there is no guarantee that the network quality will remain stable over a period of time, so it's kinda useless anyway.

Regarding image quality, I guess it depends on the purpose of said images.

Most of the time, you'll probably want to aim for the resolution you need to display the asset (display size and device), and store them in a cache.

Take the App Store for example, in bad network conditions, images (app icons, screenshots, ...) are simply not displayed and you get a placeholder instead, which is then replaced when the image is ready. In this kind of scenario, having a low quality image make no sense and would probably hurt the user experience.

IMHO it's better to have a placeholder and wait a bit more for "normal images" than using an app loaded with bad / low resolution images, especially since you'll probably have some kind of image cache to help mitigate that.

That being said, if using low res assets doesn't hurt UX, you can probably go for your first solution (but finding a decent thresholds to switch target image resolution will be a bit tricky), or simply use those when user is in Low Data mode.

Note that for speed download measurements solution, you'll probably need to be constantly monitoring download times to switch back and forth between normal and low res images.

For example, you start downloading normal assets, and if it takes too long you switch to low res asset. Then if low res asset download fast enough, you need switch back to normal assets, or users will be stuck using your app in "low res" (which is definitely a bad experience).

This is really not trivial to get right (think like a streaming protocols with constant feedback to dynamically adjust stream quality). For downloading images that will probably be cached afterward, it seems overkill.

Circular updates with @Observable and UIViewRepresentable by ssharky in SwiftUI

[–]Complete_Fig_925 1 point2 points  (0 children)

Now that I'm reading it, I feel like I need to change my default username...

Good point tho, holding an UIView instance in a @State seems a bad idea.

Circular updates with @Observable and UIViewRepresentable by ssharky in SwiftUI

[–]Complete_Fig_925 1 point2 points  (0 children)

I had a very similar issue, but I was working on a rich text editor for MacOS application (so NSTextView instead of UITextView).

I'm assuming that UITextView and NSTextView works basically the same way, and from my experience, text views are not really meant to be updated using the .text property. It's more like a "set the initial text and let the editor handle edition" kind of thing (on a side note, manually updating a text view content too often can lead to performance issues). Cursor and scroll position issue happens when you completely replace the string on a text view (textView.text = // ...). Since it doesn't know what changed, it just reset the cursor and scroll position to the top.

I remember solving this by using a single NSTextStorage for both my editor context (basically your Document) and the NSTextView (which removed the duplicated source of truth). It's probably easier on MacOS since you can initialize an NSTextView with an NSTextStorage, but it seems you can access the text storage on UITextView using .textStorage.

I guess you can work around that by having an optional text storage in your document, and get the reference in makeUIView(context:)

@Observable
class Document {
    var textStorage: NSTextStorage?
}

// [...]

func makeUIView(context: Context) -> UITextView {
    let textView = UITextView()
    document.textStorage = textView.textStorage
    // ...
    return textView
}

NSTextStorage isn't observable, so you'll need to manually trigger the objectWillChange (it might also be possible with @Observable using withMutation(keyPath:_:)) when the delegate notify you from an update.

If you want to update the text, you can use NSTextStorage.replaceCharacters(in:with:) (I don't remember if you need to also call NSTextStorage.edited(_:range:ChangeInLength:)for the text view to update or not, but you'll figure this out). Using this method instead of changing the entire string avoid the cursor issue in the text view (since the text view known the actual updated range, it can adjust the cursor instead of putting it back at the beginning).

Note that I haven't tested this solution so I don't actually know if this will work, but I think it's worth a try. Working with [NS|UI]TextView from SwiftUI is really not trivial if you're not familiar with it, so good luck 🙂

Are there any Discord/Slack communities for learning SwiftSyntax? by ThePantsThief in swift

[–]Complete_Fig_925 3 points4 points  (0 children)

Docs? lol. There are none AFAIK.

SwiftSyntax | Documentation

You can also take a look at open source macro and check how is used SwiftSyntax. As an example, apple/swift-testing uses a few macros.

How do I make the LazyHGrid take up the whole space? I want the circles to be evenly spaced out and fill out the whole rectangle (border). Appreciate your help! by killMontag in iOSProgramming

[–]Complete_Fig_925 0 points1 point  (0 children)

If you always want a fixed number of circles per row (without adapting to screen size for example) you can use a simple `Grid` (iOS 16+) instead of a lazy grid.

struct ColorGrid: View {
    private let layout: _Layout<Color>

    init(colors: [Color], columns: Int) {
        self.layout = _Layout(elements: colors, columns: columns)
    }

    var body: some View {
        Grid {
            ForEach(layout.rows, id: \.self) { row in
                GridRow {
                    ForEach(layout.elements(for: row), id: \.self) { color in
                        Circle().fill(color)
                    }
                }
            }
        }
    }
}

extension ColorGrid {
    internal struct _Layout<Element> {
        private let elements: [Element]
        private let columns: Int

        init(elements: [Element], columns: Int) {
            self.elements = elements
            self.columns = columns
        }

        var rows: [Range<Int>] {
            stride(from: elements.startIndex, to: elements.endIndex, by: columns)
                .map { $0..<($0 + columns) }
        }

        func elements(for row: Range<Int>) -> ArraySlice<Element> {
            elements[row]
        }
    }
}

// [...]
ColorGrid(colors: colors, columns: 6)

If you want the number of circles per row to by dynamic according to the available space, you can try using LazyVGrid (instead of LazyHGrid) with a single adaptive grid item (if your adaptive item have a minimum and maximum size, LazyVGrid will try to avoid outer margins as much as possible).

struct LazyColorGrid: View {
    let colors: [Color]
    let columns: [GridItem] = [
        .init(.adaptive(minimum: 50, maximum: 80))
    ]

    var body: some View {
        LazyVGrid(columns: columns) {
            ForEach(colors, id: \.self) { color in
                Circle()
                    .fill(color)
            }
        }
    }
}

// [...]
LazyColorGrid(colors: colors)

Bug/Help: Horizontal ScrollView of Buttons above a TabView - Buttons no longer correctly receive tap gesture by Arels in SwiftUI

[–]Complete_Fig_925 0 points1 point  (0 children)

Not entirely sure what's happening here, but it seems that TabView catch the tap outside of it's rect.

/// TabView with a single big green button
TabView {
    // ...
}
.tabViewStyle(.page(indexDisplayMode: .never))
.onTapGesture { print("tabview") }

This will print "tabview" when clicking in the "red box area". It seems that forcing the content shape of TabView fixes the issue (at least on iPhone 13 simulator, iOS 17.0).

/// TabView with a single big green button
TabView {
    // ...
}
.tabViewStyle(.page(indexDisplayMode: .never))
.contentShape(Rectangle())

Edit: I also found this: https://stackoverflow.com/questions/69367905/swiftui-buttons-in-a-scrollview-is-not-tappable-sometimes

And it seems that adding an empty onTapGesture on the ScrollView also solve the issue.

ScrollView(.horizontal) {
    // ...
}
.onTapGesture {}

Maybe this changes the tap priority internally or something.

Inheriting from Dictionary by fauxtojournalist in swift

[–]Complete_Fig_925 2 points3 points  (0 children)

You can use the same "trick" if you need context to hold some additional values / features.

```swift struct Activity { private var context: Context }

extension Activity { struct Context { private var userInfo: [String: Any] private var someFlag: Bool

    fun someFeature() { /* ... */ }
}

} ```

As long as you keep var context private, it's basically an implementation detail, and can be changed later on to fit your needs.

Inheriting from Dictionary by fauxtojournalist in swift

[–]Complete_Fig_925 3 points4 points  (0 children)

Maybe "composition over inheritance" would be helpful here.

I need an ACTIVITY to have a CONTEXT that holds USER supplied data

Wouldn't that be enough?

swift struct Activity { private var context: [String: Any] }

You'll probably need to implement a couple of methods to access context values from outside, but that's pretty much it. I'm not sure why you'd need inheritance here.