How to use ForEach on an array of struct in Swift? - arrays

I have an array of Struct
struct StartView: View {
struct ExampleStruct {
var name: String
var keywords: String
var destinationID: String
init(name: String, keywords: String, destinationID: String) {
self.name = name
self.keywords = keywords
self.destinationID = destinationID
}
}
// Created 3 examplmes
let AStruct = ExampleStruct(name: "Structure A",keywords: "first: school",destinationID: "SAID")
let BStruct = ExampleStruct(name: "Structure B",keywords: "second: church",destinationID: "SBID")
let CStruct = ExampleStruct(name: "Structure C",keywords: "third: bank",destinationID: "SCID")
}
// Created my array of structures
var StructureArray = (AStruct, BStruct, CStruct)
However, I am now trying to create a NavigationView with a List but I am having issues getting this to work.
var body: some View {
NavigationView{
List {
for index in StructureArray.indices {
VStack {
// Text to put ExampleStruct name
// Text to put ExampleStruct keywords
// Text to put ExampleStruct destinationID
}
.padding()
}
}
.navigationTitle("Structure Search")
}
}
I get a Closure containing control flow statement cannot be used with result builder 'ViewBuilder' based on the for index in StructureArray.indices
I have also tried:
for index in 0..<StructureArray.count {
let currentStruc = StructureArray[index]
HStack {
Text (currentStruc.name)
Text (currentStruc.keywords)
\\ etc
}
But get the same error. I have searched online for the last few hours and am still lost on how to make this work. Am I missing something obvious? Would a ForEach be better?

You can try like below. I have corrected a few of your code. Google the changes to better understand it.
ExampleStruct
struct ExampleStruct: Identifiable {
let id = UUID()
var name: String
var keywords: String
var destinationID: String
}
ContentView
import SwiftUI
struct ContentView: View {
let aStruct = ExampleStruct(name: "Structure A",keywords: "first: school",destinationID: "SAID")
let bStruct = ExampleStruct(name: "Structure B",keywords: "second: church",destinationID: "SBID")
let cStruct = ExampleStruct(name: "Structure C",keywords: "third: bank",destinationID: "SCID")
var structArray: [ExampleStruct] {
return [aStruct, bStruct, cStruct]
}
var body: some View {
List {
ForEach(structArray) { item in
Text(item.name)
}
}
}
}
Playgrounds test

Related

Check if optional array contains element and display view

In SwiftUI, I'm fetching objects via API into an optional array. In a view, I'm then trying to determine if that array contains a set of possible elements when the view is loaded.
If the array contains that element, I'll show a separate view for it. If it doesn't I won't show it.
How do I check if that optional array contains a specific element?
So for example, I want to check if projectType.landscapes? contains a "mulching" entry. Also, the ProjectType.Landscapes.types array will contain string values that correspond to the present projectType.landscape options, for example, ["mulching", "clearing", "treeCare"] or simply ["mulching"].
Here's my data structure:
// MARK: - ProjectType
struct ProjectType: Codable {
let landscapes: [Landscape]?
let generalConstructions: [GeneralConstruction]?
}
// MARK: - GeneralConstruction
struct GeneralConstruction: Codable {
let id: Int
}
// MARK: - Landscape
struct Landscape: Codable {
let id: Int
let types: [String]
let mulching: Mulching?
let clearing: Clearing?
let planting: Planting?
let treeCare: TreeCare?
let other: Other?
}
// MARK: - Clearing
struct Clearing: Codable {
let id: Int
let clearingArea, removeTrees, treeCount, approxTreeHeight: String
let treeStumps: String
}
// MARK: - Mulching
struct Mulching: Codable {
let id: Int
let mulchingType, currentlyInPlace, currentlyInPlaceCustom, roomLength: String
let roomWidth, color, customColor, length: String
let approximateLength: String
}
// MARK: - Other
struct Other: Codable {
let id: Int
let otherDetails: String
}
// MARK: - Planting
struct Planting: Codable {
let id: Int
let designPlan, treeLargerThanFiveFeet, plantCount: String
}
// MARK: - TreeCare
struct TreeCare: Codable {
let id: Int
let careDescription: String
}
Here's where I'd check to see if the landscape types are present:
import SwiftUI
struct LandscapeSpecificsView: View {
let projectType: ProjectType
var body: some View {
VStack{
Text("Here is the landscape specific project info. I'll show mulching or clearing, or treeCare, etc.")
}
}
}
struct MulchingView: View {
var body: some View {
Text("Here is the mulching info")
}
}
struct ClearingView: View {
var body: some View {
Text("Here is the clearing info")
}
}
struct PlantingView: View {
var body: some View {
Text("Here is the planting info")
}
}
struct TreeCareView: View {
var body: some View {
Text("Here is the treecare info")
}
}
struct LandscapeOtherView: View {
var body: some View {
Text("Here is the landscape other info")
}
}
first, you can use 'optional binding' methods like ' if let' or 'guard let' or simply using array count to make sure your array has a value.
after that you can use several options on Array like 'contains(_:)'
apple document
if let landScapes = project.landscapes {
if landScapes.contains(where: {$0.mulching != nil}) {
}
}

SwiftUI MVVM how to loop through data and store them inside array

I am using MVVM in my SwiftUI project and after I request to fetch data from the API, I want to loop through the data and store some of them inside an array, however it returns error, what is the correct method to do this?
Here is my code and data struct
MenuDetailView
struct MenuDetailView: View {
#ObservedObject var viewModel = MenuDetailViewModel()
var body: some View {
ForEach(self.viewModel.variantGroup, id: \.self) { vg in
/*** I Need to loop and store vg.variantGroupName into array viewModel.variantChosen, how? ***/
self.viewModel.variantChosen.append(vg.VariantGroupName)
// This always return error:
// Type '()' cannot conform to 'View'; only struct/enum/class types can conform to protocols
VStack {
HStack {
Text(vg.variantGroupName)
Text(String(self.viewModel.arrVariantChoosen[0]))
}
VStack {
ForEach(vg.variant, id: \.self) { v in
Text(v.variantName)
}
}
}
}
}
}
}
MenuDetailViewModel.swift
class MenuDetailViewModel: ObservableObject, MenuDetailService {
var apiSession: APIService
#Published var detaildata: MenuDetailData?
#Published var variantGroup = [MenuDetailVariantGroup]()
#Published var variantChosen: Array<String> = []
var cancellables = Set<AnyCancellable>()
init(apiSession: APIService = APISession()) {
self.apiSession = apiSession
}
func getMenuDetail() {
let cancellable = self.getMenuDetail()
.sink(receiveCompletion: { result in
switch result {
case .failure(let error):
print("Handle error: \(error)")
case .finished:
break
}
}) { (detail) in
self.detaildata = detail.data
self.variantGroup = detail.data.variantGroup
}
cancellables.insert(cancellable)
}
}
MenuDetailData.swift
struct MenuDetailData: Codable, Identifiable {
let id = UUID()
let idMenu: String
let menuName: String
let variantGroup: [MenuDetailVariantGroup]
}
MenuDetailVariantGroup.swift
struct MenuDetailVariantGroup: Codable, Identifiable, Hashable {
let id = UUID()
let variantGroupName: String
let variant: [MenuDetailVariant]
let limit: Int
}
MenuDetailVariant.swift
struct MenuDetailVariant: Codable, Identifiable, Hashable {
let id = UUID()
let variantName: String
}
Thank you all in advance
You can not add this inside the ForEach. In SwiftUI, ForEach is a view. It accepts view data, it's not the same as Array.forEach.
You need to do it inside the view model. Like this
func getMenuDetail() {
let cancellable = self.getMenuDetail()
.sink(receiveCompletion: { result in
switch result {
case .failure(let error):
print("Handle error: \(error)")
case .finished:
break
}
}) { (detail) in
self.detaildata = detail.data
self.variantGroup = detail.data.variantGroup
self.variantChosen = self.variantGroup.map{$0.variantGroupName} //<--here
}
cancellables.insert(cancellable)
}
Remove this from ForEach
self.viewModel.variantChosen.append(vg.VariantGroupName)

Swift - Cannot append Object to Array

I have created a New View in my App, but the Identifiable Object won't append to the Array.
I really don't know why its not appending...
Here is the Code:
struct FirstSettingsIdentifiables: Identifiable {
var id: UUID = UUID()
var name: String
var icon: String
}
struct SettingsView: View {
#EnvironmentObject var settingItems: ContentModel
#State var firstArr: [FirstSettingsIdentifiables] = []
init() {
createFirstList()
print("Settings successfully initialized.")
}
var body: some View {
return VStack {
Text("Einstellungen")
.font(.title)
NavigationView {
//Mitteilungen Liste
List(firstArr) { x in
// ForEach(firstArr) { x in
// VStack {
// Image(systemName: x.icon)
Text("Das ist ein test")
// }
// }
}.navigationBarTitle("Mitteilungen")
}
}
}
func createFirstList() {
let aText = "Mitteilungen"
let aIcon = "info.circle.fill"
let aObject = FirstSettingsIdentifiables(name: aText, icon: aIcon)
firstArr.append(aObject)
print(firstArr.count)
}
}
The problem is probably in the createFirstList() Section. In this function, the Object aObject is full of data(This is working fine), but then the Object won't append to my firstArr. The count is always 0.
What am I doing wrong here?
You are changing the value of firstArr too early. Instead of calling createFirstList() in the init, remove that and instead add the following code onto the view body:
VStack {
/* ... */
}
.onAppear(perform: createFirstList)
Alternatively, you could do the following:
init() {
_firstArr = State(initialValue: getFirstList())
print(firstArr.count)
print("Settings successfully initialized.")
}
/* ... */
func getFirstList() -> [FirstSettingsIdentifiables] {
let aText = "Mitteilungen"
let aIcon = "info.circle.fill"
let aObject = FirstSettingsIdentifiables(name: aText, icon: aIcon)
return [aObject]
}

make a button that appends structs to an array in swift

I am trying to make a macOS list/todo app with swift and want to add a button that adds appends a struct to an array, but it shows this error: "Cannot use mutating member on immutable value: 'self' is immutable"
here is my code
import SwiftUI
struct Lists{
var title : String
var id = UUID()
}
struct SidebarView: View {
var list = [Lists(title: "one"),Lists(title: "two"),Lists(title: "three")]
var body: some View {
List{
Button(action:{
let item = Lists(title:"hello")
list.append(item)
}) {
Text("add")
}
ForEach(list, id : \.id ){ item in
NavigationLink(destination:DetailView(word:item.title)){
Text(item.title)
}
}
}
.listStyle(SidebarListStyle())
.frame(maxWidth:200)
}
}
struct SidebarView_Previews: PreviewProvider {
static var previews: some View {
SidebarView()
}
}
the code that says list.append is the error
You need to declare list as #State variable.
#State var list = [Lists(title: "one"),Lists(title: "two"),Lists(title: "three")]
Then append new item to list:
Button(action:{
let item = Lists(title:"hello")
self.list.append(item)
}) {
Text("add")
}

How to handle data and data models in SwiftUI

I am having trouble with the structure of my data for creating a List in SwiftUI. Before SwiftUI I created my data models and held this data in separated files. For example,
In my UserModel.swift :
import SwiftUI
struct UserModel: Equateable {
var name: String
var items = [ItemModel]()
init(name: String, items: [ItemModel]? = nil) {
self.name = name
if let items = items {
self.items = items
}
}
}
In my ItemModel.swift :
import SwiftUI
struct ItemModel: Equatable {
var itemName: String
var price: Double
init(itemName: String, price: Double) {
self.itemName = itemName
self.price = price
}
}
Then I had a separate class called Data that held this data.
In my Data.swift :
import Foundation
class Data {
static var userModels = [UserModel]()
static var itemModels = [ItemModel]()
}
I would update the data by doing something like this in my views:
let user = UserModel(name: "Bob")
Data.userModels.append(user)
I'm having trouble populating a List from this framework in SwiftUI. I tried to make both of my model classes conform to Identifiable but that doesn't seem to help in updating the list. This is what I'm doing so far:
Updated UserModel :
struct UserModel: Identifiable {
var id = UUID()
var name: String
var items = [ItemModel]()
init(id: UUID, name: String, items: [ItemModel]? = nil) {
self.id = id
self.name = name
if let items = items {
self.items = items
}
}
}
Updated ItemModel:
struct ItemModel: Identifiable {
var id = UUID()
var itemName: String
var price: Double
init(id: UUID, itemName: String, price: Double) {
self.id = id
self.itemName = itemName
self.price = price
}
}
And in my ContentView.swift
List (Data.userModels) { user in
Text("\(user.name)")
}
But this does not update the List when I am running the app. (Btw I take input from TextField, append it to Data.userModels, and try to populate it in the List with no luck.)
Is there a different way I should be structuring my data and data models? Or am I just not using the proper syntax when creating the list? Sorry for the long question. Thanks for the help!

Resources