Why does a class become public if you inherit from NSObject in Swift? - objective-c

I'm working on a framework.
The library is written in Swift and I notice that when a class inherits from NSObject or conforms to NSObjectProtocol, its declaration can be found in *.framework/Headers/*-Swift.h.
This class is available outside of the module in Objective-C code, so it became public.
Why does it happen if the access level is internal?

Internal Swift classes need to be available to the Objective-C code of that framework, however the only way Objective-C code can access the Swift classes is by importing the -Swift.h header file.
Now, how can a Swift class be visible to Objective-C: it either needs to inherit NSObject, or conform to NSObjectProtocol. If any of these two conditions is fulfilled, and the class declaration is not decorated with #nonobjc/private/fileprivate, then it will be exported to Objective-C via the -Swift.h module header.
This is why any Swift classes that are exportable to Objective-C will automatically be present in the discussed header file. It's an (unfortunate) coincidence that for frameworks this results in the class being publicly available (due to the fact that any Objective-C declarations that appear in a header are public).
Now, if you want your class to not end up in the -Swift.h header file, but still want to keep the NSObject(Protocol) inheritance/conformance, a workaround would be to make your class generic, thus prohibiting its exposure to Objective-C. Note that this will also prevent the class from being available to Objective-C code in the same framework.
// the generic argument doesn't matter, it's only used to make the class
// Swift-only
class MyClass<T>: NSObject { }
The caveat would be that every time the class is used, you will need to specify a value for the generic argument. This can be avoided by adding an intermediary base class:
// just a class that inherits NSObject, but is not exported in the -Swift header
class Empty<T>: NSObject { }
class MyClass: Empty<String> { }

Related

Generic subclasses of '#objc' classes cannot have an explicit '#objc' attribute

I am trying to add swift library in my objective c project
https://github.com/vimeo/VimeoUpload
I have already added #import “-Swift.h” in my project
to subclass VimeoUpload, I am adding #objc in class definition and it gives me following error
Generic subclasses of '#objc' classes cannot have an explicit '#objc' attribute because they are not directly visible from Objective-C.
Objective-C doesn't support Swift Generics, that is why you can't add #objc directive to your class.

Using #objc attribute for a Swift class that doesn’t inherit from an Objective-C class

The following paragraph from the section on Interoperability of the Using Swift with Cocoa and Objective-C (Swift 2.1) documentation seems to suggest that there is a way to use a Swift class that does not inherit from an Objective-C class for interoperability.
When you create a Swift class that descends from an Objective-C class, the class and its members—properties, methods, subscripts, and initializers that are compatible with Objective-C—are automatically available from Objective-C. In some cases, you need finer grained control over how your Swift API is exposed to Objective-C. You can use the #objc attribute if your Swift class doesn’t inherit from an Objective-C class, or if you want to change the name of a symbol in your interface as it’s exposed to Objective-C code.
And I attempted the following:
import Foundation
#objc class FooBar {
#objc var name: String;
init() {
name = "Hello World!"
}
}
But unfortunately, it gives a compilation error: Only classes that inherit from NSObject can be declared #objc
Am I misinterpreting something?
Take a look at this thread on the Apple Developer Forums.
Basically what you say was possible in Xcode <=6, but removed on Xcode 7
Pretty much yes. #objc on Swift-rooted classes never quite behaved like an NSObject-rooted class, leading to various weirdness in the generated header and at runtime. You can still treat any Swift class instance as an AnyObject, mark methods and properties on a Swift class as #objc, and conform to Objective-C protocols; the class just isn't exposed in the generated header and doesn't default to having its members available in Objective-C.

Objective-C protocol not recognized in Swift

I am working in an app written in Objective-C and we would like to begin integrating some Swift into it.
I have written a simple view controller class in Swift and to work in the app it must conform to a protocol that was written in Objective-C. In my Swift class I have the following declaration:
#objc class SwfViewController: UIViewController, theProtocolName {
//some code
}
In my bridging header file I have the class name of the protocol referenced.
#import "theProtocolName.h"
I have implemented all of the required methods listed in the protocol, yet still I get an error saying
SwfViewController does not conform to protocol 'theProtocolName'
I'm fairly new to Swift and could have easily left out something. Any suggestions of what to check? Thanks!
You should take into account that Swift signature for methods is slightly different from objective-c. For example:
A protocol like this:
#protocol MyProtocol <NSObject>
- (void)didFinish:(MyClass *)class withError:(id)errorMessage;
#end
A class that conforms to this protocol in Swift should be:
#objc class SwfViewController: UIViewController, MyProtocol {
func didFinish(whatEveryouWant1: UIViewController!, withError whatEverYouWant2: AnyObject!){
}
}
Please note id becomes AnyObject and Obj-C references are translated with ! as implicitly unwrapped optionals.
Generally this error means that your class is missing required methods which is the part of the point of the protocol. The class seems to realize that it need to follow the protocol you've told it about, but now need to define the required pieces.
You should be able to expand the error message by clicking on it to find out which pieces are missing.

Objective-C – How to hide methods for a class that implements a protocol

Question is in title. Code example:
UIViewController <MyProtocol> *viewcontroller = ...;
[viewcontroller methodFromProtocol]; // I expect to be able to call all methods that the protocol defines
//UIViewControllerSubclass implements MyProtocol
UIViewControllerSubclass *viewControllerSubclassWithoutMyProtocol = [[UIViewControllerSubclass alloc] init];
[viewControllerSubclassWithoutMyProtocol methodThatIsNotInTheInterfaceIsDisplayedHere]; // I only expect to be able to call the methods that are defined in this class' interface even though this class implements MyProtocol
Your question isn't completely clear, but I think you are asking if you can "privately" conform to a protocol?
This can be done by declaring that you conform to the protocol inside the implementation file, rather than the interface file. With view controllers, you can do this in the class continuation that is generated for you automatically in the .m file, otherwise you'll need to add the class continuation in yourself:
#interface MyVCSubclass () <MyProtocol>
Now, any class that imports only the header file will not know your controller conforms to the protocol.
[viewControllerSubclassWithoutMyProtocol methodThatIsNotInTheInterfaceIsDisplayedHere];
I only expect to be able to call the methods that are defined in this class' interface even though this class implements MyProtocol
That's a bad expectation. Objective-C lets you call any method that an object implements. If you try to call a method that an object doesn't implement, two things should happen:
You get a compiler warning (not an error though)
When the code runs, it crashes, unless you've taken steps to handle such an event.
If a class implements a protocol but doesn't declare that it does so in a public header, then you can still call the method (since Objective C doesn't have private methods). I'd have thought you'd get a compiler warning, but if you're calling this from a file within the same Xcode project as your object (that is, you're not building a static library) then it's possible that Xcode is getting smart and deciding that since the method exists, it must be OK to call.
It wasn't clear from your question what you expected to happen and what actually happened. If you supply that information, we'll be able to give better answers.

Class extension vs class category

Class extensions #interface Class () are a lot more powerful and can inject variables into the class. Categories #interface Class (Category) can't.
What other differences are there, and when should one use a category over a class extension?
The main difference is that with an extension, the compiler will expect you to implement the methods within your main #implementation, whereas with a category you have a separate #implementation block. So you should pretty much only use an extension at the top of your main .m file (the only place you should care about ivars, incidentally) -- it's meant to be just that, an extension.
A class extension bears some similarity to a category, but it can only be added to a class for which you have the source code at compile time (the class is compiled at the same time as the class extension). The methods declared by a class extension are implemented in the #implementation block for the original class so you can’t, for example, declare a class extension on a framework class, such as a Cocoa or Cocoa Touch class like NSString.
The syntax to declare a class extension is similar to the syntax for a category, and looks like this:
#interface ClassName ()
#end
Because no name is given in the parentheses, class extensions are often referred to as anonymous categories.
Unlike regular categories, a class extension can add its own properties and instance variables to a class. If you declare a property in a class extension, like this:
#interface XYZAnimal () {
id _someCustomInstanceVariable;
}
...
#end
IMHO, it's best to think of class extensions as private interface to a class. The primary interface (in your .h file) acts as the public interface which defines the class's behavioural contract with other classes.
Use class extensions to Hide Private Information
Class extensions are often used to extend the public interface with additional private methods or properties for use within the implementation of the class itself. It’s common, for example, to define a property as readonly in the interface, but as readwrite in a class extension declared above the implementation, in order that the internal methods of the class can change the property value directly.
As an example, the XYZPerson class might add a property called uniqueIdentifier, designed to keep track of information like a Social Security Number in the US.
It usually requires a large amount of paperwork to have a unique identifier assigned to an individual in the real world, so the XYZPerson class interface might declare this property as readonly, and provide some method that requests an identifier be assigned, like this:
#interface XYZPerson : NSObject
...
#property (readonly) NSString *uniqueIdentifier;
- (void)assignUniqueIdentifier;
#end
In order for the XYZPerson class to be able to change the property internally, it makes sense to redeclare the property in a class extension that’s defined at the top of the implementation file for the class:
#property (readwrite) NSString *uniqueIdentifier;
Note: The readwrite attribute is optional, because it’s the default. You may like to use it when redeclaring a property, for clarity.
Categories are an Objective-C language feature that let you add new methods to an existing class. Extensions are a special case of categories that let you define methods that must be implemented in the main implementation block.
Private declarations can be in class extensions, which mainly are some properties, because we have no need to declare a method before we call it.
ios extension similiar to c#,java abstract class or interface
ios category similiar to c# class extension