SWIFT5: Initilize only selected variables in init method - swift5

Is there a way to use init method without initializing Binding parameters? If they are not mentioned, I get error of "Initialize all parameters". If they are initialized, then it gives another error.
How can I avoid initialising them?
struct DetailView: View {
#Binding var isPresented: Bool
#Binding var quest: QuestObject
#State var selectedOption : String = ""
init()
{
//self.isPresented = $isPresented //I would like to skip initializing this paremeter
//self.quest = $quest //I would like to skip initializing this paremeter
self.selectedOption = "Apples"
}

Related

SwiftUI: how to properly use init()?

I am trying to make use of init to call the fetchProducts function in my ViewModel class. When I add init though, I am getting the following 2 errors:
Variable 'self.countries' used before being initialized
and
Return from initializer without initializing all stored properties
The variable countries is binding though so there shouldn't need to be an initialized value in this view. Am I using init incorrectly?
struct ContentView: View {
#Namespace var namespace;
#Binding var countries: [Country];
#Binding var favLists: [Int];
#State var searchText: String = "";
#AppStorage("numTimeUsed") var numTimeUsed = 0;
#Environment(\.requestReview) var requestReview
#StateObject var viewModel = ViewModel();
init() {
viewModel.fetchProducts()
}
var body: some View {
}
}
Look at the initialiser that autocomplete gives you when you use ContentView…
ContentView(countries: Binding<[Country]>, favLists: Binding<[Int]>)
If you're creating your own initialiser, it will need to take those same parameters, e.g.
init(countries: Binding<[Country]>, favLists: Binding<[Int]>) {
_countries = countries
_favLists = favLists
viewModel.fetchProducts()
}
Alternatively, use the default initialiser, and instead…
onAppear {
viewModel.fetchProducts()
}

Manipulating binding variables in SwiftUI

Suppose I have a string that's binding:
#Binding var string: String
But, I want to manipulate that string, and then pass it to a child view.
struct ViewOne: View {
#State var string: String = "Hello"
var body: some View {
ViewTwo(string: string + " World") // this is invalid, as ViewTwo requires Binding<String>
}
}
struct ViewTwo: View {
#Binding var string: String
var body: some View {
Text(string)
}
}
How should I go about manipulating that string, such that it will update the UI when the state changes? Let's assume that I want to re-use ViewTwo for different components, and so I would want to manipulate the string before it is passed to the view.
A computed variable doesn't work, as it isn't Binding
private var fixedString: String {
return string + " World"
}
And I don't want to create a binding variable, because the setter makes no sense in this context
private var fixedString: Binding<String> {
Binding<String> (
get: {
string + " World"
}, set: {
// this function doesn't make sense here - how would it update the original variable?
}
)
}
Am I just using #State and #Binding wrong?
Just remove the #Binding inside ViewTwo:
struct ViewTwo: View {
var string: String /// no need for the `#Binding`!
var body: some View {
Text(string)
}
}
SwiftUI will update ViewTwo even if you don't have the #Binding (it basically re-renders the entire body whenever a #State or #Published var gets changed).
You only need Bindings when you need to update/set the original #State or #Published property. In that case you'd add something like the var fixedString: Binding<String> in your question.

Using an #Environment(\.managedObjectContext)—NSManagedObjectContext—in an init function

I have this view.
struct AView: View {
#Environment(\.managedObjectContext) private var viewContext
#State var timestamp: Date
#State private var obj: MyObject
init(date: Date)
{
// this is fine
_timestamp = State(initialValue: date)
// this gives an error
_obj = State(initialValue: MyObject(context: viewContext))
}
var body: some View {
...
}
}
I need to initialize the object obj, which must be initialized with an NSManagedObjectContext, inside of the init function. However, when I try to use the code above, I get this error
Variable 'self.obj' used before being initialized
How do I initalize a variable in the init function that depends on an NSManagedObjectContext?

How do I change the variable "url" which is inside of a class by using a struct? in swift

I'm new to swift and I cannot figure out how to change the url variable by inputting the Binding var url from the struct. I keep getting errors regardless of how I try it. Any help would v vvv appreciated
struct SearchView : View {
#State var showSearchView = true
#State var color = Color.black.opacity(0.7)
**#Binding var url: String**
#ObservedObject var Books = getData()
var body: some View{
if self.showSearchView
{
NavigationView{
List(Books.data) {i in
....}
class getData : ObservableObject{
#Published var data = [Book]()
**var url** = "https://www.googleapis.com/books/v1/volumes?q=harry+potter"
init() {....}
First of all if the current view owns the model object use #StateObject.
Second of all please name classes with starting uppercase and functions and variables with starting lowercase letter.
#StateObject var books = GetData()
...
class GetData : ObservableObject {
You don't need a Binding just address the property directly
books.url = "https://apple.com"
and delete
#Binding var url: String
And if you need to display the changed value immediately use a #Published property and bind the it directly
class GetData : ObservableObject {
#Published var url = "https://www.googleapis.com/books/v1/volumes?q=harry+potter"
...
struct SearchView : View {
#StateObject var books = GetData()
var body: some View {
VStack{
Text(books.url)
TextField("URL", text: $books.url)
}
}
}
Change the *var url** = "https://www.googleapis.com/books/v1/volumes?q=harry+potter" in the second view with: #State var url = "https://www.googleapis.com/books/v1/volumes?q=harry+potter" so it can be mutable

How to pass a value from an EnvironmentObject to a class instance in SwiftUI?

I'm trying to assign the value from an EnvironmentObject called userSettings to a class instance called categoryData, I get an error when trying to assign the value to the class here ObserverCategory(userID: self.userSettings.id)
Error says:
Cannot use instance member 'userSettings' within property initializer; property initializers run before 'self' is available
Here's my code:
This is my class for the environment object:
//user settings
final class UserSettings: ObservableObject {
#Published var name : String = String()
#Published var id : String = "12345"
}
And next is the code where I'm trying to assign its values:
//user settings
#EnvironmentObject var userSettings: UserSettings
//instance of observer object
#ObservedObject var categoryData = ObserverCategory(userID: userSettings.id)
class ObserverCategory : ObservableObject {
let userID : String
init(userID: String) {
let db = Firestore.firestore().collection("users/\(userID)/categories") //
db.addSnapshotListener { (snap, err) in
if err != nil {
print((err?.localizedDescription)!)
return
}
for doc in snap!.documentChanges {
//code
}
}
}
}
Can somebody guide me to solve this error?
Thanks
Because the #EnvironmentObject and #ObservedObject are initializing at the same time. So you cant use one of them as an argument for another one.
You can make the ObservedObject more lazy. So you can associate it the EnvironmentObject when it's available. for example:
struct CategoryView: View {
//instance of observer object
#ObservedObject var categoryData: ObserverCategory
var body: some View { ,,, }
}
Then pass it like:
struct ContentView: View {
//user settings
#EnvironmentObject var userSettings: UserSettings
var body: some View {
CategoryView(categoryData: ObserverCategory(userID: userSettings.id))
}
}