Swift instance variable with protocol - ios

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.

Related

Accessing property of forwardly declared enum from swift

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
}
}

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

JSON To Model Mapping in Objective-C

I am trying to map the JSON I received to a model object in Objective-C.Before I always used ObjectMapper when I coded in swift. I can't seem to grasp the steps I need to go through to achieve the same kind of result when I used ObjectMapper.The problem is when using ObjectMapper you write the code where the mapping happens inside the model class such as;
Ex:
public struct Item: Mappable{
public var name: String?
public var id: Int?
public var description: String?
public init?(map: Map) {
}
init(){
}
public mutating func mapping(map: Map) {
name <- map["name"]
id <- map["id"]
description <- map["description"]
}
}
I understand that I have to write some kind of underlying helper classes to achieve the same kind of functionality but I can't seem to manage because I don't understand the concept at all. I could not find or even if I did,I could not understand how to do it. Please Help.
My model class would look like this in objective-c Im guessing:
#interface BBKProductMetaData : NSObject
#property (nonatomic, strong,nonnull, readonly) NSString *name;
#property (nonatomic, strong,nonnull, readonly) NSInteger *id;
#property (nonatomic, assign,nonnull, readonly) NSString *description;
#end
What am I supposed to do to map my JSON response to this model.
Objective-c is not much advance as Swift. So you have to remember some things, like
If you are creating models, that means it must be accessible to other class, so you have to write that all properties and other declaration things which you want to access to other classes should be in .h file not in .m file
In objective-c you have to import all model classes to header, which you want to access.

NS_ENUM error when declared before class instance variables

Example .h file:
#interface MyClass : NSObject
typedef NS_ENUM(int, myType) {
Something,
SomethingElse,
SomethingElseElse,
YetAnotherSomethingElse
};
{ //Error On This Line: Expected Identifier or '('
int aInstanceVariable;
}
//Some Methods go here
#end
Why am I getting that error (see the comment in the code above)? It works fine when below the class instance variable declaration, but I would like to use it as the type for one of my instance variables.
Thanks to #CarlVeazey, I discovered that the answer was simple: Move the typedef declaration to above #interface. The reason for this is that types cannot be owned by a class or an instance of a class, and therefore cannot be in the interface for a class.

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.

Resources