Search code examples
iosxamarinvisual-studio-2015mvvmcross

Resolve "'NSUnknownKeyException', reason:This class is not key value coding-compliant for the key X” exception


I am experience the exact same issue as identified in this submission: MvvmCross/Xamarin "This class is not key value coding-compliant for the key"

The issue stems from trying to use MVVMCross databinding in an IOS CollectionView

That article was closed and marked as a duplicate but I don't see it as such. The article that it links to as the solution offers workarounds when using XCode as a development environment. My scenario and the scenario described in the issue linked above are in using Visual Studio on Windows with Xamarin to build with. With Visual Studio, the XIB editor and wiring up outlets is done differently.

I have been trying to get past this problem for a few days now and haven't figured anything out. I'm hoping that someone who has seen this in Windows Visual Studio has a solution that they can share.

(I apologize, I am new so I couldn't do this as a comment in the original post).

The Stack trace of the error is below

Foundation.MonoTouchException: Objective-C exception thrown.  
Name: NSUnknownKeyException Reason: [<NSObject 0x7c4eb9a0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key view.

Native stack trace:

0   CoreFoundation                      0x00928494 __exceptionPreprocess + 180
1   libobjc.A.dylib                     0x08fa3e02 objc_exception_throw + 50
2   CoreFoundation                      0x009280b1 -[NSException raise] + 17
3   Foundation                          0x014477f8 -[NSObject(NSKeyValueCoding) setValue:forUndefinedKey:] + 282
4   Foundation                          0x013a1e6a _NSSetUsingKeyValueSetter + 115
5   Foundation                          0x013a1def -[NSObject(NSKeyValueCoding) setValue:forKey:] + 295
6   Foundation                          0x013d654b -[NSObject(NSKeyValueCoding) setValue:forKeyPath:] + 384
7   UIKit                               0x03bc8a62 -[UIRuntimeOutletConnection connect] + 132
8   libobjc.A.dylib                     0x08fb800c -[NSObject performSelector:] + 62
9   CoreFoundation                      0x00851131 -[NSArray makeObjectsPerformSelector:] + 273
10  UIKit                               0x03bc70fc -[UINib instantiateWithOwner:options:] + 2102
11  UIKit                               0x041205ec -[UICollectionView _dequeueReusableViewOfKind:withIdentifier:forIndexPath:viewCategory:] + 750
12  UIKit                               0x04120f35 -[UICollectionView dequeueReusableCellWithReuseIdentifier:forIndexPath:] + 194
13  ???                                 0x1b909f9c 0x0 + 462462876
14  ???                                 0x1be1b820 0x0 + 467777568
15  ???                                 0x1be1b633 0x0 + 467777075
16  ???                                 0x1be1adf6 0x0 + 467774966
17  ???                                 0x1be1b08d 0x0 + 467775629
18  CompanionForSpotifyiOS              0x001bb2a1 mono_jit_runtime_invoke + 705
19  CompanionForSpotifyiOS              0x00276fef mono_runtime_invoke + 127
20  CompanionForSpotifyiOS              0x0034bb77 xamarin_trampoline + 5559
21  UIKit                               0x0410d5ee -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:isFocused:] + 448
22  UIKit                               0x0410d2ff -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:] + 65
23  UIKit                               0x04111761 -[UICollectionView _updateVisibleCellsNow:] + 6023
24  UIKit                               0x041167df -[UICollectionView layoutSubviews] + 254
25  UIKit                               0x038543d4 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 810
26  libobjc.A.dylib                     0x08fb8059 -[NSObject performSelector:withObject:] + 70
27  QuartzCore                          0x0b5d8096 -[CALayer layoutSublayers] + 144
28  QuartzCore                          0x0b5cb8b6 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 388
29  QuartzCore                          0x0b5cb71a _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 26
30  QuartzCore                          0x0b5bdee7 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 317
31  QuartzCore                          0x0b5f2847 _ZN2CA11Transaction6commitEv + 561
32  QuartzCore                          0x0b5f3108 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 92
33  CoreFoundation                      0x0083a75e __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 30
34  CoreFoundation                      0x0083a6be __CFRunLoopDoObservers + 398

The ViewModel code is here

public partial class AlbumCell : MvxCollectionViewCell
{
    public static readonly UINib Nib = UINib.FromName("AlbumCell", NSBundle.MainBundle);
    public static readonly NSString Key = new NSString("AlbumCell");

    public AlbumCell(IntPtr handle) : base(string.Empty, handle)
    {
        this.DelayBind(() =>
        {
            var set = this.CreateBindingSet<AlbumCell, Album>();
            set.Bind(artistNameLabel).To(album => album.ArtistName);
            set.Apply();
        });
    }

    public static AlbumCell Create()
    {
        return (AlbumCell)Nib.Instantiate(null, null)[0];
    }
}

And this is the generated component of the view model that has the outlets [Register ("AlbumCell")] partial class AlbumCell { [Outlet] [GeneratedCode ("iOS Designer", "1.0")] UILabel artistNameLabel { get; set; }

    void ReleaseDesignerOutlets ()
    {
        if (artistNameLabel != null) {
            artistNameLabel.Dispose ();
            artistNameLabel = null;
        }
    }
}

Finally, this is the XIB which is very basic - it just has one label on it

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
        <dependencies>
            <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
        </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="AlbumCell">
            <connections>
                <outlet property="view" destination="2" id="RRd-Eg-VrN"/>
            </connections>
        </placeholder>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <view contentMode="scaleToFill" id="2">
            <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
            <subviews>
                <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" fixedFrame="YES" text="Label" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="5">
                    <rect key="frame" x="279" y="95" width="42" height="21"/>
                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
                    <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
                    <nil key="highlightedColor"/>
                </label>
            </subviews>
            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
        </view>
    </objects>
</document>

The code where I create instanced of the cell objects from the parent ViewController is below

public partial class NewReleasesView : MvxCollectionViewController
{
    public NewReleasesView()
        : base(new UIKit.UICollectionViewFlowLayout()
        {
            ItemSize = new CoreGraphics.CGSize(250,200),
            ScrollDirection = UICollectionViewScrollDirection.Vertical
        })
    {
    }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();

        CollectionView.RegisterNibForCell(AlbumCell.Nib, AlbumCell.Key);
        var source = new MvxCollectionViewSource(CollectionView, AlbumCell.Key);
        CollectionView.Source = source;

        var set = this.CreateBindingSet<NewReleasesView, NewReleasesViewModel>();
        set.Bind(source).To(vm => vm.NewReleases);
        set.Apply();
        CollectionView.ReloadData();

        var viewModel = this.ViewModel as IMvxViewModel;
        viewModel.Start();
    }

}

Solution

  • It looks like Visual Studio was putting the outlets in the wrong location. See below where i typed "Outlet was here". That is where the UI Label was generated. I manually removed it then went into the XIB Editor in visual studio and re-added the outlet name in the designer. At that point, it put it in it's right place where i labeled "When it should have been here".

    I'm not sure what caused the behavior in the first place, but this work around definitely did the trick. Thanks to all who assisted me.

    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="AlbumCell">
            <connections>
                 ***OUTLET WAS HERE***
            </connections>
        </placeholder>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <view contentMode="scaleToFill" id="1" customClass="AlbumCell">
            <rect key="frame" x="0.0" y="0.0" width="250" height="250"/>
            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
            <color key="backgroundColor" customColorSpace="calibratedWhite" colorSpace="calibratedWhite" white="0" alpha="1"/>
            <subviews>
                <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Label" lineBreakMode="tailTruncation" minimumFontSize="10" id="4" translatesAutoresizingMaskIntoConstraints="NO" fixedFrame="YES" customClass="UILabel">
                    <rect key="frame" x="53" y="20" width="177" height="21"/>
                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
                    <color key="textColor" colorSpace="calibratedWhite" white="1" alpha="1"/>
                    <nil key="highlightedColor"/>
                </label>
            </subviews>
            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
            <size key="freeformSize" width="600" height="600"/>
            <connections>
                ***WHEN IT SHOULD HAVE BEEN HERE***
                <outlet property="artistname" destination="4" id="name-outlet-4"/>
            </connections>
        </view>
    </objects>