Swift Core Data From Scratch: Workshop 1

Today, you will use the AddBookViewController view and its class file to implement these tasks:

  • Validate required input fields
  • Insert a managed object in the database
  • Load a photo in the image view

Create a New Branch

Before you get started in implementing above tasks in the tutorial’s iOS project; I want you to use the Source Control ➤ Create New Branch… menu to create a new branch from the “master” branch. Give it the name “insert-managed-object”.

sourcecontrol-createbranch

Validate Required Input Fields

Enter this validation code in the AddViewController.swift file’s doneButtonTapped() function. It dismiss the keyboard and validate required input fields; which are the Book Title text field and the Book Author text field.

@IBAction func doneButtonTapped(sender: AnyObject) {
  // Dismiss the keyboard
  bookTitle.resignFirstResponder()
  authorName.resignFirstResponder()
  photoUrl.resignFirstResponder()

  // Make sure required text fields aren't empty
  if bookTitle.text.isEmpty || authorName.text.isEmpty {
    textView.text = "Both the Book Title and the Author Name is requred"
  } else {
      // Display a success message in the text view
      textView.text = "Book saved!"
  }
}

Output

scdfs-fig2-01 scdfs-fig2-03
Insert a Managed Object in The Database

Here is the code to add in the doneButtonTapped() function’s else block. The code create a managed object variable, set its properties with data you entered in the Add Book view’s text fields, then insert the managed object in the app’s database file.

} else {
  // Insert a managed object in the database
  insertManagedObject()
}

Here is the code to implement the insertManagedObject() function called in the doneButtonTapped() function. Read the comments to get a sense of what the code does.

// This function insert a book object (managed object) in the database then dismiss the view
func insertManagedObject() {
    // Put the Book entity in the context
    let entityDescripition = NSEntityDescription.entityForName("Book", inManagedObjectContext: context)

    // Create a managed object from the Book entity and put it in the context
    let bookObject = Book(entity: entityDescripition!, insertIntoManagedObjectContext: context)

    // Set the managed object's properties
    bookObject.bookTitle = bookTitle.text
    bookObject.authorName = authorName.text
    bookObject.photoUrl = photoUrl.text

    var savingError: NSError?

    // Insert the managed object in the database file
    if !context.save(&savingError) {
        // The context couldn't insert the managed object in the database, display an error message in the textView
        textView.text = "Failed to save the context with error = \(savingError?.localizedDescription)"
    } else {
        // The context successfully inserted the managed object in the database, display a success message in the text view
        textView.text = "Book saved!"

        // Clear out the text fields
        bookTitle.text = nil
        authorName.text = nil
        photoUrl.text = nil
    }
}

If you were to run the app after entering above code in the file, the complier will issue several fatal errors. To prevent that from happening, you have to import the CoreData module in the AddMovieViewController class. Create a constant called context and initialize it with the AppDelegate’s managedObjectContext variable.

import UIKit
import CoreData
class AddViewController: UIViewController {
   ...
   let context = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext!
}

Now, test the code you entered in the doneButtonTapped() function by using the Add Book View to add this book in the app’s database file. You will see output shown in the image below, when you click the Done button.

Book Title: One for The Money
Author Name: Janet Evanovich
Photo Url: https://unputdownables.files.wordpress.com/2010/09/7213750.jpg

Output

scdfs-fig2-03

When you finish entering a managed object (book object) in the app’s database file, it should appear in the Books View’s table view right? Well as of now, that’s not happening. Obviously, you have to add code in the BooksViewController.swift file to fetch all managed objects you inserted in the app’s database file, and display them in the Books View’s table view cells. You’ll do that in next week’s workshop.

In the mean time, I want you to use the Add Book View to add the book, “One for The Money” in the app’s database again. Next, add these ones in the database as well:

Book Title: Go Set a Watchman
Author Name: Harper Lee
Photo Url: http://img1.imagesbn.com/p/9780062409850_p0_v5_s260x420.JPG
Book Title: Odd Hours: An Odd Thomas Novel
Author Name: Dean Koontz
Photo Url: http://img2.imagesbn.com/p/9780553591705_p0_v1_s114x166.JPG

Load a Photo in The Image View

When you enter an url in the Add Book View’s Photo Url text field, then click away from the field. The app should load the url’s photo in the image view. If the url you entered in the Photo Url text field isn’t a valid url; the app should load a local photo in the image view.

Code you’ll enter in the AddBookViewController.swift file assume you’ve already inserted the local photo in the app’s bundle. So download this image put it in the project bundle by dragging-and-dropping it in the project’s Images.xcassets folder.

noimage

Here’s the code to load a photo in the image view. Implement it in the AddBookViewController.swift file.

func getImage(urlString: String) -> UIImage {
  var imageObject = UIImage(named: "noimage")!

  // Strip spaces from either end of the String
  let trimedUrlString = urlString.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
  let imgUrl: NSURL = NSURL(string: trimedUrlString)!

  // Make sure the urlString contain a valid url
  if imgUrl.scheme != nil && imgUrl.host != nil {
    // Download an NSData representation of the image at the remote url
    let imgData = NSData(contentsOfURL: imgUrl)

    if imgData != nil {
      // Put the downloaded image in the variable
      imageObject = UIImage(data: imgData!)!
    }
  }

  // Return the local/downloaded image
  return imageObject
}

Call the getImage() function in the touchesBegan() function like this:

 // This function is fired when you touch the view's background
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
  // Dismiss the keyboard
  bookTitle.resignFirstResponder()
  authorName.resignFirstResponder()
  photoUrl.resignFirstResponder()

  // Load an image in the imageView control
  imageView.image = getImage(photoUrl.text)
}
Output

Run the app in the iPhone 5s Simulator, then copy and past this url in the Photo Url text field, then click the view’s background:

http://img2.imagesbn.com/p/9780553591705_p0_v1_s114x166.JPG

The app download the url’s photo from the remote server and loaded it in the image view.

scdfs-fig2-05

If you entered nothing in the Photo Url text field or entered an invalid url in the field, then click the view’s background. The app will load the image you copied to the app’s bundle, in the image view instead. Here’s what it will look like on the iPhone 5s Simulator’s screen:

scdfs-fig2-06

That’s pretty cool, right? If you agree, leave a comment. The last thing I want you to do is return to Xcode and use the Source Control menu to “Commit…” changes you made to the project in the git repository.

scdfs-fig2-04

You’ve reached the end of the first workshop in the Swift Core Data From Scratch tutorial. I will bring you workshop 2 next week. In the mean time, comments are welcomed! 🙂

  • Zardoz

    Under Xcode 7.x, this line of code

    if imgUrl.scheme != nil && imgUrl.host != nil

    returns this error:

    Binary operator ‘!=’ cannot be applied to operands of type ‘String’ and ‘NilLiteralConvertible’

    Please advise what the fix is for this because my entire project is on hold until I solve this problem.

    • If you r using the iOS 9 and Swift 2.0 you’ll have to change the if statement to this:

      if imgUrl.scheme.isEmpty != true && imgUrl.host != nil

      Also, you’ll have to use the new error handler code in the project’s class files; which look something like this:

      do {
      try fetchedResultsController!.performFetch()
      }
      catch let error as NSError {
      print(error)
      }

      By the way, for an image to appear in the Image View, you’ll have to disable the “App Transport Security” in the project’s Info.plist file. The details of how to do that is document in this post’s section entitled, PROBLEM: THE VIDEO WON’T PLAY

      Finally, here is the Swift 2.0 version of the this post’s Xcode project:
      SwiftCoreDataFromScratch

      • Zardoz

        Thank you so much for your reply. Your tutorials have really useful for helping me put together a basic tableView app. However, might I also suggest you add UILocalizedIndexedCollation in any future update to this set of tutorials? It might seem redundant since you’ve already shown use how to add a Search Bar. But Apple includes localized indexes with their built-in apps, so end-users would likely appreciate having the flexibility of having both features.

        • Your suggestion is appreciated. I will do that when I update this tutorial.