How to write a Reactive extension to observe the change in a string property or parent class, provided by iOS - rx-swift

I am inheriting SLComposeServiceViewController. It has a contentText String that contains the text entered in one of its views. I want to write an extension like extension Reactive where Base: SLComposeServiceViewController to be able to observe any changes in the value of this variable.
I can't find proper syntax examples for achieving this
I tried something like
extension Reactive where Base: SLComposeServiceViewController {
public func controlProperty<T>(
editingEvents: ControlProperty<String> ,
getter: #escaping (Base) -> T,
setter: #escaping (Base, T) -> Void
) -> ControlProperty<T> {
}
}

The SLComposeServiceViewController doesn't provide a way to observe the contentText property so there is no hook that Rx can use to wrap it into an Observable. However, the class' textView property does have such a hook and the Observable is already provided inside RxCocoa.
Try this:
extension Reactive where Base: SLComposeServiceViewController {
public var contentText: ControlProperty<String> {
return base.textView.rx.string // could be rx.text if you are using UIKit instead of AppKit
}
}

Related

UICollectionViewDelegate in RxCocoa

I write an extension for UICollectionView which will listen the delegate's
shouldHighlightItemAt method,but it don't call.
public var shouldHighlightItem: ControlEvent<IndexPath> {
let source = self.delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:shouldHighlightItemAt:)))
.map { a in
return try self.castOrThrow(IndexPath.self, a[1])
}
return ControlEvent(events: source)
}
}
how to write an extension for UICollectionView of rx shouldHighlightItemAt?
You cannot use methodInvoked(_:) with a delegate method that has a non void return type.
collectionView(_:shouldHighlightItemAt:) expects you to return a Bool value. So you cannot use methodInvoked(_:).
If you have a look at the implementation of methodInvoked(_:) it gives you an explanation why this does not work:
Delegate methods that have non void return value can't be observed
directly using this method
because:
those methods are not intended to be used as a notification mechanism, but as a behavior customization mechanism
there is no sensible automatic way to determine a default return value
There is however a suggestion how you could achieve what you are trying to do:
In case observing of delegate methods that have return type is
required, it can be done by
manually installing a PublishSubject or BehaviorSubject and implementing delegate method.
In your case it would work like this:
In RxCollectionViewDelegateProxy you add the 'PublishSubject' and implement the UICollectionViewDelegate method:
let shouldHighlightItemAtIndexPathSubject = PublishSubject<IndexPath>
public func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool {
shouldHighlightItemAtIndexPathSubject.on(.next(indexPath))
return self._forwardToDelegate?.collectionView(collectionView, shouldHighlightItemAt: indexPath) ?? true // default value
}
In your UICollectionView RxExtension you can expose the desired Observable like this:
public var property: Observable<IndexPath> {
let proxy = RxCollectionViewDelegateProxy.proxy(for: base)
return proxy.shouldHighlightItemAtIndexPathSubject.asObservable()
}
I have not tested this, I merely took it from the RxCocoa source code and modified it to fit your needs. So in theory this should work, but you might have to tweak it a little bit ;-)

Kotlin not able to convert gradle's Action class to a lambda

So, while this is quite a kotlin-dsl for gradle specific issue, I think it overall applies to the kotlin language itself, so I am not going to use that tag.
In the gradle API, the class Action<T> is defined as:
#HasImplicitReceiver
public interface Action<T> {
/**
* Performs this action against the given object.
*
* #param t The object to perform the action on.
*/
void execute(T t);
}
So ideally, this should work in kotlin (because it is a class with a SAM):
val x : Action<String> = {
println(">> ${it.trim(0)}")
Unit
}
But I get the following two errors:
Unresolved reference it
Expected Action<String> but found () -> Unit
Fwiw, even Action<String> = { input: String -> ... } doesn't work.
Now here's the really intriguing part. If I do the following in IntelliJ (which btw, works):
object : Action<String> {
override fun execute(t: String?) {
...
}
}
IntelliJ pops the suggestion Convert to lambda, which when I do, I get:
val x = Action<String> {
}
which is better, but it is still unresolved. Specifying it now:
val x = Action<String> { input -> ... }
gives the following errors Could not infer type for input and Expected no parameters. Can someone help me with what is going on?
This is because the Action class in gradle is annotated with HasImplicitReceiver. From the documentation:
Marks a SAM interface as a target for lambda expressions / closures where the single parameter is passed as the implicit receiver of the invocation (this in Kotlin, delegate in Groovy) as if the lambda expression was an extension method of the parameter type.
(emphasis mine)
So, the following compiles just fine:
val x = Action<String> {
println(">> ${this.trim()}")
}
You could even just write ${trim()} and omit the this in front of it.
You need reference the function with class name, like:
val x: Action<String> = Action { println(it) }

Call to swift method from JavaScript hangs xcode and application

I am writing an iOS App (using xcode 7.3 and swift 2.2) using JavascriptCode framework. Calling javascript methods from swift works perfect, but when I call the swift method from javascript, xcode simply shows a "loading" type of symbol and nothing happens. I need to "force quit" xcode to get out of this state.
I have followed https://www.raywenderlich.com/124075/javascriptcore-tutorial and http://nshipster.com/javascriptcore/ and I am trying pretty simple calls.
Has anyone faced this kind of issue?
My swift code is as follows:
#objc protocol WindowJSExports : JSExport {
var name: String { get set }
func getName() -> String
static func createWindowWithName(name: String) -> WindowJS
}
#objc class WindowJS : NSObject, WindowJSExports {
dynamic var name: String
init(name: String) {
self.name = name
}
class func createWindowWithName(name: String) -> WindowJS {
return WindowJS(name: name)
}
func getName() -> String {
NSLog("getName called from JS context")
return "\(name)"
}
}
I am initializing the context as follows:
runContext = JSContext()
runContext.name = "test_Context"
windowToJs = WindowJS(name: "test")
runContext.setObject(windowToJs.self, forKeyedSubscript: "WindowJS")
If I replace the last two lines in above code with below code without instantiating it, the code simply fails to load.
runContext.setObject(WindowJS.self, forKeyedSubscript: "WindowJS")
And the javascript code is as simple as
function check() {
return WindowJS.getName()
}
I do see the breakpoint being hit in the JS function check and when the WindowJS.getName gets called, xcode simply becomes unresponsive.
The setTimeout could be solved by adding following piece of code to my swift function.
let setTimeout: #convention(block) (JSValue, Int) -> () =
{ callback, timeout in
let timeVal = Int64(timeout)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeVal), dispatch_get_main_queue(), { callback.callWithArguments(nil)})
}
To expose this native code to the JS context, I also added following.
runContext.setObject(unsafeBitCast(setTimeout, AnyObject.self), forKeyedSubscript: "setTimeout")
Things then worked fine.
You're creating a deadlock since you are calling from Swift to JavaScript back to Swift. I'm not sure exactly why it is a deadlock but I had a similar issue with WKWebView on Mac recently.
You need to decouple this and make the communication asynchronous. This obviously means you cannot simply return a value from your JS function in this case.
To decouple, you can break the deadlock by deferring the work the JavaScript function needs to do out of the current runloop iteration using setTimeout:
function myFunction() {
setTimeout(function() {
// The actual work is done here.
// Call the Swift part here.
}, 0);
}
The whole native ↔︎ JavaScript communication is very, very tricky. Avoid it if you can. There's a project called XWebView that may be able to help you as it tries to ease bridging between the two worlds.

how to define facade for a method that emits events in ScalaJS

I'm writing scalajs facade for pouchdb
code : https://gist.github.com/chandu0101/62013de47bf5ee4d2412
how to define facade for API name changes which emits events and return a cancel method ..
Defining Scala.js facade types is all about giving static types to a JavaScript API. Usually the documentation of the JavaScript does mention types, which can therefore be translated into a formal definition pretty easily.
In this case, the changes methods returns an EventEmitter, which is the first thing you need. Let us define this type, and in particular its on method:
class EventEmitter extends js.Object {
def on(event: String, listener: js.Function): this.type = js.native
...
}
We have to be very imprecise for the type of listener, unfortunately, because the actual type will very much depend on the event string, and the particular uses of that emitter. In general, we cannot predict anything at this point.
The documentation of EventEmitter doesn't mention a cancel() method, which is a bit weird since apparently that method can be called on the emitter returned by changes. So we'll define a subtype for the particular emitter returned by changes:
trait ChangesEventEmitter extends EventEmitter {
def cancel(): Unit = js.native
}
We can also take advantage of this specialized trait to enable a more precise type of listener for particular events. In this case, let us do it for the change event:
object ChangesEventEmitter {
implicit class ChangesEventEmitterEvents(
val self: ChangesEventEmitter) extends AnyVal {
def onChange(listener: js.Function1[js.Dynamic, Any]): self.type =
self.on("change", listener)
}
}
Finally, you need a type for the options parameter of the db.changes method. The type itself is usually a trait with many immutable fields:
trait DBChangesOptions extends js.Object {
val include_docs: js.UndefOr[Boolean] = js.native
val limit: js.UndefOr[Int] = js.native
...
}
To construct an instance of such a type, you may either go with an apply method in the companion object with default arguments (see this SO question), or you can create a Builder-like class for it.
And now, we can finally declare the changes method itself:
def changes(options: DBChangesOptions = ???): ChangesEventEmitter = js.native

javafx - how to disable events fired not from the user

i have a little problem with javafx. i added a change listener like this:
private final ChangeListener<String> pageItemSelected = new ChangeListener<String>()
{
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue){
pageGotSelected(newValue);
}
};
now to the problem: if i change an page item like this:
guiPageList.setValue(model.getCurrentTargetPage());
the event gets also(as it get by selecting something with the mouse or key) fired. is there a way to disable the event firing or another way?
i need the event only, if the element got selected by the user and not if i change it with the setValue() function...
perhaps consuming the event, but i don´t know what kind of event this would be.
thanks in advance!!!
You can temporarily remove the listener and add it again:
guiPageList.getSelectionModel().selectedItemProperty().removeListener(pageItemSelected);
guiPageList.setValue(model.getCurrentTargetPage());
guiPageList.getSelectionModel().selectedItemProperty().addListener(pageItemSelected);
Alternatively you could decorate the listener with another listener implementation, the code would be something like:
class InvalidationListenerEventBlocker implements InvalidationListener {
InvalidationListener decoratedListener;
boolean block;
public void invalidated(Observable observable) {
if(!block) {
decoratedListener.invalidated(observable);
}
}
}
Add a setter for the block boolean and send the listener in through the constructor. Set block to true to stop events.
This is very old question, but I came to some solution I personally use, that's reusable and does not require storing a reference to the listener (but it needs a reference to the exposing/muffling property thou).
So first the concept: we're going to create lambda (InvalidationListener), that is going to be called only if above mentioned exposing/muffling property is set to true/false. For that we are going to define another functional interface that provides described behavior:
#FunctionalInterface
private interface ManageableInvalidationListener
extends InvalidationListener {
public static InvalidationListener exposing(
BooleanProperty expose,
ManageableInvalidationListener listener) {
return ob -> {
if (expose.get()) {
listener.invalidate(ob);
}
};
}
public static InvalidationListener muffling(
BooleanProperty muffle,
ManageableInvalidationListener listener) {
return ob -> {
if (!muffle.get()) {
listener.invalidated(ob);
}
}
}
public abstract void invalidated(Observable ob);
}
This interface defines two static methods we're going to use in our code. We pass a steering property as first argument (it will tell if listener should be called) and actual implementation to be performed, when it will be called. Please note, that there is no need to extend InvalidationListener, but I'd like to keep ManageableInvalidationListener in sync with InvalidationListener.
So we would call exposing if we need to create a (manageabale) listener that would notify the (invalidation) listener if expose property has value of true. In other case we would create the listener with muffling, if true of the steering property would mean, well, to muffle the notification.
How to use it?
//- Let's make life easier and import expose method statically
import static ManageableInvalidationListener.exposing;
// ...
//- This is the steering property.
BooleanProperty notify = new SimpleBooleanProperty(true);
//- This is our main property with the listener.
ObjectProperty<Foobar> foobar = new SimpleObjectProperty<>();
//- Let's say we are going to notify the listener, if the
// notify property is set to true.
foobar.addListener(exposing(notify, ob -> {
//- Here comes the InvalidListener code.
}));
And then somewhere in the code:
//- Listener will be notified as usual.
foobar.set(new Foobar());
//- Now temporarily disable notifications.
notify.set(false);
//- The listener will not get the notification this time.
foobar.set(new Foobar());
//- Re-enable notifications.
notify.set(true);
Hope this somehow helps. You're free to use the code in this post as pleases you.

Resources