JSON To Model Mapping in Objective-C - ios

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.

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

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

how to inherit model with json Mapping functionality?

Hi I want to create model like below
is it possible to create model inheritance with Matle/JSON Model framework ?
ex.
#interface basePropertyModel : NSObject
#property (nonatomic,readwrite) int pid;
#property (nonatomic) contactModel *mContact; // contact is also model
#end
#interface projectModel : basePropertyModel
#property (nonatomic,retain) NSString *date_launch,*type_specifications;
end
Q.1)Please suggest me best way to use model
Q.2)Can i assign value of json response dict directly to my model as example ContactModel which can be directly assign json response dict.
Thanks & regards

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.

Monotouch binding - "Cannot cast from source type to destination type."

I am a newbie on Monotouch. Recently, I am working on a Monotouch binding project that binds a custom iOS framework that developed myself into a .NET framework library. I follow the instructions on Xamarin but currently I am having an issue that cannot be resolved. This is my code.
**HEADER FILE IN OBJECTIVE C**
*GRG.h*
#interface GRG: NSObject {}
// Shared instance
+ (GRG*) sharedG;
// Preference class
#property (nonatomic, readonly) GRGPreferences *preferences;
// Driver version
#property (readonly,copy) NSString* driverVersion;
// More parameters...
#end
*GRGPreferences.h*
#interface GRGPreferences : NSObject <GRGPreferencesProtocol>{}
// Enable DEBUG
#property BOOL debugEnabled;
// More parameters...
#end
*GRGPreferencesProtocol.h*
#protocol GRGPreferencesProtocol <NSObject>
// More parameters...
#end
I convert my header file into this
**API DEFINITION**
[BaseType (typeof (NSObject))]
interface GRG
{
[Static][Export("sharedG")]
GRG SharedG{ get; }
[Export("preferences")]
GRGPreferences Preferences{ get;}
[Export("driverVersion", ArgumentSemantic.Copy)]
string DriverVersion {get;}
}
[BaseType (typeof (GRGPreferencesProtocol))]
public interface GRGPreferences
{
[Export("debugEnabled")]
bool DebugEnabled{ get; set;}
}
[BaseType(typeof (NSObject))]
[Model]
public interface GRGPreferencesProtocol
{}
After that, I created a test app on mono to test the newly created library and get access to the values I created. However, I got an error.
Console.WriteLine(GRG.sharedG.DriverVersion);
- This works fine. It returns the proper value.
GRGPreferences pref = GRG.SharedG.Preferences;
- Error : "Cannot cast from source type to destination type."
Console.WriteLine(GRG.sharedG.Preferences.DebugEnabled);
- Error : "Cannot cast from source type to destination type."
Can anyone please help me?
From a quick look I think this is what you want:
[BaseType (typeof (NSObject))]
public interface GRGPreferences : GRGPreferencesProtocol {
Your GRGPreferences type inherits from NSObject while implementing the protocol you want.

Resources