How to save a struct

Hi,

At first I am a beginner in swift and i hope that you can understand my english 😉.


I want to save a struct but I dont know how. I try to use the "CoreData" but it doesn´t work.


Here my struct:

struct Section {

    var heading : String
    var items : [String]
    var foto :  [UIImage?]
    var beschreibung: [String]
    var genre: [String]
    var listeZutat: [[String]]
    var listeEinheit: [[String]]
    var listeMenge: [[Int]]
  init(title: String, objects : [String], bild : [UIImage?], rezeptbeschreibung: [String], rezeptGenre: [String], ListeZutat: [[String]], ListeEinheit: [[String]], ListeMenge: [[Int]]) {
   
        heading = title
        items = objects
        foto = bild
        beschreibung = rezeptbeschreibung
        genre = rezeptGenre
        listeMenge = ListeMenge
        listeZutat = ListeZutat
        listeEinheit = ListeEinheit
   
    }
}


Here my code

            let appDel: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
            let context: NSManagedObjectContext = appDel.managedObjectContext
          
            let newMeal = NSEntityDescription.insertNewObjectForEntityForName("Rezepte", inManagedObjectContext: context) // Rezepte is my Entity in .xcdatamodeld
          
            newMeal.setValue(sections, forKey: "rezepte") // sections is my struct, rezepte is my attribute in Enity
          
          
            do{
                try context.save()
            }
            catch{
              
              
                print("Error")
              
            }
          
            do {
              
                let request = NSFetchRequest(entityName: "Rezepte")
                let result = try context.executeFetchRequest(request)
              
                if result.count > 0 {
                  
                    for item in result as! [NSManagedObject]{
                      
                        let testLoad = item.valueForKey("rezepte")
                      
                        print(testLoad)
                    }
                    }
              
            }catch{ 
                print("Error")
            }// end catch

The error is in line 06: "Cannot convert value of type (section) to expected argument type AnyObject?"


In my .xcdatamodeld for my enity I can´t chose a struct for my DataCore only String,Double.....

I know that my Struc is not a type of AnyObject?. But I cant make my struct to AnyObject so how can I save a struct and is it possible to save a struct in a file?


Can someone give me a tipp how I can save the struct?


Bye Bye


Christian

Change your struct for a class.

You cannot save a struct directly in Core Data.


One solution is create a Core Data entity that has basic fields (String, Int, etc) that match the fields of your struct. You can then create an object of that Core Data type, copy your basic fields into it, and save that. When fetching the data back again later, you would create a new struct and move the fields from the Core Data object to your object.


However, even when this is simple, it's not very simple. For example, you can't have a UIImage field in a Core Data entity. This means you'll have to convert the image to pure data (e.g. TIFF or PNG representation) in order to put it your Core Data object.


An easier way to save data into a file using Cocoa frameworks is to use archiving:


developer.apple.com/library/prerelease/content/documentation/General/Conceptual/DevPedia-CocoaCore/Archiving.html


However, this is designed for Objective-C objects, not Swift structs. Again, you would have to convert the information in your struct into an Obj-C object, which can then be archived.


Unfortunately, in Swift, there is currently no easy mechanism to save a custom struct without some kind of conversion to raw data.

Thank you for your help! A class is an Object-C and a struct is only for Swift? When i want to use the CoreData I have to convert my struct in Strings and Ints and so one. And when I load Max struct, I load the Strings and add them into my struct. Is that right? Or I have to use and other way to Save the struct like Cocoa Framework?

It's a little bit complicated, for historical reasons.


In Swift a class can be Objective-C compatible, or not Obj-C compatible. The difference is what superclass they inherit from. (Basically, Obj-C classes inherit from NSObject.)


Core Data, and the archiving behavior I mentioned, come from the Obj-C past, and so expect Obj-C compatible objects.


A Swift struct is never Obj-C compatible. That means, to use a struct with Core Data or archiving, you have use an intermediate conversion to/from an Obj-C that is similar to the struct. That's why there are extra steps involved.

I think i understand it.

Well, I think I have to read at first a little bit more about that...

Hello again!

Thank you for your help, Now it works!

Here is my Code, maybe it helps somebody...

I have used NSCoding for saving my data, but I have one more questtion. In line 06 : Why must the type of foto = "UIImage!" and not "UIIMage?" !?


My new "struct"

import UIKit
class Section: NSObject, NSCoding {
    /
    var heading : String
    var items : [String]
    var foto :  [UIImage!]
    var beschreibung: [String]
    var genre: [String]
    var listeZutat: [[String]]
    var listeEinheit: [[String]]
    var listeMenge: [[Int]]
  
    /
  
    static let DocumentsDirectory = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
    static let ArchiveURL = DocumentsDirectory.URLByAppendingPathComponent("sections")
  
    /
  
    struct PropertyKey {
        static let headingKey = "heading"
        static let itemsKey = "items"
        static let fotoKey = "foto"
        static let beschreibungKey = "beschreibung"
        static let genreKey = "genre"
        static let listeZutatKey = "listeZutat"
        static let listeEinheitKey = "listeEinheit"
        static let listeMengeKey = "listeMenge"
    }

      
        init?(heading: String, items : [String], foto : [UIImage!], beschreibung: [String], genre: [String], listeZutat: [[String]], listeEinheit: [[String]], listeMenge: [[Int]]) {
          
            self.heading = heading
            self.items = items
            self.foto = foto
            self.beschreibung = beschreibung
            self.genre = genre
            self.listeMenge = listeMenge
            self.listeZutat = listeZutat
            self.listeEinheit = listeEinheit
          
            super.init()
      
    }
  
func encodeWithCoder(aCoder: NSCoder) {
    aCoder.encodeObject(heading, forKey: PropertyKey.headingKey)
    aCoder.encodeObject(items, forKey: PropertyKey.itemsKey)
    aCoder.encodeObject(genre, forKey: PropertyKey.genreKey)
    aCoder.encodeObject(listeZutat, forKey: PropertyKey.listeZutatKey)
    aCoder.encodeObject(beschreibung, forKey: PropertyKey.beschreibungKey)
    aCoder.encodeObject(listeMenge, forKey: PropertyKey.listeMengeKey)
    aCoder.encodeObject(listeEinheit, forKey: PropertyKey.listeEinheitKey)
    aCoder.encodeObject(foto, forKey: PropertyKey.fotoKey)
  
    }
  
    required convenience init?(coder aDecoder: NSCoder) {
        let heading = aDecoder.decodeObjectForKey(PropertyKey.headingKey) as! String
        let items = aDecoder.decodeObjectForKey(PropertyKey.itemsKey) as! [String]
        let foto = aDecoder.decodeObjectForKey(PropertyKey.fotoKey) as? [UIImage]
        let beschreibung = aDecoder.decodeObjectForKey(PropertyKey.beschreibungKey) as! [String]
        let genre = aDecoder.decodeObjectForKey(PropertyKey.genreKey) as! [String]
        let listeZutat = aDecoder.decodeObjectForKey(PropertyKey.listeZutatKey) as! [[String]]
        let listeMenge = aDecoder.decodeObjectForKey(PropertyKey.listeMengeKey) as! [[Int]]
        let listeEinheit = aDecoder.decodeObjectForKey(PropertyKey.listeEinheitKey) as! [[String]]
      
      
        self.init(heading: heading, items: items, foto: foto!, beschreibung: beschreibung, genre: genre, listeZutat: listeZutat, listeEinheit: listeEinheit, listeMenge: listeMenge)
    }
  
}


My code:



    func saveMeals() {
        print("saveMeals")
        let savefinish = NSKeyedArchiver.archiveRootObject(sections, toFile: Section.ArchiveURL.path!)
        if !savefinish {
            print("Error")
        }
    }
   
    func loadMeals() -> [Section]? {
        return NSKeyedUnarchiver.unarchiveObjectWithFile(Section.ArchiveURL.path!) as? [Section]
    }

You don't say what the error is, when you use 'UIImage?'. When I tried it, I got this error on line 55:


cannot convert value of type '[UIImage?]' to expected argument type 'AnyObject?'


That happens because a Swift array (a "value" type or struct) is automatically bridged to a Obj-C array (a "reference" type or class, specifically NSArray) by the Swift compiler, and NSArray objects are not allowed to contain nil values.


There's really no need to have either [UIImage?] or [UIImage!], is there? It's an array of images that do exist, so there's no real need to include "images" that don't exist.

Yes, is it that error. My problem is when I creat a new "meal" than I have to add an Image. Can I fix that?

Sure, set it to an empty array: []


An empty array is not a nil array, nor is it an array of nil values, so it will archive and unarchive just fine.

Like this? var image = [UIImage!]()

In the early days of Swift, there were numerous bugs with NSKeyedArchiver and other support classes (hopefully they've all been fixed).


In my case though, I ending up writing my own archiving mechanism (IIArchiver protocol) that both structs and classes can implement. Basically, it's a set of read and write APIs to read in well-defined types (Bool, UInt8 through UInt64, Strings, etc.). Also, all the write APIs ensure big-endian is used. Read APIs have both little- and big-endian variants to handle older "flat" data files written in other apps. Also, I did have to put in a minimal amount of NSCoding work to read in older NSCoding-based data files.

For cases where I need to write out an optional, I first write out a boolean on whether or not there's a value. When I write out a 'true' (0x01), it's then followed by the data in that optional type.


This is definitely much more work than using solutions such as CoreData and NSCoding, but thought I'd share.

Well, you almost certainly don't need the "!" either, so:


     var image = [UIImage] ()


or (when the compiler can infer the type):


     foto = []
How to save a struct
 
 
Q