Quantcast
Channel: FlexMonkey
Viewing all articles
Browse latest Browse all 257

Updating and Deleting with Swift and Core Data

$
0
0

I discussed creating and reading data with Core Data recently. This post looks at the rest of CRUD functions: updating and deleting. The examples in this post are taken from the demonstration project I used in my London Swift talk.

A bit of background: My SavedColoursGrid class contains a UICollectionView that displays the user's saved colours. These are instances of NamedColour, a value object that contains a name, a color and, as we'll see, a reference to its related data transfer object.

Updating

When a user saves their colour in my application or I load a saved colour at application startup, I add a reference to the related NSManagedObject to the value object itself. These methods are taken from NamedColorEntity:

    classfunc createInstanceFromEntity(entity: NamedColorEntity) -> NamedColor!
    { 
        let name = entity.colorName
        let color = UIColor.colorFromNSNumbers(redComponent: entity.redComponent, greenComponent: entity.greenComponent, blueComponent: entity.blueComponent)
  
        var namedColor = NamedColor(name: name, color: color)
        
        namedColor.entityRef = entity
        
        return namedColor
    }
    
    classfunc createInManagedObjectContext(moc: NSManagedObjectContext, namedColor: NamedColor) -> NamedColorEntity
    {
        let newEntity = NSEntityDescription.insertNewObjectForEntityForName("NamedColorEntity", inManagedObjectContext: moc) asNamedColorEntity
        
        let colorComponents = namedColor.color.getRGB()
        
        newEntity.redComponent = colorComponents.redComponent
        newEntity.greenComponent = colorComponents.greenComponent
        newEntity.blueComponent = colorComponents.blueComponent

        newEntity.colorName = namedColor.name
        
        namedColor.entityRef = newEntity
        
        return newEntity

    }

NamedColorItemRendererthe item renderer inside the UICollectionView that displays the user's saved colours, contains a text input field for editing. Since the renderer acts a the text input's UITextFieldDelegate, when the user either presses the "return" key or dismisses the keyboard I can react to the text changing and update the saved colour's name property:

    func textFieldShouldReturn(textField: UITextField) -> Bool
    {
        textField.endEditing(true)
        
        returntrue
    }
    
    func textFieldDidEndEditing(textField: UITextField)
    {
        ifnamedColor != nil
        {
            namedColor!.name = textInput.text
        }

    }

Then, maybe controversially, I allow the NamedColor value object itself to update its reference to the related data transfer object and save its updated name using Core Data:

    var name : String
    {
        didSet
        {
            iflet _entityRef = entityRef
            {
                _entityRef.colorName = name
                
                let appDelegate = UIApplication.sharedApplication().delegateasAppDelegate
                
                appDelegate.saveContext()
            }
        }

    }

I'd suggest this pattern follows the Domain Driven Data pattern. In a small application like mine, I think it's entirely appropriate. However if you're creating a larger application, you may want to marshall all Core Data related work through a single delegate to manage persistence through a single class.

Deleting

My SavedColoursGrid class contains acts as UICollectionViewDelegate for its UICollectionView and with a few of functions, I'm able to display a little 'cut' context menu when the user tap-holds on any of the items:



    func collectionView(collectionView: UICollectionView, shouldShowMenuForItemAtIndexPath indexPath: NSIndexPath) -> Bool
    {
        returntrue
    }
    
    func collectionView(collectionView: UICollectionView, canPerformAction action: Selector, forItemAtIndexPath indexPath: NSIndexPath, withSender sender: AnyObject!) -> Bool
    {
        return action.description == "cut:"

    }

When the user selects 'cut', the performAction version of the delegate method collectionView() is invoked where I use an array filter closure to remove the current item from the data:

    func collectionView(collectionView: UICollectionView, performAction action: Selector, forItemAtIndexPath indexPath: NSIndexPath, withSender sender: AnyObject!)
    {
        if action.description == "cut:"
        {
            colors = colors.filter({$0 != self.colors[indexPath.item]})
            
            sendActionsForControlEvents(UIControlEvents.SavedColorDeleted)
        }

    }

The view controller, reacting to the SavedColorDeleted control event, compares its own version of savedColors with the newly updated savedColors of the SavedColorsGrid to figure out which item the user wants to delete. Using the context's deleteObject, it's able to remove that item from Core Data:

    func savedColorsGridDeleteHandler()
    {
        for color insavedColors
        {
            iffind(savedColorsGrid.colors, color) == nil
            {
                iflet _entityRef = color.entityRef
                {
                    managedObjectContext.deleteObject(_entityRef)
                }
            }
        }
        
        appDelegate.saveContext()
        
        savedColors = savedColorsGrid.colors

    }

And the circle is complete: with Core Data I'm now able to create, read, update and delete items.

The source code for this project is all available at my GitHub repository here.

Viewing all articles
Browse latest Browse all 257

Trending Articles