React-native bridge is nil when method called from Swift ViewController - ios

I have a class to send events defined as follows:
import UIKit
#objc(NativeEventSender)
class NativeEventSender: RCTEventEmitter {
override func supportedEvents() .. {
return ["Supported Event"]
}
#objc func sendEventToJS(_ titleString: String) {
self.sendEvent(withName: "Event", body: titleString)
}
}
This class is exposed to Objective-C using RCT_EXTERN_MODULE() and RCT_EXTERN_METHOD for the sendEventToJS method.
The sendEventToJS method is called in my ViewController.swift. This ViewController is exposed to React-Native through a UIView that is returned by a view manager.
Now every time the sendEventToJS function is fired, the app crashes because the Bridge is nil, with a message saying it wasn't set. The view otherwise renders properly. I have already tried converting the Obj-C example in the documentation to Swift. Tried everything from the sending event issue on the react-native repository and it still doesn't work. Have also tried a few examples on Github where the sendEvent(withName, body) method was used. Any help is greatly appreciated!

Related

How to implement protocols and delegates in SwiftUI

I was converting one of my swift project into SwiftUI. I need to convert delegtes and protocols to SwiftUI, is it allowed in SwiftUI? or any alternative methods are there? Please help me i'm so confused in SwiftUI.
I'm calling a delegate method from one of my class, then delegate method will be present in another ViewController.
//PresenterClass
protocol SplashPresenterDelegate {
func didFetchedSystemInfo(info: String)
}
class SplashPresenter: NSObject {
var delegate: SplashPresenterDelegate?
func getSystemInfo(){
self.delegate?.didFetchedSystemInfo(info: "ResponseString")
}
}
// Viewcontroller class
class myViewController: UIViewController {
.
.
.
}
extension myViewController: SplashPresenterDelegate{
func didFetchedSystemInfo(info: String){
print("delegate called")
}
}
Please help me to convert this code to SwiftUI
Typically, SwiftUI prefers the pattern of taking callbacks rather than protocol/delegates. For instance, each button in an ActionSheet has a closure that is executed when the button is tapped.
You shouldn't just convert your code over directly, though. SwiftUI, being declarative, uses different paradigms for a lot of things. For instance, you wouldn't have a didFetchInfo method, you would just assign it to a #Published variable.
Think about having "single sources of truth", where a single variable is always correct. For example, List simply takes an array and updates when the array changes, unlike UITableView where you provide the data through numberOfRows and cellForRowAt. I don't know enough about your specific project to give more detail, but those are things to think about.

How to create the Delegate Design Pattern within an Xcode Playground in Swift 5?

I encountered some kind of a problem while using a Swift Playground and trying to set up a simple Delegate Design.
The problem seems to be the fact, that protocols can not be marked as public but the source folder in a Playground is considered as a new Module. Therefore I can't find a solution for this problem.
Here is my code so far (IN THE SOURCE FOLDER, THE VIEWCONTROLLER IS IN THE PLAYGROUND FILE)
//MARK: - Imports
import UIKit
//MARK: - Protocols
protocol UIManagerDelegate {
func didChangePage(forward: Bool)
}
//MARK: - Properties & Initialisers
public class UIManager {
// Properties
private let parentView : UIView
private var delegate: UIManagerDelegate
// Initialisers
public init(for view: UIView, delegate: UIManagerDelegate) {
self.parentView = view
self.delegate = delegate
}
}
The error message I get is the following: Initializer cannot be declared public because its parameter uses an internal type. And when trying to mark the protocol as public, this also produces an error.
Do you guys have any idea on how to fix this issue?
Thanks a lot for your help in advance.
make the delegate public
public protocol UIManagerDelegate

Is there a way to have a custom class act on a UIView without having the ViewController passed (as reference) upon its initialization?

Example: I have a SpeechSynthesizer class that needs to update something in my UIView when it’s done uttering a piece of text. Since the SpeechSynthesizer class conforms to protocol AVSpeechSynthesizerDelegate, it is the one that receives the didFinish signal when the uttering has been completed. The idea here is to keep the ViewController from having too many delegate methods and a long list of protocols to conform to. The workaround I found was to have the ViewController passed in as a SpeechSynthesizer initialization parameter. This way I get to access the ViewController connected to the UIView I want to update from inside the SpeechSynthesizer class. The thing I don’t like about it is that it looks kind of ugly to have the ViewController passed in as a parameter to every single class that needs to use it. So I wonder, which other way I could accomplish this.
I suppose another way to ask the question is: How can I make the function
private func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance)
return something to a ViewController since it's not "called" by it?
I added a reply on Quora. Copying it here:
After doing some research and testing on code of my own here are 2 solutions to this problem.
Solution 1: The Delegate Pattern:
Create a custom delegate protocol in the ViewController
protocol ViewControllerDelegate:class {
func getViewLayer() -> CALayer
}
The ViewController must conform to this newly created protocol and therefore implement all the functions defined by it, so somewhere in the class ViewController you add:
public func getViewLayer() -> CALayer {
return self.view.layer
}
Then on my custom class, ReadTextMachine, I added a variable of the ViewControllerDelegate type
private weak var viewControllerDelegate: ViewControllerDelegate?
The variable must be weak and protocol must be of type class in order to solve a “retain cycle” problem (since both the custom class and the ViewController will point to each other)
You’ll notice now that the function call inside the ViewController is already “callable” from the custom class, so in my ReadTextMachine I added:
let viewLayer = self.viewControllerDelegate?.getViewLayer()
self.cameraPreview = CameraPreview(session: self.camera.getSession(), container: viewLayer!)
self.cameraPreview?.addPreview()
In the above case, my CameraPreview (yes, a 3rd class in this example) simply adds a camera preview layer on the UIView. For that it needed access to the main View’s layer.
The above code still doesn’t work because our original viewController’s instance hasn’t been passed as reference anywhere in our code. For that we add the following function in ReadTextMachine:
public func setViewControllerDelegate(viewController: ViewController) { // call this from the ViewController so that ViewController can be accessed from here.
self.viewControllerDelegate = viewController
}
The above piece of code will have to be called from the ViewController, after we instantiate our custom class (ReadTextMachine), so that the viewControllerDelegate inside it points to the ViewController. So in our ViewController.swift:
operatingMode = ReadTextMachine()
operatingMode.setViewControllerDelegate(viewController: self)
Another example and explanation can be found in this video from LetsBuildThatApp. I derived my solution mostly from it.
My current app in development applying the above solution can be found here: agu3rra/World-Aloud
Solution 2: Notifications and Observers pattern
This one is a bit easier to understand and follow. The general idea is to have your custom class broadcast a message which triggers a function call on your ViewController since it has an observer setup, waiting to hear that message.
So to give an example, in the context I used it, I have a CameraCapture class which uses AVFoundation to capture a photo. The capture photo trigger cannot immediately return an image, since iOS has a set of steps to execute before actually generating an image. I wanted my ReadTextMachine to resume an activity after CameraCapture had a photo available. (To apply this in the context of the CustomClass triggers ViewController event is basically the same, since both are actual classes in an iOS app as well).
So the 1st thing I did was create a broadcast function since I would use it in many places in my app. I simply placed it in a Utilities.swift file in the Xcode project.
public func broadcastNotification(name: String) {
let notification = Notification.Name(rawValue: name)
NotificationCenter.default.post(name: notification, object: nil)
}
The above function takes a string, which must be a unique notification identifier, and broadcasts it thru NotificationCenter.
In my CameraCapture class, I added a static constant to reference the unique identifier of the message:
static let NOTIFY_PHOTO_CAPTURED = "agu3rra.worldAloud.photo.captured"
For those who know AVFoundation, a photo is available when event didFinishProcessingPhoto gets executed, so at the end of that I added:
broadcastNotification(name: CameraCapture.NOTIFY_PHOTO_CAPTURED)
The above is a call to my previously defined utility function.
For my ReadTextMachine class to be able to catch that notification, I added the following on its init() and deinit routines:
override init() {
super.init()
// Setup event observers
let notification1 = Notification.Name(rawValue: CameraCapture.NOTIFY_PHOTO_CAPTURED)
NotificationCenter.default.addObserver(self,
selector: #selector(self.processingDoneTakingPhoto),
name: notification1,
object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self) // cleanup observer once instance no longer exists
}
Removing the observer is important at deinit so that when your object is deallocated from memory, the observer isn’t left lingering around. The above configured observer triggers a function call inside ReadTextMachine:
#IBAction private func processingDoneTakingPhoto() {
// does my stuff
}
That’s it! Again, the entire Xcode project can be downloaded from my project’s Git repository: agu3rra/World-Aloud
Hope this can be of use to others.
Cheers!

Swift private access control causing issues

I'm working on an iOS application and I'm using swift in that. For more readability and organising functions I've used extensions in my swift file.
// MARK: Class Declaration
class PaymentView
{
// Some stuffs
}
// MARK: Lifecycle methods
extension PaymentView
{
// Overriden for adding gesture recogniser
override func awakeFromNib()
{
super.awakeFromNib()
// Causes a crash when tapped on view
let tapGesture = UITapGestureRecognizer(target: self, action: Selector("paymentViewSelected:"))
self.addGestureRecognizer(tapGesture)
// Works correctly !!!
paymentViewSelected(tapGesture);
}
}
// MARK: Private Methods
extension PaymentView
{
private func paymentViewSelected(sender : UITapGestureRecognizer)
{
print("Method called")
}
}
My issue is when I tap on my view the application crashes with unrecognised selector error. If I remove that private access control specifier from the method it works perfectly.
My question is, I can call the paymentViewSelected: directly from the awakeFromNib regardless the private. But why it is crashing when used as a selector ?
According to Swift AccessControl Reference
Private access restricts the use of an entity to its own defining
source file. Use private access to hide the implementation details of
a specific piece of functionality.
But my class, extension, gesture all are in same file. I think I'm missing some basic key concept here. Please help me to understand the issue.
About Your point : I can call the paymentViewSelected: directly from the awakeFromNib regardless the private. But why it is crashing when used as a selector ?
Its because methods that are marked with private can accessible inside the class, but the object of class cannot call the method marked with private.
In your tapGesture, it is called using object of class automatically, once you tap on the view.
In this case your method is not available because it is marked with private, this is the reason of crash error unrecognised selector.
If you are calling a method from a selector and it is private they cannot be called because the method is called from outside. When you call paymentViewSelected() in the awakeFromNib it is called inside the class. However when it is called via Selector, it is called from outside. like object.paymentViewSelected(). You cannot call private method from outside.

protocol as parameter type in swift conflicts

I am using external SDK(by including their xcode project in my project).
The SDk was working properly in objective-c, but when I switched to swift, I am getting following problem.
Whenever I implementing delegate method where parameter is of type protocol, xcode suddenly gives error to object declaration of that Class which declared globally i.e. not in any function. If I comment that particular delegate method I will not get any error and it compile/executes successfully.
Please check the following swift code followed by my # comments
//CustomView is subclass of UIView
var customview : CustomView = CustomView() // #1 error as : Use of undeclared type CustomView
#IBAction func showCustomView(sender: AnyObject)
{
// CustomView configurations
}
#pragma CustomView Delegates
func CustomViewShown(view: AnyObject!) /// #2 delegate work properly
{
}
func CustomView(view: AnyObject!, didFailWithError error: NSError!)
// #3 if I keep this method uncommented it gives error to #1 line
// if I commented this method all other works fine without error.
{
}
Surprising thing is all the above delegate and SDK works fine for objective-C but not for swift.
On the basis of my little research, I am concluding that,
We can not use the Class name and method name same in swift, i.e. in my case its CustomView. If I am using CustomView for declaring object, I can not use it as method name.
so someone please verify that, I am correct or not ? and what is solution for this issue.
It's essentially a name conflicting problem.
Inside of your class declaration, CustomView is a method name, but not a class name. So, basically, your assumption is correct.
But, you have a workaround.
Let's suppose CustomView is declared in the SDK. And that is a framework named SomeSDK. Then you can reference the CustomView like this:
import SomeSDK
class ViewController: UIViewController {
var customview: SomeSDK.CustomView = SomeSDK.CustomView()
func CustomView(view: AnyObject!, didFailWithError error: NSError!) {
}
}
If you don't want to prefix SomeSDK. everywhere, you can typealias it:
import SomeSDK
typealias SDKCustomView = CustomView // you can use `CustomView` here because there is no conflicting name.
class ViewController: UIViewController {
var customview: SDKCustomView = SDKCustomView()
func CustomView(view: AnyObject!, didFailWithError error: NSError!) {
}
}
I may be wrong, but it seems in swift you can also explicitly call the init function.
Instead of calling:
var customview : CustomView = CustomView()
you can call:
var customview : CustomView = CustomView.init()
This works in my Playground, let me know how it works out for you. This would allow you to use your function named as it is.

Resources