Async communication of swift and Javascriptcore - swift

I want to use the Async functionality offered by WKWebView outside web view.
The JS Context option does not provide the Async functionality.
In WKWebView, I write my logic as follows.
func swiftFunc1() {
webView.evaluateJavaScript("jsFunc1(), completionHandler: nil)
}
In javascript code I post a message to swift
function jsFunc1() {
window.webkit.messageHandlers.myMsg.postMessage("call swiftFunc2");
}
The swift code can then call appropriate JS callback as a part of handling message.
But this is dependent upon the webview being the foreground view. If I want to use the JS logic independent of the webview, JSContext is the option. I tried following
func swiftFunc1() {
myCtxt = JSContext()
exportedToJS = exportToJS() //confirms to JSExport and uses #objc
myCtxt.setObject(exportedToJS.self, forKeyedSubscript: "swiftIface")
myFunc = myCtxt.objectForKeyedSubscript("jsFunc1")
myFunc.callWithArguments(nil)
}
Now in javascript code I cannot post a message to swift. If I try to call a swift function as follows, code gets stuck forever.
function jsFunc1() {
swiftIface.swiftFunc2() // This creates a deadklock
}
How can I achieve either of the following without "returning" from the called Javascript function jsFunc1()?
Either post a message to swift so that it can take appropriate action
Or call a swift function so that the appropriate action is taken

Do I understand you right, if you do not want your javscript to terminate after execution?
If I understood you wrong, maybe following helps (I am not at home at Mac to test, but maybe it works if you modify your code as follows).
Option 1: Blocks
The swiftFunc1 could look like this:
func swiftFunc1() {
myCtxt = JSContext()
myCtxt.setObject(unsafeBitCast(swiftFunc2, AnyObject.self), forKeyedSubscript: "swiftFunc2")
exportedToJS = exportToJS() //confirms to JSExport and uses #objc
myCtxt.evaluateScript("swiftFunc2()")
}
Your swiftFunc2 would look like this:
let swiftFunc2: #convention(block) Void -> Void = {
// do something here
}
Your JS code would look like this:
function jsFunc1() {
swiftFunc2();
}
Option 2: JSExport
Your have an exported class which is accessible for all javascript:
import Foundation
import JavaScriptCore
#objc class JavascriptHandler: NSObject, JavascriptHandlerExport {
let context: JSContext = JSContext()
init () {
context.setObject(self, forKeyedSubscript: "MyJSHandler") // set the object name for self accessible in javascript code
}
func swiftFunc1() {
context.evaluateScript("MyJSHandler.swiftFunc2();")
}
func swiftFunc2 () {
// do something here
}
}
Your protocol for the exported class.Here you have to declare all properties and methods you want to use with Javascript.
import Foundation
import JavaScriptCore
#objc protocol JavascriptHandlerExport: JSExport {
func swiftFunc2 ( ) -> Void
}
With this it should be possible for you to call a function from javascript and still let it continue.
You can now access the functions of the class JavascriptHandler from Javascript like this in this example:
MyJSHandler.swiftFunc2();
If you want to seperate the class where your WebView is from the one where your JS logic lies that should also not be a problem. You should also be able to combine the block syntax with the JSExport method.
Let me know if it's not working/behaving as wanted.

Related

SwiftUI calling helper functions from another swift class?

I have created my app using swiftui. Then I found that sometimes I may need to do some tasks quite frequently. Instead of placing it in the same swiftui files, I think that it should be placed inside another class. However, I wonder how can we call the function of another class inside swiftui? is that I must have new an object to call it?
As I understand your question:
class MyHelper {
static func helpMe(needHelp: Bool) -> String {
if(needHelp) {
return "Help in on the way!"
} else {
return "You are ok!"
}
}
}
Usage:
let result = MyHelper.helpMe(needHelp: true)
You can use static or class functions inside the helper class, and access to them by '.' syntax. Difference between static and class functions, that you can override class function in the subclass (which is not really needed with helper classes), static not.

Invoke code in extension on object deinit?

In Swift (I'm using 4.1), is there a way to do some clean-up in an extension when an object is being destructed? I have in mind the kind of code that would go in deinit(), but an extension can't declare deinit(). (Besides which, if several extensions needed to do this, there would be multiple deinit() declarations.)
I did not find a way to exactly get what you want, but maybe this code will help. I have never tried it, so maybe use it more as an inspiration. In a nutshell, it allows you to add bits of code that will excute on deinitialization.
/// This is a simple object whose job is to execute
/// some closure when it deinitializes
class DeinitializationObserver {
let execute: () -> ()
init(execute: #escaping () -> ()) {
self.execute = execute
}
deinit {
execute()
}
}
/// We're using objc associated objects to have this `DeinitializationObserver`
/// stored inside the protocol extension
private struct AssociatedKeys {
static var DeinitializationObserver = "DeinitializationObserver"
}
/// Protocol for any object that implements this logic
protocol ObservableDeinitialization: AnyObject {
func onDeinit(_ execute: #escaping () -> ())
}
extension ObservableDeinitialization {
/// This stores the `DeinitializationObserver`. It's fileprivate so you
/// cannot interfere with this outside. Also we're using a strong retain
/// which will ensure that the `DeinitializationObserver` is deinitialized
/// at the same time as your object.
fileprivate var deinitializationObserver: DeinitializationObserver {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.DeinitializationObserver) as! DeinitializationObserver
}
set {
objc_setAssociatedObject(
self,
&AssociatedKeys.DeinitializationObserver,
newValue,
objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC
)
}
}
/// This is what you call to add a block that should execute on `deinit`
func onDeinit(_ execute: #escaping () -> ()) {
deinitializationObserver = DeinitializationObserver(execute: execute)
}
}
How it works
Now let's assume you have your object, let's call it A and you want to create some code inside an extension, you can use onDeinit to add your clean-up code like this:
extension A {
func someSetup() {
// Do your thing...
onDeinit {
/// This will be executed when A is deinitialized
print("Execute this")
}
}
}
You can also add it from outside the extension,
let a = A()
a.onDeinit {
print("Deinitialized")
}
Discussion
This is different than what you want in that instead of definition a function (deinit) you need to call one. But anyway in protocol extensions, you can never really use the underlying object's life cycle, so it should be okay for most cases.
I am not sure about the order of execution between A's deinit and the DeinitializationObserver's deinit. You might need to drop the assumption that A is still in memory when you write the code inside the closure.
If you need more than one onDeinit you can update the associated object to retain an array of DeinitializationObserver
In my code, I usually use ReactiveCocoa's Lifetime for this kind of things. However, it is more complicated than what I wrote here. Instead, it looks like they swizzle the dealloc selector. If you're interested you can take a look inside - https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoa/NSObject+Lifetime.swift

Error call function (instance member cannot be used)

It's not a duplicate cause with lazy i have an issu too :
"A C function pointer cannot be formed from a closure that captures context"
In my main class I have "detectChangeMidi" but in this code, when I try to call a function I don't understand why I can't.
(i can't use var too, anything of my class)
I'm not expert in swift, then try to explain to me what's going one.
I use the CoreMidi librarie.
UPDATE :
I replace the code by minimaliste code for better entendement.
import Foundation
import CoreMIDI
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
gestionMidi()
//...
}
func gestionMidi() {
//...
let midiNotif:MIDINotifyProc = detectChangeMidi
MIDIClientCreate("Swift3 Test Client" as CFString, midiNotif, nil, &midiClient)
//...
}
func plop(){
print("bla bla bla")
}
let detectChangeMidi: #convention(c) (UnsafePointer<MIDINotification>, UnsafeMutableRawPointer?) -> Swift.Void =
{ midiNotification,unkown in
var notification = midiNotification.pointee
self.plop() //problem here
//...
}
}
Your entire agenda here is misguided; it is not at all obvious what you can be trying to do. You cannot declare a property or function as being convention(c) and also refer to self. But you don't need to! If your goal is to pass a function as a parameter where a C pointer-to-function is expected, just pass the function. Anyway you'll have a much easier time in Swift if you call MIDIClientCreateWithBlock(_:_:_:) instead.
The problem is with the scope as I can judge from the error message.
Also the thing to get attention to is that you're executing plop() in closure, and that requires self.plop().
also midiNotification,unkown in - unkown seems to be a typo.
Check where you declare the function itself. From this snippet it's hard to understand what's your declaration scope.

How to properly convert a 3rd party library delegate into a RxSwift Observable

I have a case where I am using a 3rd party library and I would like to make it into an Observable. Appropriately, the library is designed around delegates as one would expect so I am wrapping it. The library performs an async operation and calls it's delegate with the results when it completes.
I definitely want to take advantage of the cold nature of the observable and only start the operation when someone subscribes. I have a solution that works, I just don't know if it's deeply flawed and I am missing some important understanding of RxSwift or perhaps there is a simpler way to achieve the same goal.
public final class RxLibBridge: LibDelegate{
let lib = Lib()
let _source = PublishSubject<[LibResult]>()
public init(){
lib.delegate = self
}
public func asObservable() -> Observable<[LibResult]>{
// create a cold observable to start
// the Lib's async operation on subscribe.
return Observable<Void>.create{
observer in
self.lib.startOperation()
// emit and complete
observer.onNext(())
observer.onCompleted()
return Disposables.create()
}
// convert the `Void` observable into an observable from the
// PublishSubject
.flatMapLatest{self._source}
}
// the lib's completion delegate method
public func lib(_ lib: Lib, didFinishWithResult results: [LibResult]) {
// grab the PublishSubject, emit the result and complete
let observer = _source.asObserver()
observer.onNext(results)
observer.onCompleted()
}
}
So my question is: Is this an appropriate pattern in Rx? Again, it works:
RxLibBridge()
.asObservable()
.subscribe(...)
Just because it works though doesn't mean I have not fundamentally misunderstood the proper way to work with this situation.
I know there is a way in RxSwift to handle something like this:
https://medium.com/#maxofeden/rxswift-migrate-delegates-to-beautiful-observables-3e606a863048#.rksg2ckpj
https://samritchie.net/2016/05/12/rxswift-delegateproxy-with-required-methods/
I tried this approach but it looks like the API changed since 2015. Namely, in the example links above proxyForObject cannot be found when adding the rx_delegate method in the extension.
Additionally, this approach appears to favor pure Objective-C [UIKit/AppKit] APIs. In my attempt to follow the linked example, I was editing the source of the 3rd party lib to make the delegate method optional and exposing it to #objc. The lib's delegate is required and I would rather not have to fork the lib to make the modifications.
This SO answer provided the updated API for the 2 links above:
Can not use proxyForObject function in DelegateProxyType (rxSwift)
So after digging some more, it looks like this will do the trick with a required delegate method, updated for RxSwift 3.3.1. This is using their DelegateProxy system.
import RxSwift
import RxCocoa
import Lib
public final class RxLibDelegate: DelegateProxy, LibDelegate, DelegateProxyType{
let _subject = PublishSubject<[LibResult]>()
public static func currentDelegateFor(_ object: AnyObject) -> AnyObject?{
let target = object as! Lib
return target.delegate
}
public static func setCurrentDelegate(_ delegate: AnyObject?, toObject object: AnyObject) {
let target = object as! Lib
target.delegate = delegate as? LibDelegate
}
public func lib(_ lib: Lib, didFinishWithResult results: [LibResult]) {
_subject.onNext(results)
_subject.onCompleted()
}
}
extension Lib{
public var rx_delegate: DelegateProxy{
// `proxyForDelegate` moved as compared to examples at:
// https://samritchie.net/2016/05/12/rxswift-delegateproxy-with-required-methods/
// https://medium.com/#maxofeden/rxswift-migrate-delegates-to-beautiful-observables-3e606a863048#.rksg2ckpj
return RxLibDelegate.proxyForObject(self)
}
public var rx_libResults: Observable<[LibResult]> {
// `proxyForDelegate` moved as compared to examples at:
// https://samritchie.net/2016/05/12/rxswift-delegateproxy-with-required-methods/
// https://medium.com/#maxofeden/rxswift-migrate-delegates-to-beautiful-observables-3e606a863048#.rksg2ckpj
let proxy = RxLibDelegate.proxyForObject(self)
return proxy._subject
}
}
That's about 28 LOC. My original "wrapper" (see updated version below) but I don't know if it's the best is 21 LOC; 6 of 1 half dozen of the other?
In my particular case I only have 1 delegate method to worry about. If you were working with some functionality that had multiple delegates I think the DelegateProxy + extension methods would be a lot more practical and the better choice in that case.
Regarding my original trial wrapping thing using that Void observable, it appears it's totally acceptable to alter the stream with flatMapLatest as evidenced here re: Sending continual events while a button is pressed:
https://stackoverflow.com/a/39123102/1060314
import RxSwift
import RxCocoa
let button = submitButton.rx_controlEvent([.TouchDown])
button
.flatMapLatest { _ in
Observable<Int64>.interval(0.1, scheduler: MainScheduler.instance)
.takeUntil(self.submitButton.rx_controlEvent([.TouchUpInside]))
}
.subscribeNext{ x in print("BOOM \(x)") }
.addDisposableTo(disposeBag)
//prints BOOM 0 BOOM 1 BOOM 2 BOOM 3 BOOM 4 BOOM 5 for every 0.1 seconds
Note that a new Observable is returned from flatMapLatest. The author cites the RxSwift slack channel, so I assume it is at least acceptable to do.
Here's an updated version of my wrapper version that I think might be a bit cleaner:
import RxSwift
public final class RxLibBridge: LibDelegate{
let lib = Lib()
let _source = PublishSubject<[LibResult]>()
public init(){
lib.delegate = self
}
public func asObservable() -> Observable<[LibResult]>{
// create a cold observable to start
// the Lib's async operation on subscribe.
return Observable.just(())
.do(onNext: {
self.lib.startOperation()
})
.flatMapLatest{self._source}
}
// the lib's completion delegate method
public func lib(_ lib: Lib, didFinishWithResult results: [LibResult]) {
// grab the PublishSubject, emit the result and complete
_source.onNext(results)
_source.onCompleted()
}
}

Swift Generics and Protocol Extensions

I have a protocol Reusablethat has a static function static func reuseId() -> String and a protocol extension that defines the default implementation for the function. Then, I implemented a extension on UITableViewCell to conform to the Reusable protocol. I can now use the function on my TableViewCells without a problem: SomeTableViewCell.reuseId().
The Problem I have is with Generics. I have a generic class that has a generic parameter of the type UITableViewCell:
internal class SomeClass<CellType: UITableViewCell>: NSObject {
...
}
I want to be able to use the function specified in Reusable in my generic class on CellType but unfortunately this does not work as expected. The compiler always generates the error Type 'CellType' has no member 'reuseId'.
Does anybody know why this is happening? Is there a workaround?
I am using Xcode 7.0 with Swift 2.0.
Greetings from Germany
UPDATE: Here is some sample code that better shows my problem:
import UIKit
protocol Reusable {
static func reuseId() -> String
}
extension Reusable {
static func reuseId() -> String {
return String(self).componentsSeparatedByString(".").last!
}
}
extension UITableViewCell: Reusable { }
class SomeGenericClass<CellType: UITableViewCell> {
func someFunction() {
let reuseIdentifier = CellType.reuseId()
}
}
This Code will generate the above error but I do not quite understand why this happens. I think the main difference to the sample code that jtbandes posted is that I use a static function.
UPDATE: The issue is fixed in Xcode 8.3 beta 2. The sample code above now works as expected (after migrating it to Swift 3 of course).
That's an interesting problem. Your code seems like it should work; you might want to file an enhancement request.
Here's a workaround that seems to work correctly:
class SomeGenericClass<CellType: Cell> {
func someFunction() {
let reuseIdentifier = (CellType.self as Reusable.Type).reuseId()
}
}
Another (workaround) way to get what you need:
class GenericExample<CellType:UITableViewCell where CellType:Reusable>
{
func test() -> String {
return CellType.reuseId()
}
}
GenericExample<UITableViewCell>().test() // returns "UITableViewCell"
GenericExample<MyCell>().test() // returns "MyCell"