4

When you implement a class MyGreatClass in Swift its fully qualified name will by <MyPackageName>.MyGreatClass. This is different to Objective-C, where the fully qualified name of that same class is MyGreatClass.

Unfortunately this introduces a problem for me. When I am using NSUnarchiver and the archive was written with Objective-C objects I cannot unpack it with Swift-classes(see below for a detailed description).

This means I need to find a way to rename the namespace for my Swift classes. How do I do that?

Any help would be great!

Background: Why can't NSUnarchiver see/load my swift class?

I have implemented a small program to read a file, which was archived with NSArchive.
These are my files:

main.swift:

import Foundation

// parse command line - total path to *.trace file (from Apple's Instruments app)
var traceFilePath = Process.arguments[1]

var traceFile = NSURL(fileURLWithPath: traceFilePath)
var error:NSError?

// check if the file exists
if (traceFile?.checkResourceIsReachableAndReturnError(&error) == false){
    // file does not exist or cannot be accessed
    println("\(error)")
    exit(1)
}

var rawData = NSData(contentsOfURL: traceFile!)
var data = NSUnarchiver(forReadingWithData: rawData!)
var decodedObject: AnyObject? = data?.decodeObject()

XRObjectAllocRun.swift:

import Foundation

class XRObjectAllocRun: NSObject {
    // class to be implemented   
}

When I now run my application on an Instruments-file I am getting the following error: Terminating app due to uncaught exception 'NSArchiverArchiveInconsistency', reason: '*** class error for 'XRObjectAllocRun': class not loaded'.

This is really strange because when I add the exact same class in an Objective-C file with a bridging header file I have no issues.

trace file reader-Bridging-Header.h: is empty.

XRObjectAllocRun.h:

#import <Foundation/Foundation.h>
@interface XRObjectAllocRun : NSObject
@end

XRObjectAllocRun.m:

#import "XRObjectAllocRun.h"
@implementation XRObjectAllocRun
@end

What am I missing? Why is my Objective-C class found, whereas my Swift class is not? Swift has no issues for example with var x = XRObjectAllocRun() in main.swift, but yet the NSUnarchiver still complaints about a missing XRObjectAllocRun class when I stay purely within Swift. Is the NSUnarchiver looking in the wrong places - does it for some reason only accept Objective-C classes?

If you want to know what I am trying to do check this stackoverflow question out.

Update

This is what apple writes:

Swift classes are namespaced based on the module they are compiled in, even when used from Objective-C code. Unlike Objective-C, where all classes are part of a global namespace

Further more:

For example, when you create a document–based Mac app, you provide the name of your NSDocument subclass in your app’s Info.plist file. In Swift, you must use the full name of your document subclass, including the module name derived from the name of your app or framework.

Yikes, trying to figure out the mess now...

Community
  • 1
  • 1
m3o
  • 3,881
  • 3
  • 35
  • 56
  • Could the problem be that there is no actual reference to XRObjectAllocRun in your code? I'm thinking it doesn't load unless some other code talks about it. – matt Jan 15 '15 at 23:53
  • This is what I initially thought. But I added `var x = XRObjectAllocRun()` right before `var rawData = NSData(contentsOfURL: traceFile!)` and I still got the same error. When I inspect `x` before the crash I see `(trace_file_reader.XRObjectAllocRun) $R0 = 0x0000000102000040 (ObjectiveC.NSObject = {})` – m3o Jan 15 '15 at 23:58
  • this is the what the `Objective-C` object prints out on the debug console: `(XRObjectAllocRun) $R0 = 0x00000001020004b0 { ObjectiveC.NSObject = {} }`. Notice the *difference*: (**swift**) `trace_file_reader.XRObjectAllocRun` vs (**ObjC**) `XRObjectAllocRun`. Can I remove the namespace somehow in swift? – m3o Jan 16 '15 at 00:04
  • 1
    Yes, you can declare it with `@objc(XRObjectAllocRun)`. I think that's probably the answer here. I'll give it as an answer! – matt Jan 16 '15 at 00:32
  • Well done, you really got this question revised and focussed nicely after all your detective work. I couldn't have thought of the answer otherwise! – matt Jan 16 '15 at 00:39

1 Answers1

6

Try this when you declare your class:

@objc(XRObjectAllocRun) class XRObjectAllocRun: NSObject {
    // class to be implemented   
}

That will give this class the same name as the archived class, namely XRObjectAllocRun, instead of the namespaced Swift name trace_file_reader.XRObjectAllocRun.

This is always a concern when you're translating from Objective-C to Swift and you've got an existing archive to deal with. See Apple's documentation:

https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html

Note the discussion under "Exposing Swift Interfaces in Objective-C".

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 2
    And I talk about some other places where `@objc(...)` is useful, in my new Swift tutorial: http://www.apeth.com/swiftBook/apa.html#_objective_c_objects_and_swift_objects – matt Jan 16 '15 at 00:37