DEV Community

Cover image for How to make a task list using SwiftUI and Core Data

How to make a task list using SwiftUI and Core Data

Maegan Wilson on December 18, 2019

This post is going to walk through how to make a task list app that stores the task in Core Data. This app will have the following features: Ad...
Collapse
 
cpiersigilli profile image
Cesare Piersigilli

Your post has been very helpful to me. Thank you.
I would like to ask you if you can create a swift file that groups all the methods (addTask, updateTask, ect) that can then be used in each view.
In swift with uikit I had create CoredataHelper shared class for use in every app with little modify.
Thank you

Collapse
 
maeganwilson_ profile image
Maegan Wilson

You definitely can! I do this in my production apps. I generally then add the helper as an EnvironmentObject so that I can access it when I need it.

You can see my messy source code for one of my apps here. The HelperClasses folder will have the CoreData helper CalculationManager.swift. This updates and deletes items from my CoreData context.

Collapse
 
cpiersigilli profile image
Cesare Piersigilli

Thank you. It is very difficult, for me. Could you tell me how to extract the methods from your TaskList, since I don't think I can do it?

Thread Thread
 
maeganwilson_ profile image
Maegan Wilson • Edited

Here's a branch of the original project with what you're looking to do that's been implemented.

Create a new file with the functions like this one linked here.

In the ContentView.struct file, add let core_data_helper = CoreDataHelper(). Then in the button's actions, call the functions from core_data_helper.

Here's an example of addTask():

Button(action: {
    self.core_data_helper.addTask(self.taskName, context: self.context)
}){
    Text("Add Task")
}

Here's a link to what ContentView.swift should look like.

Thread Thread
 
cpiersigilli profile image
Cesare Piersigilli

Great solution. But, wanting to move from ContentView to CoreDataHelper:
@Environment(.managedObjectContext) var context
and
@FetchRequest(
entity: Task.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \Task.dateAdded, ascending: false)],
predicate: NSPredicate(format: "isComplete == %@", NSNumber(value: false))
) var notCompletedTasks: FetchedResults
How can it be done?
It's possible?
Thank you.

Thread Thread
 
maeganwilson_ profile image
Maegan Wilson

You can, but the @Environment and @FetchRequest are used with SwiftUI views. I wouldn't move the @Environment to make sure that there is a context associated with the view. I also like to leave my FetchRequests in the view because that's where I need the data to be viewed.

Thread Thread
 
cpiersigilli profile image
Cesare Piersigilli

Your explanation convinced me. Thank you.

Collapse
 
icekold profile image
ICE-KOLD

Whenever I add the notCompletedTasks to the forEach loop it says that my app has crashed. How could I fix this, thanks for any help. I tried with your code as well with the same crash error.

ForEach(notCompletedTasks) { task in
Text("hi")
}

Collapse
 
maeganwilson_ profile image
Maegan Wilson

Is your core data entity class extending Identifiable? If not, then you will need to specify the id property of the task.

Collapse
 
icekold profile image
ICE-KOLD

I have added the Identifiable extension without luck.

Collapse
 
andywalt profile image
Andy Walt

Hi Maegan! This is very helpful. I'm trying to do something similar but using a One-To-Many relationship with Core Data.

Collapse
 
maeganwilson_ profile image
Maegan Wilson

Thanks I'm glad you found it useful!

At some point, I'd like to have more posts about Core Data and SwiftUI.

Collapse
 
andywalt profile image
Andy Walt

Looking forward to them and I'll keep hoping stackoverflow can come to the rescue haha

Thread Thread
 
maeganwilson_ profile image
Maegan Wilson

I know this feeling all too well! Hacking with Swift is also a great guide!

Thread Thread
 
andywalt profile image
Andy Walt

Been using Paul's resources! It's my favorite but trying to make something is way different than following the guides haha. Appreciate your responsiveness!

Collapse
 
bobmcnerney profile image
bobmcnerney

Thanks for the helpful post Maegan!

It helped me, a SwiftUI beginner, a great deal. But when I tried to build up I definitely got stumped.

I have a need for similar functionality but with the need to update a text field such as "name" rather than the Boolean "isComplete". I changed your code to update the "name" field from a constant:

taskUpdate.setValue("testJohn", forKey: "name")

This did not update CoreData. But, If when I added the following after the .setValue, core data is updated.

try context.save()

However, the tab view is not updated until I close and re-open the app.

Any assistance that you can provide will be immensely helpful!

Collapse
 
maeganwilson_ profile image
Maegan Wilson

I’ve run into this issue myself. I solved this by using an environment object that stores an array of the objects. It’s probably a bit of a performance hit, but it was a work around. I do mean to look into it at some point though. I’m sorry that I couldn’t be more help.