WatchKit: unable to find interface controller clas

2019-01-17 10:04发布

问题:

I tried adding an interface controller to a storyboard, setting its Custom Class to a WKInterfaceController subclass, launched the app in the simulator and navigated to the specified interface controller.

When I do so, I get the following error:

WatchKit error - unable to find interface controller class 'TestController' to instantiate

If I try to interact with the controller (e.g. try launching its button's action), I get the following error:

  • *********** ERROR -[SPRemoteInterface _interfaceControllerClientIDForControllerID:] clientIdentifier for interfaceControllerID:(null) not found
  • *********** ERROR -[SPRemoteInterface _interfaceControllerClientIDForControllerID:] clientIdentifier for interfaceControllerID:7120004 not found

I tried setting the module name as recommended on this answer, but that still gives me the following errors:

  • WatchKit error - unable to find interface controller class '_TtC29myWatchApp_WatchKit_App19TestController' to instantiate
  • *********** ERROR -[SPRemoteInterface _interfaceControllerClientIDForControllerID:] clientIdentifier for interfaceControllerID:(null) not found
  • *********** ERROR -[SPRemoteInterface _interfaceControllerClientIDForControllerID:] clientIdentifier for interfaceControllerID:6E20004 not found

回答1:

I got this error after renaming the WatchKit target, but finally realized: if you rename your WatchKit targets, you'll have to go through the interfaces in Interface Builder and make sure the module names for each of them is renamed as well.

You can do this by selecting an interface controller, clicking the Identity Inspector (or command-option-3), delete the module name, then tab away. It will automatically be filled in with the new target name. That did it for me!



回答2:

This error came up for me because I was not properly handling the didDeactivate message. My deactivated controller was still receiving messages via MMWormhole. Once I severed that connection, the error went away. It turns out that in the simulator all the deactivated interface controllers hang around in memory, so you have to carefully make sure they don't get any more messages of any kind. I don't know if this happens on the Watch itself, but of course we should assume so.



回答3:

I spent way too much time with this issue, but finally figured what it was. The Apple Watch has basically two navigation patterns:

Hierarchical:

[self pushControllerWithName:@"controllerName" context:nil];

Page-based:

[[self class] reloadRootControllersWithNames:@[@"controller1",@"controller2"] contexts:nil];

According to apple:

You cannot combine hierarchical and page-based interface styles. At design time, you must choose the style that best suits your app’s content and design for that style.

So, the issue is that I was mixing both and that leads to undefined behavior like:

*********** ERROR -[SPRemoteInterface _interfaceControllerClientIDForControllerID:] clientIdentifier for interfaceControllerID:(null) not found

Hope this helps other developers

EDIT:

Just a suggestion that worked for me as a workaround, when using Page-based navigation you can still present modal controllers (just saying):

[self presentControllerWithName:@"controllerName" context:nil];


回答4:

This bug seems to be closely related to this one: Unable to see custom classes in Interface Builder drop down.

When I originally tried solving this, I had to manually type in the module name since the drop down was empty for both the custom classes and the module names.

Examining the storyboard's source code reveals one workaround.

Interface controllers that work look like this:

<controller id="AgC-eL-Hgc" customClass="InterfaceController" 
  customModule="myWatchApp_WatchKit_App" customModuleProvider="target">

Interface controllers that don't work lack the customModule and/or the customModuleProvider attribute(s).

Therefore, a workaround is to manually add those missing attributes to the storyboard file by right-clicking it and choosing Open As > Source Code.

A longer-term solution may be to fix the storyboard so that custom classes appear in the drop downs (see linked question for some potential fixes).

Update:

Other interface controllers that also work use customModule="myWatchApp_WatchKit_Extension" (notice the _Extension vs. _App difference) and don't need the customModuleProvider attribute.



回答5:

For me, this appears to be a false negative. I tried all suggested solutions, but then proved it's an Apple error by creating a brand new Apple Kit project, making one change to the Glance (adding an image) and getting the error. I've logged the following error with Apple in their Bug Reporter.

Title

Receive clientIdentifier for interfaceControllerID not found when navigating to Glance

Description

I receive the following error which appears to be a false negative when navigating to a Glance in a watchOS 2.0 project. I reproduced this by creating a brand new Apple Kit project, making one change to the Glance (adding an image) and getting the error.

Steps to reproduce

  1. Clear all data in both watch and iOS simulators.
  2. In XCode, create a new WatchKit application including complication and glance.
  3. Run and see no issues.
  4. Add a png to your image assets.
  5. Add a UIImageView to the Glance in Interface Builder.
  6. Run the extension in the watch simulator.
  7. Enable the Glance in the iOS Watch companion app.
  8. Navigate to the Glance in the watch simulator.
  9. Note the follow error in your log.

2015-07-16 08:35:10.663 restaurant-reports WatchKit App Extension[78301:2211560] *********** ERROR -[SPRemoteInterface _interfaceControllerClientIDForControllerID:] clientIdentifier for interfaceControllerID:3118001E not found

Expected result

No false negatives should appear in log

Actual result

False negative appears in log

Watch OS version (build)

2.0 (13S5293f)

They gave me bug id 21853566.



回答6:

I think you forgot to use the storyboardID to your class. That's what i get from your error.



回答7:

I had this problem when I renamed the WatchKit target. When I renamed it back the error went away.



回答8:

The better way to do a push/pop controller is running that piece of code into the main thread so:

dispatch_async(dispatch_get_main_queue(), ^{
     [self pushControllerWithName:@"controllerIdentifier" context:data];
});

dispatch_async(dispatch_get_main_queue(), ^{
     [self popToRootController];
});

dispatch_async(dispatch_get_main_queue(), ^{
     [self popController];
});

Apple documentation says about the three methods:

Always call this method from your WatchKit extension’s main thread.



回答9:

You need to verify the view controller is in your compile source here: Select Project > Select Watchkit Extension Target > Build Phases > Compile Sources



回答10:

It's not anything you're doing wrong. This is already a known issue. See my answer here. Please dupe the following radar on Apple's Bug Reporting System to help raise the priority to get this fixed.



回答11:

I was having same issues after the recent 8.2 release. I had to go to each one of my InterfaceControllers and adjust its custom class module to whatever was shown as an option or "none".



回答12:

I had this problem when I mistakenly set root interface controller's identifier. When I remove the identifier, this warning disappeared.



回答13:

The controller’s Module name should be the same name as your Project’s root name (top left blue Xcode document icon name).

Your dynamic interface’s module name should be that same name and your destination controller’s module name (i.e. the one you're pushing to) should also be the same name.

Btw, in case you’re new or not sure I thought I’d throw this in because you might be thinking your class is wrong or something. Your Dynamic Interface Controller doesn’t have to point to the default Xcode NotificationController.swift class. Of course you can create a custom class i.e. FriendRequestNotificationController

To troubleshoot you can add this code into awakeWithContext method to see what controllers have what IDs.

override func awakeWithContext(context: AnyObject?) {
    super.awakeWithContext(context)
    if let vcID = self.valueForKey("_viewControllerID") as? NSString {
        print("Controller: \(vcID)")
    }

    // Configure interface objects here.
}


回答14:

  • If you are using page-based navigation (from where you are handling push or presenting a new view controller) then please present the new controller instead of pushing it.
  • Since first screen which if page based (like page view controller) screens, then it by default uses pushing of next page or previous page on swiping left and right and it becomes root window view of your WatchKit app. In this case if you push another controller on click from inside controls then it will not recognise the push but it will recognise present new controller. Hence please do follow the rules if possible.