Accessing property of forwardly declared enum from swift - ios

Given that there is an ObjC compatible enum written in Swift:
// from MessageType.swift
#objc enum MessageType: Int {
case one
case two
}
and an ObjC class with a property of type MessageType which has to be forwardly declared:
// from Message.h
typedef NS_ENUM(NSInteger, MessageType);
#interface Message: NSObject
#property (nonatomic, readonly) MessageType messageType;
#end
In order to use the Messagein the rest of the Swift codebase, the Message.h was added into the bridging header:
// from App-Bridging-Header.h
#import "Message.h"
Now, imagine there is a Swift class that tries to read the messageType property:
// from MessageTypeReader.swift
class MessageTypeReader {
static func readMessageType(of message: Message) -> MessageType {
return message.messageType
}
}
The compilation would fail with the following error:
Value of type 'Message' has no member 'messageType'
My question would be: Is there a way to forwardly declare a Swift enum in order for the MessageTypeReader to be able to access the property?
Note: I am aware of the possibility of rewriting the Message into Swift or importing App-Bridging-Header.h into Message.h, but that is not an option here, I am looking for a solution that would work with the current setup.

I guess one reason to use NS_ENUM on Objective-C side is to have compile time checks whether the switch statement usages are exhaustive.
If that's the case one could utilize C unions.
Objective-C Header
typedef NS_ENUM(NSInteger, MessageType);
union MessageTypeU {
MessageType objc;
NSInteger swift;
};
#interface Message : NSObject
#property (nonatomic, readonly) union MessageTypeU messageType;
#end
So the basic idea is:
Swift imports C unions as Swift structures. Although Swift doesn’t support natively declared unions, a C union imported as a Swift structure still behaves like a C union.
...
Because unions in C use the same base memory address for all of their fields, all of the computed properties in a union imported by Swift use the same underlying memory. As a result, changing the value of a property on an instance of the imported structure changes the value of all other properties defined by that structure.
see here: https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/using_imported_c_structs_and_unions_in_swift
Objective-C Implementation Example
#interface Message ()
#property (nonatomic, readwrite) union MessageTypeU messageType;
#end
#implementation Message
- (instancetype)init
{
self = [super init];
if (self) {
_messageType.objc = MessageTypeTwo;
[self testExhaustiveCompilerCheck];
}
return self;
}
- (void)testExhaustiveCompilerCheck {
switch(self.messageType.objc) {
case MessageTypeOne:
NSLog(#"messageType.objc: one");
break;
case MessageTypeTwo:
NSLog(#"messageType.objc: two");
break;
}
}
#end
Usage on Swift Side
Since the messageType.swift property comes originally from the Swift side (see definition of MessageType) we can safely use force-unwrap.
class MessageTypeReader {
static func readMessageType(of message: Message) -> MessageType {
return MessageType(rawValue: message.messageType.swift)!
}
}

Here is a workaround suggested by Cristik (all credit goes to them):
In Message.h, declare messageType as NSInteger :
#interface Message : NSObject
#property (nonatomic, readonly) NSInteger messageType;
#end
Using NS_REFINED_FOR_SWIFT is recommended by Apple, but not necessary here.
In Swift, add the following Message extension :
extension Message {
var messageType: MessageType {
guard let type = MessageType(rawValue: self.__messageType) else {
fatalError("Wrong type")
}
return type
}
}

Related

Creating a custom class in IOS Objective C

I have an existing project that requires porting a Swift Mock to to Objective C which I haven't used before. I have been trying to complete this so would value some help on the approach to take. I have tried several tutorials but I seem to have xcode sharing errors.
Error:
Xcode doesn't seem to recognise the class in the VC
Property 'assetName' not found on object of type '__strong id'
## Code to port to objective C
struct Asset {
let assetName: String
let assetColour: String
let assetValue: Int
let AssetSource: String
}
struct ExternalAsset {
let assetName: String
let assetColour: String?
let assetSupplier: String?
let assetValue: Int?
let assetMargin: Double?
}
Here is the attempt I made from Objective C from following som older tutorials
ExternalAsset.h
#interface ExternalAsset:NSObject
#property (nonatomic, strong)NSString *assetName;
#property (nonatomic, strong)NSString *assetColour;
#property (nonatomic, strong)NSString *assetSupplier;
#end
ExternalAsset.m
#import "ExternalAsset.h"
#implementation ExternalAsset
#end
in my calling class - I have the following:
AssetCatalogue.h
- (void)didPayforAsset:(ExternalAsset *)assetPaidFor // Error Message ( "Expected a Type")
{
NSMutableDictionary *paidItems = [#{
"AssetName": assetPaidFor.assetName,
// `Error: Property 'AssetName' not found on object of type '__strong id'
} mutableCopy];
}
#end

Error while assigning Swift delegate to Objective-C object

This is my Swift class :
class MyClass : NSObject {
public var inAppMessagesController: MPInAppMessagesController!
fun myFunction() {
self.inAppMessagesController.inAppInteractionDelegate = self // Error in this line - Cannot assign value of type 'MyClass' to type 'MPInAppMessageControllerDelegate?'
}
}
extension MyClass : MPInAppMessageControllerDelegate {
// Functions
}
As stated in comments, this is the error -
Cannot assign value of type 'MyClass' to type
'MPInAppMessageControllerDelegate?'
inAppInteractionDelegate in Objective-C class MPInAppMessagesController :
#interface MPInAppMessagesController : NSObject
#property (nonatomic, weak, nullable) id <MPInAppMessageControllerDelegate> inAppInteractionDelegate;
#end
MPInAppMessageControllerDelegate declared in MPInAppMessagesController.h :
#protocol MPInAppMessageControllerDelegate<NSObject>
// Functions
#end
The only missing part is you need to include this class inside the bridging file
#import "MPInAppMessagesController.h"
Look here to a SwiftObjc

Swift instance variable with protocol

I have to translate the following lines of Objective-c code into swift. This is a sample from the Objective-c JSONModel-Framework where the Optional protocol provided by the Framework is applied to an instance variable of type NSString. I found a related post but i didn't managed to achieve it. With my MYModel.swift implementation Xcode complains Cannot specialize non-generic type NSString
thx for your help!
MYModel.swift
#objc(MYModel) public class MYModel : JSONModel {
...
public var name : NSString<Optional>
...
}
MYModel.h
#interface MYModel : JSONModel
...
#property (strong, nonatomic) NSString<Optional>* name;
...
JSONModel.h
...
/**
* Protocol for defining optional properties in a JSON Model class. Use like below to define
* model properties that are not required to have values in the JSON input:
*
* #property (strong, nonatomic) NSString<Optional>* propertyName;
*
*/
#protocol Optional
#end
...
The < and > are not for conforms to protocol. It is for Types with generics like Array:
Array<T>
so you can write var a: Array<String>.
You want something else, a variable should be a Type String and conform to the protocol
You can extend String with the protocol and add the needed functions yourself.
Since your Optional protocol is empty, it is enough to write:
extension NSString: Optional {} // you can use String if you like
To create the protocol write in Swift:
protocol Optional {}
You can Objective-C create the protocol, too.
You should not use Optional, because there is already one, but because Swift has namespacing, it works.
You could of course write something like that:
extension NSString: JsonOptProtocol {}
protocol JsonOptProtocol {} // or create that in Objective-C like you did
Documentation link.
Optional is a type declared in the standard library of Swift, at the moment JSONModel is not compatible with Swift because of this.

Getting error use of undeclared identifier "value" when assigning parameter to variable

When I assign value to age, I get this error:
#interface Person : NSObject
{
int age;
}
-(void)setAge;
#end
I tried to use self.age, yet it did not work
Here is my .m file:
#implementation Person
-(void)setAge(int)value
{
age = value;
}
#end
I tried several differnet things. ..I get this error when I type this: age = value; do you know why this is?
You should add:
-(void)setAge:(int)value;
In the header file because the current method specification you have there doesn't have a parameter.
Your method in the implementation should also have the same spec as it's missing a colon currently.
You have actually declared one method (-setAge) and implemented another (-setAge:, note the colon). You should really declare age as a property and avoid explicit ivars as much as possible. Also, I hope you have properly formatted the class in your real code.
#interface Person : NSObject
#property (nonatomic) int age;
#end
#implementation Person
-(void)setAge:(int)value
{
_age = value;
}
#end
Note that it is no longer necessary to explicitly #synthesize properties, and they automatically synthesize with an underscored ivar.

Can I use Objective-C blocks as properties?

Is it possible to have blocks as properties using the standard property syntax?
Are there any changes for ARC?
#property (nonatomic, copy) void (^simpleBlock)(void);
#property (nonatomic, copy) BOOL (^blockWithParamter)(NSString *input);
If you are going to be repeating the same block in several places use a type def
typedef void(^MyCompletionBlock)(BOOL success, NSError *error);
#property (nonatomic) MyCompletionBlock completion;
Here's an example of how you would accomplish such a task:
#import <Foundation/Foundation.h>
typedef int (^IntBlock)();
#interface myobj : NSObject
{
IntBlock compare;
}
#property(readwrite, copy) IntBlock compare;
#end
#implementation myobj
#synthesize compare;
- (void)dealloc
{
// need to release the block since the property was declared copy. (for heap
// allocated blocks this prevents a potential leak, for compiler-optimized
// stack blocks it is a no-op)
// Note that for ARC, this is unnecessary, as with all properties, the memory management is handled for you.
[compare release];
[super dealloc];
}
#end
int main () {
#autoreleasepool {
myobj *ob = [[myobj alloc] init];
ob.compare = ^
{
return rand();
};
NSLog(#"%i", ob.compare());
// if not ARC
[ob release];
}
return 0;
}
Now, the only thing that would need to change if you needed to change the type of compare would be the typedef int (^IntBlock)(). If you need to pass two objects to it, change it to this: typedef int (^IntBlock)(id, id), and change your block to:
^ (id obj1, id obj2)
{
return rand();
};
EDIT March 12, 2012:
For ARC, there are no specific changes required, as ARC will manage the blocks for you as long as they are defined as copy. You do not need to set the property to nil in your destructor, either.
For more reading, please check out this document:
http://clang.llvm.org/docs/AutomaticReferenceCounting.html
#property (copy)void
#property (copy)void (^doStuff)(void);
The actual Apple documentation which states precisely what to use:
Apple doco.
Your .h file:
// Here is a block as a property:
//
// Someone passes you a block. You "hold on to it",
// while you do other stuff. Later, you use the block.
//
// The property 'doStuff' will hold the incoming block.
#property (copy)void (^doStuff)(void);
// Here's a method in your class.
// When someone CALLS this method, they PASS IN a block of code,
// which they want to be performed after the method is finished.
-(void)doSomethingAndThenDoThis:(void(^)(void))pleaseDoMeLater;
// We will hold on to that block of code in "doStuff".
Your .m file:
-(void)doSomethingAndThenDoThis:(void(^)(void))pleaseDoMeLater
{
// Regarding the incoming block of code, save it for later:
self.doStuff = pleaseDoMeLater;
// Now do other processing, which could follow various paths,
// involve delays, and so on. Then after everything:
[self _alldone];
}
-(void)_alldone
{
NSLog(#"Processing finished, running the completion block.");
// Here's how to run the block:
if ( self.doStuff != nil )
self.doStuff();
}
Beware of out-of-date example code.
With modern (2014+) systems this is the correct and documented approach.
For posterity / completeness's sake… Here are two FULL examples of how to implement this ridiculously versatile "way of doing things". #Robert's answer is blissfully concise and correct, but here I want to also show ways to actually "define" the blocks.
#interface ReusableClass : NSObject
#property (nonatomic,copy) CALayer*(^layerFromArray)(NSArray*);
#end
#implementation ResusableClass
static NSString const * privateScope = #"Touch my monkey.";
- (CALayer*(^)(NSArray*)) layerFromArray {
return ^CALayer*(NSArray* array){
CALayer *returnLayer = CALayer.layer
for (id thing in array) {
[returnLayer doSomethingCrazy];
[returnLayer setValue:privateScope
forKey:#"anticsAndShenanigans"];
}
return list;
};
}
#end
Silly? Yes. Useful? Hells yeah. Here is a different, "more atomic" way of setting the property.. and a class that is ridiculously useful…
#interface CALayoutDelegator : NSObject
#property (nonatomic,strong) void(^layoutBlock)(CALayer*);
#end
#implementation CALayoutDelegator
- (id) init {
return self = super.init ?
[self setLayoutBlock: ^(CALayer*layer){
for (CALayer* sub in layer.sublayers)
[sub someDefaultLayoutRoutine];
}], self : nil;
}
- (void) layoutSublayersOfLayer:(CALayer*)layer {
self.layoutBlock ? self.layoutBlock(layer) : nil;
}
#end
This illustrates setting the block property via the accessor (albeit inside init, a debatably dicey practice..) vs the first example's "nonatomic" "getter" mechanism. In either case… the "hardcoded" implementations can always be overwritten, per instance.. a lá..
CALayoutDelegator *littleHelper = CALayoutDelegator.new;
littleHelper.layoutBlock = ^(CALayer*layer){
[layer.sublayers do:^(id sub){ [sub somethingElseEntirely]; }];
};
someLayer.layoutManager = littleHelper;
Also.. if you want to add a block property in a category... say you want to use a Block instead of some old-school target / action "action"... You can just use associated values to, well.. associate the blocks.
typedef void(^NSControlActionBlock)(NSControl*);
#interface NSControl (ActionBlocks)
#property (copy) NSControlActionBlock actionBlock; #end
#implementation NSControl (ActionBlocks)
- (NSControlActionBlock) actionBlock {
// use the "getter" method's selector to store/retrieve the block!
return objc_getAssociatedObject(self, _cmd);
}
- (void) setActionBlock:(NSControlActionBlock)ab {
objc_setAssociatedObject( // save (copy) the block associatively, as categories can't synthesize Ivars.
self, #selector(actionBlock),ab ,OBJC_ASSOCIATION_COPY);
self.target = self; // set self as target (where you call the block)
self.action = #selector(doItYourself); // this is where it's called.
}
- (void) doItYourself {
if (self.actionBlock && self.target == self) self.actionBlock(self);
}
#end
Now, when you make a button, you don't have to set up some IBAction drama.. Just associate the work to be done at creation...
_button.actionBlock = ^(NSControl*thisButton){
[doc open]; [thisButton setEnabled:NO];
};
This pattern can be applied OVER and OVER to Cocoa API's. Use properties to bring the relevant parts of your code closer together, eliminate convoluted delegation paradigms, and leverage the power of objects beyond that of just acting as dumb "containers".
Of course you could use blocks as properties. But make sure they are declared as #property(copy). For example:
typedef void(^TestBlock)(void);
#interface SecondViewController : UIViewController
#property (nonatomic, copy) TestBlock block;
#end
In MRC, blocks capturing context variables are allocated in stack; they will be released when the stack frame is destroyed. If they are copied, a new block will be allocated in heap, which can be executed later on after the stack frame is poped.
Disclamer
This is not intended to be "the good answer", as this question ask explicitly for ObjectiveC. As Apple introduced Swift at the WWDC14, I'd like to share the different ways to use block (or closures) in Swift.
Hello, Swift
You have many ways offered to pass a block equivalent to function in Swift.
I found three.
To understand this I suggest you to test in playground this little piece of code.
func test(function:String -> String) -> String
{
return function("test")
}
func funcStyle(s:String) -> String
{
return "FUNC__" + s + "__FUNC"
}
let resultFunc = test(funcStyle)
let blockStyle:(String) -> String = {s in return "BLOCK__" + s + "__BLOCK"}
let resultBlock = test(blockStyle)
let resultAnon = test({(s:String) -> String in return "ANON_" + s + "__ANON" })
println(resultFunc)
println(resultBlock)
println(resultAnon)
Swift, optimized for closures
As Swift is optimized for asynchronous development, Apple worked more on closures.
The first is that function signature can be inferred so you don't have to rewrite it.
Access params by numbers
let resultShortAnon = test({return "ANON_" + $0 + "__ANON" })
Params inference with naming
let resultShortAnon2 = test({myParam in return "ANON_" + myParam + "__ANON" })
Trailing Closure
This special case works only if the block is the last argument, it's called trailing closure
Here is an example (merged with inferred signature to show Swift power)
let resultTrailingClosure = test { return "TRAILCLOS_" + $0 + "__TRAILCLOS" }
Finally:
Using all this power what I'd do is mixing trailing closure and type inference (with naming for readability)
PFFacebookUtils.logInWithPermissions(permissions) {
user, error in
if (!user) {
println("Uh oh. The user cancelled the Facebook login.")
} else if (user.isNew) {
println("User signed up and logged in through Facebook!")
} else {
println("User logged in through Facebook!")
}
}
Hello, Swift
Complementing what #Francescu answered.
Adding extra parameters:
func test(function:String -> String, param1:String, param2:String) -> String
{
return function("test"+param1 + param2)
}
func funcStyle(s:String) -> String
{
return "FUNC__" + s + "__FUNC"
}
let resultFunc = test(funcStyle, "parameter 1", "parameter 2")
let blockStyle:(String) -> String = {s in return "BLOCK__" + s + "__BLOCK"}
let resultBlock = test(blockStyle, "parameter 1", "parameter 2")
let resultAnon = test({(s:String) -> String in return "ANON_" + s + "__ANON" }, "parameter 1", "parameter 2")
println(resultFunc)
println(resultBlock)
println(resultAnon)
You can follow the format below and can use the testingObjectiveCBlock property in the class.
typedef void (^testingObjectiveCBlock)(NSString *errorMsg);
#interface MyClass : NSObject
#property (nonatomic, strong) testingObjectiveCBlock testingObjectiveCBlock;
#end
For more info have a look here

Resources