Unarchive Array with NSKeyedUnarchiver unarchivedObject(ofClass:from:)

后端 未结 6 2045
别跟我提以往
别跟我提以往 2020-12-03 10:58

Since upgrading to Swift 4.2 I\'ve found that many of the NSKeyedUnarchiver and NSKeyedArchiver methods have been deprecated and we must now use the type method static

相关标签:
6条回答
  • 2020-12-03 11:09

    You are likely looking for this:

    if let widgetsData = UserDefaults.standard.data(forKey: USER_DEFAULTS_KEY_WIDGET_DATA) {
            if let widgets = (try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, WidgetData.self], from: widgetsData)) as? [WidgetData] {
                // your code
            }
        }
    
    0 讨论(0)
  • 2020-12-03 11:19
    unarchiveTopLevelObjectWithData(_:) 
    

    is deprecated as well. So to unarchive data without secure coding you need to:

    1. Create NSKeyedUnarchiver with init(forReadingFrom: Data)
    2. Set requiresSecureCoding of created unarchiver to false.
    3. Call decodeObject(of: [AnyClass]?, forKey: String) -> Any? to get your object, just use proper class and NSKeyedArchiveRootObjectKeyas key.
    0 讨论(0)
  • 2020-12-03 11:22

    As unarchiveTopLevelObjectWithData is also deprecated after iOS 14.3 only the Hopreeeenjust's answer is correct now.

    But if you don't need NSSecureCoding you also can use answer of Maciej S

    It is very easy to use it, by adding extension to NSCoding protocol:

    extension NSCoding where Self: NSObject {
        static func unsecureUnarchived(from data: Data) -> Self? {
            do {
                let unarchiver = try NSKeyedUnarchiver(forReadingFrom: data)
                unarchiver.requiresSecureCoding = false
                let obj = unarchiver.decodeObject(of: self, forKey: NSKeyedArchiveRootObjectKey)
                if let error = unarchiver.error {
                    print("Error:\(error)")
                }
                return obj
            } catch {
                print("Error:\(error)")
            }
            return nil
        }
    }
    

    With this extension to unarchive e.g. NSArray you only need:

    let myArray = NSArray.unsecureUnarchived(from: data)
    

    For Objective C use NSObject category:

    + (instancetype)unsecureUnarchivedFromData:(NSData *)data {
    NSError * err = nil;
    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData: data error: &err];
    unarchiver.requiresSecureCoding = NO;
    id res = [unarchiver decodeObjectOfClass:self forKey:NSKeyedArchiveRootObjectKey];
    err = err ?: unarchiver.error;
    if (err != nil) {
        NSLog(@"NSKeyedUnarchiver unarchivedObject error: %@", err);
    }
    return  res;
    

    }

    Note that if the requiresSecureCoding is false, class of unarchived object is not actually checked and objective c code returns valid result even if it is called from wrong class. And swift code when called from wrong class returns nil (because of optional casting), but without error.

    0 讨论(0)
  • 2020-12-03 11:23

    Swift 5- IOS 13

    guard let mainData = UserDefaults.standard.object(forKey: "eventDetail") as? NSData
    else {
        print(" data not found in UserDefaults")
        return
    }
    do {
        guard let finalArray =
        try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(mainData as Data) as? [EventDetail]
        else {
            return
        }
        self.eventDetail = finalArray
    }
    
    0 讨论(0)
  • 2020-12-03 11:25
     if #available(iOS 12.0, *) {
            guard let unarchivedFavorites = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(favoritesData!)
                else {
                    return
            }
            self.channelFavorites = unarchivedFavorites as! [ChannelFavorite]
        } else {
            if let unarchivedFavorites = NSKeyedUnarchiver.unarchiveObject(with: favoritesData!) as? [ChannelFavorite] {
                self.channelFavorites = unarchivedFavorites
            }
    

    // Achieving data

     if #available(iOS 12.0, *) {
                // use iOS 12-only feature
                do {
                    let data = try NSKeyedArchiver.archivedData(withRootObject: channelFavorites, requiringSecureCoding: false)
                    UserDefaults.standard.set(data, forKey: "channelFavorites")
                } catch {
                    return
                }
            } else {
                // handle older versions
                let data = NSKeyedArchiver.archivedData(withRootObject: channelFavorites)
                UserDefaults.standard.set(data, forKey: "channelFavorites")
            }
    

    This is the way I have updated my code and its working for me

    0 讨论(0)
  • 2020-12-03 11:29

    You can use unarchiveTopLevelObjectWithData(_:) to unarchive the data archived by archivedData(withRootObject:requiringSecureCoding:). (I believe this is not deprecated yet.)

    But before showing some code, you should better:

    • Avoid using NSData, use Data instead

    • Avoid using try? which disposes error info useful for debugging

    • Remove all unneeded casts


    Try this:

    private static func archiveWidgetDataArray(widgetDataArray : [WidgetData]) -> Data {
        do {
            let data = try NSKeyedArchiver.archivedData(withRootObject: widgetDataArray, requiringSecureCoding: false)
    
            return data
        } catch {
            fatalError("Can't encode data: \(error)")
        }
    
    }
    
    static func loadWidgetDataArray() -> [WidgetData]? {
        guard
            isKeyPresentInUserDefaults(key: USER_DEFAULTS_KEY_WIDGET_DATA), //<- Do you really need this line?
            let unarchivedObject = UserDefaults.standard.data(forKey: USER_DEFAULTS_KEY_WIDGET_DATA)
        else {
            return nil
        }
        do {
            guard let array = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(unarchivedObject) as? [WidgetData] else {
                fatalError("loadWidgetDataArray - Can't get Array")
            }
            return array
        } catch {
            fatalError("loadWidgetDataArray - Can't encode data: \(error)")
        }
    }
    

    But if you are making a new app, you should better consider using Codable.

    0 讨论(0)
提交回复
热议问题