How to handle data and data models in SwiftUI - arrays

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!

Related

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

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

Assign value from array to a variable outside its constructor in Swift

I am trying to get variables in my view structure which will be updated from Firestore every time the user submits a rating so that a new currentRating can be determined. (currentRating is the average of all currentRatings, but I'm not storing individual ratings - I'm just trying to have my currentRating for each of the schools keep being updated after each new rating). I have created the class, FireStoreManager which creates an array which should be reading data to an array. The base of my array is from my identifiable structure RatingSubmission. I then have the first part of my final view structure where I've referenced FirestoreManager() because I'm trying to then be able to reference the different items in the array, Rating Submission, but I keep getting errors.
class FirestoreManager: ObservableObject {
#Published var ratingSubmission = [RatingSubmission]()
var currentRating: CGFloat = 0.0
func fetchRatingSubmission() {
let db = Firestore.firestore()
let docRef = db.collection("RatingInformation").document()
docRef.getDocument{(document, error) in
if let document = document, document.exists {
let dataDescription = document.data().map(String.init(describing:)) ??
"nil"
print("Document data: \(dataDescription)")
let data = document.data()
self.currentRating = data!["RatingInformation"]! as! CGFloat
print("after update: \(self.currentRating)")
}
}
}
init() {
fetchRatingSubmission()
}
}
struct RatingSubmission: Identifiable {
var id: String = UUID().uuidString
var location: String
var currentRating: Float
var usualRating: Float
var totalRatings: Float
}
struct NewRatingView: View {
var model = FirestoreManager()
var schools = ["North Avenue", "West Village", "Brittain"]
#State private var location = "West Village"
var currentRating: CGFloat {
didSet {
model.fetchRatingSubmission(currentRating)
}
}

Show genre name based on which genre id movie has from TMDB API

I want to display genre of the movie using TMDB api. But in a movie model i get an array of genre id.
How can i show genre name based on which id movie has?
Array of id in movie struct:
let genreIDS: [Int]
Genre model struct:
struct GenreID: Codable {
let genres: [Genre]
}
struct Genre: Codable {
let id: Int
let name: String
}
My movie cell view model:
class PopularMovieCellViewModel: CellViewModelType {
var movieID: Int
var title: String
var image: URL?
var rating: Double
var releaseDate: String
//var genres: String
init(movie: Result) {
self.movieID = movie.id
self.title = movie.title
self.rating = movie.voteAverage
self.releaseDate = movie.releaseDate
self.image = makeImageURL(movie.posterPath)
}
How i get data:
private func mapPopularMovieData() {
popularMovie.value = self.popularModel?.results.compactMap({ PopularMovieCellViewModel(movie: $0)
})
func getData() {
dataFetchService.fetchMovie { [weak self] (result) in
self?.popularModel = result
self?.mapPopularMovieData()
}

SwiftUI EXC_BAD_INSTRUCTION when filtering and mapping Array

I have an Entity called Lessons, which consists of:
#NSManaged public var timeMinutes: Double
#NSManaged public var id: UUID
#NSManaged public var lessonDescription: String
#NSManaged public var date: Dates
The 'date' attribute is linked with a one-to-many relationship to a separate 'Dates' entity, which consists of:
#NSManaged public var date: Date
I am now trying to display a graph that has the summed 'timeMinutes' by 'date'. This finalArray would have the following structure:
struct FinalArray {
let summedMinutes : Double
let date : Date
}
My code to generate (and display) this array looks as following:
import SwiftUI
import SwiftUICharts
struct Test: View {
#Environment(\.managedObjectContext) var managedObjectContext
#FetchRequest(fetchRequest: Lessons.allLessonsFetchRequest()) var lessons: FetchedResults<Lessons>
var finalArray = [FinalArray]()
init(){
let allKeys = Set<Date>(lessons{$0.date.date})
for key in allKeys {
let sum = lessons.filter({$0.date.date == key}).map({$0.timeMinutes}).reduce(0, +)
finalArray.append(FinalArray(summedMinutes:sum, date:key))
}
finalArray = finalArray.sorted(by: {$0.date < $1.date})
}
var body: some View {
VStack{
LineView(data: finalArray.map {$0.summedMinutes}, title: "This graph returns empty").padding()
LineView(data: lessons.map {$0.timeMinutes}, title: "This graph works").padding()
}
}
}
I get the following error, on the first line of init:
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
Does anyone know how I can fix this? What is the reason for this?
If I do a ForEach on the Core Data within my view, it works OK:
var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateStyle = .long
return formatter
}
ForEach(lessons.map {$0.date.date}, id: \.self) { item in
VStack{
Text("\(item, formatter: self.dateFormatter)")
Text(String(self.lessons.filter{$0.date.date == item}.map {$0.timeMinutes}.reduce(0, +)))
}
}
This code works, but it's not what I want, I want to display the summed 'timeMinutes' in an Array. Any ideas or workarounds would be super welcome!
The lessons is fetched on view initial update, but on init it is not defined yet, so trying to use it in there you've got a crash.
Here is possible approach:
#State private var finalArray = [FinalArray]()
var body: some View {
VStack{
LineView(data: finalArray.map {$0.summedMinutes}, title: "This graph returns empty").padding()
LineView(data: lessons.map {$0.timeMinutes}, title: "This graph works").padding()
}
.onAppear {
let allKeys = Set<Date>(lessons{$0.date.date})
for key in allKeys {
let sum = lessons.filter({$0.date.date == key}).map({$0.timeMinutes}).reduce(0, +)
finalArray.append(FinalArray(summedMinutes:sum, date:key))
}
finalArray = finalArray.sorted(by: {$0.date < $1.date})
}
}

Create array in convenience init

I was learning S.O.L.I.D right now from the book iOS programming Big Nerd Ranch. I want to make a an array from convenience init but I have a problem, I want to display the text and image like the exact order of an array but I don't know how. here I show you my code
This is my item class
class Item: NSObject {
var imageName: String
var label: String
init(imageName: String, label: String) {
self.imageName = imageName
self.label = label
super.init()
}
convenience init(list: Bool = false) {
if list {
let imageList = ["milada-vigerova", "david-rodrigo", "quran"]
let labelList = ["Fiqih", "Hadist", "Tafsir"]
let sortImageName = imageList[imageList.count - 1]
let sortLabel = labelList[labelList.count - 1]
self.init(imageName: sortImageName, label: sortLabel)
} else {
self.init(imageName: "", label: "")
}
}
}
this my ItemStore class that create an array from Item class
class ItemStore {
var allItems = [Item]()
#discardableResult func createItem() -> Item {
let newItem = Item(list: true)
allItems.append(newItem)
return newItem
}
// I make this for in loop to make the table view numberOfSection will return 3 of an allItems
init() {
for _ in 0..<3 {
createItem()
}
}
}
Please help me
Good Lord, this is pretty nerdy and cumbersome code.
First of all a struct is sufficient, you get the initializer for free
struct Item {
let imageName: String
let label: String
}
In ItemStore create the array instantly
class ItemStore {
var allItems = [Item]()
func createArray() {
allItems = [Item(name: "Fiqih", image: "milada-vigerova"),
Item(name: "Hadist", image: "david-rodrigo"),
Item(name: "Tafsir", image: "quran")]
}
}
In the table view return allItems.count in numberOfRows (not numbersOfSection)

Resources