NSSecureCoding crash in Core Data - objective-c

I am getting crash reports from users when fetching from my Core Data entity with a fetch request. This is what the crash looks like:
SIGABRT: Unhandled error (NSCocoaErrorDomain, 4864) occurred during
faulting and was thrown: Error Domain=NSCocoaErrorDomain Code=4864
"The data couldn’t be read because it isn’t in the correct format."
UserInfo={NSUnderlyingError=0x2822582d0 {Error
Domain=NSCocoaErrorDomain Code=4864 "value for key 'NS.objects' was of
unexpected class 'NSTextAlternatives (0x1ffa90890)
[/System/Library/PrivateFrameworks/UIFoundation.framework]'. Allowed
classes are '{( "NSTextAttachment (0x1ff3bc730)
[/System/Library/PrivateFrameworks/UIFoundation.framework]", "NSNumber
(0x1ff35d8c8) [/System/Library/Frameworks/Foundation.framework]",
"NSDictionary (0x1ff352418)
[/System/Library/Frameworks/CoreFoundation.framework]", "NSFont
(0x1ff3bc3e8)
[/System/Library/PrivateFrameworks/UIFoundation.framework]",
"NSGlyphInfo (0x1ffa906d8)
[/System/Library/PrivateFrameworks/UIFoundation.framework]", "NSArray
(0x1ff352238) [/System/Library/Frameworks/CoreFoundation.framework]",
"NSParagraphStyle (0x1ff3bbec0)
[/System/Library/PrivateFrameworks/UIFoundation.framework]", "NSUUID
(0x1ff35f290) [/System/Library/Frameworks/Foundation.framework]",
"NSDate (0x1ff3522b0)
[/System/Library/Frameworks/CoreFoundation.framework]", "NSColor
(0x1ff3a67a8)
[/System/Library/PrivateFrameworks/UIKitCore.framework]", "NSNull
(0x1ff3527b0) [/System/Library/Frameworks/CoreFoundation.framework]",
"NSData (0x1ff3519c8)
[/System/Library/Frameworks/CoreFoundation.framework]", "UIColor
(0x1ff3a6a78)
[/System/Library/PrivateFrameworks/UIKitCore.framework]",
"NSAttributedString (0x1ff359c00)
[/System/Library/Frameworks/Foundation.framework]", "NSURL
(0x1ff352e18) [/System/Library/Frameworks/CoreFoundation.framework]",
"NSSet (0x1ff352878)
[/System/Library/Frameworks/CoreFoundation.framework]", "NSValue
(0x1ff35d940) [/System/Library/Frameworks/Foundation.framework]",
"UIFont (0x1ff3bc780)
[/System/Library/PrivateFrameworks/UIFoundation.framework]", "NSString
(0x1ff35d170) [/System/Library/Frameworks/Foundation.framework]" )}'."
UserInfo={NSDebugDescription=value for key 'NS.objects' was of
unexpected class 'NSTextAlternatives (0x1ffa90890)
[/System/Library/PrivateFrameworks/UIFoundation.framework]'. Allowed
classes are '{( "NSTextAttachment (0x1ff3bc730)
[/System/Library/PrivateFrameworks/UIFoundation.framework]", "NSNumber
(0x1ff35d8c8) [/System/Library/Frameworks/Foundation.framework]",
"NSDictionary (0x1ff352418)
[/System/Library/Frameworks/CoreFoundation.framework]", "NSFont
(0x1ff3bc3e8)
[/System/Library/PrivateFrameworks/UIFoundation.framework]",
"NSGlyphInfo (0x1ffa906d8)
[/System/Library/PrivateFrameworks/UIFoundation.framework]", "NSArray
(0x1ff352238) [/System/Library/Frameworks/CoreFoundation.framework]",
"NSParagraphStyle (0x1ff3bbec0)
[/System/Library/PrivateFrameworks/UIFoundation.framework]", "NSUUID
(0x1ff35f290) [/System/Library/Frameworks/Foundation.framework]",
"NSDate (0x1ff3522b0)
[/System/Library/Frameworks/CoreFoundation.framework]", "NSColor
(0x1ff3a67a8)
[/System/Library/PrivateFrameworks/UIKitCore.framework]", "NSNull
(0x1ff3527b0) [/System/Library/Frameworks/CoreFoundation.framework]",
"NSData (0x1ff3519c8)
[/System/Library/Frameworks/CoreFoundation.framework]", "UIColor
(0x1ff3a6a78)
[/System/Library/PrivateFrameworks/UIKitCore.framework]",
"NSAttributedString (0x1ff359c00)
[/System/Library/Frameworks/Foundation.framework]", "NSURL
(0x1ff352e18) [/System/Library/Frameworks/CoreFoundation.framework]",
"NSSet (0x1ff352878)
[/System/Library/Frameworks/CoreFoundation.framework]", "NSValue
(0x1ff35d940) [/System/Library/Frameworks/Foundation.framework]",
"UIFont (0x1ff3bc780)
[/System/Library/PrivateFrameworks/UIFoundation.framework]", "NSString
(0x1ff35d170) [/System/Library/Frameworks/Foundation.framework]"
)}'.}}}CrashVersion 5.6.8 (5.6.9)1 user1 report
The line it crashes at is when NSFetchedResultsController is calling performFetch:
7 CoreData 0x00000001b48c1a94
__43-[NSFetchedResultsController performFetch:]_block_invoke + 572 8 CoreData 0x00000001b487c01c
developerSubmittedBlockToNSManagedObjectContextPerform + 152 9
CoreData 0x00000001b474eda4
-[NSManagedObjectContext performBlockAndWait:] + 204 10 CoreData 0x00000001b475cae4 -[NSFetchedResultsController
_recursivePerformBlockAndWait:withContext:] + 144 11 CoreData 0x00000001b475cc1c -[NSFetchedResultsController performFetch:] + 220
This is almost certainly happening because I recently changed a Transformable attribute which didn't have any transformer set (so essentially using NSKeyedUnarchiveFromDataTransformerName) to using a subclass of NSSecureUnarchiveFromDataTransformer. The attribute is storing an NSAttributedString that comes from a UITextView in an iOS app. And I also save the NSRange for the 'bold' and 'italics' items as an NSValue.
I added the following transformer:
#objc(AttributedStringDictionaryTransformer)
final class AttributedStringDictionaryTransformer: NSSecureUnarchiveFromDataTransformer {
override static var allowedTopLevelClasses: [AnyClass] {
return super.allowedTopLevelClasses + [NSValue.self]
}
}
This 'transformer' is used in the Transformable attributed in the Core Data model. It adds the NSValue class to the allowed list of classes.
When it says "value for key 'NS.objects' was of unexpected class 'NSTextAlternatives " ... I'm not sure what NSTextAlternatives is. I have never (explicitly) used this in my code. Any ideas where it might be coming from?
EDIT:
On further investigation, it turns out NSAttributedString can tack on all sorts of objects, like objects for NSTextAlternatives and _UITextInputDictationResultMetadata, which might be present if the user uses dictation on the UITextView. So the NSSecureUnarchiveFromDataTransformer, which technically supports NSAttributedString, doesn't actually support it properly. These classes are coming from private frameworks, so not sure how I can add them to my custom transformer. Not sure there is a work-around other than filing a bug. If there is, please let me know.

Indeed, NSAttributedString can be a bit of a grab-bag of text-system-related attributes: various operations in text views can cause the text system (CoreText, UIKit, etc.) to annotate ranges with bits of metadata to improve the iOS experience, but it does mean that any given NSAttributedString can end up with bits of baggage you might not be aware of. (Conceptually, NSAttributedString is like an NSString with an associated NSDictionary which can contain anything, if you squint just right.) Regular usage of text views can cause this, and when you factor in pasting text from other attributed string sources, too, you can end up with many attribute values you don't expect. This is the text system working as expected.
Unfortunately, this design butts heads with NSSecureCoding, which requires you to list out the types that you expect to decode from an archive before decoding it. You're correctly whitelisting NSAttributedString and NSValue objects, and NSAttributedString itself whitelists several types that it is aware of (and NSSecureUnarchiveFromDataTransformer also allows some known Foundation types), but the issue is that the string ultimately contains types that neither you nor NSAttributedString know about, and without whitelisting those, decoding will be prevented.
Since these types are indeed private, you're going to have a rough time trying to find all possible private types the text system can throw at you. The easiest way forward would be on the encoding side: before you encode an NSAttributedString, iterate over all of its attribute ranges and strip out any attributes whose types don't meet your whitelisting criteria. Using the same criteria at encode time that you use at decode time will allow you to decode successfully.

Related

How to make a class that inherits the same methods as IO::Path?

I want to build a class in Raku. Here's what I have so far:
unit class Vimwiki::File;
has Str:D $.path is required where *.IO.e;
method size {
return $.file.IO.s;
}
I'd like to get rid of the size method by simply making my class inherit the methods from IO::Path but I'm at a bit of a loss for how to accomplish this. Trying is IO::Path throws errors when I try to create a new object:
$vwf = Vimwiki::File.new(path => 't/test_file.md');
Must specify a non-empty string as a path
in block <unit> at t/01-basic.rakutest line 24
Must specify a non-empty string as a path
I always try a person's code when looking at someone's SO. Yours didn't work. (No declaration of $vwf.) That instantly alerts me that someone hasn't applied Minimal Reproducible Example principles.
So I did and less than 60 seconds later:
IO::Path.new
Yields the same error.
Why?
The doc for IO::Path.new shows its signature:
multi method new(Str:D $path, ...
So, IO::Path's new method expects a positional argument that's a Str. You (and my MRE) haven't passed a positional argument that's a Str. Thus the error message.
Of course, you've declared your own attribute $path, and have passed a named argument to set it, and that's unfortunately confused you because of the coincidence with the name path, but that's the fun of programming.
What next, take #1
Having a path attribute that duplicates IO::Path's strikes me as likely to lead to unnecessary complexity and/or bugs. So I think I'd nix that.
If all you're trying to do is wrap an additional check around the filename, then you could just write:
unit class Vimwiki::File is IO::Path;
method new ($path, |) { $path.IO.e ?? (callsame) !! die 'nope' }
callsame redispatches the ongoing routine call (the new method call), with the exact same arguments, to the next best fitting candidate(s) that would have been chosen if your new one containing the callsame hadn't been called. In this case, the next candidate(s) will be the existing new method(s) of IO::Path.
That seems fine to get started. Then you can add other attributes and methods as you see fit...
What next, take #2
...except for the IO::Path bug you filed, which means you can't initialize attributes in the normal way because IO::Path breaks the standard object construction protocol! :(
Liz shows one way to workaround this bug.
In an earlier version of this answer, I had not only showed but recommended another approach, namely delegation via handles instead of ordinary inheritance. I have since concluded that that was over-complicating things, and so removed it from this answer. And then I read your issue!
So I guess the delegation approach might still be appropriate as a workaround for a bug. So if later readers want to see it in action, follow #sdondley's link to their code. But I'm leaving it out of this (hopefully final! famous last words...) version of this answer in the hope that by the time you (later reader) read this, you just need to do something really simple like take #1.

Accord.net Codification can't handle non-strings

I am trying to use the Accord.net library to build test method of several of the machine learning algorithms that library supports.
One of the issues I have run into is that when I am trying to codify my string data, the Codification class does not seem capable of dealing with any datatable columns that are not strings, despite the documentation saying otherwise.
Codification codebook = new Codification(fulldata, AllAttributeNames);
I call that line where fulldata is a datatable, and I have tried including columns of both Int32 type and Double type, and the Codification class has thrown an error saying it is unable to convert them to type String.
"System.InvalidCastException: 'Unable to cast object of type 'System.Double' to type 'System.String'.'"
EDIT: It turns out this error is because the Codification system can only handle alternate data types if it is encoding the entire table. I suppose I can see the logic here, although I would prefer a better error, or that the method was a little smarter.
I now have another issue that has cropped up related to this. After changing my code to this:
Codification codebook = new Codification(fulldata);
I then learning.Learn(inputs, outputs) my algorithm and want to use the newly trained algorithm. So the next step would be to take a bunch of test data, make sure it matches the codebooks encoding, and send it through the algorithm. Unfortunately, when I try and use the
int[][] testinput = codebook.Transform(testData, inputColumnNameArray);
It blows up claiming it could not find a mapping to transform. It does this in reference to an Integer column that the codebook correctly did not map to new values. So now it seems this Transform method is not capable of handling non-string columns, and I have not found an overload of it that can, even though the documentation indicates it should be able to handle this.
Does anyone know how to get around this issue without manually building the entire int[][] testinput array one value at a time?
Turns out I was able to answer my own question eventually.
The Codification class has two methods of using it as near as I can tell. The constructor that takes a list of column names, as well as the Transform methods both lack intelligence in dealing with non-string data types, perhaps these methods are going away in the future.
The constructor that just takes a datatable by itself, as well as the Apply method, are both capable of handling data types other than strings. Once I switched to using these two methods my errors went away.
Codification codebook = new Codification(fulldata);
int[][] testinput = codebook.Apply(testData, inputColumnNameArray);
The confusion for me lay in all the example code seemingly randomly using these two methods, but using the Apply method only when processing the training data, and using the Transform method when encoding test data.
I am not sure why they chose to do this in the documentation example code, but it definitely took me a long time to figure out what was going on enough to stop having this particular issue.

Dart serialization error: Invalid reference

I have a wrapped Serialization class from the serialization package in my class MySerialization. In the constroctor of MySerialization, I add a bunch of rules. Consumer classes have seperate instances of the wrapper MySerialization class to (de)serialize objects.
This setup, with a seperate instance of MySerialization in consumer classes throws an error in the Reference class constructor:
Reference(this.parent, this.ruleNumber, this.objectNumber) {
if (ruleNumber == null || objectNumber == null) {
throw new SerializationException("Invalid Reference");
}
if (parent.rules.length < ruleNumber) {
throw new SerializationException("Invalid Reference"); // <---- here
}
}
thus spawnes error in the console
Breaking on exception: SerializationException(Invalid Reference)
This means a rule cannot be found which is referenced. The starnge thing howver is, that I have the same rules applied in all Serialization instances through the MySerialization wrapper.
I tried serializing with only one instance of MySerialization. This does not spawn the error. When I debug in DartEditor, I get the <optimized out> message in the debugger window.
I have CustomRule subclasses rules defined. The behavior does not change when I enable/disabled these CustomRules
What cuases the invalid reference, and how to solve & workaround this error?
Dart Editor version 1.5.3.release (STABLE)
Dart SDK version 1.5.3
It's difficult to answer without a little more detail on your setup. However, I'm going to guess that you're using the default setup in which it will automatically generate instances of BasicRule when it encounters a class that it doesn't know about, and those are added to the list of rules. Your other instance doesn't know about those, so it fails.
You can try examining (or just printing) the list of rules in your original serialization after it has written out the objects and see if this is the case.
To fix this, you would need to write rules for the other objects that are being serialized and weren't in your original list. Or you could use the "selfDescribing" option, in which case it will send the rules that were used along with the original. But that won't work if you have hard-coded custom rules which it can't serialize.

Runtime method to get names of argument variables?

Inside an Objective-C method, it is possible to get the selector of the method with the keyword _cmd. Does such a thing exist for the names of arguments?
For example, if I have a method declared as such:
- (void)methodWithAnArgument:(id)foo {
...
}
Is there some sort of construct that would allow me to get access to some sort of string-like representation of the variable name? That is, not the value of foo, but something that actually reflects the variable name "foo" in a local variable inside the method.
This information doesn't appear to be stored in NSInvocation or any of its related classes (NSMethodSignature, etc), so I'm not optimistic this can be done using Apple's frameworks or the runtime. I suspect it might be possible with some sort of compile-time macro, but I'm unfamiliar with C macros so I wouldn't know where to begin.
Edit to contain more information about what I'm actually trying to do.
I'm building a tool to help make working with third-party URL schemes easier. There are two sides to how I want my API to look:
As a consumer of a URL scheme, I can call a method like [twitterHandler showUserWithScreenName:#"someTwitterHandle"];
As a creator of an app with a URL scheme, I can define my URLs in a plist dictionary, whose key-value pairs look something like #"showUserWithScreenName": #"twitter://user?screenName={screenName}".
What I'm working on now is finding the best way to glue these together. The current fully-functioning implementation of showUserWithScreenName: looks something like this:
- (void)showUserWithScreenName:(NSString *)screenName {
[self performCommand:NSStringFromSelector(_cmd) withArguments:#{#"screenName": screenName}];
}
Where performCommand:withArguments: is a method that (besides some other logic) looks up the command key in the plist (in this case "showUserWithScreenName:") and evaluates the value as a template using the passed dictionary as the values to bind.
The problem I'm trying to solve: there are dozens of methods like this that look exactly the same, but just swap out the dictionary definition to contain the correct template params. In every case, the desired dictionary key is the name of the parameter. I'm trying to find a way to minimize my boilerplate.
In practice, I assume I'm going to accept that there will be some boilerplate needed, but I can probably make it ever-so-slightly cleaner thanks to NSDictionaryOfVariableBindings (thanks #CodaFi — I wasn't familiar with that macro!). For the sake of argument, I'm curious if it would be possible to completely metaprogram this using something like forwardInvocation:, which as far as I can tell would require some way to access parameter names.
You can use componentsSeparatedByString: with a : after you get the string from NSStringFromSelector(_cmd) and use your #selector's argument names to put the arguments in the correct order.
You can also take a look at this post, which is describing the method naming conventions in Objective C

Bizzare method signature, with unnamed arguments (obj-c)

I wasn't aware this syntax was valid.
+ (void) methodName:(TypeObject *)typeObject1:(TypeObject *)typeObject2;
Which is then called like so:
[object methodName:obj1:obj2];
I find it ugly and disturbing, but it builds.
Can someone point me at a reference which explains why this is valid.
FWIW the codebase (inherited) that this comes from, is rife with sloppy, lazy stuff, dozens of spelling errors and looks like it was formatted by someone with no need to ever read it again. (Thank you again uncrustify.)
This is a well-kown and documented feature (pdf, p. 14)
In principle, a Rectangle class could instead implement a setOrigin::
method with no label for the second parameter, which would be invoked
as follows:
[myRectangle setOrigin:30.0 :50.0]; // This is a bad example of multiple parameters
but apple discourage everbody of using parameter passing without keyword:
Use keywords before all arguments.
- (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag; -> Right.
- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag; -> Wrong.
Why it was allowed by the creators of objective-C, I dont know. Maybe it has to do with the Smalltalk heritage.