2016-04-21 3 views
6

Ho creato un esempio molto semplice per dimostrare il problema che sto tentando di aggiornare un tipo trasformabile e ottenere la modifica da mantenere tra riavvii dell'app.Come posso ottenere attributi non standard (trasformabili) per aggiornare/salvare e persistere utilizzando Core Data con Swift?

devo un'entità di tipo Destinazione ...

import Foundation 
import CoreData 

class Destination: NSManagedObject { 
    @NSManaged var name: String 
    @NSManaged var location: Location 
} 

... che ha un attributo semplice nome (di tipo String) e un attributo di tipo Località:

import Foundation 

class Location: NSObject, NSCoding { 
    var address: String 
    var latitude: Double 
    var longitude: Double 

    required init?(coder aDecoder: NSCoder) { 
     address = aDecoder.decodeObjectForKey("Address") as? String ?? "" 
     latitude = aDecoder.decodeObjectForKey("Latitude") as? Double ?? 0.0 
     longitude = aDecoder.decodeObjectForKey("Longitude") as? Double ?? 0.0 
     super.init() 
    } 

    init(address: String, latitude: Double, longitude: Double) { 
     self.address = address 
     self.latitude = latitude 
     self.longitude = longitude 

     super.init() 
    } 

    func encodeWithCoder(aCoder: NSCoder) { 
     aCoder.encodeObject(address, forKey: "Address") 
     aCoder.encodeObject(latitude, forKey: "Latitude") 
     aCoder.encodeObject(longitude, forKey: "Longitude") 
    } 
} 

La posizione è configurata come "trasformabile" in Dati principali, perché ha una struttura che nessuno degli altri tipi di base può gestire.

Utilizzando boilerplate codice Core Data di Apple, ecco un controller della vista che fa semplicemente il seguente:

  • ottiene l'AppDelegate necessaria/ManagedApplicationContext riferimento
  • recupera una destinazione, se ne esiste uno, crea uno se non
  • Stampa il nome e la posizione.indirizzo della destinazione
  • Aggiorna il nome e la posizione.indirizzo della destinazione
  • Salva le modifiche a the ManagedObjectContext

Quando l'app viene eseguita e rieseguita, verranno mantenute solo le modifiche al nome. Le modifiche apportate all'indirizzo location.address non persistono.

import UIKit 
import CoreData 

class ViewController: UIViewController { 

    var appDelegate: AppDelegate! 
    var context: NSManagedObjectContext! 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     updateDestination() 
    } 

    func updateDestination() { 
     var destination: Destination 
     appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate 
     context = appDelegate.managedObjectContext 

     if let dest = fetchOneDestination() { 
      destination = dest 
     } 
     else { 
      destination = create()! 
     } 
     print("destination named: \(destination.name), at: \(destination.location.address)") 

     destination.name = "New name of place that will update and persist" 
     destination.location.address = "123 main st (change that will never persist)" 
     appDelegate.saveContext() 
    } 

    func create() -> Destination? { 
     guard let newDestination = NSEntityDescription.insertNewObjectForEntityForName("Destination", inManagedObjectContext: context) as? Destination else { 
      return nil 
     } 
     newDestination.name = "Original name of place that can be updated" 
     newDestination.location = Location(address: "100 main st", latitude: 34.051145, longitude: -118.243595) 
     return newDestination 
    } 

    func fetchOneDestination() -> Destination? { 
     let request = NSFetchRequest() 
     request.entity = NSEntityDescription.entityForName("Destination", inManagedObjectContext: context) 
     do { 
      let fetchResults = try context.executeFetchRequest(request) 
      if fetchResults.count > 0 { 
       if let dest = fetchResults[0] as? Destination { 
        return dest 
       } 
      } 
     } 
     catch {} 
     return nil 
    } 
} 

Come è possibile mantenere gli aggiornamenti dell'attributo di posizione della destinazione?

risposta

3

I dati di base non sono in grado di tracciare lo stato sporco di quell'oggetto perché non è al corrente dei suoi interni. Invece di mutare l'oggetto, creane una copia, mutila e poi imposta il nuovo oggetto. Potrebbe funzionare per mutare e quindi reimpostare lo stesso oggetto, non è sicuro, non l'ha provato.

È possibile controllare, è sufficiente modificare l'indirizzo e quindi chiedere all'oggetto gestito se ha delle modifiche, in caso contrario non verrà salvato.

+0

Grazie, l'assegnazione di un nuovo oggetto ha funzionato perfettamente. Ho testato il caso di mutare e reimpostare lo stesso oggetto, ma questo non funziona, probabilmente perché il puntatore non sarebbe mai stato aggiornato in quel caso. Vuole davvero un oggetto nuovo di zecca. –

4

La risposta di Wain è corretta: sembra che il riferimento all'oggetto debba essere modificato affinché i dati di base conservino l'aggiornamento. La modifica di un attributo figlio nell'istanza di posizione non aggiornerà tale riferimento. Né funzionerà per mutare e ri-impostare lo stesso oggetto. Solo quando viene assegnato un nuovo riferimento a un oggetto, lo stick di cambiamento.

Ecco alcuni esempi di codice:

Questo fa NON lavoro:

let newLocation = destination.location 
newLocation.address = "a new street that doesn't stick" 
destination.location = newLocation 

ma questo:

destination.location = Location(address: "a new street that sticks", latitude: 34.051145, longitude: -118.243595) 

Questo funziona anche, a condizione che la classe Location implementa la Metodo copyWithZone:

let newLocation = destination.location.copy() as! Location 
newLocation.address = "another new street that sticks" 
destination.location = newLocation