Here is the desired result (a List from a viewModel derived using functions to generate yearly pay raises and show user's age from year to year). When the user alters their birthdate and hiredate in the ProfileFormView, this List should update the Age and Year group accordingly:
Unfortunately I have not been able to successfully create this view due to errors in attempts to incorporated functions within the viewModel's array.
Here is my viewModel:
class WorkerViewModel:ObservableObject {
#Published var userData: Worker =
Worker(name: "Robert", birthdate: Date(timeIntervalSince1970: 10000), hiredate: Date(timeIntervalSince1970: 30000), department: "HR")
func calcAge(birthdate: String) -> Int {
let dateFormater = DateFormatter()
dateFormater.dateFormat = "MM/dd/yyyy"
let birthdayDate = dateFormater.date(from: birthdate)
let calendar: NSCalendar! = NSCalendar(calendarIdentifier: .gregorian)
let now = Date()
let calcAge = calendar.components(.year, from: birthdayDate!, to: now, options: [])
let age = calcAge.year
return age!
}
func calcYearGroup(hiredate: String) -> Int {
let dateFormater = DateFormatter()
dateFormater.dateFormat = "MM/dd/yyyy"
let newHireDateFormatted = dateFormater.date(from: hiredate)
let calendar: NSCalendar! = NSCalendar(calendarIdentifier: .gregorian)
let now = Date()
let calcYearGroup = calendar.components(.year, from: newHireDateFormatted!, to: now, options: [])
let yearGroup = calcYearGroup.year! + 1
return yearGroup
}
func calcAnnualEarnings(hourlyRate: Double, monthlyHours: Double) -> Double {
return (hourlyRate * monthlyHours) * 12
}
#Published var userDataList: [WorkerInfo] = [
WorkerInfo(age: calcAge(birthdate: $WorkerInfo.birthdate), yearGroup: calcYearGroup(hiredate: $WorkerInfo.hiredate), salary: calcAnnualEarnings(hourlyRate: PayRates.hR[1], monthlyHours: 72), dept: "\($WorkerInfo.department)"),
WorkerInfo(age: calcAge(birthdate: $WorkerInfo.birthdate) + 1, yearGroup: calcYearGroup(hiredate: $WorkerInfo.hiredate) + 1, salary: calcAnnualEarnings(hourlyRate: PayRates.hR[2], monthlyHours: 72), dept: "\($WorkerInfo.department)"),
WorkerInfo(age: calcAge(birthdate: $WorkerInfo.birthdate) + 2, yearGroup: calcYearGroup(hiredate: $WorkerInfo.hiredate) + 2, salary: calcAnnualEarnings(hourlyRate: PayRates.hR[3], monthlyHours: 72), dept: "\($WorkerInfo.department)"),
WorkerInfo(age: calcAge(birthdate: $WorkerInfo.birthdate) + 3, yearGroup: calcYearGroup(hiredate: $WorkerInfo.hiredate) + 3, salary: calcAnnualEarnings(hourlyRate: PayRates.hR[4], monthlyHours: 72), dept: "\($WorkerInfo.department)"),
WorkerInfo(age: calcAge(birthdate: $WorkerInfo.birthdate) + 4, yearGroup: calcYearGroup(hiredate: $WorkerInfo.hiredate) + 4, salary: calcAnnualEarnings(hourlyRate: PayRates.hR[5], monthlyHours: 72), dept: "\($WorkerInfo.department)"),
]
}
Here is my model data:
struct Worker: Codable, Identifiable {
var id = UUID()
var name: String
var birthdate: Date
var hiredate: Date
var department: String
}
struct WorkerInfo: Codable, Identifiable {
var id = UUID()
var age: Int
var yearGroup: Int
var salary: Double
var dept: String
}
struct MockData {
static let sampleUser = WorkerInfo(age: 41, yearGroup: 1, salary: 80000, dept: "HR")
}
struct PayRates {
let hR: [Double] = [0,92,128,150,154,158,162,166,170,172,174,177,180]
let management: [Double] = [0,235,236,238,240,242,244,246,248,250,252,254,256]
}
Here is my Content View:
struct ContentView: View {
#StateObject var vm = WorkerViewModel()
var body: some View {
TabView {
ProfileFormView()
.tabItem {
Image(systemName: "square.and.pencil")
Text("Profile")
}
WorkerView()
.tabItem {
Image(systemName: "house")
Text("Home")
}
.padding()
}.environmentObject(vm)
}
}
Here is my Worker View:
struct WorkerView: View {
#EnvironmentObject var vm: WorkerViewModel
var body: some View {
ZStack {
List($vm.userDataList) { $worker in
WorkerCardView(workerInfo: $worker)
}
}
}
}
Here is my ProfileForm View:
struct ProfileFormView: View {
#EnvironmentObject var vm: WorkerViewModel
#State var depts = ["HR","Management","Marketing","Development"]
var body: some View {
Form {
Section(header: Text("Personal Information")) {
TextField("Name", text: $vm.userData.name)
DatePicker("Birthdate", selection: $vm.userData.birthdate, displayedComponents: .date)
DatePicker("New Hire Date", selection: $vm.userData.hiredate, displayedComponents: .date)
Picker("Department", selection: $vm.userData.department) {
ForEach(depts, id: \.self) {
Text ($0)
}
}
}
}
}
}
And lastly, here is my WorkerCard View:
struct WorkerCardView: View {
#Binding var workerInfo: WorkerInfo
var body: some View {
HStack{
VStack(alignment: .leading) {
Text("Year \(workerInfo.yearGroup)")
.font(.headline)
Label("Age \(workerInfo.age)", systemImage: "person")
.foregroundStyle(.blue)
.font(.subheadline)
}
Spacer()
VStack(alignment: .leading) {
Text("$ \(workerInfo.salary, specifier: "%.0f") salary")
.font(.headline)
Label("\(workerInfo.dept) Dept", systemImage: "building")
.foregroundStyle(.blue)
.font(.subheadline)
}
}
}
}
Thank you in advance for your help!!
//UPDATE 1//
I have converted my functions to computed properties seemingly error free. This also has an added benefit of making the code cleaner which is nice.
I have boiled the problem down now to an initialization issue. Each of my three computed properties trigger compiler errors when placed in the userDataList array:
"Cannot use instance member 'computedProperty' within property initializer; property initializers run before 'self' is available"
//UPDATE 2//
I converted the userDataArray into a computed property, but the resulting compiler error is related to the #Published property wrapper. Naturally, computed properties cannot receive property wrappers, but I need the userDataArray to be visible to the WorkerView in order for the List to iterate over the userDataArray.
Removing the #Published causes this complier error in the WorkerView:
"Cannot assign to property: 'userDataList' is a get-only property"
Related
I'm new to Swift and can't understand what's wrong with my code:
#State var selectedCourse: String = "1"
#State var napravlenie: [String] = [""]
var body: some View {
NavigationView {
ZStack{
VStack{
Text("Выберите вашу группу")
.padding(.bottom,80)
.font(.system(size: 30, weight: .semibold))
Text("Курс")
.font(.system(size: 28, weight: .bold))
.frame(width: 370,alignment: .leading)
//.padding()
Menu {
Picker(selection: $selectedCourse, label: EmptyView()) {
ForEach(course, id: \.self) {option in
Text (option)
.tag (1)
}
}
} label: {
customLabel_1
} .onChange(of: selectedCourse) { newValue in
napravlenie = viewModel.readValue(kyrs: newValue)
}
Text("Направление")
.font(.system(size: 28, weight: .bold))
.frame(width: 370,alignment: .leading)
.padding(.bottom,28)
Menu {
Picker(selection: $selectedNapravlenie, label: EmptyView()) {
ForEach(napravlenie, id: \.self) {option in
Text (option)
.tag (2)
}
}
} label: {
customLabel_2
}
}
}
the thing is that I want to update napravlenie array each time the first Picker is used and than pass that array as a ForEach of the second Picker, but it only updates after second trigger and shows an array that was triggered in the first time. I want the second Picker to update immediately, what should I do?
here's my viewModel code
import Foundation
import FirebaseCore
import FirebaseDatabase
class userViewModel: ObservableObject{
#Published var key = ""
#Published var moreKey = []
#Published var key2 = ""
#Published var moreKey2 = []
#Published var nameKey2 = ""
#Published var nameKey3 = ""
//#Published var nameMoreKey = []
#Published var object: String? = nil
#Published var userName: Any? = []
var ref = Database.database().reference()
// функция которая находит институты, из фб по выбору курса
func readValue(kyrs:String) -> [String]{
ref.child("Курс - " + kyrs).observeSingleEvent(of: .value) { snapshot in
for child in snapshot.children{ //тут он проверяет типо каждый раз нужный курс
let snap = child as! DataSnapshot
self.key = snap.key //все что тут находится я хз
self.moreKey.append(self.key)
print(self.moreKey)
}
}
let newArray: [String] = self.moreKey.compactMap { String(describing: $0) }
self.moreKey.removeAll()
return newArray
}
For example, when i choose anything in first Picker, the second one doesn't update napravlenie array, but after second trigger, the second picker shows array, that should've been presented after first trigger
I have two frames, one which displays the employees and another that adds employees, which then updates the first frame using #Binding. In the ListFrame struct where I display the list of employees, I am attempting to read in the data from Firebase (getData func) and append it to the array. I am successful in reading the data, as the employeesTemp array which I initialize in the getData function gets populated correctly. However, when I try to append the values in the correctly populated employeesTemp array to the employees global array, the global array stays empty, which means my first frame doesn't display anything. I have read other posts that talk about the fact that Firebase's operations are async, but after going through dozens of stackoverflows I have not been able to find a solution. Any help is appreciated.
import SwiftUI
import Firebase
struct Employee: Identifiable {
let id = UUID()
var firstName: String
var lastName: String
var emailID: String
var education: String
// combo drop down box
var department: String
var address: String
var salary: String
}
struct ListFrame: View {
#State var employees = [Employee]()
init() {
self.getData()
}
func getData() {
// get reference to data base
let db = Firestore.firestore()
// temp employee list (gets populated correctly)
var employeesTemp = [Employee]()
// read in the employees from employeelist data base
db.collection("EmployeeList").getDocuments { snapshot, error in
// checking for errors
if error == nil {
// no errors
if let snapshot = snapshot {
// get all documents and create employees
DispatchQueue.main.async {
print("adding employee")
employeesTemp = [Employee]()
employeesTemp = snapshot.documents.map { document in
let new_employee:Employee = Employee(firstName: document["firstName"] as? String ?? "", lastName: document["lastName"] as? String ?? "" , emailID: document["emailID"] as? String ?? "", education: document["education"] as? String ?? "", department: document["department"] as? String ?? "", address: document["address"] as? String ?? "", salary: document["salary"] as? String ?? "")
print(new_employee)
employeesTemp.append(new_employee)
print(employeesTemp)
print("appending to global employees")
employees.append(contentsOf: employeesTemp)
print(employees)
//self.employees = employeesTemp
return new_employee
}
}
}
}
else {
// handle error
print("error")
}
}
}
var body: some View {
NavigationView {
VStack {
Spacer()
List {
ForEach(employees) { employee in
Text(employee.firstName)
}
}
.navigationTitle("List of Employees")
Spacer()
HStack {
Button(action: {
}) {
NavigationLink(destination: AddEmployeeFrame(employees: $employees)) {
Text("Add Employee")
}
}
}
}
}
}
}
struct AddEmployeeFrame: View {
#State private var firstName: String = ""
#State private var lastName: String = ""
#State private var emailID: String = ""
#State private var education: String = ""
// combo drop down box
#State private var department: String = ""
#State private var address: String = ""
#State private var salaryString: String = ""
#Binding var employees: [Employee]
#Environment(\.presentationMode) private var presentationMode: Binding<PresentationMode>
func addData(firstName: String, lastName: String, emailID: String, education: String, department: String, address: String, salary: String) {
// get reference to database
let db = Firestore.firestore()
// add a document to collection
db.collection("EmployeeList").addDocument(data: ["firstName": firstName, "lastName": lastName, "emailID": emailID,
"education": education, "department": department, "address": address,
"salary": salary]) { error in
if error == nil {
// no error
//self.getData()
}
else {
// handle error
}
}
}
var body: some View {
NavigationView {
// text fields
VStack(spacing: 50) {
Group {
TextField("First Name", text: $firstName).multilineTextAlignment(.center)
TextField("Last Name", text: $lastName).multilineTextAlignment(.center)
TextField("Email ID", text: $emailID).multilineTextAlignment(.center)
TextField("Education", text: $education).multilineTextAlignment(.center)
}
.navigationTitle("Add Employee")
Menu("Select Department") {
Button("Other") {
department = "Other"
print(department)
}
Button("IT Department") {
department = "IT Department"
print(department)
}
Button("Storage Department") {
department = "Storage Department"
print(department)
}
Button("HR Department") {
department = "HR Department"
print(department)
}
Button("Testing Department") {
department = "Testing Department"
print(department)
}
}
TextField("Address", text: $address).multilineTextAlignment(.center)
TextField("Salary", text: $salaryString).multilineTextAlignment(.center)
// cancel and save buttons
HStack() {
Spacer()
Button(action: {
presentationMode.wrappedValue.dismiss()
}, label: {
Text("Cancel")
})
Spacer()
Button(action: {
let new_employee = Employee(firstName: firstName, lastName: lastName, emailID: emailID, education: education, department: department, address: address, salary: salaryString)
// adding data to server
self.addData(firstName: new_employee.firstName, lastName: new_employee.lastName, emailID: new_employee.emailID, education: new_employee.education, department: new_employee.department, address: new_employee.address, salary: new_employee.salary)
employees.append(new_employee)
print(employees)
presentationMode.wrappedValue.dismiss()
}, label: {
Text("Save")
})
Spacer()
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ListFrame()
}
}
so, having problems updating my String array into the firebase database. I'm able to update my strings, my Int values, but the one string array variable won't change when I have the user pick from the MultiSelectPickerView.
I also check if the user's selection isn't changed then nothing should be updated into the database.
My code:
#Binding var teleClubGenres: String
#State var teleClubGenres2 = [String]()
#State var genres: [String] = ["Action", "Adventure", "Animated", "Children/Family", "Comedy", "Crime" , "Drama", "Historical", "Fantasy", "Horror", "Musical", "Mystery", "Science Fiction", "Romance", "Thriller", "Western"]
NavigationLink(destination: {
MultiSelectPickerView(genres: genres, teleClubGenres: $teleClubGenres2)
.navigationTitle("Change the Genres:")
}, label: {
HStack {
Text("Change the Genres:").font(Font.headline.weight(.semibold))
Spacer()
Image(systemName: "\($teleClubGenres2.count).circle").font(.title2)
}
})
Text("Genres Selected:").font(Font.headline.weight(.semibold))
Text(teleClubGenres2.joined(separator: "\n"))
Button(action: {
model.updateClub(clubCreator: (String(describing: uidxs)), clubName: clubName , clubName2: clubName2, description: des, platform: streamingPlatforms2, teleClubGenres: teleClubGenres2, streamingType: streamingType2, membersLimit: membersLimit2)
}, label: {
Text("Submit change").frame(maxWidth: .infinity, alignment: .center).font(Font.headline.weight(.semibold))
})
func updateClub(clubCreator: String, clubName: String, clubName2: String, description: String , platform: Int, teleClubGenres: [String], streamingType: Int, membersLimit: Int) {
let db = Firestore.firestore()
db.collection("clubs").whereField("clubName",isEqualTo: clubName).getDocuments{
(snap, err) in
if err != nil {
print("Error")
return
}
for i in snap!.documents {
DispatchQueue.main.async {
if teleClubGenres.count != 0 {
i.reference.updateData(["TeleClubGenres": teleClubGenres])
}
}
}
}
}
struct ProfileInfo: Identifiable, Codable {
var id: String = UUID().uuidString
var userID: String
var userName: String
var exists: Bool
}
I have Student class that is connected to a struct - Details. Which has a nested struct - Subjects. I know how to append to a normal struct or class but I am having difficulties trying to append to a nested struct. What I have is a Form where a student's name and number of subjects are asked after which they have to enter the subject name and grade. Then, press the save button in the Toolbar items/ NavigationBarItems.
class Students: ObservableObject {
#Published var details = [Details]()
}
struct Details: Identifiable {
let id = UUID()
let name: String
struct Subjects: Identifiable {
let id = UUID()
let name: String
let grade: String
}
let subjects: [Subjects]
}
The View class:
import SwiftUI
struct TestStudentView: View {
#StateObject var students = Students()
#State private var name = ""
#State private var numberOfSubjects = ""
#State private var subject = [String](repeating: "", count: 10)
#State private var grade = [String](repeating: "", count: 10)
#State private var details = [Details.Subjects]()
var body: some View {
NavigationView {
Group {
Form {
Section(header: Text("Student details")) {
TextField("Name", text: $name)
TextField("Number of subjects", text: $numberOfSubjects)
}
let count = Int(numberOfSubjects) ?? 0
Text("Count: \(count)")
Section(header: Text("Subject grades")) {
if count>0 && count<10 {
ForEach(0 ..< count) { number in
TextField("Subject", text: $subject[number])
TextField("Grade", text: $grade[number])
}
}
}
}
VStack {
ForEach(students.details) { student in
Text(student.name)
ForEach(student.subjects) { subject in
HStack {
Text("Subject: \(subject.name)")
Text("Grade: \(subject.grade)")
}
}
}
}
}
.navigationTitle("Student grades")
.navigationBarItems(trailing:
Button(action: {
//let details = Details(name: name, subjects: [Details.Subjects(name: "Physics", grade: "A"), Details.Subjects(name: "Computer Science", grade: "A*")])
//students.details.append(details)
//^Above to append
}, label: {
Text("Save")
})
)
}
}
}
I have tried creating a variable of type [Subjects] but that would not let me append to it after the Textfield values are entered it gives me the error : “Type '()' cannot conform to 'View'; only struct/enum/class types can conform to protocols” (Which makes sense, as it would require a button). I have also tried appending to it once the save button is pressed using a ForEach but that also gives me the same error.
I think you want the model change to be the responsibility of your Students class. Try adding a public method to Students and call it from your view, like this:
class Students: ObservableObject {
#Published var details = [Details]()
public func addDetails(_ details : Details) {
details.append(details)
}
}
Then in your button action in the View, replace students.details.append(details) with a call to this method:
let details = Details(name: name, subjects: [Details.Subjects(name: "Physics", grade: "A"), Details.Subjects(name: "Computer Science", grade: "A*")])
students.details.append(details)
Is that what you're trying to do?
Your view tries to do too much for a single view. Your trying to add students and grades in a single view. The answer of Asperi is correct. However your error is indicating that something else is wrong with your code. Try to run the code below in an isolated environment and it should work fine.
For now I just added a save button for each grade it will add the grade to the first student always.
import SwiftUI
class Students: ObservableObject {
#Published var details = [Details]()
}
struct Details: Identifiable {
let id = UUID()
let name: String
struct Subjects: Identifiable {
let id = UUID()
let name: String
let grade: String
}
var subjects: [Subjects]
}
struct TestStudentView: View {
#StateObject var students = Students()
#State private var name = ""
#State private var numberOfSubjects = ""
#State private var subject = [String](repeating: "", count: 10)
#State private var grade = [String](repeating: "", count: 10)
#State private var details = [Details.Subjects]()
var body: some View {
NavigationView {
Group {
Form {
Section(header: Text("Student details")) {
TextField("Name", text: $name)
TextField("Number of subjects", text: $numberOfSubjects)
}
let count = Int(numberOfSubjects) ?? 0
Text("Count: \(count)")
Section(header: Text("Subject grades")) {
if count>0 && count<10 {
ForEach(0 ..< count) { number in
TextField("Subject", text: $subject[number])
TextField("Grade", text: $grade[number])
Button(action: {
if students.details.count > 0 {
var test = students.details[0]
students.details[0].subjects.append(Details.Subjects(name: subject[number], grade: grade[number]))
}
}) {
Text("Save")
}
}
}
}
}
VStack {
ForEach(students.details) { student in
Text(student.name)
ForEach(student.subjects) { subject in
HStack {
Text("Subject: \(subject.name)")
Text("Grade: \(subject.grade)")
}
}
}
}
}
.navigationTitle("Student grades")
.navigationBarItems(trailing:
Button(action: {
let details = Details(name: name, subjects: [Details.Subjects(name: "Physics", grade: "A"), Details.Subjects(name: "Computer Science", grade: "A*")])
students.details.append(details)
//^Above to append
}, label: {
Text("Save")
})
)
}
}
}
I´m trying to save an array of structs into coredata. I did a lot of research, but i cannot find the solution.
Here´s what i´ve got:
import Cocoa
import CoreData
class ViewController: NSViewController {
struct StudentsStruct {
let firstName: String
let lastName: String
let age: Int
}
let Studentsdata: [StudentsStruct] = [StudentsStruct(firstName: "Albert", lastName: "Miller", age: 24), StudentsStruct(firstName: "Susan", lastName: "Gordon", age: 24), StudentsStruct(firstName: "Henry", lastName: "Colbert", age: 24)]
override func viewDidLoad() {
super.viewDidLoad()
let student: Student = NSEntityDescription.insertNewObject(forEntityName: "Student", into: DatabaseController.getContext()) as! Student
for items in Studentsdata {
student.firstName = StudentsStruct.firstName
student.lastName = StudentsStruct.lastName
student.age = StudentsStruct.age
}
DatabaseController.saveContext()
let fetchRequest: NSFetchRequest<Student> = Student.fetchRequest()
print (student)
}
}
The DatabaseController is solution i´ve got from this tutorial:
https://www.youtube.com/watch?v=da6W7wDh0Dw
It´s not so important, it´s just making the "getContext" function.
Whats important, in teh commandline "student.firstName = StudentsStruct.firstName" i´m getting the error "instance member "firstName" cannot be used on type ViewController.StudentStruct.
After trying and trying, i´m running out of ideas how to get the array of structs into coredata.
This is the DatabaseController file:
import Foundation
import CoreData
class DatabaseController {
private init() {
}
class func getContext() -> NSManagedObjectContext {
return DatabaseController.persistentContainer.viewContext
}
// MARK: - Core Data stack
static var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "StudentCoreFile")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error {
fatalError("Unresolved error \(error)")
}
})
return container
}()
class func saveContext () {
let context = DatabaseController.persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}
for any help thanks in advance!
Ok, you are right, i forgot to execute the fetchrequest. Here´s my current code:
import Cocoa
import CoreData
class ViewController: NSViewController {
struct StudentsStruct {
let firstName: String
let lastName: String
let age: Int
}
let Studentsdata: [StudentsStruct] = [StudentsStruct(firstName: "Albert", lastName: "Miller", age: 24), StudentsStruct(firstName: "Susan", lastName: "Gordon", age: 24), StudentsStruct(firstName: "Henry", lastName: "Colbert", age: 24)]
override func viewDidLoad() {
super.viewDidLoad()
let student: Student = NSEntityDescription.insertNewObject(forEntityName: "Student", into: DatabaseController.getContext()) as! Student
for item in Studentsdata {
let student: Student = NSEntityDescription.insertNewObject(forEntityName: "Student", into: DatabaseController.getContext()) as! Student
student.firstName = item.firstName
student.lastName = item.lastName
student.age = Int16(item.age)
}
DatabaseController.saveContext()
let fetchRequest: NSFetchRequest<Student> = Student.fetchRequest()
do {
let searchResults = try DatabaseController.getContext().fetch(fetchRequest)
print("number of results: \(searchResults.count)")
for result in searchResults as [Student] {
print(student)
}
} catch {
print ("error")
}
}
}
It´s running without errors. Now i´m getting 32 search results. Every entry is: age = 0; firstName = nil; lastName = nil;
For comparison, this code, without the loop is working:
import Cocoa
import CoreData
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let student: Student = NSEntityDescription.insertNewObject(forEntityName: "Student", into: DatabaseController.getContext()) as! Student
student.firstName = "henry"
student.lastName = "miller"
student.age = 22
DatabaseController.saveContext()
let fetchRequest: NSFetchRequest<Student> = Student.fetchRequest()
do {
let searchResults = try DatabaseController.getContext().fetch(fetchRequest)
print("number of results: \(searchResults.count)")
for result in searchResults as [Student] {
print(student)
}
} catch {
print ("error")
}
}
}
You need to access the item in your for loop also you are currently accessing the same object Student object in for loop instead of that you need to create a new Student in every iteration of for loop.
for item in Studentsdata {
//Create new student in for loop
let student = NSEntityDescription.insertNewObject(forEntityName: "Student", into: DatabaseController.getContext()) as! Student
//To get firstName, lastName and age access the item
student.firstName = item.firstName
student.lastName = item.lastName
student.age = item.age
}
//Save context now
DatabaseController.saveContext()
In case someone is interested, I found the solution:
You first have to set up the struct in the CoredataEntity Class like that:
import Foundation
import CoreData
struct StudentsStruct {
let firstName: String
let lastName: String
let age: Int
}
#objc(Student)
public class Student: NSManagedObject {
#NSManaged public var firstName: String?
#NSManaged public var lastName: String?
#NSManaged public var age: Int16
var allAtributes : StudentsStruct {
get {
return StudentsStruct(firstName: self.firstName!, lastName: self.lastName!, age: Int(self.age))
}
set {
self.firstName = newValue.firstName
self.lastName = newValue.lastName
self.age = Int16(newValue.age)
}
}
}
Then use the same struct to paste the data:
import Cocoa
import CoreData
class ViewController: NSViewController {
let studentsdata: [StudentsStruct] = [StudentsStruct(firstName: "Albert", lastName: "Miller", age: 24), StudentsStruct(firstName: "Susan", lastName: "Gordon", age: 24), StudentsStruct(firstName: "Henry", lastName: "Colbert", age: 24)]
override func viewDidLoad() {
super.viewDidLoad()
for items in studentsdata {
let student: Student = NSEntityDescription.insertNewObject(forEntityName: "Student", into: DatabaseController.getContext()) as! Student
student.allAtributes = items
}
DatabaseController.saveContext()
let fetchRequest: NSFetchRequest<Student> = Student.fetchRequest()
do {
let searchResults = try DatabaseController.getContext().fetch(fetchRequest)
print("number of results: \(searchResults.count)")
for result in searchResults as [Student] {
print("student: \(firstName), \(lastName), \(age)" )
}
} catch {
print ("error: \(error)")
}
}
}
Thats it.
Perhaps this is lazy. You could also just encode your array as a json object and then create a field on your NSManagedObject for it as a transformable. When you want to retrieve you'd just decode and downcast to the proper type. That's what I did in one of my projects; worked fine.