EddieF
EddieF

Reputation: 81

How can I create a function to search for an ID, locate and change one field of this record?

Model:

enum TaskType: Int, Codable {
    
    case upcoming = 0
    case inProgress
    case testing
    case completed
    
    var title: String {
        switch self {
        case .upcoming:
            return "Upcoming"
        case .inProgress:
            return "In Progress"
        case .testing:
            return "Testing"
        case .completed:
            return "Completed"
        }
    }
}

struct TasksModel: Encodable, Decodable {
    
    var upcomingArray: [TaskInfo]
    var inProgressArray: [TaskInfo]
    var testingArray: [TaskInfo]
    var completedArray: [TaskInfo]
}

struct TaskInfo: Codable, Equatable, Identifiable {
    
    var id: String
    var title: String
    var description: String
    var taskStatus: TaskType
    var taskDate = Date()
}

VM:

class HomeVM: ObservableObject {
            
      @Published var tasksArray: TasksModel
                    
      self.tasksArray = TasksModel.init(upcomingArray: [], inProgressArray: [], testingArray: [], completedArray: [])
}

So now that I could locate the record with received taskID and change the taskStatus, I need also to move the record from upcomingArray to inProgressArray. This is what I’m trying:

func inProgressSetTask(taskID: String) {
    @StateObject var viewModel = HomeVM()
    if let index = viewModel.tasksArray.upcomingArray.firstIndex(where: {$0.id == taskID}) {
        
        // Update task status
        
        viewModel.tasksArray.upcomingArray[index].taskStatus = .inProgress
        
        // Need to remove from upcomingArray and append into inProgressArray
        
        viewModel.tasksArray.upcomingArray.remove(at: index)
        
        var lastIndex = viewModel.tasksArray.inProgressArray.last
        
        viewModel.tasksArray.inProgressArray[lastIndex].append()
        
        viewModel.save()
        
        // End
        
    } else {

Updating taskStatus above working fine but remove from one array into another is not.

This code above will repeat for each array after else. Appreciate any help.

Upvotes: 0

Views: 117

Answers (2)

you could try the following example code to achieve what you want:
(note, you should have @StateObject var viewModel = HomeVM() outside of the func inProgressSetTask(taskID: String) {...} or pass it in as a parameter)

EDIT-1: moving the function with all arrays into HomeVM and assuming id are unique.

func inProgressSetTask(taskID: String) {
    print("InProgress Set ID# \(taskID)")
    
    // with index, using `firstIndex`
    if let index = viewModel.tasksArray.inProgressArray.firstIndex(where: {$0.id == taskID}) {
        // do something with the index
        viewModel.tasksArray.inProgressArray[index].title = "xxx"
    }
    
    // with TaskInfo, using `first`
    if var taskInfo = viewModel.tasksArray.inProgressArray.first(where: {$0.id == taskID}) {
        // do something with the taskInfo
        taskInfo.title = "xxx"
    }

}

With all arrays of TaskInfo, use the function setTaskFromAll(...) in HomeVM. For example: viewModel.setTaskFromAll(taskID: "1")

class HomeVM: ObservableObject {
    @Published var tasksArray: TasksModel = TasksModel.init(upcomingArray: [], inProgressArray: [], testingArray: [], completedArray: [])

    func setTaskFromAll(taskID: String) {
        if let index = tasksArray.inProgressArray.firstIndex(where: {$0.id == taskID}) {
            tasksArray.inProgressArray[index].title = "inProgress"
        } else {
            if let index = tasksArray.completedArray.firstIndex(where: {$0.id == taskID}) {
                tasksArray.completedArray[index].title = "completed"
            } else {
                if let index = tasksArray.testingArray.firstIndex(where: {$0.id == taskID}) {
                    tasksArray.testingArray[index].title = "testing"
                } else {
                    if let index = tasksArray.upcomingArray.firstIndex(where: {$0.id == taskID}) {
                        tasksArray.upcomingArray[index].title = "upcoming"
                    }
                }
            }
        }
    }
    
}

EDIT-2:

However, since you already have the "TaskType" of each array in the TaskInfo struct, why not remove TasksModel and use a single array of TaskInfo in your HomeVM. Like this:

class HomeVM: ObservableObject {
    @Published var tasksArray: [TaskInfo] = [
        TaskInfo(id: "1", title: "title1", description: "description1", taskStatus: .upcoming),
        TaskInfo(id: "2", title: "title2", description: "description2", taskStatus: .inProgress)
        // ....
    ]
    
    func setTask(taskID: String, to taskType: TaskType) {
        if let index = tasksArray.firstIndex(where: {$0.id == taskID}) {
            tasksArray[index].taskStatus = taskType
        }
    }

    func getAllTaskInfo(_ oftype: TaskType) -> [TaskInfo] {
        return tasksArray.filter{$0.taskStatus == oftype}
    }
}

and use it like this: viewModel.setTask(taskID: "1", to: .testing) and viewModel.getAllTaskInfo(.inProgress)

EDIT-3: to remove from one array and append to another, using your TasksModel scheme, use this:

class HomeVM: ObservableObject {
    @Published var tasksArray: TasksModel = TasksModel(upcomingArray: [
        TaskInfo(id: "1", title: "title1", description: "description1", taskStatus: .upcoming),
        TaskInfo(id: "2", title: "title2", description: "description2", taskStatus: .upcoming)
    ], inProgressArray: [
        TaskInfo(id: "3", title: "title3", description: "description3", taskStatus: .inProgress),
        TaskInfo(id: "4", title: "title4", description: "description4", taskStatus: .inProgress)
    ], testingArray: [], completedArray: [])
    
    func inProgressSetTask(taskID: String) {
        if let index = tasksArray.upcomingArray.firstIndex(where: {$0.id == taskID}) {
            // update task status
            tasksArray.upcomingArray[index].taskStatus = .inProgress
            // get the upcomingArray taskInfo
            let taskInfo = tasksArray.upcomingArray[index]
            // remove from upcomingArray
            tasksArray.upcomingArray.remove(at: index)
            // append to inProgressArray
            tasksArray.inProgressArray.append(taskInfo)
        } else {
            // ...
        }
    }
    
}

Use it like this: viewModel.inProgressSetTask(taskID: "1")

As you can plainly see, you are much better-off with the EDIT-2, you are repeating/duplicating things in EDIT-3 for no reason. There is no need for separate arrays for the different TaskType, you already have this info in the TaskInfo var taskStatus: TaskType. With EDIT-2, use viewModel.getAllTaskInfo(.inProgress) to get all TaskInfo of a particular type, just like it would be if you used a separate array.

Upvotes: 1

Yrb
Yrb

Reputation: 9695

You are attempting to compare a String to a TaskInfo, because the elements of an inProgressArray are of type TaskInfo. What you need to do is drill into the array and get to the .id. That is simple to do. In the .first(where:), you simply pass a closure of $0.id == taskID.

    if let index = viewModel.tasksArray.inProgressArray.first(where: { $0.id == taskID } ) {

Upvotes: 0

Related Questions