In the previous post we saw how to build a simple Core Data application. Also if you haven’t completed the previous post I would suggest you to look at them before continuing.

In this post we will provide users to add inventory and display all the existing inventory. Also the user will be able to scan the barcode and get necessary detail about that product.

The datamodel for the product will look like below.

Most of the data in the CoreDataStack is boilerplate code and is automatically generated. The most important function in our case is insertInventory which creates an entity in the managed object context and store it in the core data stack.

// // CoreDataStack.swift // SimplyBarcode // // Created by Shrikar Archak on 2/8/15. // Copyright (c) 2015 Shrikar Archak. All rights reserved. // import Foundation import CoreData class CoreDataStack { lazy var applicationDocumentsDirectory: NSURL = { // The directory the application uses to store the Core Data store file. This code uses a directory named "com.shrikar.SimplyBarcode" in the application's documents Application Support directory. let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask) return urls[urls.count-1] as NSURL }() lazy var managedObjectModel: NSManagedObjectModel = { // The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model. let modelURL = NSBundle.mainBundle().URLForResource("SimplyBarcode", withExtension: "momd")! return NSManagedObjectModel(contentsOfURL: modelURL)! }() lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = { // The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail. // Create the coordinator and store var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel) let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SimplyBarcode.sqlite") var error: NSError? = nil var failureReason = "There was an error creating or loading the application's saved data." if coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil, error: &error) == nil { coordinator = nil // Report any error we got. let dict = NSMutableDictionary() dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" dict[NSLocalizedFailureReasonErrorKey] = failureReason dict[NSUnderlyingErrorKey] = error error = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict) // Replace this with code to handle the error appropriately. // abort() 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. NSLog("Unresolved error \(error), \(error!.userInfo)") abort() } return coordinator }() func insertInventory(productName : String, supplierName : String, quantity: Int, cost: Double, barcodeString: String, barcode: NSData) { let record = NSEntityDescription.insertNewObjectForEntityForName("Product", inManagedObjectContext: self.managedObjectContext!) as Product record.productName = productName record.supplierName = supplierName record.quantity = quantity record.cost = cost record.barcodeString = barcodeString record.barcode = barcode saveContext() } lazy var managedObjectContext: NSManagedObjectContext? = { // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail. let coordinator = self.persistentStoreCoordinator if coordinator == nil { return nil } var managedObjectContext = NSManagedObjectContext() managedObjectContext.persistentStoreCoordinator = coordinator return managedObjectContext }() // MARK: - Core Data Saving support func saveContext () { if let moc = self.managedObjectContext { var error: NSError? = nil if moc.hasChanges && !moc.save(&error) { // Replace this implementation with code to handle the error appropriately. // abort() 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. NSLog("Unresolved error \(error), \(error!.userInfo)") abort() } } } }

Lets go ahead and customize the table view controller which will display all the inventory. To get started customize and add UILabel to make the cell designed like below.

Create a new TableViewCell.Swift class which subclasses UITableViewCell.

import UIKit class TableViewCell: UITableViewCell { @IBOutlet weak var quantity: UILabel! @IBOutlet weak var cost: UILabel! @IBOutlet weak var supplierName: UILabel! @IBOutlet weak var productName: UILabel! override func awakeFromNib() { super.awakeFromNib() } override func setSelected(selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) } }

We will be using pod 'RSBarcodes', '~> 0.1' to create barcode in barcode 128 format.

Lets create a new file which subclasses UIViewController and call it AddInventoryViewController. Also drag a UIViewController onto the Storyboard and setup the text fields for product name, supplier etc. Also drag the tap gesture recognizer from the object library onto the viewController and create a IBAction to handle tap event.

We will use the name from the textfield to create the barcode and set it as the barcode image.

import UIKit class AddInventoryViewController: UIViewController { @IBOutlet weak var productName: UITextField! @IBOutlet weak var supplierName: UITextField! @IBOutlet weak var quantity: UITextField! @IBOutlet weak var cost: UITextField! @IBOutlet weak var barcodeImageView: UIImageView! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } @IBAction func tapped(sender: AnyObject) { self.productName.resignFirstResponder() self.supplierName.resignFirstResponder() self.quantity.resignFirstResponder() } @IBAction func previewBarcode(sender: AnyObject) { let data = RSUnifiedCodeGenerator().genCodeWithContents(productName.text, machineReadableCodeObjectType: AVMetadataObjectTypeCode128Code) self.barcodeImageView.image = data } @IBAction func cancelPressed(sender: AnyObject) { self.dismissViewControllerAnimated(true, completion: nil) } @IBAction func savePressed(sender: AnyObject) { let data = UIImageJPEGRepresentation(self.barcodeImageView.image, 0.90) coreDataStack.insertInventory(self.productName.text!, supplierName: self.supplierName.text!, quantity: self.quantity.text.toInt()!, cost: (self.cost.text as NSString).doubleValue, barcodeString: self.productName.text!, barcode: data) self.dismissViewControllerAnimated(true, completion: nil) } }

If you have followed along this is how the final storyboard should look like.



Finally once you have added the functionality to create a new entry lets go ahead and implement the listing functionality. There is nothing fancy in the viewcontroller . It uses the NSFetchedResultsController which integrates very well with table view. Create the NSFetchRequest and NSFetchedResultsController and provide the data returned by NSFetchedResultsController in the cellForRowAtIndexPath and numberOfRowsInSection methods.

// // InventoryViewController.swift // SimplyBarcode // // Created by Shrikar Archak on 2/8/15. // Copyright (c) 2015 Shrikar Archak. All rights reserved. // import UIKit import CoreData let coreDataStack = CoreDataStack() class InventoryViewController: UITableViewController, NSFetchedResultsControllerDelegate { var fetchedResultsController: NSFetchedResultsController { if _fetchedResultsController != nil { return _fetchedResultsController! } let fRequest = fetchRequest() let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fRequest, managedObjectContext: coreDataStack.managedObjectContext!, sectionNameKeyPath: nil, cacheName: nil) aFetchedResultsController.delegate = self _fetchedResultsController = aFetchedResultsController var error: NSError? = nil if !_fetchedResultsController!.performFetch(&error) { } return _fetchedResultsController! } var _fetchedResultsController: NSFetchedResultsController? = nil func fetchRequest() -> NSFetchRequest { let fetchRequest = NSFetchRequest(entityName: "Product") // Set the batch size to a suitable number. fetchRequest.fetchBatchSize = 20 // Edit the sort key as appropriate. let sortDescriptor = NSSortDescriptor(key: "supplierName", ascending: false) fetchRequest.sortDescriptors = [sortDescriptor] return fetchRequest } override func viewDidLoad() { super.viewDidLoad() fetchedResultsController.performFetch(nil) } func controllerDidChangeContent(controller: NSFetchedResultsController) { self.tableView.reloadData() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } // MARK: - Table view data source override func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1 } override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { let products = self.fetchedResultsController.fetchedObjects as [Product] return products.count } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as TableViewCell let product = self.fetchedResultsController.objectAtIndexPath(indexPath) as Product cell.productName.text = product.productName cell.supplierName.text = product.supplierName cell.cost.text = "$ \(product.cost)" cell.quantity.text = "\(product.quantity)" println(product.cost) return cell } }

Github Repo

At this point you should be able to create new products in the inventory and use the scanner to get details about that product.