Post

Replies

Boosts

Views

Activity

scenePhase not work consistently on watchOS
Hi there, I'm using WCSession to communicate watchOS companion with its iOS app. Every time watch app becomes "active", it needs to fetch data from iOS app, which works e.g. turning my hand back and forth. But only when the app is opened after it was minimised by pressing digital crown, it didn't fetch data. My assumption is that scenePhase doesn't emit a change on reopen. Here is the ContentView of watch app: import SwiftUI struct ContentView: View { @EnvironmentObject private var iOSAppConnector: IOSAppConnector @Environment(\.scenePhase) private var scenePhase @State private var showOpenCategories = true var body: some View { NavigationStack { VStack { if iOSAppConnector.items.isEmpty { WelcomeView() } else { ScrollView { VStack(spacing: 10) { ForEach(iOSAppConnector.items, id: \.self.name) { item in ItemView(item: item) } } } .task { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { loadItems() } } .onChange(of: scenePhase, initial: true) { newPhase, _ in if newPhase == .active { loadItems() } } } fileprivate func loadItems() -> Void { if iOSAppConnector.items.isEmpty { iOSAppConnector.loadItems() } } } What could be the issue? Thanks. Best regards Sanjeev
1
0
213
1d
App group not working between iOS and watchOS
Hi everyone, I'm using an app group to share data between iOS and it's watch companion app. I ensured that is has the same identifier in Signing & Capabilities and in the .entitlements files. Here is the UserDefaults part: class UserDefaultsManager { private let suitName = "group.com.sanjeevbalakrishnan.Test" public func saveItems(_ items: [ItemDTO]) { print("Save \(items.count) items to shared defaults") let defaults = UserDefaults(suiteName: suitName) let data = try? JSONEncoder().encode(items) defaults?.set(data, forKey: "items") } public func loadItems() -> [ItemDTO] { let defaults = UserDefaults(suiteName: suitName) print(defaults) guard let data = defaults?.data(forKey: "items") else { print("watchOS received data is empty") return [] } let items = [ItemDTO].from(data: data) print("Load \(items.count) items from user defaults") return items } } For testing I called loadItems after saveItems on iOS app and it returned items. However, on watchOS app it always returns empty array. What do I need to consider? Thanks. Best regards Sanjeev
3
0
142
6d
SwiftUI: View is not rendered properly
Problem: When clicking on an item, it will be (un-)completed. When it's completed then ChildView should show GroupsView. However, that's not the case. The logs are like this: init, group Fruits - items not completed false - showItems false body, group Fruits - items not completed false - showItems true init, group Fruits - items not completed false - showItems false init, group Fruits - items not completed false - showItems false import SwiftUI import SwiftData @main struct MainApp: App { var body: some Scene { WindowGroup { SomeView() } .modelContainer(appContainer) } } struct SomeView: View { @Query private var items: [AItem] var body: some View { ParentView(items: items) } } struct ParentView: View { private var groupedItems: [GroupedAItems] = [] init(items: [AItem]) { Dictionary(grouping: items) { $0.categoryName } .forEach { let groupedItems = GroupedAItems(categoryName: $0.key, items: $0.value) self.groupedItems.append(groupedItems) } } var body: some View { ScrollView { VStack(spacing: 15) { ForEach(groupedItems, id: \.self.categoryName) { groupedItems in ChildView(groupedItems) } } } } } struct ChildView: View { public var groupedItems: GroupedAItems @State private var showItems: Bool init(_ groupedItems: GroupedAItems) { self.groupedItems = groupedItems self._showItems = State(initialValue: !groupedItems.completed) print("init, group \(groupedItems.categoryName) - items not completed \(!groupedItems.completed) - showItems \(showItems)") } var body: some View { print("body, group \(groupedItems.categoryName) - items not completed \(!groupedItems.completed) - showItems \(showItems)") if showItems { return AnyView(ItemsSampleView(items: groupedItems.items, onClick: { showItems = false })) } else { return AnyView(GroupsView(groupedItems: groupedItems, onClick: { showItems = true })) } } } struct ItemsSampleView: View { public var items: [AItem] public var onClick: () -> Void private let gridColumns = [GridItem(.adaptive(minimum: CGFloat(70)))] var body: some View { VStack { Button { onClick() } label: { Image(systemName: "chevron.down") } Spacer() LazyVGrid(columns: gridColumns) { ForEach(items.sorted(by: {$0.name < $1.name})) { item in Button { item.completed.toggle() } label: { Text(item.name) } } } } } } struct GroupsView: View { public var groupedItems: GroupedAItems public var onClick: () -> Void var body: some View { VStack { Button { onClick() } label: { Image(systemName: "chevron.down") } Spacer() Text(groupedItems.categoryName) } } } @Model final class AItem: Identifiable { @Attribute(.unique) public var id: String public var name: String public var categoryName: String public var completed = false internal init(name: String, categoryName: String) { self.id = UUID().uuidString self.name = name self.categoryName = categoryName } } struct GroupedAItems { var categoryName: String var items: [AItem] var completed: Bool { items.filter { !$0.completed }.isEmpty } } @MainActor let appContainer: ModelContainer = { do { let container = try ModelContainer(for: AItem.self) // Make sure the persistent store is empty. If it's not, return the non-empty container. var itemFetchDescriptor = FetchDescriptor<AItem>() itemFetchDescriptor.fetchLimit = 1 guard try container.mainContext.fetch(itemFetchDescriptor).count == 0 else { return container } container.mainContext.insert(AItem(name: "Apple", categoryName: "Fruits")) return container } catch { fatalError("Failed to create container") } }()
1
0
462
Jul ’24
SwiftUI view is not updated properly
Task: A child view should show depending on a boolean flag the corresponding view. The flag is calculated from a model which is given by parent view. Problem: The flag is false in init, which is correct. However in body it's true, which is incorrect. Why value in body isn't consistent to init? Is there a race condition? The child view's is rendered thrice, that's another issue, but flag is in init and body as described before. Parent view looks like this: struct ParentView: View { private var groupedItems: [GroupedItems] = [] init(items: [Item]) { Dictionary(grouping: items) { $0.category.name } .forEach { let groupedItems = GroupedItems(categoryName: $0.key, items: $0.value) self.groupedItems.append(groupedItems) } } var body: some View { ChildView(groupedItems) } } Child view looks like this: struct ChildView: View { @State private var showItems: Bool init(_ groupedItems: GroupedItems) { self._showItems = State(initialValue: !groupedItems.completed) } var body: some View { if showItems { AView() } else { BView() } } } Model looks like this: @Model final class Item: Identifiable { @Attribute(.unique) public var id: String public var name: String public var completed = false } struct GroupedItems { var categoryName: String var items: [Item] var completed: Bool { items.filter { !$0.completed }.isEmpty } }
3
0
653
Jul ’24
Closure | Error when referring static variable
struct Preview: View { private static let NAME = "Test" @Query(filter: #Predicate<Item> { $0.name == Preview.NAME}) private var items: [Item] var body: some View { MyView(name: Preview.NAME, items: items) } } Compile error occurs: key path cannot refer to static member 'NAME' How can a (static) constant be referred in member closure?
2
0
675
Jun ’24