How to persist state from a class into a separate SwiftUI view? [closed] - swift

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 1 year ago.
Improve this question
I am having problems persisting a class's state into a SwiftUI struct's view.
I have a class that acts as a controller, defined in one file, and a SwiftUI view that is supposed to change according to properties in that controller.
I've defined these files as such:
ClockController.swift
class ClockController:ObservableObject {
#Binding var isAM:Bool
init(){
self.isAM = false
}
func toggleAMPM(){
self.isAM = !self.isAM
}
}
and TestUI.swift
struct TestUI:View{
#ObservedObject var clockController:ClockController = ClockController()
var body: some View {
Button(action: {
self.clockController.toggleAMPM()
}){
Text("Toggle")
}
Text(self.clockController.isAM ? "AM" : "PM")
}
}
I want the TestUI to change/re-render every time the self.clockController.isAM variable changes (when the toggle button is pressed), which is why I have made ClockController an ObservableObject and added the #Binding keyword to the isAM property. However, I keep getting the following errors with this setup on ClockController's initializer method:
'self' used in property access 'isAM' before all stored properties are initialized and Return from initializer without initializing all stored properties
How can I get my TestUI to bind on ClockController's isAM variable?

all! I've fixed the problem myself. I needed to remove the #Binding keyword from ClockController's isAM property and change it to #Published. That did the trick.
Kudo's to YouTube and this video: https://youtu.be/-yjKAb0Pj60?t=919

Related

Does anyone know what this stack is called SwfitUI? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 8 months ago.
Improve this question
I'll keep it quick: Does anyone know if SwiftUI have a built in method that renders something like this image:
where the view just changes based on what label you tap? I wonder if it's possible to achieve this using some sort of navigation view or stack? I'd appreciate any input! Thanks.
EDIT:
HStack {
Picker(selection: $selected, label: Text("Mode"), content:{
Text("Projects").tag(1)
Text("Notes").tag(2)
Text("Docus").tag(3)
}).pickerStyle(SegmentedPickerStyle())
.frame(width: 200, height: 10)
.padding(EdgeInsets(top: 0, leading: 0, bottom: 10, trailing: -0))
if selected == 1 {
// how would i show another view if the user selects option 1?
}
}
This is a picker. to be precise, this is a segmented picker.
You can create it like so:
struct ContentView: View {
#State private var favoriteColor = 0
var body: some View {
Picker("What is your favorite color?", selection: $favoriteColor) {
Text("Red").tag(0)
Text("Green").tag(1)
Text("Blue").tag(2)
}
.pickerStyle(.segmented)
}
}
When we create the picker we pass in a binding (when we change the picker's value it will know to switch to it) this is the thing with the dollar sign ($)
The next thing is to add the segments.
So we add text views with a tag attached to each one.
Lastly we need to set the picker style (in this case the segmented)
.pickerStyle(.segmented)
I suggest you to look here: Create a segmented control and read values from it
Hope this helps!

what's an efficient way in swift to get a "change of day" event [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
so I've a swift-ui app where I show information for the current day - so at midnight I want it to switch to a new day - trouble is I want this to be as "light" as possible - eg I don't want anything consuming any resources as all I want to do is refresh the view, if it's displayed.
How do I do this without consuming background resources when the view isn't shown?
you could try something like this:
(note I did not wait to see if this works)
import SwiftUI
import Combine
#if os(iOS)
import UIKit
#endif
#main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
var body: some View {
Text("day change")
.onReceive(NotificationCenter.default.publisher(
for: UIApplication.significantTimeChangeNotification)) { _ in
print("----> day time has changed <----\n")
print("A notification that posts when there is a significant change in time, \n for example, change to a new day (midnight), \n carrier time update, and change to or from daylight savings time.")
}
}
}

Unable to use ForEach loop on #Binding variable [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 1 year ago.
Improve this question
I recently asked a question however it was removed as they thought the answer existed already and so I checked out this question. I have tried implementing the solution found here into my code and now run into a slightly different error.
Basically I am trying to create a simple application with a list of items that can be clicked on to navigate to a detail view with an edit feature. The data is read from a JSON file which is why the data is in a binding so it can be updated and this is not a problem anywhere else in the project.
import SwiftUI
struct EateryList: View {
#Binding var eateries: [Eatery]
var body: some View {
NavigationView {
VStack {
List {
ForEach(eateries.indicies, id: \.self) { i in
NavigationLink(destination: EateryDetail(eatery: $eateries[i])) { //errors appear here
EateryRow(eatery: $eateries[i])
}
}
}
.navigationTitle("Favourite Eateries")
.navigationBarItems(leading: EditButton(), trailing: Button( action: add)
{
Image(systemName: "plus")
}
)
.listStyle(InsetGroupedListStyle())
}
}
}
func add() {
eateries.append(Eatery(name: "Eatery", location: "Insert location here", notes: "Insert notes here", reviews: ["Insert reviews here"], url: "https://i.imgur.com/y3MMnba.png"))
}
}
I get 3 errors with this code currently which I do not fully understand...
Generic struct 'ForEach' requires that 'Binding' conform to 'RandomAccessCollection'
Referencing subscript 'subscript(dynamicMember:)' requires wrapper 'Binding<[Eatery]>'
Value of type '[Eatery]' has no dynamic member 'indicies' using key path from root type '[Eatery]'
All help is greatly appreciated right now and if you need further clarification of other parts of the project please let me know :)
It's a spelling mistake. It's indices not indicies
ForEach(eateries.indices, id: \.self) { i in //<-- Here
NavigationLink(destination: EateryDetail(eatery: $eateries[i])) {
EateryRow(eatery: $eateries[i])
}
}

How can I prevent SwiftUI from reinitializing my wrapped property just like it does with #StateObject?

From what I've read whenever you instantiate an object yourself in your view, you should use #StateObject instead of #ObservedObject. Because apparently if you use #ObservedObject, SwiftUI might decide in any moment to throw it away and recreate it again later, which could be expensive for some objects. But if you use #StateObject instead then apparently SwiftUI knows not to throw it away.
Am I understanding that correctly?
My question is, how does #StateObject communicate that to SwiftUI? The reason I'm asking is because I've made my own propertyWrapper which connects a view's property to a firebase firestore collection and then starts listening for live snapshots. Here's an example of what that looks like:
struct Room: Model {
#DocumentID
var id: DocumentReference? = nil
var title: String
var description: String
static let collectionPath: String = "rooms"
}
struct MacOSView: View {
#Collection({ $0.order(by: "title") })
private var rooms: [Room]
var body: some View {
NavigationView {
List(rooms) { room in
NavigationLink(
destination: Lazy(ChatRoom(room))
) {
Text(room.title)
}
}
}
}
}
The closure inside #Collection is optional, but can be used to build a more precise query for the collection.
Now this works very nicely. The code is expressive and I get nice live-updating data. You can see that when the user would click on a room title, the navigation view would navigate to that chat room. The chatroom is a view which shows all the messages in that room. Here's a simplified view of that code:
struct ChatRoom: View {
#Collection(wait: true)
private var messages: [Message]
// I'm using (wait: true) to say "don't open a connection just yet,
// because I need to give you more information that I can't give you yet"
// And that's because I need to give #Collection a query
// based on the room id, which I can only do in the initializer.
init(_ room: Room) {
_messages = Collection { query in
query
.whereField("room", isEqualTo: room.id!)
.order(by: "created")
}
}
var body: some View {
List(messages) { message in
MessageBubble(message: message)
}
}
}
But what I've noticed, is that SwiftUI initializes a new messages-collection every single time the user interacts with the UI in any way. Like even when an input box is clicked to give it focus. This is a huge memory leak. I don't understand why this happens, but is there a way to tell SwiftUI to only initialize #Collection once, just like it does with #StateObject?
Use #StateObject when you want to define a new source-of-truth reference type owned by that View and tied to its life-cycle. The object will be created just before the body is run for the first time and is stored in a special place by SwiftUI. If the View struct is recreated (e.g. by a parent View's body recompute by a state change) then the previous object will be set on the property instead of a new object. If the View is no longer init during body updates then the object will be deinit.
When you pass a #StateObject into a child View then in that child View uses #ObservedObject to enable the body of the child view to be recomputed when the object changes, just the same way the body of the parent View will also be recomputed because it used an #StateObject. If you use #ObservedObject for an object that was not an #StateObject in a parent View then since no View is owning it then it will be lost every time the View is init during body updates. Also, any objects that you create in the View init, like your Collection, those are immediately lost too. These excessive heap allocations could cause a leak and slows down SwiftUI. Objects the View owns must be wrapped in #StateObject to avoid this.
Lastly, don't use #StateObject for view state and certainly don't try and implement a legacy "View Model" pattern with them. We have #State and #Binding for that. #StateObject is only for model objects (as in your domain types: Book, Person etc.), loaders or fetchers.
WWDC 2020 Data Essentials in SwiftUI explains all this very nicely.

Redux conform Bindable for SwiftUI 2.0

I've been implementing Redux in my SwiftUI Project successfully but struggle when it comes to handling bindings properly. I want to use the binding functionality of SwiftUI while also storing the information in the Redux State.
As you see this kind of contradicts itself since the state can't be bound two-way.
This is my current code
#State var tab: Tab = .tab1
TabView(selection: $tab) { ... }
Ideally it should be comfortably usable like this if it's possible. I am also open to other ideas, that's just what i initially came up with - it's far from perfect.
#State var store = ReduxStore(...)
TabView(selection: $store.state.tab) { ... }
I figured out how to handle such cases. The solution for me is a Binding like:
let tabBinding = Binding<Tab> (
get: { self.store.state.currentTab }, // return the value from the state
set: { self.store.dispatch(action: NavigationAction.updateTab(tab: $0)) } // send the action here
)
Although this probably could be beautified it works, is pretty straight forward and solves the problem of working with Bindings in Redux.