I develop an app that using CommonCrypto library. The problem is I can create an instance in Swift file. My object created using Objective- C. It seems can't create bridging header very well.
Error message
/Users/MNurdin/Documents/iOS/xxxxx/Models/Main.swift:15:9: 'CustomObject' does not have a member named 'encrypt'
CustomObject.h
#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonCrypto.h>
#import "GTMBase64.h"
#interface CustomObject : NSObject
+ (NSString*)encrypt:(NSString*)plainText withKey:(NSString*)key;
#end
CustomObject.m
#import "CustomObject.h"
#implementation CustomObject
+ (NSString*)encrypt:(NSString*)plainText withKey:(NSString*)key{
/*--*/
return result;
}
#end
Global.swift
var instanceOfCustomObject: CustomObject = CustomObject()
println(instanceOfCustomObject.encrypt("p#$$w0rd","12345678"))
The initial + in the declaration indicates that
+ (NSString*)encrypt:(NSString*)plainText withKey:(NSString*)key;
is a class method in Objective-C. You have to call it on the
class (or type in Swift linguage) itself, not on an instance:
let encrypted = CustomObject.encrypt("p#$$w0rd", withKey: "12345678")
Related
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
oc project;
I has class like this
#interface MyObject : NSObject
#property(noatomic,getter=getNode)id node;
#end
and in swift file
var node = MyObject().getNode
and getNode method not found ?
anyone helps
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
}
}
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
Hello I want to use this method inside my swift class, already create the header, but I only manage to access the method (setApiKey). this is the code in objective c
#import <Foundation/Foundation.h>
#class CLQResponseHeaders, CLQError;
#class CLQToken;
NS_ASSUME_NONNULL_BEGIN
#interface Culqi : NSObject
/**
* gets singleton object.
* #return singleton
*/
+ (Culqi *_Nonnull)sharedInstance;
+ (void)setApiKey:(NSString *_Nonnull)apiKey;
#pragma mark - Tokens
- (void)createTokenWithCardNumber:(NSString *_Nonnull)cardNumber
cvv:(NSString *_Nonnull)cvv
expirationMonth:(NSString *_Nonnull)expirationMonth
expirationYear:(NSString *_Nonnull)expirationYear
email:(NSString *_Nonnull)email
metadata:(NSDictionary * _Nullable)metadata
success:(void (^_Nullable)(CLQResponseHeaders *_Nonnull responseHeaders, CLQToken * _Nonnull token))success
failure:(void (^_Nullable)(CLQResponseHeaders *_Nonnull responseHeaders, CLQError * _Nonnull businessError, NSError * _Nonnull error))failure;
#end
NS_ASSUME_NONNULL_END
My swift code is
import Foundation
import UIKit
class RegisterController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//THIS METHOD CALL SUCCESS
Culqi.setApiKey("")
}
}
Update: the method setApiKey is accessible, what happens is that I can not call the method "createTokenWithCardNumber"
Declare your function/method setApiKey in header file of your Objective-C class.
// Move following line/code to header file (.h) of your objective-c class
+ (void)setApiKey:(NSString *_Nonnull)apiKey;
Your method createTokenWithCardNumber is an instance method (not a class method), you need to create an instance of your class to access it. Try this,
let c = Culqi()
c.createTokenWithCardNumber(#<your parameter arguments>#)
// or use shared instance
Culqi.sharedInstance().createTokenWithCardNumber(#<your parameter arguments>#)