How to call swift native function from react native? - objective-c

I have a swift class, which i am exposing to react-native, inside that i have a function that is also exposed to react-native. Now when react native calls that function it does a lot of this internally, but after some point of time it returns an object.
Now it will call a specific function that will get the object. I cannot change the parameter to that function. But i have another function, to which i want to return to react native. How can i do it.
func AckCallback(response:APIResponse) -> Void
{
print(response)
}
for this function I cannot change the paremeter, becaused it had been used a lot of places, But I want to return that response from this function to react-native. If anybody know this issue, please let me know.
#objc func sendEvent(_ response: APIResponse, callback: (NSObject) -> ())
-> Void {
callback( [[
"responseCode" : "Working",
]] as NSObject)
}
I just want to know how to use this sendEvent inside the AckCallback, or is there any other way to send that **
response: APIResponse
**
to react-native.

For the first create Swift class (e.g YourModule.swift)
//
// YourModule.swift
//
#objc(YourModule)
class YourModule: NSObject {
#objc func callNativeEvent(callback:RCTResponseSenderBlock)
-> Void {
// Here you can do your work and pass an object to the callback function.
// You can save assign a `callback` to the class property (e.g self.eventCallback = callback)
// and invoke that self.eventCallback after the asynchronous code ol somewhere else
NSObject *obj = [[NSObject alloc] init]; // your object here
callback([NSNull(), obj]);
// or if you want to return an error
// callback(["Error calling NativeEvent", NSNull()]);
// I'm not sure that RCTResponseSenderBlock works the same as in previous react-native versions. Maybe now you can pass an Object instead of an Array.
}
}
Create a Bridge file (e.g. YourModuleBridge.m)
//
// YourModuleBridge.m
//
#import <Foundation/Foundation.h>
#import "UIKit/UIKit.h"
#import <React/RCTBridgeModule.h>
#interface RCT_EXTERN_MODULE(YourModule, NSObject)
RCT_EXTERN_METHOD(callNativeEvent:(RCTResponseSenderBlock)callback);
#end
Also, you need Bridging-Header file if it doesn't exist in your project.
//
// YourModule-Bridging-Header.h
//
#ifndef YourModule_Bridging_Header_h
#define YourModule_Bridging_Header_h
#if __has_include("RCTBridgeModule.h")
#import "RCTBridgeModule.h"
#else
#import <React/RCTBridgeModule.h>
#endif
#endif /* YourModule_Bridging_Header_h */
And from JS
import { NativeModules } from 'react-native';
const YourModule = NativeModules.YourModule;
...
YourModule.callNativeEvent((error, response) => {
console.log('Error', error, 'Response', response);
});

In iOS Project Create 2 Files
SwiftComponentManager.swift and SwiftComponentManager.m create these 2 files.
SwiftComponentManager.swift ->
#objc(SwiftComponentManager)
class SwiftComponentManager: NSObject {
#objc func passValueFromReact(_ value : String) {
debugPrint(" Print Here \(value)")
}
}
In SwiftComponentManager.m
#import <Foundation/Foundation.h>
#import "React/RCTBridgeModule.h"
#interface RCT_EXTERN_MODULE(SwiftComponentManager, NSObject)
RCT_EXTERN_METHOD(passValueFromReact:(NSString *)value) //Here exported your swift function for React Native
#end
Here start work in React-Native project
Now How will call this Swift function in React Native.
Import your SwiftComponent in React JS file
const { SwiftComponentManager } = NativeModules
Now call your function with value where you want in JS file
SwiftComponentManager.passValueFromReact("Hello World")

Related

Custom React Native Plugin is null (iOS)

I'm creating a react native plugin. Android works like charm, but iOS makes some troubles. The plugin is always null.
Files:
myPlugin-bridging-header.h
#import <React/RCTBridgeModule.h>
myPlugin.m
#import <React/RCTBridgeModule.h>
#interface RCT_EXTERN_MODULE(MyPlugin, NSObject)
RCT_EXTERN_METHOD(multiply:(float)a withB:(float)b
withResolver:(RCTPromiseResolveBlock)resolve
withRejecter:(RCTPromiseRejectBlock)reject)
#end
myPlugin.swift
import Foundation
#objc(MyPlugin)
class MyPlugin: NSObject {
#objc(multiply:withB:withResolver:withRejecter:)
func multiply(a: Float, b: Float, resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock) -> Void {
resolve(a*b)
}
}
index.js
import { NativeModules } from 'react-native';
const { MyPlugin } = NativeModules;
export default MyPlugin;
Usage
import MyPlugin from 'my-plugin';
[...]
console.log(MyPlugin); // is null
MyPlugin.multiply(2, 8).then((value) => {
console.log('value: ', value);
});
I don't know what is missing. The plugin is always null. Is #interface RCT_EXTERN_MODULE(MyPlugin, NSObject) not enough to make the module available?
Add missing function moduleName in your swift file
override class func moduleName() -> String! {
return "MyPlugin"
}

Not able to import "project-name-Swift.h" file xcode

I have 2 files.
"CodeInjection.m"
#import <Foundation/Foundation.h>
#import "BiplovCodeInjection-Swift.h"
#interface CodeInjection: NSObject
#end
#implementation CodeInjection
static void __attribute__((constructor)) initialize(void){
NSLog(#"==== Code Injection in Action==== with sniffer");
[[CodeInjectionSwift shared] performTask];
}
#end
My second file is.
"BiplovCodeInjectionSwift.swift"
import Foundation
import NetworkInterceptor
#objc class CodeInjectionSwift: NSObject {
#objc public static let shared = CodeInjectionSwift()
override private init(){}
#objc func performTask(){
let requestSniffers: [RequestSniffer] = [
RequestSniffer(requestEvaluator: AnyHttpRequestEvaluator(), handlers: [
SniffableRequestHandlerRegistrable.console(logginMode: .nslog).requestHandler()
])
]
let requestRedirectors: [RequestRedirector] = [
RequestRedirector(requestEvaluator: DomainHttpRequestEvaluator(domain: "www.antennahouse.com"), redirectableRequestHandler: AlternateUrlRequestRedirector(url: URL(string: "https://www.rhodeshouse.ox.ac.uk/media/1002/sample-pdf-file.pdf")!))
]
let networkConfig = NetworkInterceptorConfig(requestSniffers: requestSniffers,
requestRedirectors: requestRedirectors)
NetworkInterceptor.shared.setup(config: networkConfig)
NetworkInterceptor.shared.startRecording()
}
}
It fails on the line "#import "BiplovCodeInjection-Swift.h"
I built a new project > framework.
Here's my file strucuture
Why is it that I can't import the header file?

Why do I have to label my second argument for a react native to swift bridged function

Say I have the following Swift class:
#objc(ExampleClass)
class ExampleClass: NSObject {
init() {}
#objc func exampleMethod(_ message: String, _ properties: [String: Any]? = nil) -> Void {}
}
And the following Header:
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#interface RCT_EXTERN_MODULE(ExampleClass, NSObject)
RCT_EXTERN_METHOD(exampleMethod:(NSString *)name (NSDictionary *)properties)
#end
Which I then call with the following React Native code:
import { NativeModules } from 'react-native'
NativeModules.ExampleClass.exampleMethod('example', {'hello': 'world'})
This results in the following error message:
ExceptionsManager.js:73 Exception 'exampleMethod: is not a recognized Objective-C method.' was thrown while invoking trackEvent on target SegmentTracker with params (
"example",
{
hello = world;
}
)
However, if I change the function signature to be labelled like so:
#objc func exampleMethod(_ message: String, withProperties properties: [String: Any]? = nil) -> Void {}
}
And adjust the RCT_EXTERN_METHOD function like so:
RCT_EXTERN_METHOD(exampleMethod:(NSString *)name withProperties:(NSDictionary *)properties)
Why does the second argument have to be labelled? Why doesn't the first one?

How to access private members of an Objective-C class from a Swift extension?

I'm trying to extend an Objective-C class in Swift and make it conform to the Equatable protocol. This requires to access some private members of the extended class, which the compiler doesn't let me do. What is the correct way to do it without making the private members public?
My Swift code:
import Foundation
extension ShortDate : Equatable { }
public func == (lhs: ShortDate, rhs: ShortDate) -> Bool {
if (lhs.components.year == rhs.components.year)
&& (lhs.components.month == rhs.components.month)
&& (lhs.components.day == rhs.components.day) {
return true;
}
return false;
}
Objective-C:
#interface ShortDate : NSObject<NSCopying, NSCoding> {
NSDate *inner;
NSDateComponents *components; // The date split into components.
}
...
#end
The error I'm getting:
ShortDate.swift:26:9: 'ShortDate' does not have a member named 'components'
I came across this question while trying to find a way to access a private variable of a class from one of the SDKs we use. Since we don't have or control the source code we can't change the variables to properties. I did find that the following solution works for this case:
extension ObjcClass {
func getPrivateVariable() -> String? {
return value(forKey: "privateVariable") as? String
}
open override func value(forUndefinedKey key: String) -> Any? {
if key == "privateVariable" {
return nil
}
return super.value(forUndefinedKey: key)
}
}
Overriding value(forUndefinedKey:) is optional. value(forKey:) will crash if the private variable doesn't exist on the class unless you override value(forUndefinedKey:) and provide a default value.
I believe that there is no way to access Objective-C instance variables from Swift. Only Objective-C properties get mapped to Swift properties.

swift class in Objective-C: unknown receiver

I wrote a class in a swift-file:
class UtilityMethods {
class func userId() -> Integer {
...
}
class func setUserId(userId : Int) {
...
}
}
I'm importing the -swift.h-header which compiles fine, but I can't use
[UtilityMethods userId];
in my Objective-C code:
Unknown receiver 'UtilityMethods'; did you mean 'UtilMethods'?
UtilMethodsis an Objective-C class I'd like to replace. Am I missing something?
EDIT
With the help of Lance, the class is now recognized, but the getter method isn't, unfortunately, the header files looks like the following:
SWIFT_CLASS("_TtC15...14UtilityMethods")
#interface UtilityMethods : NSObject
+ (void)setUserId:(NSInteger)userId;
- (instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
Why is the getter missing?
In order to have a Swift class available to Objective C you have two options:
Option 1: Subclass NSObject (or some other Objective C class)
class UtilityMethods : NSObject {
class func userId() -> Int {
...
}
class func setUserId(userId: Int) {
...
}
}
Option 2: Add the #objc attribute to your class telling the Swift compiler to make an Objective C object that uses dynamic dispatch rather than static dispatch for method calls
#objc class UtilityMethods {
class func userId() -> Int {
...
}
class func setUserId(userId: Int) {
...
}
}
In my case, I was doing #objc correctly, but in changing from my old Objective-C .h file to my new swift file, I had accidentally lost the Target Membership of the new file, so it wasn't being included in the build.