Why do I keep getting a "Cannot find '...' in scope" error? - swift

I am trying to make a second view on an app which is like a leaderboard. When the game has completed 10 rounds on ContentView, I want the calculated score to be stored and printed on the ScoreboardView (accessed by a button). I used #Binding to connect the score variable from one view to the other, but keep getting the "out of scope" error. Does anyone know why this is? Here is my code for the ScoreboardView:
'''
import SwiftUI
struct scoreboardView: View {
#Binding var score: Int
var body: some View {
List {
ForEach(1..<9) {
Text("Game \($0): \(score) ")
}
}
}
}
struct scoreboardView_Previews: PreviewProvider {
static var previews: some View {
scoreboardView(score: $scoreTracker)
}
}
'''
This is not my final code, therefore ignore the middle. However, I get the error in the last line of the initializing of the preview.

You don't have anything defined called scoreTracker in your code, but you're trying to use it in your preview. Instead, you can pass a constant in place of your binding (just for preview purposes):
struct scoreboardView: View {
#Binding var score: Int
var body: some View {
List {
ForEach(1..<9) {
Text("Game \($0): \(score) ")
}
}
}
}
struct scoreboardView_Previews: PreviewProvider {
static var previews: some View {
scoreboardView(score: .constant(50))
}
}

Related

No ObservableObject of type ModelData found: Missing environmentObject, but the environmentObject is actually there (SwiftUI, Xcode)

Xcode version: 14.2
I am trying to display a list of sports clubs. Since my modelData.clubs is still an empty array when just viewing the preview (modelData.clubs gets its actual value in my contentView, when the app is loaded), I would like it to display an array of default clubs if modelData.clubs.isEmpty. However, when I try to run my code, it gives me the following error in the marked line: Thread 1: Fatal error: No ObservableObject of type ModelData found. A View.environmentObject(_:) for ModelData may be missing as an ancestor of this view.
struct ClubList: View {
#EnvironmentObject var modelData: ModelData
#State private var sortByValue = false
var clubs: [Club] = [Club.default, Club.default, Club.default, Club.default, Club.default]
init() {
if (!modelData.clubs.isEmpty) { // ERROR IN THIS LINE
clubs = modelData.clubs
}
if sortByValue {
clubs.sort {
$0.value < $1.value
}
} else {
clubs.sort {
$0.name < $1.name
}
}
}
var body: some View {
NavigationView {
List {
ForEach(clubs) { club in
NavigationLink {
ClubDetail(club: club)
} label: {
ClubRow(club: club)
}
}
}
}
}
}
struct ClubList_Previews: PreviewProvider {
static var previews: some View {
ClubList()
.environmentObject(ModelData())
}
}
ClubList() gets called in my ContentView:
struct ContentView: View {
#EnvironmentObject var modelData: ModelData
// ... //
var body: some View {
if clubsLoaded {
ClubList()
.environmentObject(ModelData())
} // ... //
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(ModelData())
}
}
And ContentView gets called in my App:
#main
struct clubsApp: App {
#StateObject private var modelData = ModelData()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(modelData)
}
}
}
I am pretty new to Xcode, but I think I put the environmentObject(ModelData()) everywhere, where it would be required, and I am pretty lost now on what to do.
Any help would be greatly appreciated!
I think I figured it out!
This post here made me realise that my problem was that I was trying to access my modelData in init(), where it is not possible yet.
Someone from the link said:
The environment is passed down when the body is called, so it doesn't yet exist during the initialization phase of the View struct.
Thus, I solved my problem by moving
if (!modelData.clubs.isEmpty) { clubs = modelData.clubs }
from init() into its own function which is then called in onAppear of my NavigationView.

SwiftUI | Preview not updating on #Binding var value change

I am learning SwiftUI and tried to make a simple todo list but I'm having issues understanding why #Binding property doesn't update my preview.
The code is the following.
import SwiftUI
struct TodoRow: View {
#Binding var todo: Todo
var body: some View {
HStack {
Button(action: {
todo.completed.toggle()
}, label: {
Image(systemName: todo.completed ? "checkmark.square" : "square")
})
.buttonStyle(.plain)
Text(todo.title)
.strikethrough(todo.completed)
}
}
}
struct TodoRow_Previews: PreviewProvider {
static var previews: some View {
TodoRow(todo: .constant(Todo.sampleData[0]))
}
}
The preview doesn't update when I click the square button but the app works fine. Am I using it incorrectly?
EDIT:
Even without .constant(#), the preview doesn't work.
struct TodoRow_Previews: PreviewProvider {
#State private static var todo = Todo.sampleData[0]
static var previews: some View {
TodoRow(todo: $todo)
}
}
Found a solution in the article Testing SwiftUI Bindings in Xcode Previews.
In order for previews to change you must create a container view that holds state and wraps the view you're working on.
In my case what I've ended up doing was changing my preview to the following.
struct TodoRow_Previews: PreviewProvider {
// A View that simply wraps the real view we're working on
// Its only purpose is to hold state
struct TodoRowContainer: View {
#State private var todo = Todo.sampleData[0]
var body: some View {
TodoRow(todo: $todo)
}
}
static var previews: some View {
Group {
TodoRow(todo: .constant(Todo.sampleData[0]))
.previewDisplayName("Immutable Row")
TodoRowContainer()
.previewDisplayName("Mutable Row")
}
}
}

No exact matches in call to initializer of Text, how can I solve this issue in SwiftUI?

I am new to swift, this might be a stupid question but it confuses me a long time.
Here is my code, I wrote this code by imitating the sandwich example from WWDC 2020's intro to swiftUI.
import SwiftUI
struct movieDetail: View {
var idnum:Int
var body: some View {
Text(idnum)
}
}
struct movieDetail_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
movieDetail(idnum:24428)
}
}
}
while it displays as compile error:
No exact matches in call to initializer.
WWDC 2020's example is below
import SwiftUI
struct sandwichdetail: View {
var sandwich:Sandwich
#State private var zoomed = false
var body: some View {
VStack {
Text(sandwich.name)
}
.navigationTitle(sandwich.name)
.edgesIgnoringSafeArea(.bottom)
}
}
struct sandwichdetail_Previews: PreviewProvider {
static var previews: some View {
NavigationView{
sandwichdetail(sandwich: testData[1])
}
}
}
I don't quite understand the difference between these 2 pieces of code and why mine fail and the sandwich example succeed displaying the sandwich's name.
The Text type doesn't have an initializer that takes an Int. Try this instead:
Text("\(idnum)")
Another more Swifty way, is using description because Int conforms to CustomStringConvertible protocol.
Text(idnum.description)
You can create your own initialiser for Text if this a common use case for you, using Swift's string interpolation. This may reduce type visibility at the call-site however.
extension Text {
init(_ int: Int) {
self = Text("\(int)")
}
}

SwiftUI Preview not working with Binding<Bool> [duplicate]

I present this view as a sheet from its parent view
struct NamesView: View {
#Binding var match: Match
var body: some View {
...
}
}
Since the match source of truth is in the parent view presenting this NamesView sheet, when the view is constructed I pass in a $match binding and data flows as intended.
However, when constructing this view in a preview provider
struct NamesView_Previews: PreviewProvider {
static var previews: some View {
NamesView()
}
}
the compiler says that NamesView() expects a match argument of type Binding<Match> (Match being the parent view presenting this view as a sheet). I'm not sure what would be a good way to proceed from here or if this is a limitation of SwiftUI.
If you want only constant preview, then it can be
struct NamesView_Previews: PreviewProvider {
static var previews: some View {
NamesView(match: .constant(Match()))
}
}
if you want it in live, the it can be
struct NamesView_Previews: PreviewProvider {
struct BindingTestHolder: View {
#State private var testedMatch = Match()
var body: some View {
NamesView(match: $testedMatch)
}
}
static var previews: some View {
BindingTestHolder()
}
}
Try this:
struct NamesView_Previews: PreviewProvider {
static var previews: some View {
NamesView(match:.constant(Match()))
}
}
I wrote about this in depth here, but the short version is simply to add the following extension:
extension Binding {
public static func variable(_ value: Value) -> Binding<Value> {
var state = value
return Binding<Value> {
state
} set: {
state = $0
}
}
}
And then do...
struct NamesView_Previews : PreviewProvider {
static var previews: some View {
NamesView(match: .variable(Match()))
}
}
This lets you actually mutate the value, useful when doing live previews in Xcode 14.

TextField: Generic parameter 'Subject' could not be inferred

I have a form view designed to edit data for an existing item and I'm able to display the Binded (bound?) value without a problem. The TextField gives me an error of "Generic parameter 'Subject' could not be inferred"
Here's the file where the problem is occurring.
import SwiftUI
struct EditMeasurementView: View {
#Binding var measurementItem: MeasurementItem
var body: some View {
Form {
Text("\(measurementItem.weight)")
TextField("Weight", text: $measurementItem.weight)
// TextField("Date", text: $measurementItem.mdate)
}
}
}
struct EditMeasurementView_Previews: PreviewProvider {
static var previews: some View {
EditMeasurementView(measurementItem: .constant(MeasurementItem(weight:"99",mdate:"1/1/2001")))
}
}
The first item in the form is fine and if I click on '$measurementItem' is is bound to the #Binding. The second item is where the error appears and if I click on '$measurementItem' there, it does not indicate is is linked back to the #Binding.
What am I missing?
Here is some code to duplicate the problem described above:
import SwiftUI
struct MeasurementItem {
let weight: String
let mdate: String
}
struct EditMeasurementView: View {
#State var measurementItem: MeasurementItem
var body: some View {
Form {
Text("\(measurementItem.weight)")
TextField("Weight", text: $measurementItem.weight)
}
}
}
struct EditMeasurementView_Previews: PreviewProvider {
static var previews: some View {
EditMeasurementView(measurementItem: MeasurementItem(weight:"99",mdate:"1/1/2001"))
}
}
The line TextField("Weight", text: $measurementItem.weight) will generate the error described by the OP.
However the real problem is not with TextField("Weight", text: $measurementItem.weight) itself but that $measurementItem.weight is a constant (i.e it is declared with let instead of var) and because it's a constant the TextField cannot change the value of $measurementItem.weight .