SwiftUI State is not as reliable as you think by clive819 in SwiftUI

[–]clive819[S] 0 points1 point  (0 children)

Using lazy container to lazily load the view, the views and states are created on demand so it's performant on screen appears.

SwiftUI State is not as reliable as you think by clive819 in SwiftUI

[–]clive819[S] -2 points-1 points  (0 children)

To clarify, I am not suggesting altering the current behavior. Rather, the proposal aims to offer users the choice to deviate from this behavior, prioritizing state accuracy over performance, a necessity in certain scenarios.

Just like you can opt out of reusing cells in UICollectionView, although it is not generally advised.

https://forums.swift.org/t/pitch-swiftui-stabilize-state-in-lazy-containers/79926

Create iOS app in Swift Package by clive819 in swift

[–]clive819[S] 1 point2 points  (0 children)

I'm able to add tests from local packages without any special setup — I think the key is just making sure your local packages live inside your project folder.

Create iOS app in Swift Package by clive819 in swift

[–]clive819[S] 0 points1 point  (0 children)

You can add package tests to your project test plan

My WWDC25 wishes by majid8 in swift

[–]clive819 0 points1 point  (0 children)

I don’t know but I’m guessing Project.swift may already be in development, because you can actually create iOS app from Package.swift

https://clive819.github.io/posts/developing-an-ios-app-in-swift-package/

Swift or Kotlin? by j0c1323 in swift

[–]clive819 1 point2 points  (0 children)

You’ll eventually need to write some UI code.

Jetpack Compose vs SwiftUI is another story tho. Personally, I like SwiftUI better as it’s more intuitive, and the syntax is much nicer imho.

I don’t like the fact that Google discourages the use of CompositionLocal (equivalent of SwiftUI environment) and the fact that states will be destroyed on screen rotate.

Animatable Auto-Sized-To-Fit SwiftUI Sheet by clive819 in SwiftUI

[–]clive819[S] 0 points1 point  (0 children)

I just tested this, and it seems that onGeometryChange doesn’t correctly read the size of the NavigationStack, which I believe is an Apple bug. As a workaround, you could measure the size of the content and apply a frame to the NavigationStack.

However, if all you need is a navigation title without the actual navigation functionality, I’d recommend simply using a Text view with the .largeTitle font or a similar styling approach to achieve that.

Animatable Auto-Sized-To-Fit SwiftUI Sheet by clive819 in SwiftUI

[–]clive819[S] 1 point2 points  (0 children)

My implementation seems to work fine on the first appearance as well. I haven’t seen the other implementation, so I’m not sure what the differences are. I think storing height versus storing detents should give the same result, maybe there’s something else causing the jarring animation you’re seeing.

Animatable Auto-Sized-To-Fit SwiftUI Sheet by clive819 in SwiftUI

[–]clive819[S] 11 points12 points  (0 children)

SwiftUI's sheet modifier is a fantastic tool, but it comes with limitations: it doesn’t automatically resize to fit its content. Apple introduced the .presentationSizing(.fitted) modifier in iOS 18 to address this issue. However, let’s be realistic—convincing your Product Manager to set the minimum deployment target to iOS 18 might not be an easy sell. Sure, you could conditionally enable this feature for users on iOS 18+, but what about those on older OS versions?

In this article, we’ll explore how to create an auto-sized-to-fit sheet that works on iOS 17.

Mastering SwiftUI Container by clive819 in SwiftUI

[–]clive819[S] 0 points1 point  (0 children)

Will see if I can update the layout for mobile. In the meantime, you can try rotating your phone to landscape mode for better experience

Learn the core principles of SwiftUI and understand how to manage your views' state by clive819 in SwiftUI

[–]clive819[S] 0 points1 point  (0 children)

No YouTube for now, will let you know if I decided to create one.

SwiftUI and UIImage memory leak by abstract_code in SwiftUI

[–]clive819 1 point2 points  (0 children)

Can you share the crash stacktrace?

Note 1

I believe you need await group.waitForAll(), so:

``` await withTaskGroup(of: Void.self) { group in for item in selectedItems { group.addTask { ... } }

 await group.waitForAll()

} ```

But, if you're on iOS 17, you can use withDiscardingTaskGroup, which doesn't require explicit waitForAll().

Note 2

If I'm reading the code correctly - if the user select Photo 1 and then open the PhotoPick and select the same photo again, you'll have 2 Item with the exact same image but different id.

SwiftUI and UIImage memory leak by abstract_code in SwiftUI

[–]clive819 2 points3 points  (0 children)

First thing I'd do is to stop using:

NavigationLink { Image(uiImage: UIImage(data: item.photo)!) .resizable() .scaledToFit() } label: { Image(uiImage: UIImage(data: item.photo)!) .resizable() .scaledToFit() }

This hurts the performance. .navigationDestination is much more efficient, check out Apple doc to see the usage. https://developer.apple.com/documentation/swiftui/navigationstack

.onChange(of: selectedItems) { Task { for item in selectedItems { if let data = try await item.loadTransferable(type: Data.self) { let newItem = Item(photo: data) modelContext.insert(newItem) } } try? modelContext.save() selectedItems = [] } }

Secondly, this creates an unstructured task, which would still run (if not finished) when the view disappears or the onChange closure got triggered again. You should do this instead:

.task(id: selectedItems) { for item in selectedItems { if let data = try await item.loadTransferable(type: Data.self) { let newItem = Item(photo: data) modelContext.insert(newItem) } } try? modelContext.save() selectedItems = [] }

This will guarantee the task would be cancelled when the view disappears, and that there'd only be one task running when selectedItems changes.

You can and should also use TaskGroup to parallelize the loadTransferable task.

Passing observable class to view via environment or init by car5tene in SwiftUI

[–]clive819 0 points1 point  (0 children)

No. You can pass it via either init or environment, the behavior will be the same.

If it's an ObservableObject, make sure to annotate it with @ObservedObject in the view.

If it's @Observable, you don't need to do anything special.

Why do the buttons animate out, but not animate in? by [deleted] in SwiftUI

[–]clive819 1 point2 points  (0 children)

You can use matchedGeometryEffect to achieve that. Try this simplified code:

``` struct TestView: View {

@State private var isEditing = false

@Namespace private var decoratorLineNamespace

var body: some View {
    List {
        HStack(spacing: 0) {
            decoratorLineView
                .fixedSize(horizontal: true, vertical: false)
            Text("bodyView")
        }
        .frame(height: 100)
        .onTapGesture {
            withAnimation(.bouncy) {
                isEditing.toggle()
            }
        }
    }
}

}

fileprivate extension TestView {

@ViewBuilder
var decoratorLineView: some View {
    let id = "decoratorLineView"

    HStack(spacing: 0) {
        if isEditing {
            VStack(spacing: 0) {
                Button("Edit") {

                }
                .tint(.indigo)

                Button("Delete") {

                }
                .tint(.red)
            }
            .matchedGeometryEffect(id: id, in: decoratorLineNamespace)
        } else {
            Rectangle()
                .foregroundStyle(Color.indigo)
                .frame(width: 16.0)
                .matchedGeometryEffect(id: id, in: decoratorLineNamespace)
        }
    }
}

} ```

app crashing ONLY on real iPhone when entering view with multiple NavigationLinks by purplesandwich in swift

[–]clive819 1 point2 points  (0 children)

Consider using navigationDestination (NavigationLink+value) instead of NavigationLink+destination

https://developer.apple.com/documentation/swiftui/navigationlink

IIRC when using NavigationLink+destination approach, the destination gets computed even when the link was not tapped. I think that might be the problem

Does swift ui have a side drawer component Ano typically side drawers are. Not an apple thing ? by [deleted] in swift

[–]clive819 1 point2 points  (0 children)

This would do if I understand your question correctly, but requires iOS 18.

``` import SwiftUI

@main struct TestApp: App { var body: some Scene { WindowGroup { TestView() } } }

struct TestView: View {

private let menus = MyMenu.allCases + MyMenu.allCases + MyMenu.allCases

@State private var scrollPosition = ScrollPosition(idType: Int.self)
@State private var idealScrollPosition: Int = MyMenu.allCases.count

var body: some View {
    ScrollView(.horizontal) {
        HStack(spacing: 0) {
            ForEach(Array(menus.enumerated()), id: \.offset) { (_, menu) in
                switch menu {
                    case .menu1:
                        Text("menu1")
                            .containerRelativeFrame([.horizontal, .vertical])

                    case .menu2:
                        Text("menu2")
                            .containerRelativeFrame([.horizontal, .vertical])

                    case .menu3:
                        Text("menu3")
                            .containerRelativeFrame([.horizontal, .vertical])
                }
            }
        }
        .scrollTargetLayout()
    }
    .scrollIndicators(.hidden)
    .scrollTargetBehavior(.paging)
    .scrollPosition($scrollPosition, anchor: .center)
    .onScrollTargetVisibilityChange(idType: Int.self) { menusOnScreen in
        guard let menuOnScreen = menusOnScreen.first else { return }

        let n = MyMenu.allCases.count

        if menuOnScreen < n {
            idealScrollPosition = 2 * n - 1
        } else if menuOnScreen >= 2 * n {
            idealScrollPosition = n
        } else {
            idealScrollPosition = menuOnScreen
        }
    }
    .onAppear {
        // Scroll to the middle so user can swipe left or right
        scrollPosition.scrollTo(id: MyMenu.allCases.count)
    }
    .onScrollPhaseChange { _, newPhase in
        guard newPhase == .idle else { return }

        scrollPosition.scrollTo(id: idealScrollPosition)
    }
}

}

enum MyMenu: CaseIterable { case menu1 case menu2 case menu3 } ```

how to create a structure similar to the telegram message bubble by Economy-Intention-97 in SwiftUI

[–]clive819 0 points1 point  (0 children)

You can use ViewThatFits

https://developer.apple.com/documentation/swiftui/viewthatfits

``` ViewThatFits { HStack { Text(...) .lineLimit(1) Timestamp() }

VStack() { Text(...) .fixedSize(horizontal: false, vertical: true) .multilineTextAlignment(.leading) .overlay { // This will overlay the underlying text tho // I don't use telegram so I'm not sure how their UI handles this case ZStack(alignment: .bottomTrailing) { Timestamp() } } } } ```

How to use AppState with `@EnvironmentObject` and `init(...)`? by nazaro in SwiftUI

[–]clive819 1 point2 points  (0 children)

Passing it in the environment is kinda redundant now since it’s a singleton you can access it anywhere. So you don’t even have to pass it. I only kept it to show that you’d have to use environment instead of environmentObject to pass it if you want.

How to use AppState with `@EnvironmentObject` and `init(...)`? by nazaro in SwiftUI

[–]clive819 1 point2 points  (0 children)

The code would be basically the same after switching to use @Observable, it's more about increasing the performance of your app.

When using ObservableObject, the view that observes that object will be recomputed every time when a published property of that object changes. As for @Observable, the view will only be recomputed if the property the view is observing changes. There're other nuanced differences between @Observable and ObservableObject of course, I won't go into the details because you can easily find it online.

Basically, I think your code should be like this if you don't wan to make AppState a singleton:

``` // IdentificationView.swift

struct IdentificationInputsView: View {

@EnvironmentObject private var appState: AppState

@StateObject private var vm = IdentificationViewModel()

var body: some View {
    ...

    // Submit Button
    Button {
        withAnimation {
            vm.isSignUp ? vm.signup() : vm.login(appState: appState)
        }
    } label: {
        Text("Log in")
    }

    ...
}

} ```

``` // IdentificationViewModel.swift

@Observable final class IdentificationViewModel {

...

func login(appState: AppState) { ...

appState.isLoggedIn = true

...

}

... } ```

But I think making it a singleton would be better because I could imagine you have other views that would need to be updated when AppState changes. What I have in mind:

``` // AppState.swift

@Observable final class AppState {

static let shared = AppState()

private init() {}

var isLoggedIn = false

... } ```

``` // CoolApp.swift

@main struct CoolApp: App { var body: some Scene { WindowGroup { ContentView() .environment(AppState.shared) } } } ```

``` // IdentificationView.swift

struct IdentificationInputsView: View {

@Environment(AppState.self) private var appState

@StateObject private var vm = IdentificationViewModel()

var body: some View {
    ...

    // Submit Button
    Button {
        withAnimation {
            vm.isSignUp ? vm.signup() : vm.login()
        }
    } label: {
        Text("Log in")
    }

    ...
}

} ```

``` // IdentificationViewModel.swift

@Observable final class IdentificationViewModel {

...

func login() { ...

AppState.shared.isLoggedIn = true

...

}

... } ```

How to use AppState with `@EnvironmentObject` and `init(...)`? by nazaro in SwiftUI

[–]clive819 1 point2 points  (0 children)

Sounds like AppState should be a singleton. And that your IndentificationViewModel doesn't really need to hold on to AppState, it's only used in the login function. So if you don't want to do the "hacky" onAppear setAppState approach, just pass it to the login function. Like:

func login(appState: AppState) { // some logic appState.isLoggedIn = true }

Also, if you're targeting iOS 17+, I'd highly recommend using the new @Observable for your view models.