11

I'm a newcomer to Cocoa programming, having never got into Objective-C. Now, I am trying to learn it with Swift by going through the Aaron Hillegrass book “Cocoa Programming for Mac OS X, 4e" and implementing everything there in Swift instead of Obj-C. It has been going okay so far, but I hit a roadblock in Chapter 8 (the RaiseMan application).

Here is the Objective-C code from the book:

The header:

#import <Foundation/Foundation.h>

@interface Person : NSObject {
    NSString *personName;
    float expectedRaise;
}
@property (readwrite, copy) NSString *personName;
@property (readwrite) float expectedRaise;
@end

And here is the implementation

#import "Person.h"

@implementation Person

@synthesize personName;
@synthesize expectedRaise;

- (id)init
{
   self = [super init];
   if (self) {
      expectedRaise = 0.05;
      personName = @"New Person";
   }
   return self;
}

@end

You are then supposed to go to Interface Builder, get an Array Controller, specify Person as its class, and add personName and expectedRaise as its keys.

I rewrote the Person class in Swift as follows:

import Cocoa

class Person: NSObject {
    var personName = String()
    var expectedRaise = Float()
}

and connected it to the ArrayController as the book told me to.

There is also some code in the Document file:

init() {
    employees = NSMutableArray()
    println("hi")
    super.init()
    // Add your subclass-specific initialization here.
    var p = Person()
    p.personName = "New Person"
    p.expectedRaise = 0.05                               
}

Here is what the interface looks like (SO won't let me post it directly) https://www.dropbox.com/s/e5busxes9ex3ejd/Screenshot%202014-06-13%2019.02.37.png

When I try to run the app and click the "add employees" button, I get this error in the console:

"RaiseMan[9290:303] Cannot find object class with name Person"

So, my question is: what am I doing wrong?

1
  • +1 for having the exact same issue as me ! I thought I was crazy to write Cocoa apps in Swift with this book from 2008. I see I am not the only one to learn the good stuff the hard way. Commented Apr 2, 2020 at 12:08

3 Answers 3

16

Short answer: you need to specify the class namespace (module name) in Interface Builder: RaiseMan.Person


Details and other options:

This is because Swift adds a prefix to the name of every class injected into the Objective-C runtime in order to avoid name collisions. The prefix follows this convention: _TtC$$AppName%%ClassName, where $$ is the length of AppName and %% is the length of ClassName (see this other SO question for more info).

So in order for the array controller to be able to instantiate the Person class, you need to provide the mangled name in Interface Builder: _TtC8RaiseMan6Person.

Another option is to provide an explicit Objective-C name for your Swift class by using the @objc(<#name#>) attribute:

@objc(Person)
class Person: NSObject {
}

In that case, you can provide the name specified in your @objc attribute to Interface Builder (e.g. Person). See the Using Swift with Cocoa and Objective-C guide for more details.

Sign up to request clarification or add additional context in comments.

4 Comments

This also works for Cocoa Bindings. There you have to prepend the module/namespace name in the Object Controller’s Class Name field.
I'm facing this issue myself...longtime iOS developer but totally new to Cocoa. I did fix it using the explicit Obj-C class name, but the first way seems cleaner so I'd like to figure it out. On which object in IB do I set this please? I keep trying to set it in different places (the Array Controller, the table columns) but Xcode immediately erases whatever I set...Thanks!
Under Xcode 7.2/OSX 10.10.5 I've had not much luck with My_App.ClassName, but the @objc variant works very well indeed. Just as a datapoint.
I had to use the explicit Objective-C name to get this to work
0

If I'm reading your example right, and assuming your init() method belongs in the Person class, you need to put the init() method inside the class braces.

Also, declare variables of a certain type with the following format:

var/let (name): (type)(optional)

So, for your example, you would instead have for the Person class:

class Person {
    var personName: String
    var expectedRaise: Float?
    init() {...}
}

You don't necessarily have to subclass from NSObject if you don't need to; in Swift objects don't have to subclass from NSObject (or another parent) if they don't require it. And the "Float?" means that it is of type Optional Float, meaning that it is a Float that may have a value, or it may have no value, depending if the person is expected to get a raise or not (or you may not want it Optional and just set the value to 0).

2 Comments

I actually rewrote my Person class this morning, and it now has an initialiser, but the problem still persists.
Can you post your rewritten Person class? And also how you're declaring an instance of it?
0

Keep in mind, if you have multiple build targets, every new class you create needs to be added to that build target. If you don't have its target membership set to the build target, Swift will not find the class file.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.