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?
Related
I was able to see an interesting case using
Estimote nearables SDK
They have a class ESTNearable with property called zone.
// ENUM
typedef NS_ENUM(NSInteger, ESTNearableZone ) {
ESTNearableZoneUnknown = 0,
ESTNearableZoneImmediate,
ESTNearableZoneNear,
ESTNearableZoneFar,
};
// CLASS
#interface ESTNearable : NSObject <NSCopying, NSCoding>
// ...
#property (nonatomic, assign, readonly) ESTNearableZone zone;
// ...
#end
So when I try to use this method in Swift, compiler fails with that error:
As I understand there is some kind of compiler bug and for some reason it believes that I want to use old zone method from NSObject - (struct _NSZone *)zone OBJC_ARC_UNAVAILABLE; I can use other specific properties of that class without any problems.
As I use an SDK I can not change the name of the zone method. I believe I can write some kind of obj-c category, add some new method there, which will return value of original one, but I do not want to add obj-c classes in my project.
Is there any possibility to call this method from swift as I believe correct zone method will be called for class instances?
Thanks in advance!
Here I found the same question. I answered more deeply there. I could not find something more good, so I went ahead with my old assumptions.
I Added this category to Bridging Header. It worked fine.
#import <EstimoteSDK/EstimoteSDK.h>
#interface ESTNearable (Ex)
/// Solving the compiler problem that "zone" method is unavailable in Swift
#property (readonly) ESTNearableZone nearableZone;
#end
// Implementation
#implementation ESTNearable (Ex)
- (ESTNearableZone)nearableZone
{
return self.zone;
}
#end
After that I just used nearableZone method in Swift
var zone = someNearable.nearableZone
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
}
}
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.
I've got a property that I want to change the name of. Basically I want the old property to return/set the value of the new property so existing code doesn't break, but it'll throw warnings to use the new name.
This is in my header file:
#property (nonatomic, strong) MyClass *newProperty;
#property (nonatomic, strong) MyClass *oldProperty __attribute__((deprecated));
To make oldProperty's getters and setters just set/return newProperty's values in the implementation i'd like to do something like
#synthesize oldProperty=_newProperty;
This throws an error 'oldProperty and newProperty both claim instance variable _newProperty'. Whats the best way to achieve what I want to do? (I'm deprecating and renaming about 30 properties)
Setting the getters/setters manually returns the same error
- (void)setOldProperty:(MyClass *)oldProperty {
_newProperty=oldProperty;
}
- (MyClass *)oldProperty:(MyClass *)oldProperty {
return _newProperty;
}
EDIT: Solution I used with the help of BlackRiders input -------------------------------------------------------------
Interface:
#property (nonatomic, strong) MyClass *newProperty;
- (void)setOldProperty:(MyClass *)oldProperty __attribute__((deprecated));
- (MyClass *)oldProperty __attribute__((deprecated));
Implementation:
- (void)setOldProperty:(MyClass *)oldProperty {
_newProperty=oldProperty;
}
- (MyClass *)oldProperty {
return _newProperty;
}
I would have just one actual property to avoid confusion and name collisions. For example:
property (getter=oldProperty, setter=setOldProperty:) MyClass *newProperty;
You can also optionally create the methods newProperty and setNewProperty:. You can also throw in some #warning statements in the getter and setter you want people to stop using.
This question already has answers here:
Protected methods in Objective-C
(9 answers)
Closed 9 years ago.
Simply put, I need a way to have some private methods in a class that are only exposed for its subclasses, and it is difficult (maybe impossible) to do this in Objective-C.
What I did so far:
// MyClass.h
#protocol MyClassProtectedMethodsProtocol
- (void)__protectedMethod;
#end
#interface MyClass : NSObject
- (void)publicMethod;
- (id<MyClassProtectedMethodsProtocol>)protectedInstanceForSubclass:(id)subclass;
#end
Then:
// MyClass.m
#import "MyClass.h"
#interface MyClass() <MyClassProtectedMethodsProtocol>
#end
#implementation MyClass
- (void)publicMethod
{
// something
}
- (id<MyClassProtectedMethodsProtocol>)protectedInstanceForSubclass:(id)subclass
{
if ([subclass isKindOf:MyClass.class] && ![NSStringFromClass(subclass.class) isEqualToString:NSStringFromClass(MyClass.class)])
{
// the subclass instance is a kind of MyClass
// but it has different class name, thus we know it is a subclass of MyClass
return self;
}
return nil;
}
- (void)__protectedMethod
// something protected
{
}
#end
Then the subclass of MyClass can just:
id<MyClassProtectedMethodsProtocol> protectedMethodInstance = [self protectedMethodForSubclass:self];
if (protectedMethodInstance != nil)
{
[protectedMethodInstance protectedMethod];
}
This way does not break OO (compared to calling the private method and ignoring the compiler warning, or even guessing the private method name as only .h is known), but a protocol is needed for the available protected methods and once this is exposed, in a big project that we only deliver interface and static library to client, client can actually know the private methods and try to call them regardless of warning. And the bigest problem is from outside of the subclass, user can as well call this method to get the protectedInstance. Can anyone advice?
Thanks
Check this: Protected methods in Objective-C
Simply put, there is no way to prevent a method from being called in Objective-C, since ultimately, the client can still call performSelector on any object.
A standard way to handle this scenario is to include the internal methods in a separate header, like MySuperClass_Internal.h. Use a class extension: #interface MySuperClass (Internal). Do not install MySuperClass_Internal.h at /usr/local/include or in the framework, or however you're delivering the library to your clients.