I'm aware that we no longer need to prefix class names in swift. But I'm working on a project containing both swift and Objective-c.
Should I prefix the swift class names of keep them the "swift way"?
You can go ahead and keep it the Swift way. If you're worried about name-clashes and adding a prefix, you can change the Objective-C class name by adding #objc(...) before the class declaration.
E.g.
#objc(XXMyClass) class MyClass: ... {
...
}
Related
I have a model class written in Swift and a backend service controller written in Objective-C.
Let's say the file names are SomeModel.swift and SomeRequestController.m and it's header.
I don't want to import the ProjectName-Swift.h inside the header file. So I have used forward declaration inside the header file. I import ProjectName-Swift.h inside SomeRequestController.m file.
The Swift file looks like following:
import Foundation
#objc class SomeModel: NSObject {
#objc var prop1: String?
#objc var prop2: String?
#objc var prop3: String?
}
I can reach this model class in objective-c in the other parts of the code. Forward declaration makes enable to create an object inside the code. So Xcode finds the class. However when I compile it fails with the message:
Property 'prop1' not found on object of type '__strong id'
How can I solve this without changing the import mechanism?
I have solved the problem. Here, I would like to introduce this specific case for those who may encounter with such a problem in the future.
The objective-c file is in target membership with two projects. The Swift file was in target membership with one of them. I have assigned this Swift file to the second project. It compiles now.
So I am slowly adding more swift files to codebase.
Suppose I have class in Objective c:
ListData<Type : CSJSONData *> : ServerData <CSListData>
I want to subclass it in swift class like this:
#objc class ArticlesData: ListData<Article>
Compiler gives me error:
Generic subclasses of '#objc' classes cannot have an explicit '#objc' because they are not directly visible from Objective-C
Replace '#objc ' with ''
But I need to have it #objc to keep available in my objc part of code ! How to do it ?
From The Swift Programming Language (4.2), Attributes:
The compiler implicitly adds the objc attribute to subclasses of any class defined in Objective-C. However, the subclass must not be generic, and must not inherit from any generic classes. You can explicitly add the objc attribute to a subclass that meets these criteria, to specify its Objective-C name as discussed below.
Unfortunately for you this is pretty definitive.
This question isn't about bridging headers and all that. I've got all that working. My question is specifically about what I need to do to my Swift classes to get them to show up in Obj-C code.
Take, for example, this simple class:
class MyClass {
var value: String = ""
}
If I have this class in my Project, it doesn't get included in the MyProject-Swift.h file that gets auto-generated. My understanding is that in order to use a Swift class in Objective-C, my class needs to derive from a class that Objective-C knows about. This is where I start to doubt the actual requirements.
If my class were derived from a UIViewController, then no problem. But if this is just a model object, then it's not deriving from anything. While it is entirely possible to easily make my class derive from NSObject, and thus, it gets properly imported into the Obj-C code, deriving from NSObject can cause other issues down the road.
So if I don't want to make my class derive from NSObject, what can I do to make it visible to my Obj-C files? Is there a doc I just couldn't find that explains how to do this?
As far as I am aware currently, Only Swift classes that inherit from NSObject can be declared #objc and bridged into an Objective-C project.
Without that conformance/inheritance, you'll end up missing some crucial functionality to Objective-C like message sending.
All of that being said, an Objective-C class has to inherit from a parent class and the default root class is NSObject. You almost definitely want to just inherit and make your class a PONSO.
When I declare static parameter in extension of class then I have to write #nonobjc before variable like:
#nonobjc static let test = "test"
and sometimes I have to write #objc before method, so what is use of #objc and #nonobjc in Swift.
Can anyone help me for this problem?
This is explained in the Apple's official documentation about Objective-C - Swift interoperability:
When you use the #objc(name) attribute on a Swift class, the class is
made available in Objective-C without any namespacing. As a result,
this attribute can also be useful when migrating an archivable
Objective-C class to Swift. Because archived objects store the name of
their class in the archive, you should use the #objc(name) attribute
to specify the same name as your Objective-C class so that older
archives can be unarchived by your new Swift class.
Conversely, Swift also provides the #nonobjc attribute, which makes a
Swift declaration unavailable in Objective-C. You can use it to
resolve circularity for bridging methods and to allow overloading of
methods for classes imported by Objective-C. If an Objective-C method
is overridden by a Swift method that cannot be represented in
Objective-C, such as by specifying a parameter to be a variable, that
method must be marked #nonobjc.
To summarize, use #objc when you want to expose a Swift attribute to Objective-C without a namespace . Use #nonobjc if you want to keep the attribute available and accessible only in Swift code.
(Addendum/additional official details to #bontoJR well summarizing answer)
From the Swift Language Reference - Attributes [emphasis mine]:
objc
Apply this attribute to any declaration that can be represented in
Objective-C — for example, non-nested classes, protocols, nongeneric
enumerations (constrained to integer raw-value types), properties and
methods (including getters and setters) of classes and protocols,
initializers, deinitializers, and subscripts. The objc attribute tells
the compiler that a declaration is available to use in Objective-C
code.
...
nonobjc
Apply this attribute to a method, property, subscript, or initializer
declaration to suppress an implicit objc attribute. The nonobjc
attribute tells the compiler to make the declaration unavailable in
Objective-C code, even though it is possible to represent it in
Objective-C.
...
Here you can find more details in this Swift Documentation : InteractingWithObjective-C
As an answer of your question, overview from attached link is as below.
#objc : You can use attribute to change the name of a class, property, method, enumeration type, or enumeration case declaration in
your interface as it’s exposed to Objective-C code.
Example : if the name of your Swift class contains a character that isn’t supported by Objective-C, you can provide an alternative name to use in Objective-C.
#nonobjc : It makes a swift declaration unavailable in Objective-C. You can use it to resolve circularity for bridging
methods and to allow overloading of methods for classes imported by
Objective-C.
I had a Swift class declared like this:
#objc class MyHelper {
// class code
}
I had to do this to access such class from Objective-C classes, since I'm mixing languages in my project. Now that I upgraded to Xcode 7, I get this error:
Only classes that inherit from NSObject can be declared #objc
And such class is not known by my Objective-C classes anymore. I guess that then I should inherit my Swift class from NSObject, will that have any impact in the way the app was working?
Thanks
See the Apple staff SevenTenEleven's reply in the Apple Developer Forum.
He mentioned that this is because of #objc on Swift-rooted classes never quite behaved like an NSObject-rooted class, leading to various weirdness in the generated header and at runtime.
We 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.