locked
Objective sharpie generation from dynamic swift library (.framework) causes NSUnknownKeyException RRS feed

  • Question

  • User29920 posted

    Hi, I'm trying to generate a xamarin.iOS Binding Library which exposes a dynamic swift library (.framework). I did follow the tutorial using _objective sharpie tool _and the c# wrapper is well generated. The SDK is working fine, but I have some _callbacks _in my application which are well called, but some of them are passing me references *to complex objects who inherit from NSObject. I know the objects composition (it's a swift structure) so I should be able to access my objects properties using > obj.ValueForKey("propertyName"), but every time this raises a *NSUnknownKeyException: this class is not key value coding-compliant for the key propertyName.

    For exemple here i'm interested into TripPoint object, this is the native library definition :

    @class TripPoint;
    @class CLLocation;
    
    SWIFT_PROTOCOL("_TtP20DriveKitTripAnalysis12TripListener_")
    @protocol TripListener
    - (void)tripStartedWithStartMode:(enum StartMode)startMode;
    - (void)tripPointWithTripPoint:(TripPoint * _Nonnull)tripPoint;
    - (void)tripFinishedWithPost:(PostGeneric * _Nonnull)post response:(PostGenericResponse * _Nonnull)response;
    - (void)tripCancelledWithCancelTrip:(enum CancelTrip)cancelTrip;
    - (void)tripSavedForRepost;
    - (void)beaconDetected;
    - (void)significantLocationChangeDetectedWithLocation:(CLLocation * _Nonnull)location;
    - (void)sdkStateChangedWithState:(enum State)state;
    @end
    
    
    SWIFT_CLASS("_TtC20DriveKitTripAnalysis9TripPoint")
    @interface TripPoint : NSObject
    - (nonnull instancetype)init SWIFT_UNAVAILABLE;
    + (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable");
    @end
    

    The website documentation references this object as :

    public struct TripPoint {
        let latitude: Double
        let longitude: Double
        let speed: Double
        let accuracy: Double
        let elevation: Double
        let distance: Double
        let heading: Double
        let duration: Double
    }
    

    So sharpie is generating the entity in ApiDefinitions.cs as : // @interface TripPoint : NSObject [BaseType (typeof(NSObject), Name = "_TtC20DriveKitTripAnalysis9TripPoint")] [DisableDefaultCtor] interface TripPoint { }

    and the output object looks like :

    public unsafe partial class TripPoint : NSObject {
    
            [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
            static readonly IntPtr class_ptr = Class.GetHandle ("_TtC20DriveKitTripAnalysis9TripPoint");
    
            public override IntPtr ClassHandle { get { return class_ptr; } }
    
            [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
            [EditorBrowsable (EditorBrowsableState.Advanced)]
            protected TripPoint (NSObjectFlag t) : base (t)
            {
                IsDirectBinding = GetType ().Assembly == global::ApiDefinitions.Messaging.this_assembly;
            }
    
            [BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
            [EditorBrowsable (EditorBrowsableState.Advanced)]
            protected internal TripPoint (IntPtr handle) : base (handle)
            {
                IsDirectBinding = GetType ().Assembly == global::ApiDefinitions.Messaging.this_assembly;
            }
    
        } /* class TripPoint */
    

    Everything looks fine to me and my guess is that I should be accessing TripPoint properties like this : > tripPoint.ValueForKey((NSString)"speed"), but I have NSUnknownKeyExceptionevery time. So I must be missing something.

    Maybe you would have a guess ?

    Thanks

    Friday, March 20, 2020 4:06 PM

All replies

  • User35201 posted

    I know the objects composition (it's a swift structure) so I should be able to access my objects properties using > obj.ValueForKey("propertyName"), but every time this raises a **NSUnknownKeyException: this class is not key value coding-compliant for the key propertyName. I don't believe this is a safe assumption. The definition for TripPoint:

    SWIFT_CLASS("_TtC20DriveKitTripAnalysis9TripPoint")
    @interface TripPoint : NSObject
    - (nonnull instancetype)init SWIFT_UNAVAILABLE;
    + (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable");
    @end
    

    Does not list any selector for them, and I don't believe arbitrary swift struct properties are available via ValueForKey.

    Unless you have an objective-c sample showing that this works, I don't think this works that way. The NSUnknownKeyException you see is not coming from us but the native object, so I believe you would get the same behavior from an objective-c app.

    Friday, March 20, 2020 5:31 PM
  • User29920 posted

    Hi Chris, thank you for your answer. As you suggested, I implemented the native iOS SDK into a native Swift application using Pod dependency tool.

    I jumped to the definition of this specific class to get this :

    `@objc public class TripPoint : NSObject {

    public let latitude: Double
    
    public let longitude: Double
    
    public let speed: Double
    
    public let accuracy: Double
    
    public let elevation: Double
    
    public let distance: Double
    
    public let heading: Double
    
    public let duration: Double
    
    public init(latitude: Double, longitude: Double, speed: Double, accuracy: Double, elevation: Double, distance: Double, heading: Double, duration: Double)
    

    }`

    And with that I can access my TripPoint properties into my AppDelegate callback : func tripPoint(tripPoint: TripPoint) { let speed = tripPoint.speed }

    It feels like I have a shortened class definition into my .framework header (the one that is used by objective sharpie to generate the C# wrapper), but the whole definition is located into the compiled swift library files located into .swiftmodule folder, and it can be understood/ compiled from a native application. Beside, the SDK is working well, all the methods are called without any error, I just miss this final piece.

    Thanks

    Thursday, March 26, 2020 9:44 AM
  • User29920 posted

    I tried to update the ApiDefinitions.cs like this :

    [BaseType(typeof(NSObject), Name = "_TtC20DriveKitTripAnalysis9TripPoint")] [DisableDefaultCtor] interface TripPoint { // @property (readonly) double speed; //[BindAs(typeof(NSNumber))] [Export("speed")] NSNumber Speed { [Bind("speed")]get; } }

    But I still got

    Foundation.MonoTouchException: Objective-C exception thrown. Name: NSInvalidArgumentException Reason: -[DriveKitTripAnalysis.TripPoint speed]: unrecognized selector sent to instance

    Thursday, March 26, 2020 9:53 AM
  • User35201 posted

    I believe you may need to use the @objc attribute to expose those properties to the objective c runtime.

    Thursday, March 26, 2020 1:46 PM
  • User29920 posted

    Hi Chris, just to let you know, I resolved my issue by creating my own native iOS swift framework, which is serving as a proxy to expose everything I need from the original framework (the one I don't access the source code). Just as you stated, @objc annotation is used to expose methods/properties/classes to the objectiveC runtime, and it indicates to objective sharpie to generate the corresponding c# instructions.

    Thank you for your assistance.

    Wednesday, April 1, 2020 1:35 PM