I am trying to make an app that check people in. So far so good, but I got stuck on the following problem: I want to have an array with the name of the person and a date that this person will check in., in 1 array.
So when I select a date it will show a list all that people that will check that day.
Here the example of the array:
import Foundation
import SwiftUI
class Data: ObservableObject {
var id = UUID()
@Published var person = [“Micheal Jordan”,
“Scottie Pippen”,
“Dennis Rodman”,
“Steve Kerr”
@Published var date: [String] = [ “6 April 2023”,
“7 April 2023”,
“20 April 2023”,
“28 April 2023”
]
}
How can I combine them together. I know I can use a JSON file and it will be much easier but this app won’t be using internet. When the person is checked in, it will be moved to personChecked.
I just want to confirm my understanding, you want to create an array of people with their check-in dates, and once checked-in, move the person to the personChecked list. Is that correct?
Yes, a list with people and the date that they need to check in and once they checked in they will be moved to personChecked list, the last part I already have it done with .append. The reason that I want the date and name in one array is because when I select one date I want have the list of people that will check in that day.
Hey Claudio, just a small point that may be something to consider is that naming your class as Data is probably not a good idea since Data is a swift type and there is a chance that you are going to cause some confusion if you ever want to use the Data type later on.
If you create a struct for your Data Model and you do something like this:
struct Data: Identifiable {
var id: UUID
var data: Data
var name: String
}
It will throw an error related to ambiguity.
If you leave your class as named Data then you declare a field in your DataModel with a type of Data it will cause confusion too.
Here’s an example of how you can structure your code using a struct and a list of tuples:
import Foundation
import SwiftUI
struct Person {
var name: String
var checkInDate: String
}
class Data: ObservableObject {
var id = UUID()
@Published var people: [Person] = [
Person(name: "Michael Jordan", checkInDate: "6 April 2023"),
Person(name: "Scottie Pippen", checkInDate: "7 April 2023"),
Person(name: "Dennis Rodman", checkInDate: "20 April 2023"),
Person(name: "Steve Kerr", checkInDate: "28 April 2023")
]
@Published var personChecked: [Person] = []
func checkIn(person: Person) {
if let index = people.firstIndex(where: { $0.name == person.name }) {
people.remove(at: index)
personChecked.append(person)
}
}
func peopleCheckingIn(on date: String) -> [Person] {
return people.filter { $0.checkInDate == date }
}
}
In this example, we define a Person struct with name and checkInDate properties. We create an array of Person objects for people . We also provide a checkIn(person:) function to move a person from people to personChecked .
Additionally, we provide a peopleCheckingIn(on:) function to get a list of people checking in on a specific date. You can use this function to display the list of people checking in on the selected date.
I will try it out later on. Just for my understanding the struct Person part would be the Model in the MVVM. I know the class Data part would be the ViewModel part. Correct me if I am wrong.
Correct, although I agree, definitely don’t call this Data, especially as you’ve emphasized it’s actually part of the view model. Because “data” and “model” are pretty synonymous and calling it data when it’s really a view model is confusing
I was testing the example but the “id = UUID()” has to be in the Struct Person right? I changed the Data into listOfPeople.
The code looks like this now:
struct Person {
var id = UUID()
var name: String
var checkInDate: String
}
class ListOfPeople: ObservableObject {
@Published var people: [Person] = [
Person(name: "Michael Jordan", checkInDate: "6 April 2023"),
Person(name: "Scottie Pippen", checkInDate: "7 April 2023"),
Person(name: "Dennis Rodman", checkInDate: "20 April 2023"),
Person(name: "Steve Kerr", checkInDate: "28 April 2023")
]
@Published var personChecked: [Person] = []
func checkIn(person: Person) {
if let index = people.firstIndex(where: { $0.name == person.name }) {
people.remove(at: index)
personChecked.append(person)
}
}
func peopleCheckingIn(on date: String) -> [Person] {
return people.filter { $0.checkInDate == date }
}
You’re correct. If you want each Person to have a unique id, you should move the id property to the Person struct. Your updated code looks correct:
import Foundation
import SwiftUI
struct Person {
var id = UUID()
var name: String
var checkInDate: String
}
class ListOfPeople: ObservableObject {
@Published var people: [Person] = [
Person(name: "Michael Jordan", checkInDate: "6 April 2023"),
Person(name: "Scottie Pippen", checkInDate: "7 April 2023"),
Person(name: "Dennis Rodman", checkInDate: "20 April 2023"),
Person(name: "Steve Kerr", checkInDate: "28 April 2023")
]
@Published var personChecked: [Person] = []
func checkIn(person: Person) {
if let index = people.firstIndex(where: { $0.id == person.id }) {
people.remove(at: index)
personChecked.append(person)
}
}
func peopleCheckingIn(on date: String) -> [Person] {
return people.filter { $0.checkInDate == date }
}
}
With this code, each Person now has a unique id. Also, I updated the checkIn(person:) function to compare the id property instead of name, as it’s safer to use unique identifiers for such comparisons.
I have a View called ShowList and I am using it only to show the name of people who are on the list. Now that the code is changed I m trying to access it like the following:
import SwiftUI
struct ShowList: View {
@EnvironmentObject var listOfPeople: ListOfPeople
@EnvironmentObject var enterName: EnterName
var body: some View {
List {
ForEach (listOfPeople.people.name, id: \.self) { person in
Text(person)
.padding(4)
}
.onDelete { indexSet in
listOfPeople.person.remove(atOffsets: indexSet)
}
}
.cornerRadius(20)
}
struct ShowList_Previews: PreviewProvider {
static var previews: some View {
ShowList()
.environmentObject(ListOfPeople())
.environmentObject(EnterName())
}
}
}
After the code changed I had to change it also in the ForEach and to my opinion I should be:
ForEach (listOfPeople.people.name, id: .self) { person in
Text(person)
.padding(4)
but it gives me an error : Value of type ‘[Person]’ has no member ‘name’. I cant access listOfPeople.people.name from other view. What could be wrong?
Thnx for the help and sharing your knowledge. I know now why it didn’t work. Now I got stocked on the following problem:
In the array people there are a couple of checkInDate with the same date, no problem with that. The problem comes when I try to use a Picker to select the date so I would get a list of the people who needs to check in that day. It gives me the all the double dates. Here is the code of the picker:
import SwiftUI
class ChangeViews: ObservableObject { @Published var showAdd = false @Published var dateSelection = “”
}
struct SettingsConfig: View {
@EnvironmentObject var changeViews: ChangeViews
@EnvironmentObject var listOfPeople: ListOfPeople
var body: some View {
NavigationView {
VStack {
Toggle("Add Guest/ Delete Guest", isOn: $changeViews.showAdd)
.font(.title2)
Picker("Select Guest List date", selection: $changeViews.dateSelection) {
ForEach(listOfPeople.people, id: \.checkInDate) { person in
Text(person.checkInDate)
.tag(0)
}
}
.pickerStyle(SegmentedPickerStyle())
.font(.title2)
.padding()
The idea is when I select a date in this view it will show the list of people for that day in an other view. The last part I already figure it out, but the problem is in that the picker shows double dates. I think it shows double dates because each checkInDates has it’s own unique id (correct me if I am wrong). How can I solve the problem.
Beside that I am planning to use the checkInDate to delete the whole list of people who has to be checkIn that day.
One of the problems I see is that the method you are using to store dates is a String starting with a number, a space, a Month and a space, then a year. This makes it near on impossible to sort your data by date if you needed to and it also means that the method you use to select a date is more difficult. If you want to stick with a date as a String then store it in the format of YYYY.MM.DD like this for example: Person(name: "Michael Jordan", checkInDate: "2023.04.06")
This makes it easy to sort by date if you need to since even though it is a String, the format is such that 2023.04.06 is less than 2023.04.07 for example.
The better solution is to store the checkInDate as type Date. When you add a record, you pick a date using a Date Picker and when you want to select Dates that match a specific Date then once again you use a Date Picker to choose the date you want to search for and the test to find a checkInDate that matches that Date selected is much simpler.
var id = UUID()
var name: String
var checkInDate = Date().formatted(.dateTime
.day().month(.wide).year()
.hour().minute())
}
class ListOfPeople: ObservableObject {
@Published var people: [Person] = [
Person(name: "Michael Jordan", checkInDate: "6 April 2023"),
Person(name: "Scottie Pippen", checkInDate: "7 April 2023"),
Person(name: "Dennis Rodman", checkInDate: "28 April 2023"),
Person(name: "Steve Kerr", checkInDate: "28 April 2023"),
Person(name: "Steph Curry", checkInDate: "6 April 2023"),
Person(name: "Draymond Green", checkInDate: "07 April 2023")
]
I made it to the point to create a DatePicker to add a Name and checkInDate to a person, but to select from a list of checkInDate through a DatePicker that I wasn’t able to made. How should it look like?