Re: Pasteboards and NSSecureCoding

2017-12-20 Thread Jeremy Hughes
Thanks for your detailed comments!

Something I’ve realised through this is that you need to be really careful 
about upgrading your system on a development machine. Going from 10.12 to 10.13 
- and finding that pasteboard functions compile just fine but no longer work - 
is almost as big a jump as going from Swift 3 to Swift 4.

I don’t think there's a non-hacky way to upgrade to a new system while 
continuing to use the previous SDK, but there should be! What I’d like is 
something similar to Swift transitions: Xcode could issue warnings and make 
similar helpful suggestions.

Jeremy

--

> On 20 Dec 2017, at 05:03, Quincey Morris 
>  wrote:
> 
> On Dec 19, 2017, at 18:32 , Jeremy Hughes  wrote:
>> 
>>> On 20 Dec 2017, at 02:22, Jeremy Hughes  wrote:
>>> 
>>> What I don’t like about [NSArray.self] is that it’s an artefact of 
>>> bridging. I’m not actually using it in the encoder:
>>> 
>>> coder.encode(arrayOfInts, forKey: kArrayKey)
>> 
>> The declaration:
>> 
>> encode(_ object: Any?, forKey key: String)
>> 
>> seems to indicate that it encodes any object
> 
> Yeah, you’re right, it’s secure on the decoding side only. 
> 
> This “encode” method is @objc, as I think you already noted, which AFAIK 
> means that if the value is an array, it’s going to be automatically bridged 
> to a NSArray. I would also expect it to become NSArray*, but I 
> can’t remember the rules, so I won’t go out on that limb. I also haven’t 
> looked at secure decoding recently, so I don’t know why or if the absence of 
> the element type matters when you’re securely decoding. It used to.
> 
> Note that the first parameter (“Any?”) doesn’t have to be an object in Swift, 
> although an object reference is required for the Obj-C method underneath. If 
> it’s a non-object in Swift, it’s actually passed as an opaque object that 
> wraps the Swift value.
> 
> After a while, you start to feel you need a ouija board to figure this stuff 
> out. As an alternative, if you are in control of both encoding and decoding, 
> and don’t need Obj-C compatibility inside the archive, you might do better to 
> use encode/decodeEncodable instead of encode/decodeObject. That takes type 
> bridging out of the picture, and trill preserves Swift types.
> 
> The last piece of this is that you should use one of the “decodeTopLevel…” 
> methods to decode the root object of your archive, for example 
> “decodeTopLevelDecodable(_:forKey:)”. This enables the relatively new — only 
> 5 years old! — failable decoding mechanism, where an error is thrown at the 
> top level if any of the decoding fails anywhere in the archive, 
> distinguishing failure from an init?(coder:) method that merely returns nil 
> to signify an optional value that isn’t present. (You use “failWithError” to 
> supply an error if you need to fail the decoding.)
> 
> Putting all that together, you can use NSKeyedArchiver/Unarchiver to encode 
> and decode more or less completely in the Swift domain (Codable), with proper 
> error handling and no obscure messing around with the types.

___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com


Re: Pasteboards and NSSecureCoding

2017-12-19 Thread Quincey Morris
On Dec 19, 2017, at 18:32 , Jeremy Hughes  wrote:
> 
>> On 20 Dec 2017, at 02:22, Jeremy Hughes > > wrote:
>> 
>> What I don’t like about [NSArray.self] is that it’s an artefact of bridging. 
>> I’m not actually using it in the encoder:
>> 
>> coder.encode(arrayOfInts, forKey: kArrayKey)
> 
> The declaration:
> 
> encode(_ object: Any?, forKey key: String)
> 
> seems to indicate that it encodes any object

Yeah, you’re right, it’s secure on the decoding side only. 

This “encode” method is @objc, as I think you already noted, which AFAIK means 
that if the value is an array, it’s going to be automatically bridged to a 
NSArray. I would also expect it to become NSArray*, but I can’t 
remember the rules, so I won’t go out on that limb. I also haven’t looked at 
secure decoding recently, so I don’t know why or if the absence of the element 
type matters when you’re securely decoding. It used to.

Note that the first parameter (“Any?”) doesn’t have to be an object in Swift, 
although an object reference is required for the Obj-C method underneath. If 
it’s a non-object in Swift, it’s actually passed as an opaque object that wraps 
the Swift value.

After a while, you start to feel you need a ouija board to figure this stuff 
out. As an alternative, if you are in control of both encoding and decoding, 
and don’t need Obj-C compatibility inside the archive, you might do better to 
use encode/decodeEncodable instead of encode/decodeObject. That takes type 
bridging out of the picture, and trill preserves Swift types.

The last piece of this is that you should use one of the “decodeTopLevel…” 
methods to decode the root object of your archive, for example 
“decodeTopLevelDecodable(_:forKey:)”. This enables the relatively new — only 5 
years old! — failable decoding mechanism, where an error is thrown at the top 
level if any of the decoding fails anywhere in the archive, distinguishing 
failure from an init?(coder:) method that merely returns nil to signify an 
optional value that isn’t present. (You use “failWithError” to supply an error 
if you need to fail the decoding.)

Putting all that together, you can use NSKeyedArchiver/Unarchiver to encode and 
decode more or less completely in the Swift domain (Codable), with proper error 
handling and no obscure messing around with the types.
___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com


Re: Pasteboards and NSSecureCoding

2017-12-19 Thread Jeremy Hughes
> On 20 Dec 2017, at 02:22, Jeremy Hughes  wrote:
> 
> What I don’t like about [NSArray.self] is that it’s an artefact of bridging. 
> I’m not actually using it in the encoder:
> 
> coder.encode(arrayOfInts, forKey: kArrayKey)

The declaration:

encode(_ object: Any?, forKey key: String)

seems to indicate that it encodes any object

But maybe it’s safe to assume that NSCoder will always encode/decode arrays as 
NSArrays, until it gets replaced with a Swift “Coder” class.

Jeremy

___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com


Re: Pasteboards and NSSecureCoding

2017-12-19 Thread Jeremy Hughes
> On 20 Dec 2017, at 02:07, Quincey Morris 
>  wrote:
> 
> The class must be a kind of AnyClass, so you can’t specify a struct type. 
> Sorry I sent you off in the wrong direction on that.

That’s what I just concluded in an email I started writing.

>> The code I mentioned in my follow-up email seems to work:
>> 
>> let array = decoder.decodeObject(of: [NSArray.self], forKey: kArrayKey) as! 
>> [Int]
> 
> That will compile, but might not work. If you’re doing *secure* decoding then 
> the array of types must contain NSArray *and* the type of the elements in the 
> array. 
> 
> However, if you’re not doing secure decoding (and I don’t think you’re 
> required to, even if secure encoding was used to create the archive), then 
> [NSArray.self] should work.

It compiles and it seems to work. But, as I understand it, NSSecureCoding 
refers to decoding rather than encoding (there aren’t any special encoding 
functions) so I think I am doing secure decoding. I get an exception if I use 
an insecure decoding method, such as:

let array = decoder.decodeObject(forKey: kArrayKey) as! [Int]

What I don’t like about [NSArray.self] is that it’s an artefact of bridging. 
I’m not actually using it in the encoder:

coder.encode(arrayOfInts, forKey: kArrayKey)

Jeremy


___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com


Re: Pasteboards and NSSecureCoding

2017-12-19 Thread Quincey Morris

> On Dec 19, 2017, at 17:38 , Jeremy Hughes  wrote:
> 
> let array = decoder.decodeObject(of: [[Int].self], forKey: kArrayKey) as! 
> [Int]
> 
> gives an error:
> 
> Cannot invoke 'decodeObject' with an argument list of type '(of: 
> [[Int].Type], forKey: String)’

The class must be a kind of AnyClass, so you can’t specify a struct type. Sorry 
I sent you off in the wrong direction on that.

> The code I mentioned in my follow-up email seems to work:
> 
> let array = decoder.decodeObject(of: [NSArray.self], forKey: kArrayKey) as! 
> [Int]

That will compile, but might not work. If you’re doing *secure* decoding then 
the array of types must contain NSArray *and* the type of the elements in the 
array. 

However, if you’re not doing secure decoding (and I don’t think you’re required 
to, even if secure encoding was used to create the archive), then 
[NSArray.self] should work.
___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com


Re: Pasteboards and NSSecureCoding

2017-12-19 Thread Jeremy Hughes
> On 20 Dec 2017, at 01:25, Quincey Morris 
>  wrote:
> 
> You’ll have to figure out what type to use for the Ints. If they were 
> actually saved compatibly with Obj-C, the Ints will actually be NSNumbers, 
> and you’ll need to say “NSNumber.self”. If the Ints are stored opaquely as 
> Ints, I guess it would be Int.self. You should be able to figure out the 
> right type by trial and error, I’d say.
> 
> (Actually, if the array itself was saved opaquely, then you’ll need something 
> like [[Int].self], I guess. This is an area subject to automatic bridging, so 
> what you get depends on the exact code used to encode the archive.)

The array is saved as an array of Ints, not NSNumbers

coder.encode(arrayOfInts, forKey: kArrayKey)

Jeremy

___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com


Re: Pasteboards and NSSecureCoding

2017-12-19 Thread Jeremy Hughes
Thanks, Quincey.

> That looks like Swift 3 code. The current version of the function is like 
> this:
> 
>> @nonobjc func decodeObject(of classes: [AnyClass]?, forKey key: String) -> 
>> Any?

@nonobjc means this is Swift-specfic then?

The init function I’m using is marked as @objc:

@objc required init(coder decoder: NSCoder)
{
}

> On 20 Dec 2017, at 01:25, Quincey Morris 
>  wrote:
> 
> so you’d specify [NSArray.self, .self] for the first parameter. 
> (Note, it’s a Swift-specific wrapper function, not just a direct API 
> translation.)
> 
> You’ll have to figure out what type to use for the Ints. If they were 
> actually saved compatibly with Obj-C, the Ints will actually be NSNumbers, 
> and you’ll need to say “NSNumber.self”. If the Ints are stored opaquely as 
> Ints, I guess it would be Int.self. You should be able to figure out the 
> right type by trial and error, I’d say.
> 
> (Actually, if the array itself was saved opaquely, then you’ll need something 
> like [[Int].self], I guess. This is an area subject to automatic bridging, so 
> what you get depends on the exact code used to encode the archive.)

let array = decoder.decodeObject(of: [[Int].self], forKey: kArrayKey) as! [Int]

gives an error:

Cannot invoke 'decodeObject' with an argument list of type '(of: [[Int].Type], 
forKey: String)’

The code I mentioned in my follow-up email seems to work:

let array = decoder.decodeObject(of: [NSArray.self], forKey: kArrayKey) as! 
[Int]

Presumably as! would throw an exception if it’s not an array of Ints, so that 
does get checked (and using as? with a failable initializer would presumably be 
better).

If I replace [NSArray.self] with [Array.self] I get an error:

Ambiguous reference to member 'decodeObject(of:forKey:)’

I’ve also tried [Array.self], which gives a different error:

Cannot invoke 'decodeObject' with an argument list of type '(of: 
[Array.Type], forKey: String)’

Jeremy



___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com


Re: Pasteboards and NSSecureCoding

2017-12-19 Thread Jeremy Hughes
It seems that I can write:

let array = decoder.decodeObject(of: [NSArray.self], forKey: kArrayKey) as! 
[Int]

But shouldn't I be able to specify that this is an array of Ints, and not just 
an array of anything?

I should probably avoid force unwrapping - this ought to be a failable 
initializer, right?

Jeremy

--


> On 20 Dec 2017, at 01:09, Jeremy Hughes  wrote:
> 
> The release notes for 10.13 say:
> 
> If your application is linked on macOS 10.13 SDK or later, classes that 
> return NSPasteboardReadingAsKeyedArchive from 
> readingOptionsForType:pasteboard: must support secure coding, and will be 
> decoded with a coder that requires secure coding.
> 
> So I’m updating some of my classes to support NSSecureCoding, and I’m having 
> trouble figuring out how I should decode an array of Ints.
> 
> Previously, I had:
> 
> let array = decoder.decodeObject(forKey: kArrayKey) as! [Int]
> 
> I think that I need to replace that with something like:
> 
> let array = decoder.decodeObject(of: allowedClasses, forKey: kArrayKey) as! 
> [Int]
> 
> but I can’t work out how to define allowedClasses.
> 
> In a previous discussion, Quincey Morris wrote:
> 
>> The solution is to fall back to an explicit NSSet object:
>> 
>>  let classes = NSSet (objects: NSArray.self, MyElementClass.self)
>>  let myArray = coder.decodeObjectOfClasses (classes, forKey: “myArray”)
> 
> So I’ve tried:
> 
> let allowedClasses = NSSet(objects: NSArray.self, Int.self)
> 
> But I get an error: Cannot invoke 'decodeObject' with an argument list of 
> type '(of: NSSet, forKey: String)’
> 
> I also think there should be a way of doing this that uses Array and Set 
> rather than NSArray and NSSet
> 
> Jeremy
> 
> 

___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com


Re: Pasteboards and NSSecureCoding

2017-12-19 Thread Quincey Morris
On Dec 19, 2017, at 17:09 , Jeremy Hughes  wrote:
> 
> In a previous discussion, Quincey Morris wrote:
> 
>> The solution is to fall back to an explicit NSSet object:
>> 
>>  let classes = NSSet (objects: NSArray.self, MyElementClass.self)
>>  let myArray = coder.decodeObjectOfClasses (classes, forKey: “myArray”)
> 
> So I’ve tried:
> 
> let allowedClasses = NSSet(objects: NSArray.self, Int.self)

That looks like Swift 3 code. The current version of the function is like this:

> @nonobjc func decodeObject(of classes: [AnyClass]?, forKey key: String) -> 
> Any?


so you’d specify [NSArray.self, .self] for the first parameter. 
(Note, it’s a Swift-specific wrapper function, not just a direct API 
translation.)

You’ll have to figure out what type to use for the Ints. If they were actually 
saved compatibly with Obj-C, the Ints will actually be NSNumbers, and you’ll 
need to say “NSNumber.self”. If the Ints are stored opaquely as Ints, I guess 
it would be Int.self. You should be able to figure out the right type by trial 
and error, I’d say.

(Actually, if the array itself was saved opaquely, then you’ll need something 
like [[Int].self], I guess. This is an area subject to automatic bridging, so 
what you get depends on the exact code used to encode the archive.)

___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com


Pasteboards and NSSecureCoding

2017-12-19 Thread Jeremy Hughes
The release notes for 10.13 say:

If your application is linked on macOS 10.13 SDK or later, classes that return 
NSPasteboardReadingAsKeyedArchive from readingOptionsForType:pasteboard: must 
support secure coding, and will be decoded with a coder that requires secure 
coding.

So I’m updating some of my classes to support NSSecureCoding, and I’m having 
trouble figuring out how I should decode an array of Ints.

Previously, I had:

let array = decoder.decodeObject(forKey: kArrayKey) as! [Int]

I think that I need to replace that with something like:

let array = decoder.decodeObject(of: allowedClasses, forKey: kArrayKey) as! 
[Int]

but I can’t work out how to define allowedClasses.

In a previous discussion, Quincey Morris wrote:

> The solution is to fall back to an explicit NSSet object:
> 
>   let classes = NSSet (objects: NSArray.self, MyElementClass.self)
>   let myArray = coder.decodeObjectOfClasses (classes, forKey: “myArray”)

So I’ve tried:

let allowedClasses = NSSet(objects: NSArray.self, Int.self)

But I get an error: Cannot invoke 'decodeObject' with an argument list of type 
'(of: NSSet, forKey: String)’

I also think there should be a way of doing this that uses Array and Set rather 
than NSArray and NSSet

Jeremy


___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com