Hi guys Im kinda new to Objective-C and I've got a little code that I would like to convert it from Swift -> Objective-C. I've got a variable which is a closures but not sure how doing it in Objective-C
Here's the variable:
var didTimerFire: ((UICollectionViewCell) -> Void)?
also is there's any "self" in objective-C? sorry for being a noob but again kinda new to Objective-C :)
In Objective-C there are Blocks:
If you want to use them as property it goes like:
#property (nonatomic, copy, nullability) returnType (^blockName)(parameterTypes);
Or as method parameters:
- (void)method:(returnType (^nullability)(parameterTypes))blockName;
So for you example it will go like:
#property (nonatomic, copy, nullable) void (^didTimerFire)(UICollectionViewCell);
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.
I am trying to convert my Objective C source code to swift, I have searched alot to change syntax for Blocks.
Here is my Objective C code that I want to switch to Swift :
class.h file :
#property (nonatomic, copy) void (^tapBlock)(CGFlagsCell *);
+ (NSString *)cellIdentifier;
Class.m file :
self.tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(didTapCell)];
- (void)didTapCell {
self.tapBlock(self);
}
In Swift, blocks, functions and closures are the same thing, they have the same signature and are interchangeable.
So this would give you something like this
var tapBlock: CGFlagsCell -> Void
You can add parenthesis around the parameter (optional if there is only one, recommended if there are multiple input parameters) and around the return type (optional):
var tapBlock: (CGFlagsCell) -> (Void)
Define a closure a variable of your class :
var tapBlock: (parameterTypes) -> (returnType)
Swift Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks Objective-C
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 have a block thats stored as an instance variable in a class
typedef void ((^didSelectWord)(NSString* word));
#property (nonatomic,strong) didSelectWord wordSelected;
and i want xcode to auto fillout the block like when you type [UIView animateWithDuration and xcode autocompletes a block for it.
When i autocomplete my block it just fills out
[self.suggestedSearchTermView setWordSelected:(didSelectWord)wordSelected
instead of
[self.suggestedSearchTermView setWordSelected:^(NSString *word) {
Is it possible to change something to make Xcode understand how to autocomplete this block?
Ok I did some testing.
Apparently you have two (far from perfect) options:
avoid the typedef and declare the property as
#property (nonatomic,strong) void (^wordSelected)(NSString * word);
As noted in the comments, this has the drawback of skipping the parameter name in the autocompletion.
explicitly add a setter declaration in the interface
typedef void ((^DidSelectWordBlock)(NSString* word));
#interface YourClass : NSObject
#property (nonatomic,strong) DidSelectWordBlock wordSelected;
- (void)setWordSelected:(DidSelectWordBlock)wordSelected;
#end
this will cause Xcode to resolve the type definition before the setter definition, giving you the nice autocompletion that you would expect. The obvious drawback is the extra setter declaration in the interface.
That said, you should fill in a bug report: http://openradar.appspot.com/
Declare your property without typedef, like this:
#property (nonatomic,strong) void (^wordSelected)(NSString *word);
With this definition Xcode would give you the expansion below:
MyClass *test = [MyClass new];
[test setWordSelected:(void (^)(NSString *))wordSelected];
In exacerbated frustration, I made a macro consolidating this gross process..
#define BlockProperty(SIGNATURE,TYPENAME,varname,Varname) typedef SIGNATURE; #property (nonatomic,copy) TYPENAME varname; - (void) set##Varname:(TYPENAME)_
Now what Previously would've required (for proper autocompletion)..
typedef void(^OnEvent)(BOOL ok,id result);
#property (nonatomic,copy) OnEvent varname;
- (void) setVarname:(OnEvent)_;
is simply
BlockProperty(void(^OnEvent)(BOOL ok, id result),OnEvent,varname,VarName);
QUITE a bit easier, less verbose, AND you get the benefit of the typedef AND and you don't have to create the unsightly, theoretically unneeded setter declaration!
If you WANT to reuse a "type" you'll need another one (which this time will only take THREE parameters (as the block type cannot be redeclared).
#define BlockProp(TYPENAME,varname,Varname) #property (nonatomic,copy) TYPENAME varname; - (void) set##Varname:(TYPENAME)_
BlockProp(OnEvent,anotherVar,AnotherVar);
You could just create a new block type (name) for each property even if their signatures match (using the first macro), but that's kind of gross. Enjoy!