Mudith Chathuranga Silva
Mudith Chathuranga Silva

Reputation: 7434

Shows an error Non - fail able initializer "init(coder:)" error

When i'm trying to run the int(coder:) it shows this error, i don't know why ?? "Non failable initializer requirement init(coder:) cannot be satisfied by a failable initalizer ('init?')"

class Note: NSObject, NSCoding {

    var name: String
    var photo: UIImage?
    var rating: Int

    static let DocumentsDirectory: AnyObject = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
    static let ArchiveURL = DocumentsDirectory.URLByAppendingPathComponent("notes")

    struct PropertyKey {
    static let nameKey = "name"
    static let photoKey = "photo"
    static let ratingKey = "rating"
     }


    init?(name: String, photo: UIImage?, rating: Int) {

    self.name = name
    self.photo = photo
    self.rating = rating

    super.init()

    // Initialization should fail if there is no name or if the rating is negative.
    if name.isEmpty || rating < 0 {
        return nil
        }

    }

    func encodeWithCoder(aCoder: NSCoder) {
    aCoder.encodeObject(name, forKey: PropertyKey.nameKey)
    aCoder.encodeObject(photo, forKey: PropertyKey.photoKey)
    aCoder.encodeInteger(rating, forKey: PropertyKey.ratingKey)
    }

    required convenience init?(coder aDecoder: NSCoder) {

    let name = aDecoder.decodeObjectForKey(PropertyKey.nameKey) as! String

    // Because photo is an optional property of Meal, use conditional cast.
    let photo = aDecoder.decodeObjectForKey(PropertyKey.photoKey) as? UIImage

    let rating = aDecoder.decodeIntegerForKey(PropertyKey.ratingKey)

    // Must call designated initializer.
    self.init(name: name, photo: photo, rating: rating)

    }


}

i'm using xcode 6 , and by the way when this code runs in xcode 7 it do not show any errors , what is the reason ?

Upvotes: 1

Views: 5796

Answers (3)

Jo&#227;o Costa
Jo&#227;o Costa

Reputation: 21

So I found a way to make it work, the 'init(coder:)' method can't be failable so what I did was override the 'init()' method to be called by the 'init(coder:)' method because it needs to call 'self.init()'. So here is the code:

class Meal: NSObject, NSCoding {

// MARK: Archiving Paths
static let DocumentsDirectory   = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
static let ArchiveURL           = DocumentsDirectory.URLByAppendingPathComponent("meals")

// MARK: Properties
var name:   String
var rating: Int
var photo:  UIImage?

// MARK: Types
struct PropertyKey {
    static let nameKey      = "name"
    static let photoKey     = "photo"
    static let ratingKey    = "rating"
}

// MARK: Initialization

init? (name: String, rating: Int, photo: UIImage?) {
    // Intialize stored properties.
    self.name   = name
    self.rating = rating
    self.photo  = photo

    super.init()

    // Initialization should fail if there is no name or if the rating is negative.
    if self.name.isEmpty || (rating < 0) {
        return nil
    }
}

override private init () {
    self.name   = ""
    self.rating = 0
    self.photo  = nil
}

// MARK: NSCoding

func encodeWithCoder(aCoder: NSCoder) {
    aCoder.encodeObject(self.name, forKey: PropertyKey.nameKey)
    aCoder.encodeObject(self.photo, forKey: PropertyKey.photoKey)
    aCoder.encodeObject(self.rating, forKey: PropertyKey.ratingKey)
}

convenience required init(coder aDecoder: NSCoder) {
    self.init()

    let name    = aDecoder.decodeObjectForKey(PropertyKey.nameKey) as! String
    if let rating  = aDecoder.decodeIntegerForKey(PropertyKey.ratingKey) {
        self.rating = rating
    }

    // Because photo is an optional property of Meal, use conditional cast.
    let photo   = aDecoder.decodeObjectForKey(PropertyKey.photoKey) as? UIImage

    self.name   = name
    self.photo  = photo
}
}

I made the 'init()' method private so that only methods inside the class can call it. I also had to optional unwrap the rating because the app was crashing complaining about not being able to unarchive Int with the ratingKey.

Upvotes: 2

Mudith Chathuranga Silva
Mudith Chathuranga Silva

Reputation: 7434

Found a solution to run above code in xcode6 , should remove "convenience" from the init(coder aDecoder) and use super.init() instead of self.init(name: name, photo: photo, rating: rating) then it works perfectly

class Note: NSObject, NSCoding {

    var name: String
    var photo: UIImage?
    var rating: Int

    struct PropertyKey {
        static let nameKey = "name"
        static let photoKey = "photo"
        static let ratingKey = "rating"
    }

    init(name: String, photo: UIImage?, rating: Int) {

        self.name = name
        self.photo = photo
        self.rating = rating

        super.init()

        // Initialization should fail if there is no name or if the rating is negative.

    }

    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(name, forKey: PropertyKey.nameKey)
        aCoder.encodeObject(photo, forKey: PropertyKey.photoKey)
        aCoder.encodeInteger(rating, forKey: PropertyKey.ratingKey)
    }



    required init(coder aDecoder: NSCoder) {
        self.name = aDecoder.decodeObjectForKey(PropertyKey.nameKey) as! String

        // Because photo is an optional property of Meal, use conditional cast.
        self.photo = aDecoder.decodeObjectForKey(PropertyKey.photoKey) as? UIImage

        self.rating = aDecoder.decodeIntegerForKey(PropertyKey.ratingKey) as Int

        // Must call designated initializer.
        super.init()
    }



  }

Upvotes: 0

Martin Haeberli
Martin Haeberli

Reputation: 304

Speculation on my part as to specifics, but, generally - the precise syntax and semantics of Swift changed significantly between Xcode 6 (say, Xcode 6.4) and the new Xcode 7 betas. Xcode 6.4 supports / uses Swift language version 1.2; Xcode 7 uses Swift language version 2.0 (beta)

Upvotes: 0

Related Questions