Hello,
I have a search view that displays a list from a .json file, when an object is tapped a detailed sheet view is meant to appear. The problem is that my
.sheet(isPresented: $showSheet) {
ProductDetail(product: product)
.environmentObject(DataModel())
}
block isn’t accepting product
as a valid argument.
Any help would be appreciated,
Josh
This is my Search View:
import SwiftUI
struct SearchView: View {
@StateObject var model = DataModel()
@State private var searchText = ""
@State private var showSheet = false
var body: some View {
NavigationStack {
List {
ForEach(filteredProducts, id: \.self) { product in
NavigationLink {
Text(product.name)
} label: {
PageView(pages: model.features.map { FeatureCard(product: $0) })
.aspectRatio(3 / 2, contentMode: .fit)
.listRowInsets(EdgeInsets())
.onTapGesture {
showSheet = true
}
}
}
}
.navigationTitle("Search")
.navigationBarTitleDisplayMode(.inline)
}
.searchable(text: $searchText, prompt: "Tap to Search for Light Products")
.sheet(isPresented: $showSheet) {
ProductDetail(product: product)
.environmentObject(DataModel())
}
}
var filteredProducts: [Product] {
if searchText.isEmpty {
return model.products
} else {
return model.products.filter {
$0.name.lowercased().contains(searchText.lowercased()) ||
$0.flavour.lowercased().contains(searchText.lowercased())
}
}
}
}
struct SheetView: View {
let product: Product
var body: some View {
VStack {
Text(product.name)
}
}
}
struct SearchView_Previews: PreviewProvider {
static var previews: some View {
SearchView()
.environmentObject(DataModel())
}
}
My ProductDetail() View:
import SwiftUI
struct ProductDetail: View {
@StateObject var model = DataModel()
@EnvironmentObject var dataModel: DataModel
var product: Product
var productIndex: Int {
dataModel.products.firstIndex(where: { $0.id == product.id })!
}
var body: some View {
ScrollView {
VStack(alignment: .leading) {
PageView(pages: model.features.map { FeatureCard(product: $0) })
.aspectRatio(3 / 2, contentMode: .fit)
.listRowInsets(EdgeInsets())
Text(product.name)
.font(.title)
Text(product.flavour)
.font(.subheadline)
.foregroundColor(.secondary)
NavigationLink(destination: BagView())
{Text("$7.99")}
.buttonStyle(.borderedProminent)
.padding()
Divider()
Text(product.description)
}
.padding(.bottom)
}
.navigationTitle(product.name)
.navigationBarTitleDisplayMode(.inline)
}
}
struct ProductDetail_Previews: PreviewProvider {
static let dataModel = DataModel()
static var previews: some View {
ProductDetail(product: dataModel.products[0])
.environmentObject(dataModel)
}
}
My DataModel:
import Foundation
import Combine
class DataModel: ObservableObject {
@Published var products = [Product]()
init() {
products = load("ProductData.json")
}
var features: [Product] {
products.filter { $0.isFeatured }
}
func load<T: Decodable>(_ filename: String) -> T {
let data: Data
guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
else {
fatalError("Couldn't find \(filename) in main bundle.")
}
do {
data = try Data(contentsOf: file)
} catch {
fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
}
do {
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
}
}
}
My Product definition file:
import Foundation
import SwiftUI
struct Product: Hashable, Codable, Identifiable {
var id: Int
var name: String
var flavour: String
var description: String
var isFeatured: Bool
var category: Category
enum Category: String, CaseIterable, Codable {
case drink = "Drink"
case yoghurt = "Yoghurt"
}
var image: String
private var imageName: Image {
Image(image)
}
var image2: String
private var image2Name: Image {
Image(image2)
}
var image3: String
private var image3Name: Image {
Image(image3)
}
var featureImage: Image? {
isFeatured ? Image(image) : nil
}
}
Here is a sample of what my .JSON file looks like:
{
"id": 1001,
"name": "Lemon Zest Energy",
"category": "Drink",
"flavour": "Lemon",
"description": "Revitalize your senses and conquer your day with the zesty and refreshing taste of Lemon Zest Energy. Packed with essential vitamins and energy-boosting ingredients, this lemon-flavored energy drink will give you the boost you need to power through your day. Perfect for those who need a quick pick-me-up or for those who want to add a little extra zest to their daily routine.",
"image": "Lemon 1",
"image2": "Lemon 2",
"image3": "Lemon 3"
}