CoreData Ejemplo Swift.pdf

download CoreData Ejemplo Swift.pdf

of 26

Transcript of CoreData Ejemplo Swift.pdf

  • !!CoreData tutoiral in Swift using NSFetchedResultsController!rshankar July 14, 2014!!Let us see an example TaskManager app for CoreData written in Swift Programming language. This app provides the following functionality!! Allow users to enter new task! Update existing task! delete task!! ! !

    !! ! !!!You can download source code for this project from Github. If you are familiar with user interface then move on to the Core Data implementation in Swift section!!Create a new File -> New -> Project and select template Single View Application!!!!!!

  • !!!Enter Product Name, Language as Swift and select Use Core Data for the new Project.!

    !!!!!User Interface Implementation!

  • !Add new TableViewController for managing tasks!!Delete ViewController.swift and Add new view controller which will be used for displaying the list of tasks.! !!!!!!!!!!!!!!!!!!!!!!!!Right click on the Project and select New File!! !!!!!!!!!!!!!!!!!!!!!!!!!

  • Choose the template as Cocoa Touch under iOS -> Source!!

    !Enter name of the file as TaskManagerViewController with Subclass as UITableViewController and Language as Swift.!

    !!

  • !Add new UITableViewController to the Storyboard.!!Navigate to Main.storyboard, delete the ViewController and add new TableViewController to the Storyboard!

    !Embed this TableViewController inside a navigation controller. You can do this by clicking Editor menu -> Embed In -> Navigation Controller.!! !!!!!!!!!!!!!!!!!!!!

  • Navigate to Storyboard file and select Table View Controller. Click Identity Inspector and set the Class to the TaskManagerViewController.!!Add button to the navigation bar.!!Users can navigate to new task screen by tapping the button on the TaskManager. Drag and drop the button bar item to the navigation bar.!

    !In the attributes inspector, set the identifier for button bar item as Add. Also enter title as Task Manager in the navigation bar.!!Add View Controller for entering task detail.!!Now to enter task detail, let us add new View Controller. From the Objects library, drag and drop View Controller on to storyboard. Then drag and drop a navigation item to this View Controller. Add a Done bar button item for saving the changes, Cancel bar button item for dismissing the screen. Also provide a title for the screen as Task Detail.!!

    !Then add a textfield to the View Controller to enter details about the task.!

  • !Now to connect the Task Manager screen to Task Detail screen, select the Add button in Task Manager screen, hold the control button and make a connection to the Task Detail screen. Select type of Action Segue as Show.! !!!!!!!!!!!!!!!!!!!!!!!!!

    !!!!!!!!!!!!

  • Select each View Controller and Click on the Resolve Auto Layout Issues option and pick Reset to Suggested Constraints. This would ensure that the controls alignment and display remains same in different screen sizes.!! !!!!!!!!!!!!!!!!!!!Add View Controller Class!!Right click on the Project, select New File from menu list.!! !!!!!!!!!!!!!!!!!!!!!!!!!!Select Cocoa Touch Class as template.!!!

  • !Enter the Class name as TaskDetailViewController, Subclass of UIViewController and Language as Swift.!

    !!!Navigate to Storyboard file and select Task Detail View Controller. Click Identity Inspector and set the Class to the TaskDetailViiewController.!!!!

  • !!!!!!!!!!!!!!!!!!!!!!Now let us add the function required for dismissing the View Controller. This gets called when user taps done and cancel buttons. Navigate to TaskDetailViewController.swift file and add the following functions.!!@IBAction func done(sender: AnyObject) {!!dismissViewController()!!}!! @IBAction func cancel(sender: AnyObject) {!! dismissViewController()!!}!! func dismissViewController() {!!navigationController.popViewControllerAnimated(true)!!}!!Connect the IBActions with the corresponding done and cancel buttons!!!! !!!!!!!!

  • !!Try to compile and run the app on the Simulator. You should see the following Task Manager screen and tapping + sign should display the Task Detail screen with TextField, Cancel and Done button.! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!When you tap the Cancel or Done button should take you to the Task Manager screen.!!Create IBOutlet element for UITextField Element!!Add the following piece of code after the Class TaskDetailViewController: UIViewController!!@IBOutlet var txtDesc: UITextField!!Now use the connection inspector to connect the IBOutlet variable to the textfield on the User Interface.!!Core Data Implementation!!Click Show Project Navigator and select CoreData model file (ending with extension .xcdatamodelid)!!!!!!!

  • !Click Add Entity option available at the bottom of Xcode Editor window.!! !!!!!!!!!!!!Then enter the name of the entity as Tasks.! !!!!!!!!!!!!!!Click Add Attribute then enter name as desc and choose the type as String. This attribute will be used for storing the task detail information.!

  • !!!!!!!!!!!!!!!!!Now to generate the CoreData mapping class, click Editor and select Create NSManagedObject Subclass.! !!!!!!!!!!!!!!!!!!!Select the data models and click Next! !!!!!!!!!!!!!!!!!

  • Then select entities and in this example it is Tasks!! !!!!!!!!!!!!!!!!!!!!Make sure to select Language for NSManagedObject class as Swift. Click Create to the create new class.! !!!!!!!!!!!!!!!!!!!!!!!!!!!!Click Yes to to configure an Objective-C birding header for the Swift class.!!!!

  • !!!!!!!!!!!!!!This should create a new class called Tasks.swfit with variable for corresponding attribute defined in Entities!!Write code to save new task details.!!Add the following line at the top of TaskDetailViewController class!!let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext! !!!!!!!!!!!!!!!!!!!!!!!The above line defines a variable to store the ManagedObjectContext. In order to save the task information entered in the UITextField, add the following code to TaskDetailViewController.swift. Then call this function in the @IBAction done.!!func createTask() {!!let entityDescripition = NSEntityDescription.entityForName(Tasks, inManagedObjectContext: managedObjectContext)!let task = Tasks(entity: entityDescripition, insertIntoManagedObjectContext: managedObjectContext)!

  • task.desc = txtDesc.text!!managedObjectContext?.save(nil)!!}!!@IBAction func done(sender: AnyObject) {!!createTask()!!dismissViewController()!!}!!The createTask function uses the Tasks Entity class and ManagedObjectContext class to save the information entered in the text field in to SQLite using CoreData.!!Now if you try to run this app in Xcode simulator, you should be able to enter the details in textfield and save the task. But there is no way to see the saved tasks.!!Write code to retrieve information from SQLite using NSFetchedResultsController!!We will be using NSFetchedResultsController to retrieve and manage information returned from Core Data. Write the following code after the class declaration of TaskManagerViewController class!!let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext!!!var fetchedResultController: NSFetchedResultsController = NSFetchedResultsController()!!

    !You might see the above error message Use of undeclared type NSFetchedResultsContoller. This can be fixed by importing CoreData module and making your class a delegate of NSFetchedResultsControllerDelegate.!!!import CoreData!!!class TaskManagerViewController: UITableViewController, NSFetchedResultsControllerDelegate {!!Then add the following function to populate fetchedResultController.!!func getFetchedResultController() -> NSFetchedResultsController {!!fetchedResultController = NSFetchedResultsController(fetchRequest: taskFetchRequest(), !!

  • managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)!!return fetchedResultController!!}!! !!func taskFetchRequest() -> NSFetchRequest {!!let fetchRequest = NSFetchRequest(entityName: Tasks)!!let sortDescriptor = NSSortDescriptor(key: desc, ascending: true)!!fetchRequest.sortDescriptors = [sortDescriptor]!!return fetchRequest!!}!!Update the ViewDidLoad method of TableViewController to populate fetchedResultController variable and set the delegate to self and call the function to retrieve the results.!! override func viewDidLoad() {!!super.viewDidLoad()!!!fetchedResultController = getFetchedResultController()!!fetchedResultController.delegate = self!!fetchedResultController.performFetch(nil)!!}!!!Implement the TableView related functions to display the data!!!Navigate to Main.storyoard then to TaskManagerViewController and set Identifier for Prototype Cells as Cell!

  • Add the following functions required for population of rows in tableView and to refresh the tableView when content is changed.!!override func numberOfSectionsInTableView(tableView: UITableView!) -> Int!!return fetchedResultController.sections.count!!}!! !!override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {!!return fetchedResultController.sections[section].numberOfObjects!!}!! !!override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell? {!!var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell!!let task = fetchedResultController.objectAtIndexPath(indexPath) as Tasks!!cell.textLabel.text = task.desc!!return cell!!}!!!func controllerDidChangeContent(controller: NSFetchedResultsController!) {!!tableView.reloadData()!!}!! !Swift uses namespaces for classes, so the downcast of NSManagedObject class to Tasks class will work only when you append the module name with the class for Entity!!let task = fetchedResultController.objectAtIndexPath(indexPath) as Tasks!!!Navigate to TaskManager.xcdatamodeld, select Tasks under Entities and using the Data Model Inspector append Module name i.e TaskManager with the class.!!!!!!!!

  • !!!!!!!!!!!!!!!!!!Now you should be able to see the data added via TaskDetailScreen in the TaskManager Screen.!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  • Implementation of deleting rows from tableView and CoreData entity!!Copy and paste the following code to provide the users with the option to delete the rows.!!override func tableView(tableView: UITableView!, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath!) {!!let managedObject:NSManagedObject = fetchedResultController.objectAtIndexPath(indexPath) as NSManagedObject!!managedObjectContext?.deleteObject(managedObject)!!managedObjectContext?.save(nil)!!}! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!Implementation of editing task details!!Now let us see how to edit a task in the TaskManager screen. We will re-use the same TaskDetail screen for editing the task information. First let create segue from UITableViewCell to TaskDetailViewController. You can do this by holding the Control key and drag and drop the connection to TaskDetailViewController.!!!!

  • !!!!!!!!!!!!!!!!!!Make sure to set the Segue identifier as edit using Attributes Inspector!!!!!!!!!!!!!!Add the following piece of code in TaskManagerViewController to populate the task detail for the edited row.!! override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {!!if segue.identifier == "edit" {!!let cell = sender as UITableViewCell!!let indexPath = tableView.indexPathForCell(cell)!!let taskController:TaskDetailViewController = segue.destinationViewController as TaskDetailViewController!!let task:Tasks = fetchedResultController.objectAtIndexPath(indexPath) as Tasks!!taskController.task = task!!}!!}!!Navigate to TaskDetailViewController and variable which used to pass the task details across the ViewControllers!

  • !var task: Task? = nil!!In viewDidLoad fund, populate the textField with the task details for edit operation i.e task is not nil!!override func viewDidLoad() {!!super.viewDidLoad()!!if task != nil {!!txtDesc.text = task?.desc!!}!!}!!!Then add a new function to save the edited task. Also modify the done function to handle create and edit task functionality!!func editTask() {!!task?.desc = txtDesc.text!!managedObjectContext.save(nil)!!}!!@IBAction func done(sender: AnyObject) {!!if task != nil {!!editTask()!!} else {!!createTask()!!}!!dismissViewController()!!}!!Now when you run the app in Xcode simulator, you should be able to edit the task.!!!The final piece of code left is to add background colour to the navigation bar for both the controllers. Click AppDelegate.swift file and add the following function. Then call this function from the didFinishLaunchingWithOptions.!!!func setupAppearance() {!! var navigationBarAppearance = UINavigationBar.appearance()!

  • ! navigationBarAppearance.barTintColor = UIColor(red: 51.0/255.0, green: 104.0/255.0, blue: 121.0/255.0, alpha: 1.0)!!navigationBarAppearance.tintColor = UIColor.whiteColor()!!navigationBarAppearance.titleTextAttributes = [NSForegroundColorAttributeName:UIColor.whiteColor()]!! !!}!!! func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {!!// Override point for customization after application launch.!!setupAppearance()!!return true!!}!!!Running the app on the iOS simulator should change the appearance for the navigation bar.!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!

  • Hope you found this beginner tutorial useful. Please use the comments section to share your thoughts and suggestion!!Download the source code for this tutorial from Github!!!!!!14 comments add one!!!!Chris !!I love this tutorial! Thanks for posting.!!When trying to retrieve my list of stored tasks, the app crashes while running :!!let task = fetchedResultController.objectAtIndexPath(indexPath) as Tasks and shows Swift dynamic cast failed in one of the threads. Any idea what might cause this? It started when I added the prototype cell into the mix, although I have assigned Cell as the identifier, like in the tutorial.!! rshankar !! Please make changes to entity class name then try again.!! Swift uses namespaces for classes, so the downcast of NSManagedObject class to Tasks class will work only when you append the module name with the class for Entity! let task = fetchedResultController.objectAtIndexPath(indexPath) as Tasks!! Navigate to TaskManager.xcdatamodeld, select Tasks under Entities and using the Data Model Inspector append Module name i.e TaskManager with the class.!!Bin !!Hi,you!!Thanks for posting!!!I did as your tutorial above, but do not understand why I get some errors:!Using TaskDetailViewController.swift underclared type Tasks!Using underclared type task TaskDetailViewController.swift!Using unresolved identifier NSEntityDescription TaskDetailViewController.swift!Using TaskManagerViewController.swift underclared type Tasks!And there are two files in the file TaskManager: Tasks.h and Tasks.m, I found a bit strange compared to the performance of your project.!I started learning about the swift, nor as desirable CoreData. I hope you can help me.Thanks!!! rshankar !! Can you please check whether import CoreData added to Task TaskManagerViewController.swift file?! Neil !!

  • You have .m and .h files instead of .swift file for Tasks. When you created your Tasks file (subclass of NSManagedObject) you did not change language from Obj-C to Swift.!!rshankar !!Thanks Niel!!Bin, can you please check as suggested by Neil.!Neil !!Hi!!Just working through your tutorial just what I need for my own project, so many thanks.!!I noticed on the line:!let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate)!that .managedObjectContext is missing from the end. Your source code is correct but it is missing from the webpage.!Thanks again.!! rshankar !! Thank you, the webpage is updated now.!!Neil !!Just finished and I really am so thankful that you took the time to write this. Ive been pulling my hair out for a few days with Core Data and Swift. Your advice about appending the module name with the class was excellent.!Just a few more typos. Hope you dont think Im being picky.!!In Implementation of editing task details section:!let task: Task? = nil should be let task: Tasks? = nil!!edit segue has identifier = edit but the create segue needs to have identifier = create or some other name to prevent segue.identifier returning nil when + is clicked. I could not find this step on the webpage but I have had some wine so maybe I missed it :) Apologies if so.!!In beta 4, in AppDelegate, managedObjectContext is defined as:!lazy var managedObjectContext: NSManagedObjectContext?!with the ?. So I have had to add a ! to the as AppDelegate).managedObjectContext! in the two view controllers.!! rshankar !! Thanks Neil for your feedback. Much appreciated!!!Geoff !!This is a great tutorial but Im stuck on the part about appending the module name with the class.!!When I put in TaskManager.Task into the Entity Class field and create the subclass it generates a file titled TaskManager.swift and I cant use the Task subclass. Do you have to generate the subclasses and then go back and add the module name afterwards? That doesnt seem rightcan you help me understand what Im missing here?!

  • !Thanks!!! rshankar !! Please generate the class then append the module name to the Entity Class field.!!Mick Smith !!Great tutorial, best way Ive seen in any tutorials of fetching from core data, far better than what I was using.!Its been a great help to me.!Thanks!John cooper !!Hi instead of the data populating a table View can you set it to populate into a label on a button and when the button is pressed all the info is displayed ?!Using your example the button would read buy vegetables ( populated from the users input) and when the button is pressed it would display more info like buy carrots and cabbage.!!Hope this makes sense.!I just want to make a better look app then just table views.!Will improve the user experience better design.!The app I have in mind would suite it perfect better then a boring table view.!!I have made up some example diagrams to explain better but cant post it here.!!Thank for taking the time to read this .!