Creating a custom class in IOS Objective C - ios

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

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.

AWS API Gateway Client for iOS returns error "Failed to serialize the body JSON"

I defined my REST API using AWS API Gateway and generated client code for iOS. When I call a method the SDK outputs this error message:
AWSiOSSDKv2 [Error]
AWSAPIGatewayClient.m line:190
__118-[AWSAPIGatewayClient invokeHTTPRequest:URLString:pathParameters:
queryParameters:headerParameters:body:responseClass:]_block_invoke_2 |
Failed to serialize the body JSON.
(null)
What is wrong?
Easy!
Make sure your AWSModel has the same number of class members as the number of JSON key paths property keys. Mine has no class member and 2 property keys.
Make sure the name of each property key matches the name of the class member. Again I had a key for "code" and no matching "code" property.
For clarity, look at the JSONKeyPathsByPropertyKey function. If you see #"abc": #"def" then you must have a property "abc" in your class otherwise the JSON conversion will fail.
// Sample JSON returned by AWS API Gateway
{"code":200, "message":"OK", "data":{"phone":"(555) 555-1234"}}
// APISample.h
#import
#import
#interface APISample : AWSModel
// We count 4 class members
#property (nonatomic, strong) NSNumber *code;
#property (nonatomic, strong) NSString *message;
#property (nonatomic, strong) NSDictionary *data;
#property (nonatomic, strong) NSNumber *phone;
#end
// APISample.m
#import "APISample.h"
#implementation APISample
// We count 4 property keys
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return #{
#"code": #"code",
#"message": #"message",
#"data": #"data",
#"phone": #"data.phone"
};
}
Tip: Notice how you can access a branch (data as NSDictionary) and traverse the document structure with dot notation (data.phone).
Bonus: a working Swift example just for you.
// Swift sample code to access AWS API Gateway under iOS
// Create a client with public access
var client : APISampleClient = APISampleClient.defaultClient()
// Comment next line if your API method does not need API key
client.APIKey = "Your API key"
client.SampleMethodGet().continueWithBlock { (task : AWSTask) -> AnyObject? in
if task.error != nil {
print("Error \(task.error)")
}
else if task.result != nil {
let output = task.result as! APISample
print("Success \(output)")
}
return nil
}

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