NSKeyedArchiver & NSKeyedUnarchiver / Swift 3.0

1.5k views Asked by At

I'm using NSKeyedArchiver & NSKeyedUnarchiver to store some complex data in Core-Data and retrieve it later in my app. This was working perfectly until now, but after migration, Swift 3.0 does not seem to be happy with my code.

I've this early in my code:

        var firstArray = [Int](), secondArray = [CGFloat]()
        .......
        // stores some values in firstArray and also in secondArray.
         .......

Here is how the code to store the data looks like:

        let masterArray = [firstArray, secondArray] as [Any]
        let dataForApp:NSData = NSKeyedArchiver.archivedData(withRootObject: masterArray) as NSData
        entityFieldsDico = ["dataForAppArray":dataForApp]
        // Use entityFieldsDico to save dataForApp in Core-Data under the key "dataForAppArray".

Here is how the code to retrieve the data looks like:

    if let archiveData = dbRecord.value(forKey: "dataForAppArray") {
        let archiveArray = NSKeyedUnarchiver.unarchiveObject(with: archiveData as! Data)
        firstArray = (archiveArray as! Array)[0] as [Int]
        secondArray = (archiveArray as! Array)[1] as [CGFloat]
    }

The issue appears whith the code retrieving the data. It simply crashes at build time.

If I comment out those 2 lines:

        //firstArray = (archiveArray as! Array)[0] as [Int]
        //secondArray = (archiveArray as! Array)[1] as [CGFloat]

The program works, except for the fact that the data in firstArray & is (obviously) not available.

If I do not comment them out, then I get a crash, with a very long message ending with something like what lies below. (I add some ...(dots) to shorten the message.)

.............
0  swift                    0x000000010d71fa3d PrintStackTraceSignalHandler(void*) + 45
1  swift                    0x000000010d71f466 SignalHandler(int) + 470
2  libsystem_platform.dylib 0x00007fffa0c5a52a _sigtramp + 26
3  libsystem_platform.dylib 0x0000000000000003 _sigtramp + 1597659891
4  swift                    0x000000010b25b4e3 swift::constraints::ConstraintGraphScope::~ConstraintGraphScope() + 899
5  swift                    0x000000010b2f45f4 swift::constraints::ConstraintSystem::solveSimplified(llvm::SmallVectorImpl<swift::constraints::Solution>&, swift::FreeTypeVariableBinding) + 24868
...........
Objects-normal/arm64/UP_ViewController.dia -emit-dependencies-path /Users/me/Library/Developer/Xcode/DerivedData/TheApp-dszaazmmftlmwbicuwcwaplkjdfs/Build/Intermediates/TheApp.build/Debug-iphoneos/TheApp.build/Objects-normal/arm64/UP_ViewController.d -emit-reference-dependencies-path /Users/me/Library/Developer/Xcode/DerivedData/TheApp-dszaazmmftlmwbicuwcwaplkjdfs/Build/Intermediates/TheApp.build/Debug-iphoneos/TheApp.build/Objects-normal/arm64/UP_ViewController.swiftdeps -o /Users/me/Library/Developer/Xcode/DerivedData/TheApp-dszaazmmftlmwbicuwcwaplkjdfs/Build/Intermediates/TheApp.build/Debug-iphoneos/TheApp.build/Objects-normal/arm64/UP_ViewController.o -embed-bitcode-marker 
1.  While type-checking 'computeFunction' at /Users/me/Documents/iOS/TheApp/TheApp/UP_ViewController.swift:184:5
2.  While type-checking expression at [/Users/me/Documents/iOS/TheApp/TheApp/UP_ViewController.swift:235:17 - line:235:66] RangeText="firstArray = (archiveArray as! Array)[0] as [Int]"

I anyone has experienced this kind of problem, please let me know how you solved it.

1

There are 1 answers

4
jignesh Vadadoriya On BEST ANSWER

You need to cast your archiveArray to Array<Array<Any>> then you can get the array of array with contains Any type value.

For your Solution is

if let archiveData = dbRecord.value(forKey: "dataForAppArray") {
            if let archiveArray = NSKeyedUnarchiver.unarchiveObject(with: archiveData as! Data) as? Array<Array<Any>> {
                firstArray = archiveArray[0] as! [Int]
                secondArray = archiveArray[1] as! [CGFloat]
            }

}

Let's take an example for batter understanding

var firstArray = [Int](), secondArray = [CGFloat]()
firstArray.append(1)
firstArray.append(1)
firstArray.append(1)

secondArray.append(1.1)
secondArray.append(1.2)
secondArray.append(1.3)

print(firstArray) //[1, 1, 1]
print(secondArray) //[1.1, 1.2, 1.3]

let masterArray = [firstArray, secondArray] as [Any] //[[1, 1, 1], [1.1, 1.2, 1.3]]
let dataForApp:NSData = NSKeyedArchiver.archivedData(withRootObject: masterArray) as NSData

Now unarchiveObject which return Any then print output so can differentiate both output.

let archiveArray1 = NSKeyedUnarchiver.unarchiveObject(with: dataForApp as Data)
print(archiveArray1!)

output will be

(
        (
        1,
        1,
        1
    ),
        (
        "1.1",
        "1.2",
        "1.3"
    )
)

Now cast array to Array<Array<Any>>

if let archiveArray = NSKeyedUnarchiver.unarchiveObject(with: dataForApp as Data) as? Array<Array<Any>> {
            print(archiveArray)
}

Output will be

[[1, 1, 1], [1.1, 1.2, 1.3]]

Hope you understand my point.