3 Replies
      Latest reply on Sep 23, 2019 5:13 AM by craigaps
      craigaps Level 1 Level 1 (0 points)



        I recently updated to Xcode 11 GM seed.  However I've noticed that the dreaded "Unknown class in Interface Builder file" is crashing my app.  I haven't changed any class names or storyboards.  Interestingly the app runs perfectly in teh simulator, but crashed on my phone.


        Here is what is being printed in the output window:

        MyAppName[9513:4230222] Unknown class _TtC10MyApp24SlideTableViewController in Interface Builder file.

        Could not cast value of type 'UIViewController' (0x1ebe282b0) to 'MyApp.SlideTableViewController' (0x104d05e08).

        MyAppName[9513:4230222] Could not cast value of type 'UIViewController' (0x1ebe282b0) to 'MyApp.SlideTableViewController' (0x104d05e08).


        I've deleted the class and recreated, removed the View Controller from the story board, made sure the view controller is references correctly as is the target, but the problem persists and I'm out of ideas.

        Is there a "reset" of the storyboard to reference the elements?  Or some other way to resolve this?


        Many thanks



        • Re: Xcode 11 Unknown class in Interface Builder file.
          Claude31 Level 8 Level 8 (6,955 points)

          Did you do a clean Build folder (Product menu) ?


          Did you also restart the Mac after upgrade to XCode 11 GM ? You could.


          Finally, there is a new GM 2 seed which corrected some bugs (did not read about your point however).


          If that's not enough, could you post the code where you get the error as well as the definition of the class of the involved objects ?

            • Re: Xcode 11 Unknown class in Interface Builder file.
              craigaps Level 1 Level 1 (0 points)


              Thanks for the reply. I've done a clean build and deleted the files and folders in the derived data folder and installed GM 2 ssed and rebooted the Mac.  I read the release notes for the GM 2 seed and saw a similar error under the Reality Composer heading, but the suggested workaround didn't really apply even though I did "manually" link my custom framework as a binary in the build phrase.


              With that said, here is code that will crash the device but run ok on the simulator:


               let storyboard = UIStoryboard(name: "Verification", bundle: nil)
               let slideViewController = storyboard.instantiateViewController(withIdentifier: "Slide") as! SlideTableViewController

              The code to SlideTableViewController:


              import VerifyKit
              import UIKit
              class SlideTableViewController: UIViewController {
                  // MARK: Instance Properties
                  /// The heights of the slide view.
                  let closedHeight: CGFloat = UIApplication.shared.keyWindow!.safeAreaInsets.bottom + 90
                  let openHeight: CGFloat = UIApplication.shared.keyWindow!.frame.height - UIApplication.shared.keyWindow!.safeAreaInsets.top - 140
                  /// The mid point between the `closedHeight` and `openHeight` of the slide view.
                  lazy var midPoint = (openHeight + closedHeight) / 2
                  /// The sticky points to which the slide view should snap to.
                  lazy var middleStickyPoints: [CGFloat] = [midPoint]
                  /// The sorted array of all sticky points, including `closedHeight` and `openHeight`.
                  lazy var allStickyPoints = ([closedHeight, openHeight] + middleStickyPoints).sorted()
                  /// The transaction.
                  var transaction: PendingTransaction?
                  /// The data to display in the slide table view.
                  private var dataToDisplay: [(title: String, body: String)]?
                  /// The bottom cosntraint of the slide view to animate.
                  lazy var bottomConstraint = view.superview!.bottomAnchor.constraint(equalTo: view.topAnchor, constant: closedHeight)
                  /// The grip bar.
                  let gripBar = UIView()
                  /// The close button.
                  let closeButton = UIButton()
                  /// The dim view.
                  let dimView = UIView()
                  /// The parent view controller.
                  lazy var parentVC = parent as! ChallengeViewController
                  /// The open state of the slide view.
                  var isOpen: Bool = false {
                      didSet {
                          if isOpen {
                              // Move approve/deny buttons to top of screen.
                              parentVC.buttonCollectionViewConstraint.constant = openHeight - UIApplication.shared.keyWindow!.safeAreaInsets.bottom + 20
                          } else {
                              // Move approve/deny buttons to bottom of screen.
                              parentVC.buttonCollectionViewConstraint.constant = 110
                  // MARK: Control References
                  @IBOutlet var previewLabel: UILabel!
                  @IBOutlet var tableView: UITableView!
                  // MARK: View functions and events
                  /// Called after the controller'€™s view is loaded into memory.
                  override func viewDidLoad() {
                      tableView.delegate = self
                      tableView.dataSource = self
                      // Hide the last separator line by adding a footer view.
                      tableView.tableFooterView = UIView(frame: CGRect(origin: .zero, size: CGSize(width: 0, height: 1)))
                      // Pan the slide view.
                      let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(panSlideView))
                      // Activate slide table view constraints.
                      tableView.translatesAutoresizingMaskIntoConstraints = false
                      NSLayoutConstraint.activate([tableView.widthAnchor.constraint(equalTo: view.widthAnchor),
                                                   tableView.heightAnchor.constraint(equalToConstant: openHeight - 26),
                                                   tableView.topAnchor.constraint(equalTo: view.topAnchor, constant: 26)])
                      // Display the grip bar.
                      gripBar.layer.cornerRadius = 3
                      // Activate grip bar constraints.
                      gripBar.translatesAutoresizingMaskIntoConstraints = false
                      NSLayoutConstraint.activate([gripBar.topAnchor.constraint(equalTo: view.topAnchor, constant: 10),
                                                   gripBar.centerXAnchor.constraint(equalTo: view.centerXAnchor),
                                                   gripBar.heightAnchor.constraint(equalToConstant: 6),
                                                   gripBar.widthAnchor.constraint(equalToConstant: 42)])
                      // Display the close button.
                      closeButton.setImage(UIImage(named: "Close")?.withRenderingMode(.alwaysTemplate), for: .normal)
                      closeButton.layer.cornerRadius = 16
                      closeButton.addTarget(self, action: #selector(closeSlideView), for: .touchUpInside)
                      // Activate close button constraints.
                      closeButton.translatesAutoresizingMaskIntoConstraints = false
                      NSLayoutConstraint.activate([closeButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 16),
                                                   view.trailingAnchor.constraint(equalTo: closeButton.trailingAnchor, constant: 16),
                                                   closeButton.widthAnchor.constraint(equalToConstant: 32),
                                                   closeButton.heightAnchor.constraint(equalToConstant: 32)])
                  /// Notifies the view controller that its view is about to be added to a view hierarchy.
                  /// - parameter animated: If true, the view is being added to the window using an animation.
                  override func viewWillAppear(_ animated: Bool) {
                      // Set up dim view.
                      dimView.frame = view.bounds
                      dimView.backgroundColor = ThemeManager.shared.current.dimBackgroundColor
                      parentVC.view.insertSubview(dimView, belowSubview: parentVC.buttonCollectionView)
                      // Activate slide view constraints.
                      view.translatesAutoresizingMaskIntoConstraints = false
                                                   view.leadingAnchor.constraint(equalTo: view.superview!.leadingAnchor),
                                                   view.trailingAnchor.constraint(equalTo: view.superview!.trailingAnchor),
                                                   view.bottomAnchor.constraint(equalTo: view.superview!.bottomAnchor)])
                      // Get the table representation of the transaction data.
                      dataToDisplay = transaction?.tableRepresentation
                      // Set the preview label's text to the transaction's type.
                      if let type = transaction?.additionalData[.type] {
                          previewLabel.text = "\(type.capitalized) \(String.localize("ChallengeInfo"))"
                  /// Called when the user taps the `closeButton`.
                  @objc func closeSlideView() {
                      animateSlideView(to: allStickyPoints[0])
                  /// Handles the pan gestures on the slide view to animate its states.
                  /// - parameter recognizer: The gesture recognizer of the pan event.
                  @objc func panSlideView(_ recognizer: UIPanGestureRecognizer) {
                      switch recognizer.state {
                      case .changed:
                          let translation = recognizer.translation(in: view)
                          let panPosition = bottomConstraint.constant - translation.y
                          // Restrict the pan between `closedHeight` and `openHeight`.
                          if panPosition > closedHeight, panPosition < openHeight {
                              bottomConstraint.constant = panPosition
                              recognizer.setTranslation(.zero, in: view)
                      case .ended:
                          // Animate the slide view.
                          let velocity = recognizer.velocity(in: view)
                          animateSlideView(with: velocity.y)
                  /// Handles the animations of the slide view to its closest sticky point.
                  /// - parameter velocity: The velocity the slide view was released with.
                  private func animateSlideView(with velocity: CGFloat) {
                      let velocityThreshold: CGFloat = 300
                      var closestIndex = allStickyPoints.closestIndex(to: bottomConstraint.constant)!
                      if velocity < -velocityThreshold {
                          // Swipe up velocity threshold exceeded, snap to next sticky point.
                          closestIndex = min(allStickyPoints.count - 1, closestIndex + 1)
                      } else if velocity > velocityThreshold {
                          // Swipe down velocity threshold exceeded, snap to previous sticky point.
                          closestIndex = max(0, closestIndex - 1)
                      // Animate the slide view's bottom constraint to its closest sticky point index.
                      animateSlideView(to: allStickyPoints[closestIndex])
                  /// Handles the animations of the slide view to the given sticky point index.
                  /// - parameter point: The point to animate the slide view to.
                  private func animateSlideView(to point: CGFloat) {
                      isOpen = point == allStickyPoints.last
                      let transitionAnimator = UIViewPropertyAnimator(duration: 0.5, dampingRatio: 0.75, animations: {
                          self.bottomConstraint.constant = point
                  /// Returns the fraction completed by the slide view between the given `start` and `end` points, based on the slide view's current position.
                  /// - parameter start: The starting point.
                  /// - parameter end: The ending point.
                  func fractionCompleteBySlideView(from start: CGFloat, to end: CGFloat) -> CGFloat {
                      return (bottomConstraint.constant - start) / (end - start)
                  /// Called to notify the view controller that its view has just laid out its subviews.
                  override func viewDidLayoutSubviews() {
                      // Animates the `closeButton` and `dimView` opacity.
                      closeButton.alpha = fractionCompleteBySlideView(from: closedHeight, to: midPoint)
                      dimView.alpha = fractionCompleteBySlideView(from: midPoint, to: openHeight)
                      // Animates the `tableView` and `previewLabel` opacity.
                      tableView.alpha = fractionCompleteBySlideView(from: closedHeight, to: midPoint)
                      previewLabel.alpha = fractionCompleteBySlideView(from: midPoint, to: closedHeight)
                      // Animate roundness of top left and top right corners.
                      view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
                      view.layer.cornerRadius = 16 * fractionCompleteBySlideView(from: closedHeight, to: openHeight)

              The reference to PendingTransaction class is located in VerifyKit framework.  I can't understand why this view controller fails but others are  display ok in both devive and simulator.


              Appreciate the help