I am calling a native SDK which goes off and does some API calls, which is obviously time-bound work. For 90% of this work I am using callbacks and method channel calls going bi-directional so Dart can call Swift, and Swift can then call Dart when finished.
The problem I have is when I want to synchronously await the API response (or timeout).
I ran some tests, as I originally was going to just hold off sending back the FlutterResult until the Swift callback was hit, but this severely slows down the UI, as I believe all platform calls are made on the UI thread. I simulated a 10 second response time and the app would completely hang for a good few seconds before becoming responsive again, and if I spammed a few of these requests the app would hang for 10 seconds plus.
I am a bit confused on what the best way to run synchronous platform code is, because I don't think platform code can be called from an isolate.
I want to
Get user details
User presses button
Show animation whilst waiting
Get response and move on.
The SHOW ANIMATION WHILE WAITING is the part that of course, janks. I'm not entirely sure why either because running general CPU work whether its native or dart shouldn't freeze the rendering engine surely?
Anyway, the only thing I can think of is:
Dart calls platform method A
Set flag AwaitingMethodA to true
Immediately return FlutterResult but set a callback in Swift
while (AwaitingMethodA)....
When callback in Swift hits, jump on the UI thread and send message back to Dart saying complete, which sets AwaitingMethodA to false
Dart code continues....
^ and I could set a timeout timer for the above to break out the loop if necessary.
Is there anything wrong with this approach, or is there a better convention for synchronous, time-bound platform work?
Don't wait in while loop - this is the problem.
You should use Completer class (https://api.flutter.dev/flutter/dart-async/Completer-class.html). As the documentation says - with Completer you can easily convert callback based api to future based one.
Your code could look like this:
class SdkResultType {
//let's assume this is the type that Sdk will return
}
class MyClass {
Completer _completer;
Future<SdkResultType> callSdk() {
_completer=Completer();
///this is where you would call the SDK
somehowCallTheSDK();
// this is where your code will wait
return _completer.future;
}
// your SDK callback - SDK should call this when completed
void sdkCallback(SdkResultType sdkResult) {
if (!_completer.isCompleted) {
//this is where you complete the _completer.future, unblocking your mainSdkCall function.
_completer.complete(sdkResult);
}
}
void mainSdkCall() async {
//this is whare you wait
var sdkResult=await callSdk();
// do the rest here
}
}
Related
In Dart/Flutter, I have a StreamController defined like this:
MyEventStreamer() {
_controller = StreamController<TimedEvent>(
onListen: _startStream,
onResume: () {
throw UnimplementedError();
},
onPause: () {
throw UnimplementedError();
},
onCancel: _stopStream);
_calculateEventTimes();
}
The reason I have the throws is that I don't want onPause or onResume to be used... and I want to throw an error in case I forget this later.
However, since these are apparently not called explicitly (and the docs don't seem to describe when they are called), I'm concerned that these may be called by the system under certain unknown circumstances... such as lifecycle events on mobile when the app is backgrounded, etc... which would be bad.
Is this a legitimate concern -- or are these methods never called by anything other than code the programmer wrote?
OnListen - When there is a listener attached.
OnCancel - When listener cancels listening to the events.
There are few scenarios where pause/resume is called.
If you create a stream but don't want any event to emit until there is at least one listener. This way, all the events prior to the first listener is buffered and sent to the attached listener. Here you create a stream, pause it and call it's resume once there is onListen called.
While using asyncExpand/asyncMap where each event is transformed into another event as per user's provided convert method. Each event is passed to asyncExpand/asyncMap to get new event which could take some time (in rare case). While these converter methods haven't returned their values, stream should not emit more values and it should buffer new events.
Maybe you are connected to a websocket or firebase and you want to not listen to any events when app is in background, you can call pause/resume to defer data once app is in foreground.
There could be other use cases but these are the use-cases provided in flutter framework I am aware of.
In short, I have a table row with an onclick event. I am getting this via
const row = screen.getByRole('row', { name: /My row/i })
await userEvent.click(row)
This does not trigger the event handler. However, if I do:
const row = screen.getByRole('row', { name: /My row/i })
fireEvent.click(row)
This works fine.
I know userEvent uses more events to simulate a row click, but the event handler works fine in the live application too. Are there any reasons why userEvent might not work?
Like most very strange things, the problem lied elsewhere. But for documentation purposes, this was due to the app rerendering while doing my assertions. What would happen was this:
App renders, making a bunch of API calls
My API call for my test finishes, say, get user
findByText('My User') passes and gets me my DOM element
Another API call finishes, re-rendering the component to show this data
The result of findByText is no longer the current active DOM element
click fires
As its no longer in the document, there's nothing to click/fire an event
I changed my previous lines to check for ALL data loads before grabbing my row and it seems to consistently be working. This means I have to assert things unrelated to my tests, but that may be due to my app having poor UX with things popping in as they load?
Either way, I'm not 100% confident this is the reason, but if
userEvent.click is not firing events, or
toBeInTheDocument is failing, even if findBy worked
It may be due to your app rerendering after you've asserted everything has loaded. Hope I can save someone else 3 days of suffering like I had to to find that simple fact...
I have my object in Unity and this object has a destructor as well as Awake method
...
private void Awake()
{
Debug.Log("AWAKE");
}
~UnityHumanObject()
{
Debug.Log("DESTRUCTOR");
if (stream != null)
{
stream_release(stream);
}
}
...
when I click on Run button according to the log I see that destructor prints his log message 3 times and just after that I see Awake log message... Next, if I click stop (Run button again), I don't see that destructor even get a call.
So, question is - why when I click on run button first of all I get 3 time call to destructor and secondly why if I stop Unity I don't actually get destructor call?
You may wish to implement a receiver for OnDestroy() instead.
See https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnDestroy.html
I don't know the full ins and outs of how Unity engine works, but I can tell you that things that derive from UnityEngine.Object (which includes MonoBehaviors) are a hybrid type that is part managed-code and part unmanaged-code. The Editor is also an arcane beast in that it runs your scene as you're editing it, but restricts certain Messages/calls until you're in "play" mode (or register with the ExecuteInEditor attribute).
With all of that, it becomes quite impossible to manage your code using low-level constructors and destructors if you're extending any of the Unity classes. Simply put, don't do it unless the Unity-specific functionality doesn't support what you need. In your case, closing a stream, using an OnDestroy() function should be perfectly sufficient.
I'm having a very simple problem with my implemented 3D Touch dynamic quick action shortcuts.
I want the shortcuts to be cleared whenever the app is terminated (by double clicking the Home button and swiping up).
I am calling UIApplication.sharedApplication().shortcutItems.removeAll() as follows:
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
UIApplication .sharedApplication().shortcutItems?.removeAll()
self.saveContext()
}
However it has no effect, and the quick actions still show when 3D touch is used.
If I place UIApplication.sharedApplication().shortcutItems?.removeAll() inside
func applicationDidEnterBackground(application: UIApplication), this works exactly as intended...
I read something about applicationDidEnterBackground being the function used in most cases due to background processing or something...but there has to be a way to achieve what I want when the user terminates the app using the app monitor swipe up.
Thanks
Didn't tried this. But this tweak should work.
Start a background task on applicationWillTerminate and end it after some small delay. In the mean time, you can call 'UIApplication .sharedApplication().shortcutItems?.removeAll()'.
This will hopefully clear the shortcut items.
There are dynamic and static quick actions. The first kind you define through the shortcutItems property of the UIApplication instance (like in your example). The second kind you register in the plist file.
From the documentation:
Your code creates dynamic quick actions, and registers them with your app object, at runtime.
The system registers your static quick actions when your app is installed.
If a user installs an update for your app but has not yet launched the update, pressing your Home screen icon shows the dynamic quick actions for the previously-installed version.
This means that even when the app is closed the system remembers about both kinds of quick actions. While your app is in memory, such as when going into background, the system can still query the UIApplication for the dynamic actions but it must keep some other sort of persistence of quick actions when the app is closed.
I think there is just no guarantee about the point at which the system synchronizes with the dynamic quick actions. My guess is that the system does not necessarily synchronize when closing the app, yours might be an unsupported use case.
I'm new to GWT. I creating a MVP based project (as described here) that uses a number of custom events. There are several widgets (10+) that listen for some global events and perform some action (including writing to the DOM) in the event handlers.
What I'm finding is that the UI blocks and doesn't update until each and every one of the handlers for the one event finishes processing. This is causing the UI to perform slowly on page load and for any other events that cause the widget to update.
I created a similar project in plain JavaScript/jQuery and this was not an issue with that project. In fact, the UI was blazing fast. What am I doing wrong here? The documentation states that GWT is very performant, so I have to conclude that I'm just doing it wrong.
One example, I have a drop down that selects a date preset (like Yesterday, or Last Week). When this happens I set the selected preset in the model like so:
public void setDateRange(DatePreset dateRange) {
this.dateRange = dateRange;
eventBus.fireEvent(new DateChangedEvent(dateRange));
}
Each of the widgets has access to the same eventbus and registers to handler DateChanged events. Each of the widgets needs to do a fair amount of logic and processing (including making an ajax call) to then update itself with correct data.
#Override
public void onDateChanged(DateChangedEvent event) {
DatePreset dateRange = event.getDate();
… additional processing and logic
… ajax call
}
I've determined after some basic profiling that each widget requires about 100-150ms to finish processing (which means there's a UI delay of over one to two seconds). When I say blocking, I mean the dropdown where I selected the date preset doesn't even close (and I see the spinny wheel) until everything finishes.
What can I do to make this run faster (and without UI blocking)? I am open to any ideas. Thanks!
Measuring the speed of the project in developer mode can be a reason for this extreme slowness.
You can check out the real speed of the application if you deploy it to an appserver or if you delete the &gwt.codesvr=127.0.0.1:9997 part from the end of the url in devmode.