I'm implementing a protocol from an Obj-C library in a Swift class that defines two properties as:
#property (nonatomic, assign, getter = isLoading) BOOL loading;
#property (nonatomic, readonly) UIExpansionStyle expansionStyle;
Although I'm not sure how to conform to these requirements in my Swift class.
I've looked at the Obj-C examples, but I haven't gleaned any solutions from that. I've tried declaring class variables with the same name, but that hasn't worked. Any suggestions on how I would go about this?
PS, the library in question is https://github.com/OliverLetterer/SLExpandableTableView
I think you're making this harder than it needs to be. With a protocol defined like this:
#protocol MyProtocol
#property (nonatomic, assign, getter = isLoading) BOOL loading;
#property (nonatomic, readonly) UIExpansionStyle expansionStyle;
#end
the following class conforms:
class Conformer : MyProtocol {
var loading: Bool
var expansionStyle: UIExpansionStyle
init(loading: Bool, expansionStyle: UIExpansionStyle) {
self.loading = loading
self.expansionStyle = expansionStyle
}
}
Prefixing boolean getters with is is a Cocoa convention, so Swift already knows about it and doesn't require you to do anything special on the swift side.
The confusing part might be all the modifiers to the Objective C property declaration. Let's go through them one by one.
nonatomic has no equivalent in Swift, nothing to do here.
assign is automatic for value types, nothing to do here either.
getter = isLoading is a Cocoa convention which Swift understands and needs nothing from you in order to make this work.
readonly you can do this in Swift (just use the get{ } syntax) but it is not necessary. This is because you are allowed to expand on the contract made by the protocol. MyProtocol requires there be a property called expansionStyle that can be read from, it does NOT say that it must not be possible to write to that property in the type that implements the protocol, just like it doesn't say you can't have other properties/methods on that same class.
Lance's answer didn't work for me, this is how I got isLoading to conform to the protocol (Swift 2.2)
var expansionStyle: UIExpansionStyle = UIExpansionStyle(0)
var _loading: Bool = false
var loading:Bool {
#objc(isLoading) get {
return self._loading
}
set(newValue){
_loading = newValue
}
}
Related
In one of my projects I want to use a Swift class from an Objective C class. I've created XXX-swift.h file and a bridging header file. Most swift files are working perfectly, but I found a problem of an APiResult class of mine.
#objc class ApiResult:NSObject {
var success:Bool?
var message:String?
}
and in XXX-swift.h file
SWIFT_CLASS("_TtC16RemoteController9ApiResult")
#interface ApiResult : NSObject
#property (nonatomic, copy) NSString * _Nullable message;
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
there the success property is missing. A rebuild & clean build didn't work.
Why is this happening
Objective-C doesn't support optionals. The bridging from Swift to Objective-C allows optionals for class types because those just translate to nil on the ObjC side. However, Bool is a primitive type, and there's no such thing as a nil boolean value in Objective-C.
What's the difference between declaring a #private ivar in the header file and declaring the same ivar in the class extension without #private? As far as I understand it's the same thing.
Also, can you declare a private property in the header?
The concept is to declare in the header file only those things (methods, properties, etc) which are public. Declare all private items in the implementation file's class extension.
This provides the class users only information that is available for their use and hides all else. It also make it easier for a user of the class quickly see the functionality available to him. Writing code is all about readability and understandability to the developer.
This way a developer is free to change anything that is not exposed in the header files without making any externally visible changes.
In recent versions of Objective this is finally fully releasable via class extensions.
What's the difference between declaring a #private ivar in the header file and declaring the same ivar in the class extension without #private?
There are a few differences. In short, variables declared in the header file are visible to subclasses and class categories. Variables declared in the implementation are not.
1) Instance variables declared in a class's main #interface block are available to external class categories or extensions, even if those variables are declared #private. E.g.:
// YourClass.h
#interface YourClass : NSObject {
#private
int _yourPrivateIVar;
}
#end
// MyExtension.m
#implementation YourClass(MyExtension)
- (void)reset { _yourPrivateIVar = 0; } // This is allowed.
#end
Instance variables declared in the implementation are not available to external class categories.
2) A base class and its subclass cannot both declare the same ivar in their #interface, even if both ivars are #private. E.g., this is not allowed:
#interface Base : NSObject
{
#private
int _foo;
}
#end
#interface Subclass : Base
{
#private
int _foo; // Error: Duplicate member _foo
}
#end
If both ivars are declared in a class extension or implementation block then not only does it compile but it works as expected: both classes have their own separate _foo ivars that do not conflict with one another. On other words, both variables are truly private and separate:
#implementation Base {
int _foo;
}
#end
#implementation Subclass {
int _foo;
}
- (void)reset { _foo = 123; } // Does not affect base class's _foo
#end
Note: If the base class and subclass declare a "private" property or method with the same name it will compile without warning or error, but it will fail spectacularly at runtime as both classes unknowingly interfere with each other's private data.
I have something like
#property(nonatomic,retain) UIImageView *whiteBfFillUp;
#end
#synthesize locationManager;
I am new to swift coding. Can anyone tell me the equivalent code in swift.
There is no equivalent.
In Swift when you write a varor let in a class or struct declaration you already declaring a property.
Define properties to store values
This is what is written in Swift documentation.
If you are concerned about access control you can use private or public modifiers.
public var somePublicVariable = 0
If you'd like to override properties such as you did in Objective-C you will find useful properties observers such as didSet{} willSet{}.
If you need a a readonly properties you can make the setter private.
public private(set) var hours = 0
If you are only looking for equivalent of property, then you just need to create your class level variables. All class level variables are by default 'strong' or 'retain'. If, however, you want them to be weak then use weak.
This would be something like
var whiteBfFillUp: UIImageView? = nil
The ? at the end means that this is an optional type. If it's not, you would need to assign it some value in the init method, or right there.
What is the equivalent of the following Objective-C code in Swift?
#property (nonatomic, assign, getter = isOpen) BOOL open;
Specifically, how does one declare a variable in Swift to synthesize the getter with a custom name?
Furthermore, how can you subsequently override the implementation of the getter and setter?
Your assumption was close, but a few things could be changed. I will try to help you get as close as possible to the Objective-C version.
First of all, the nonatomic and assign are irrelevant in swift. That leaves us with
#property (getter = isOpen) BOOL open;
Since properties in swift are just instance variables, the swift translation would be as follows.
var open:Bool
Although this has the same basic functionality as the Objective-C version, it is lacking the named getter (isOpen). Unfortunately, there is no direct translation to swift for this (yet). You could use a custom getter and setter.
var open:Bool {
get {
// custom getter
}
set {
// custom setter
}
}
A rather crude work around would be to make another function literally called isOpen that would act as a getter.
func isOpen() -> Bool { return self.open }
In conclusion, what you are asking is only slightly possible, but hopefully in later releases of swift can become a reality.
var open: Bool {
#objc(isOpen)
get {
// custom getter
}
set {
// custom setter
}
}
Leads to this generated header:
SWIFT_CLASS("_TtC11SwiftToObjC9TestClass")
#interface TestClass : NSObject
#property (nonatomic, getter=isOpen) BOOL open;
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
As a remarq, for the setter you need to repeat the #objc directive:
#objc( setOpen:) set { self.open = newValue }
Don't forguet the semi-column.
A particular thing is that, doing this, self.open will call the setter/getter itself and produce an infinite loop.
In Obj-C you fix it using self->open. How do this if swift?
Having an Objective c base class:
#interface ObjcClass : NSObject {
NSString *aVariable_;
}
And a swift sub-class:
class SwiftClass : ObjcClass {
func init() {
// aVariable_ can't be accessed here. An Objective-c derived
// class has direct access to it's super's instance variables!
}
}
How do I access ObjcClass aVariable_ from within SwiftClass?
Great query. We have tried to hard to get this done. The only working solution I found
get value by using self.valueForKey("aVariable_")
set value using self.setValue("New Value", forKey: "aVariable_")
Hope that helps. Possible solution without altering super class.
I couldn't find a "proper" way to do this, but I needed badly for it to work. My solution was to create a simple getter method in my Objective C superclass, like this:
header file
#interface ObjcClass : NSObject {
NSString *myVariable;
}
- (NSString *)myVariable;
in the implementation file
- (NSString *)myVariable {
return myVariable;
}
I'd love to hear of a better way of doing it, but this at least works.
I've searched a lot for this.
Eventually I changed my code from:
#interface PrjRec : NSObject {
#public
NSString* name;
}
#end
To:
#interface PrjRec : NSObject {
}
#property NSString* name;
#end
similar to #JasonTyler solution.
Then I can access to my object property from Swift code with simple dot notation <object instance>.name,
But I needed to change all existing objective-c references from
<object instance>->name
To:
<object instance>.name
or
_name
if inside class unit.
I hope for a better solution too.
This worked as a pretty neat solution for me, just adding a Swift variable like:
var myInstanceVar: String {
return self.value(forKey: "myInstanceVar") as! String
}
If you are willing to have a property, then you can create the property to fit your needs.
#interface ObjcClass : NSObject {
NSString *aVariable_;
}
#property (nonatomic) NSString *aVariable_;
...
#implementation ObjcClass
#synthesize aVariable_ = aVariable_;
This allows the variable to be accessed as inst->aVariable_ or as inst.aVariable_. In the Objective C class the variable can be accessed as aVariable_ or self.aVariable_.
I seriously don't know why anyone does instance variables anymore (for one, they're private by default) vs properties. See Giorgio Calzolato's answer on this (apart from his last line about looking for a better solution - that IS the best solution :) ).
In my case I already had a property and was extra perplexed over why it didn't work. But I realized that the property had a custom time and it needed to be added into my SDK-Bridging-Header.h file.
So if your property is set to a custom type like this:
#property (nonatomic, weak) IBOutlet SDKMyCustomObject *customObject;
...then remember to add it to the bridging header.