Exception thrown in Xamarin.Mac's NSKeyedUnarchiver constructor binding

UPDATE: Here's a link for the test file I'm using: https://www.dropbox.com/s/y8717mxpdbhr2kh/miniform.nib?dl=0

I'm using Xamarin.Mac to port a small fragment of Objective-C code to C#. The purpose of the code is to display a text representation of a typed stream object.

The Objective-C code works great:

NSKeyedUnarchiver *typedStreamUnarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:[NSData dataWithContentsOfFile:@"/Temp/miniform.nib"]];                
id object = [typedStreamUnarchiver decodeObject];
NSLog(@"Decoded object: %@", object);

But the C# version throws an exception [NSLog() is a simple helper function that calls Foundation's NSLog]:

        using (var d = NSData.FromFile("/Temp/miniform.nib"))
        {
            // Exception thrown on following line
            using (var typedStreamUnarchiver = new NSKeyedUnarchiver(d))
            {
                using (var obj = typedStreamUnarchiver.DecodeObject())
                {
                    NSLog($"Decoded object: {obj}");
                }
            }
        }

The exception is as follows:

Could not initialize an instance of the type 'Foundation.NSKeyedUnarchiver': the native 'initForReadingWithData:' method returned nil.
It is possible to ignore this condition by setting ObjCRuntime.Class.ThrowOnInitFailure to false.

Debugging shows that NSData d is correctly initialized, so no issue there.

Can anyone explain this difference in behavior and suggest a potential workaround?

1 answer

  • answered 2018-07-11 02:44 SushiHangover

    NSKeyedUnarchiver.UnarchiveFile:

    var path = Path.Combine(Path.GetTempPath(), "so.archive");
    
    var number = new NSNumber(42);
    NSKeyedArchiver.ArchiveRootObjectToFile(number, path);
    
    var foo = NSKeyedUnarchiver.UnarchiveFile(path);
    Console.WriteLine(foo);
    

    Update:

    UnarchiveFile() appears to be deprecated

    The ObjC example you show is also deprecated: initForReadingWithData:

    If you actually mean unarchiveTopLevelObjectWithData:error: (Swift's init(forReadingFrom:)) which is not deprecated, then use:

    var data = NSData.FromFile(path);
    var foo = NSKeyedUnarchiver.UnarchiveTopLevelObject(data, out var error);
    if (error == null)
    {
        Console.WriteLine(foo);
    }
    

    If you mean initForReadingFromData:error: then there is no current translation in Xamarin.Mac as that implementation does not exist currently. Since is just ObjC messaging, you can manually define and call it...