Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Swift and Core Data: Saving Data with Thumbnail Previews

DZone's Guide to

Swift and Core Data: Saving Data with Thumbnail Previews

· Java Zone
Free Resource

Make it happen: rapid app development on Kubernetes as a managed service.

I've spent some time adding Core Data support to my Swift and Metal based reaction diffusion application. Core Data is a framework that allows me to persist different reaction diffusion configurations so that the user can save and reload different patterns.

There are several wrappers to Core Data that simplify its implementation including Alecrim and Sugar Record, but this blog post looks at the framework itself. My first ports of call were Jameson Quave's excellent tutorials and, of course, Ray Wenderlich.

When I first built my application, I'd omitted to check the 'Use Core Data' checkbox, so my first task was to create a new Swift project with that option checked and copy all the guts of the new AppDelegate into mine and ensuring the references in managedObjectModel and persistentStoreCoordinator were correct.

The next manual step is to create a data model that reflects the data I want to persist. This is done via FileNew → File and selecting Data Model under the Core Data options. Xcode now opens an editor where the model schema can be defined. Here's a screen grab of how my ReactionDiffusionEntity looks:

Once that's been created, I need a Swift class to mirror it. This is done from Editor → Create NSManaged Subclass and generates the following Swift:

class ReactionDiffusionEntity: NSManagedObject {
            @NSManaged var model: String
            @NSManaged var timestep: NSNumber
            @NSManaged var a0: NSNumber
            @NSManaged var a1: NSNumber
            @NSManaged var epsilon: NSNumber
            @NSManaged var delta: NSNumber
            @NSManaged var k1: NSNumber
            @NSManaged var k2: NSNumber
            @NSManaged var k3: NSNumber
            @NSManaged var f: NSNumber
            @NSManaged var k: NSNumber
            @NSManaged var du: NSNumber
            @NSManaged var dv: NSNumber
            @NSManaged var alpha: NSNumber
            @NSManaged var beta: NSNumber
            @NSManaged var gamma: NSNumber
            @NSManaged var imageData: NSData
            }
Now I have the plumbing inside AppDelegate to access the Core Data framework, the data model and a Swift representation of the model, I can write code to save and load reaction diffusion models. 

I've added two new UIAlertAction instances to the drop down hamburger menu in the ReactionDiffusionEditor for saving and loading. These invoke saveModel() and loadModel() in my main view controller

To save, I need to create an instance of ReactionDiffusionEntity in the Core Data context that copies the different parameter values from my existing value object.  Inside the view controller's init(), I create a reference to both the application delegate and the context:

 let appDelegate: AppDelegate
    let managedObjectContext: NSManagedObjectContext

    required init(coder aDecoder: NSCoder)
    {
        appDelegate = UIApplication.sharedApplication().delegate as AppDelegate

        managedObjectContext = appDelegate.managedObjectContext!
        [...]
...and then in save I create the new entity based on my model, reactionDiffusionModel:
  func saveModel()
    {
        var newEntity = ReactionDiffusionEntity.createInManagedObjectContext(managedObjectContext, model: reactionDiffusionModel.model.rawValue, reactionDiffusionStruct: reactionDiffusionModel.reactionDiffusionStruct, image: self.imageView.image!)
        
       appDelegate.saveContext()

    }
managedObjectContext and saveContext() both came for free when I created a new project with 'Use Core Data' checked.

The createInManagedObjectContext() method not only inserts a new object into the context, it also contains lines that map between my value object and the generated NSManagedObject:

newItem.timestep = reactionDiffusionStruct.timestep
        newItem.a0 = reactionDiffusionStruct.a0
        newItem.a1 = reactionDiffusionStruct.a1
        newItem.epsilon = reactionDiffusionStruct.epsilon
        newItem.delta = reactionDiffusionStruct.delta
        [...]

You may also notice that I passed the view controller's image view's image into createInManagedObjectContext() - by using a little extension I wrote to resize images and UIImageJPEGRepresentation, I'm able to save a thumbnail of the reaction diffusion simulation as binary data:

newItem.imageData = UIImageJPEGRepresentation(image.resizeToBoundingSquare(boundingSquareSideLength: 160.0), 0.75)

To load, I use executeFetchRequest() to populate an array of ReactionDiffusionEntity:

     func loadModel()
    {
        let fetchRequest = NSFetchRequest(entityName: "ReactionDiffusionEntity")
        
        if let fetchResults = managedObjectContext.executeFetchRequest(fetchRequest, error: nil) as? [ReactionDiffusionEntity]
        {
            // retrieved fetchResults.count records....
        }

    }

 

Now that fetchResults is populated, I pass that into my BrowseAndLoadController which is presented as a popover dialog. This contains not more than a UICollectionView to display all the entities inside fetchResults. 

The item renderer creates a UIImage version of the entity's imageData using an observer on its reactionDiffusionEntity property:

var reactionDiffusionEntity: ReactionDiffusionEntity?
    {
        didSet
        {
            if let _entity = reactionDiffusionEntity
            {
                label.text = _entity.model
            
                let thumbnail = UIImage(data: _entity.imageData as NSData)
            
                imageView.image = thumbnail
            }
        }

    }

When the user selects a model to load, I need to do the opposite of createInManagedObjectContext() - that is create a value object from my entity. I do this with a class function on ReactionDiffusionEntity which looks like a mirror image of createInManagedObjectContext():

class func createInstanceFromEntity(entity: ReactionDiffusionEntity) -> ReactionDiffusion!
    {
        var returnObject: ReactionDiffusion!
        
        var model: ReactionDiffusionModels = ReactionDiffusionModels(rawValue: entity.model)!
        
        switch model
        {
            case .BelousovZhabotinsky:
                returnObject = BelousovZhabotinsky()
            case .GrayScott:
                returnObject = GrayScott()
            case .FitzHughNagumo:
                returnObject = FitzhughNagumo()
        }
        
        // populate numeric params...
        returnObject.reactionDiffusionStruct.timestep = Float(entity.timestep)
        returnObject.reactionDiffusionStruct.a0 = Float(entity.a0)

        [...]

        returnObject.reactionDiffusionStruct.beta = Float(entity.beta)
        returnObject.reactionDiffusionStruct.gamma = Float(entity.gamma)
        
        return returnObject

    }
That's the basic saving and loading done. Because Xcode auto generates so much code, Core Data is pretty simple stuff. The next steps are to support deleting, editing and persisting state between sessions.

All the source code for this project is available at my Git Hub repository here.

Tutorial: WordPress as distributed microservices on Kubernetes.

Topics:

Published at DZone with permission of Simon Gladman, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}