2017-06-11 4 views
1

Swift 3 iOS 10, Essayer d'enregistrer tableau avec des objets personnalisés dans NSKeyedArchiver, essayant fondamentalement d'enregistrer la vue de la table après que l'utilisateur utilise les boutons pour basculer entre les sections. J'ai essayé plusieurs post pour résoudre le problème mais pas de chance, maintenant j'essaye de le faire moi-même NSCoding et NSKeyedArchiver.Swift: erreur dans NSKeyedArchiver

Erreur:

Cannot convert value of type '[Blog]' to expected argument type 'NSCoder'

erreur est en code Blog.swift, code qui gère NSCoding et mon Blog Objets

import UIKit 

class BlogsCoding: NSObject, NSCoding { 

var blogList : [Blog] 

init(blogList : [Blog]) { 
    self.blogList = blogList 
} 

convenience required init?(coder aDecoder: NSCoder) { 

    guard let blogList = aDecoder.decodeObject(forKey: "blogs") as? [Blog] 
     else { 
      return nil 
    } 
    self.init (blogList : blogList) 
} 

func encode(with aCoder: NSCoder) { 

    aCoder.encode(blogList, forKey: "blogs") 
} 
} 

class Blog: NSObject, NSCoding { // To conform to NSCoding 

// Strings 
var blogName: String? 
var blogStatus1: String? 
var blogStatus2: String? 
var blogURL: String? 
var blogID: String? 
var blogType: String? 
var blogDate: String? 
var blogPop: String? 
var blogList : [Blog] // To conform to NSCoding 

override init() { 

} 
// Converting Strings into Objects 
init(blogName bName: String, 
    andBlogStatus1 bStatus1: String, 
    andBlogStatus2 bStatus2: String, 
    andBlogURL bURL: String, 
    andBlogID bID: String, 
    andBlogType bType: String, 
    andBlogDate bDate: String, 
    andBlogPop bPop: String, 
    blogList : [Blog]) // To conform to NSCoding 
{ 
    super.init() 

    self.blogName = bName 
    self.blogStatus1 = bStatus1 
    self.blogStatus2 = bStatus2 
    self.blogURL = bURL 
    self.blogID = bID 
    self.blogType = bType 
    self.blogDate = bDate 
    self.blogPop = bPop 
    self.blogList = blogList // To conform to NSCoding 
} 

// To conform to NSCoding 
convenience required init?(coder aDecoder: NSCoder) { 

    guard let blogList = aDecoder.decodeObject(forKey: "blogs") as? [Blog] 
     else { 
      return nil 
    } 
    self.init (coder : blogList) // *---* Error is here *---* 
} 

func encode(with aCoder: NSCoder) { 

    aCoder.encode(blogList, forKey: "blogs") 
} 

} 

Dans MainController.swift - Où mon tableview est

override func viewDidLoad() { 

var path : String { 
     let manager = FileManager.default 
     let url = manager.urls(for: .documentDirectory, in: .userDomainMask).first! as NSURL 
     return url.appendingPathComponent("blogs")!.path // I have a blogs.plist for this, doing it right? 
    } 
} 

Bouton de suivi

// Follow Button 
@IBAction func followButtonClick(_ sender: UIButton!) { 

// After Updating Table, Save Arrays 
     var success = false 
      // mainArray is array holding custom objects from json 
     success = NSKeyedArchiver.archiveRootObject(mainArray, toFile: "path") // If I dont use "" I get undeclared 'path' 

     if success { 
      print("Saved Blogs") 
     } else { 
      print("Didn't Save Blogs") 
     } 
} 

Réception de données à partir du serveur

// Retrieving Data from Server 
func retrieveData() { 

    let getDataURL = "http://blogexample.com/receiving.php" 
    let url: NSURL = NSURL(string: getDataURL)! 

    do { 

     let data: Data = try Data(contentsOf: url as URL) 
     jsonArray = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! NSMutableArray 

     // Looping through jsonArray 
     for i in 0..<jsonArray.count { 

      // Create Blog Object 
      let bID: String = (jsonArray[i] as AnyObject).object(forKey: "id") as! String 
      let bName: String = (jsonArray[i] as AnyObject).object(forKey: "blogName") as! String 
      let bStatus1: String = (jsonArray[i] as AnyObject).object(forKey: "blogStatus1") as! String 
      let bStatus2: String = (jsonArray[i] as AnyObject).object(forKey: "blogStatus2") as! String 
      let bURL: String = (jsonArray[i] as AnyObject).object(forKey: "blogURL") as! String 
      // New 
      let bType: String = (jsonArray[i] as AnyObject).object(forKey: "blogType") as! String 
      let bDate: String = (jsonArray[i] as AnyObject).object(forKey: "blogDate") as! String 
      let bPop: String = (jsonArray[i] as AnyObject).object(forKey: "blogPop") as! String 
      // NSCoding 
      let blogList: NSObject = ((jsonArray[i]) as! NSObject).value(forKey: "blogList") as! NSObject 

      // Add Blog Objects to Main Array 
      mainArray.append(Blog(blogName: bName, andBlogStatus1: bStatus1, andBlogStatus2: bStatus2, andBlogURL: bURL, andBlogID: bID, andBlogType: bType, andBlogDate: bDate, andBlogPop: bPop, blogList: blogList as! [Blog])) 

     } 
    } 
    catch { 
     print("Error: (Retrieving Data)") 
    } 

Aussi, est défini 'chemin' et utilisé à droite? Je vous remercie!

+1

Vous décoder un objet '[Blog]', mais en faisant passer l'objet la méthode 'init (coder' qui attend' NSCoder'.) C'est une incompatibilité de type classique Btw: Votre méthode 'retrieveData()' est plutôt laide.Ne pas utiliser 'NSMutableArray' et' .mutableContainers'. omettez le paramètre 'options' et transtypez le résultat en' [[String: Any]] ', puis remplacez la boucle par' pour item dans jsonArray' et les lignes dans le corps par exemple 'let bID = item ["id"] comme! Chaîne C'est beaucoup plus propre et plus efficace. – vadian

+0

Comment puis-je corriger la différence de type, en remplaçant le blogList avec NSCoder, il donne la même erreur si c'est ce que vous vouliez dire. Pour retrieveData() je vous remercie de vouloir nettoyer ce code, j'ai juste l'intention de remplir une tableview avec cette information. Comment dois-je mettre à jour le code? – BroSimple

+0

Les méthodes 'encoder/decoder' sont pour coder/décoder chaque propriété de la classe, pas pour la classe elle-même. – vadian

Répondre

3

Le but de NSCoding est de convertir chaque propriété unique de la classe en un format conforme à la liste de propriétés.

Tout d'abord, déclarez toutes les propriétés String comme non facultatives car l'initialiseur personnalisé ne transmet que des paramètres non facultatifs.

Ensuite, ajoutez les lignes dans les init(coder et encode(with méthodes (édités)

class Blog: NSObject, NSCoding { // To conform to NSCoding 

    // Strings 
    var blogName: String 
    var blogStatus1: String 
    var blogStatus2: String 
    var blogURL: String 
    var blogID: String 
    var blogType: String 
    var blogDate: String 
    var blogPop: String 
    var blogList : [Blog] // To conform to NSCoding 

    // Converting Strings into Objects 
    init(blogName bName: String, 
     andBlogStatus1 bStatus1: String, 
     andBlogStatus2 bStatus2: String, 
     andBlogURL bURL: String, 
     andBlogID bID: String, 
     andBlogType bType: String, 
     andBlogDate bDate: String, 
     andBlogPop bPop: String, 
     blogList : [Blog]) // To conform to NSCoding 
    { 
     self.blogName = bName 
     self.blogStatus1 = bStatus1 
     self.blogStatus2 = bStatus2 
     self.blogURL = bURL 
     self.blogID = bID 
     self.blogType = bType 
     self.blogDate = bDate 
     self.blogPop = bPop 
     self.blogList = blogList // To conform to NSCoding 
     super.init() 
    } 

    // To conform to NSCoding 
    convenience required init?(coder aDecoder: NSCoder) { 
     self.blogName = aDecoder.decodeObject(forKey: "blogName") as! String 
     self.blogStatus1 = aDecoder.decodeObject(forKey: "blogStatus1") as! String 
     self.blogStatus2 = aDecoder.decodeObject(forKey: "blogStatus2") as! String 
     self.blogURL = aDecoder.decodeObject(forKey: "blogURL") as! String 
     self.blogID = aDecoder.decodeObject(forKey: "blogID") as! String 
     self.blogType = aDecoder.decodeObject(forKey: "blogType") as! String 
     self.blogDate = aDecoder.decodeObject(forKey: "blogDate") as! String 
     self.blogPop = aDecoder.decodeObject(forKey: "blogPop") as! String 
     self.blogList = aDecoder.decodeObject(forKey: "blogs") as! [Blog] 
     super.init (coder : aDecoder) 
    } 

    func encode(with aCoder: NSCoder) { 
     aCoder.encode(blogName, forKey: "blogName") 
     aCoder.encode(blogStatus1, forKey: "blogStatus1") 
     aCoder.encode(blogStatus2, forKey: "blogStatus2") 
     aCoder.encode(blogURL, forKey: "blogURL") 
     aCoder.encode(blogID, forKey: "blogID") 
     aCoder.encode(blogType, forKey: "blogType") 
     aCoder.encode(blogDate, forKey: "blogDate") 
     aCoder.encode(blogPop, forKey: "blogPop") 
     aCoder.encode(blogList, forKey: "blogs") 
    } 
} 

Cependant, il y a une mise en garde potentiel:

encodage/décodage d'un tableau d'objets de la classe cible peut provoquer un comportement inattendu, par exemple une boucle infinie. Considérez cela. Vous pouvez encoder le tableau séparément.


La deuxième question est une faute de frappe, vous passez "path" comme une chaîne littérale plutôt que d'une path variable:

NSKeyedArchiver.archiveRootObject(mainArray, toFile: path) 
+0

Merci pour la réponse détaillée, pas d'erreurs. Que peut-il se passer dans la mise en garde potentielle dans la boucle infinie, qu'est-ce qui peut déclencher cela? De plus, pour 'path', j'obtiens une erreur disant "Utilisation du chemin d'identification de l'identificateur non résolu" – BroSimple

+0

Etes-vous sûr de vouloir avoir une propriété contenant un tableau d'objets de la classe englobante? Vous pouvez ajouter l'extension de fichier 'blogs.plist " – vadian

+0

Alors j'ai construit le projet et j'ai eu cette erreur" erreur fatale: trouvé de manière inattendue nil tout en déballant une valeur optionnelle "dans retrieveData" let blogList: NSObject = ((jsonArray [i]) comme! NSObject) .value (forKey: "blogList") comme! NSObject "J'ai essayé d'ajouter une erreur if let still mais aussi, ne devrais-je pas avoir une propriété contenant des objets? Little lost there.Pour le 'path'I ajouter le' blogs.plist 'à" var path: String "? – BroSimple