NSDictionary passed through Swift loses access to category methods

I'm having an issue with an NSDictionary that is passing through Swift code
and back to Objective-C losing access to a method implemented by a category
on NSDictionary. There is clearly some subtlety about bridged dictionaries
that I'm missing, and I'd appreciate any clarification that the list can
provide.

Specifically: I have a Swift 3 application that uses some legacy
Objective-C classes. One of these classes relies on a category on
NSDictionary that implements a method called `-boolValueForKey:`.

In the main application, the Swift code calls a method on a remote object
proxy that returns an NSDictionary, which get bridged back to a Swift
dictionary (`[AnyHashable: Any]`). This bridged dictionary is passed to a
method on the legacy Objective-C class that calls the `-boolValueForKey:`
method from the category. At this point, a runtime exception occurs that
says the dictionary object (which at this point is a
`_SwiftDeferredNSDictionary`, according to the debugger) doesn’t recognize
the selector `-boolValueForKey:`.

I can work around the issue in my code by modifying the legacy Objective-C
class and inlining the implementation of `-boolValueForKey:` — but is there
a better general approach?

Thanks — Russell

I don't think this is the good place for your question. Maybe stackoverflow
or apple developer forum is more suitable.

Did you added the header filename of the category to your bridge file?
Also, is there the method name in the header file as well? You know,
sometimes people wouldn't include the method name in the header but
implementing it directly in the .m file.

Zhaoxin

···

On Wed, May 24, 2017 at 4:10 AM, Russell Finn via swift-users < swift-users@swift.org> wrote:

I'm having an issue with an NSDictionary that is passing through Swift
code and back to Objective-C losing access to a method implemented by a
category on NSDictionary. There is clearly some subtlety about bridged
dictionaries that I'm missing, and I'd appreciate any clarification that
the list can provide.

Specifically: I have a Swift 3 application that uses some legacy
Objective-C classes. One of these classes relies on a category on
NSDictionary that implements a method called `-boolValueForKey:`.

In the main application, the Swift code calls a method on a remote object
proxy that returns an NSDictionary, which get bridged back to a Swift
dictionary (`[AnyHashable: Any]`). This bridged dictionary is passed to a
method on the legacy Objective-C class that calls the `-boolValueForKey:`
method from the category. At this point, a runtime exception occurs that
says the dictionary object (which at this point is a
`_SwiftDeferredNSDictionary`, according to the debugger) doesn’t recognize
the selector `-boolValueForKey:`.

I can work around the issue in my code by modifying the legacy Objective-C
class and inlining the implementation of `-boolValueForKey:` — but is there
a better general approach?

Thanks — Russell

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

This should work. _SwiftDeferredNSDictionary is a subclass of NSDictionary and should inherit all of NSDictionary's categories.

What is the exact exception you get?

···

On May 23, 2017, at 1:10 PM, Russell Finn via swift-users <swift-users@swift.org> wrote:

I'm having an issue with an NSDictionary that is passing through Swift code and back to Objective-C losing access to a method implemented by a category on NSDictionary. There is clearly some subtlety about bridged dictionaries that I'm missing, and I'd appreciate any clarification that the list can provide.

Specifically: I have a Swift 3 application that uses some legacy Objective-C classes. One of these classes relies on a category on NSDictionary that implements a method called `-boolValueForKey:`.

In the main application, the Swift code calls a method on a remote object proxy that returns an NSDictionary, which get bridged back to a Swift dictionary (`[AnyHashable: Any]`). This bridged dictionary is passed to a method on the legacy Objective-C class that calls the `-boolValueForKey:` method from the category. At this point, a runtime exception occurs that says the dictionary object (which at this point is a `_SwiftDeferredNSDictionary`, according to the debugger) doesn’t recognize the selector `-boolValueForKey:`.

I can work around the issue in my code by modifying the legacy Objective-C class and inlining the implementation of `-boolValueForKey:` — but is there a better general approach?

--
Greg Parker gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler

- Why Swift continues to not support Float80 math functions?
Example:

print("\( log( Float( 1.0 ) ) )")
print("\( log( Double( 1.0 ) ) )")
// print("\( log( Float80( 1.0 ) ) )") // don’t compile and logl is unavailable

- Why min and max continue to not act like fmin and fmax (IEEE 754 standard)?
Example:

print("\( min( Double.nan,0.0 ) )") // print nan (!)
print("\( min( 0.0,Double.nan ) )") // print 0.0
print("\( max( Double.nan,0.0 ) )") // print nan (!)
print("\( max( 0.0,Double.nan ) )") // print 0.0
print("\n")

print("\( fmin( Double.nan,0.0 ) )") // print 0.0
print("\( fmin( 0.0,Double.nan ) )") // print 0.0
print("\( fmax( Double.nan,0.0 ) )") // print 0.0
print("\( fmax( 0.0,Double.nan ) )") // print 0.0

Greg -- Thanks for your interest and sorry for not getting back to you
sooner. When faced with a deadline last week, I simply modified the legacy
class to run the implementation of the category method `-boolValueForKey:`,
as I suggested.

Now that I have a brief opportunity, I tried to reproduce the failure in a
test project, and naturally I couldn't. (I did a dummy implementation of
the remote object proxy that created an NSDictionary and passed it to a
handler block. This mimics the original code, where the dictionary is being
returned as the result of an XPC call to another process.)

If I get a chance to resurrect the original implementation so I can record
the exact runtime exception message, I will. Right now I'm busy preparing
to go to WWDC for the first time in a decade. (Will you be available, by
any chance?)

Thanks again -- Russell

···

On Wed, May 24, 2017 at 6:03 PM, Greg Parker <gparker@apple.com> wrote:

On May 23, 2017, at 1:10 PM, Russell Finn via swift-users < > swift-users@swift.org> wrote:

I'm having an issue with an NSDictionary that is passing through Swift
code and back to Objective-C losing access to a method implemented by a
category on NSDictionary. There is clearly some subtlety about bridged
dictionaries that I'm missing, and I'd appreciate any clarification that
the list can provide.

Specifically: I have a Swift 3 application that uses some legacy
Objective-C classes. One of these classes relies on a category on
NSDictionary that implements a method called `-boolValueForKey:`.

In the main application, the Swift code calls a method on a remote object
proxy that returns an NSDictionary, which get bridged back to a Swift
dictionary (`[AnyHashable: Any]`). This bridged dictionary is passed to a
method on the legacy Objective-C class that calls the `-boolValueForKey:`
method from the category. At this point, a runtime exception occurs that
says the dictionary object (which at this point is a
`_SwiftDeferredNSDictionary`, according to the debugger) doesn’t recognize
the selector `-boolValueForKey:`.

I can work around the issue in my code by modifying the legacy Objective-C
class and inlining the implementation of `-boolValueForKey:` — but is there
a better general approach?

This should work. _SwiftDeferredNSDictionary is a subclass of NSDictionary
and should inherit all of NSDictionary's categories.

What is the exact exception you get?

--
Greg Parker gparker@apple.com Runtime Wrangler

- Why Swift continues to not support Float80 math functions?
Example:

print("\( log( Float( 1.0 ) ) )")
print("\( log( Double( 1.0 ) ) )")
// print("\( log( Float80( 1.0 ) ) )") // don’t compile and logl is unavailable

In this case, it's because 'log' only is defined for Float and Double:

  log(Double) -> Double
  log(x: Double) -> Double
  log(x: Float) -> Float

The problem is that Float80 types are represented in C as long double, and currently the importer doesn't import things with long doubles. As a result, logl doesn't get imported which is why it's not seen:

     #include <math.h>

     double
     log(double x);

     long double
     logl(long double x);

The ones that do have Float80 support are done by hand, e.g.:

The open bug is here, if you want to follow it:

- Why min and max continue to not act like fmin and fmax (IEEE 754 standard)?
Example:

print("\( min( Double.nan,0.0 ) )") // print nan (!)
print("\( min( 0.0,Double.nan ) )") // print 0.0
print("\( max( Double.nan,0.0 ) )") // print nan (!)
print("\( max( 0.0,Double.nan ) )") // print 0.0
print("\n")

print("\( fmin( Double.nan,0.0 ) )") // print 0.0
print("\( fmin( 0.0,Double.nan ) )") // print 0.0
print("\( fmax( Double.nan,0.0 ) )") // print 0.0
print("\( fmax( 0.0,Double.nan ) )") // print 0.0

There is another open bug here:

Can you add your observations to that?

Thanks,

Alex

···

On 26 May 2017, at 11:01, Antonino Ficarra via swift-users <swift-users@swift.org> wrote: