setting up LET in closure not returning?? Swift 4 [duplicate] - arrays

I have a simple function loading data from Firebase.
func loadFromFireBase() -> Array<Song>? {
var songArray:Array<Song> = []
ref.observe(.value, with: { snapshot in
//Load songArray
})
if songArray.isEmpty {
return nil
}
return songArray
}
Currently this function returns nil always, even though there is data to load. It does this because it doesn't ever get to the perform the completion block where it loads the array before the function returns. I'm looking for a way to make the function only return once the completion block has been called but I can't put return in the completion block.

(Variations on this question come up constantly on SO. I can never find a good, comprehensive answer, so below is an attempt to provide such an answer)
You can't do that. Firebase is asynchronous. Its functions take a completion handler and return immediately. You need to rewrite your loadFromFirebase function to take a completion handler.
I have a sample project on Github called Async_demo (link) that is a working (Swift 3) app illustrating this technique.
The key part of that is the function downloadFileAtURL, which takes a completion handler and does an async download:
typealias DataClosure = (Data?, Error?) -> Void
/**
This class is a trivial example of a class that handles async processing. It offers a single function, `downloadFileAtURL()`
*/
class DownloadManager: NSObject {
static var downloadManager = DownloadManager()
private lazy var session: URLSession = {
return URLSession.shared
}()
/**
This function demonstrates handling an async task.
- Parameter url The url to download
- Parameter completion: A completion handler to execute once the download is finished
*/
func downloadFileAtURL(_ url: URL, completion: #escaping DataClosure) {
//We create a URLRequest that does not allow caching so you can see the download take place
let request = URLRequest(url: url,
cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,
timeoutInterval: 30.0)
let dataTask = URLSession.shared.dataTask(with: request) {
//------------------------------------------
//This is the completion handler, which runs LATER,
//after downloadFileAtURL has returned.
data, response, error in
//Perform the completion handler on the main thread
DispatchQueue.main.async() {
//Call the copmletion handler that was passed to us
completion(data, error)
}
//------------------------------------------
}
dataTask.resume()
//When we get here the data task will NOT have completed yet!
}
}
The code above uses Apple's URLSession class to download data from a remote server asynchronously. When you create a dataTask, you pass in a completion handler that gets invoked when the data task has completed (or failed.) Beware, though: Your completion handler gets invoked on a background thread.
That's good, because if you need to do time-consuming processing like parsing large JSON or XML structures, you can do it in the completion handler without causing your app's UI to freeze. However, as a result you can't do UI calls in the data task completion handler without sending those UI calls to the main thread. The code above invokes the entire completion handler on the main thread, using a call to DispatchQueue.main.async() {}.
Back to the OP's code:
I find that a function with a closure as a parameter is hard to read, so I usually define the closure as a typealias.
Reworking the code from #Raghav7890's answer to use a typealias:
typealias SongArrayClosure = (Array<Song>?) -> Void
func loadFromFireBase(completionHandler: #escaping SongArrayClosure) {
ref.observe(.value, with: { snapshot in
var songArray:Array<Song> = []
//Put code here to load songArray from the FireBase returned data
if songArray.isEmpty {
completionHandler(nil)
}else {
completionHandler(songArray)
}
})
}
I haven't used Firebase in a long time (and then only modified somebody else's Firebase project), so I don't remember if it invokes it's completion handlers on the main thread or on a background thread. If it invokes completion handlers on a background thread then you may want to wrap the call to your completion handler in a GCD call to the main thread.
Edit:
Based on the answers to this SO question, it sounds like Firebase does it's networking calls on a background thread but invokes it's listeners on the main thread.
In that case you can ignore the code below for Firebase, but for those reading this thread for help with other sorts of async code, here's how you would rewrite the code to invoke the completion handler on the main thread:
typealias SongArrayClosure = (Array<Song>?) -> Void
func loadFromFireBase(completionHandler:#escaping SongArrayClosure) {
ref.observe(.value, with: { snapshot in
var songArray:Array<Song> = []
//Put code here to load songArray from the FireBase returned data
//Pass songArray to the completion handler on the main thread.
DispatchQueue.main.async() {
if songArray.isEmpty {
completionHandler(nil)
}else {
completionHandler(songArray)
}
}
})
}

Making Duncan answer more precise. You can make the function like this
func loadFromFireBase(completionHandler:#escaping (_ songArray: [Song]?)->()) {
ref.observe(.value) { snapshot in
var songArray: [Song] = []
//Load songArray
if songArray.isEmpty {
completionHandler(nil)
}else {
completionHandler(songArray)
}
}
}
You can return the songArray in a completion handler block.

Related

ConfigureAwait , UI, await async

I have a small project - WinForms
On .net frameWork - just a small test :
private void button9_Click(object sender, EventArgs e)
{
string text = GetTitleAsync().Result;
button9.Text = text;
}
private async Task<string> GetTitleAsync()
{
await Task.Delay(3000);
return "Hello!";
}
As I ran the application ,
Clicking the button: "button9" - caused a dead lock,
(since the thread was hung on the ".result" )
Writing GetTitleAsync() this way:
private async Task<string> GetTitleAsync()
{
await Task.Delay(3000).ConfigureAwait(false);
return "Hello!";
}
solved the deadlock - and the application ran ok.
But I don't understand how ?
I would have expected, that using ".ConfigureAwait(false)" would cause a
situation in which :
"button9.Text = text;" is executed on a different thread than
the one, on which the UI was created,
and an excpetion would be throwed !
but it works excellent !
how??
I would have expected, that using ".ConfigureAwait(false)" would cause a situation in which "button9.Text = text;" is executed on a different thread than the one, on which the UI was created, and an excpetion would be throwed ! but it works excellent ! how??
I recommend reading my async/await intro; I try to include everything you need to know about async/await and their contexts, without getting into too much detail for an intro.
Specifically, there are two points from that post that are worth noting:
Every async method begins executing synchronously, on the calling thread.
await captures a context unless you use ConfigureAwait(false).
So, walking through this code:
private void button9_Click(object sender, EventArgs e)
{
string text = GetTitleAsync().Result;
button9.Text = text;
}
private async Task<string> GetTitleAsync()
{
await Task.Delay(3000).ConfigureAwait(false);
return "Hello!";
}
This is what happens, in order, with special attention paid to which thread runs which code:
button9_Click calls GetTitleAsync() on the UI thread.
GetTitleAsync() calls Task.Delay(3000) and gets back a task that will complete in 3 seconds.
GetTitleAsync() calls ConfigureAwait(false) and gets back a configured awaiter that will not resume on the current (UI) context.
GetTitleAsync() uses await to asynchronously wait for the task to complete. This await will not resume on the current (UI) context because the await has been configured not to.
The await examines the task and sees it is not complete, so it returns an incomplete Task<string> to its caller.
button9_Click calls .Result on that task. This blocks the UI thread until that task completes (i.e., GetTitleAsync() is finished executing).
Three seconds later, the task returned from Task.Delay(3000) completes.
GetTitleAsync() resumes executing after its await. Since this was a configured await, it continues executing on a thread pool thread.
GetTitleAsync() returns "Hello!". This is done on a thread pool thread.
By returning a value, GetTitleAsync() is now complete, and the Task<string> that it returned earlier is now completed with a result value. This completion also happens on a thread pool thread.
Since the task is now complete, the UI thread is no longer blocked, and it continues executing button9_Click.
button9_Click executes button9.Text = text; on the UI thread.

Creating a new UI thread while calling methods on the original thread

I am in a similar situation as this poster (What's the best way to create a new UI thread and call back methods on the original thread?)
I have an API object which I would like to perform lengthy calculations on, however any properties or methods of this object must be accessed on the current thread (which is the UI thread) or else I get "Accessing disposed TPS.NET DataObject" exceptions
Is there an elegant way of accomplishing this using F# async workflows or will I be stuck managing thread dispatchers as in his solution.
For reference, here is his solution to the issue:
public class Plugin
{
public void Run(Context context)
{
// Get the application's UI thread dispatcher
var dispatcher = Dispatcher.CurrentDispatcher;
// Create a dispatcher frame to push later
var frame = new DispatcherFrame();
// Create a new UI thread (using an StaTaskScheduler)
Task.Factory.StartNew(async () =>
{
var window = new MyWindow();
// The Click event handler now uses the original
// thread's dispatcher to run the slow method
window.MyButton.Click += async (o, e) =>
await dispatcher.InvokeAsync(() => context.SlowMethod());
window.ShowDialog();
// When the window is closed, end the dispatcher frame
frame.Continue = false;
}, CancellationToken.None, TaskCreationOptions.None, new StaTaskScheduler(1));
// Prevent exiting this Run method until the frame is done
Dispatcher.PushFrame(frame);
}
}
Without know the exact details I would suggest having the Click handler on the main thread and do the following:
Copy any data needed off the UI into an F# record and passes this into an async workflow
Return immediately after putting the UI into a 'loading' state
The following code is untested but should put you on the right track:
//Get the context of the UI
let context = System.Threading.SynchronizationContext.Current
//Gather any needed data from the UI into immutable F# records
//Put the UI into a 'loading' state
async {
// Do work on a different thread
do! Async.Sleep 1000
let x = 1
// Switching back to the UI
do! Async.SwitchToContext context
//Update UI
return ()
}
|> Async.Start
This link should also provide some useful information http://tomasp.net/blog/async-non-blocking-gui.aspx/
EDIT:
If you need to go back and forth between the UI thread and a background thread to gather additional information in the async workflow you can make alternating calls between do! Async.SwitchToThreadPool() and do! Async.SwitchToContext context

ReactiveCommand in WPF/ReactiveUI app blocks UI thread

I am quite a beginner with ReactiveUI and have a strange behavior with a ReactiveCommand.
I want to query data from a database that currently does not support
asynchronous operations. Since we want to exchange the database in the
future with an asynchronous interface I want to write everything as
if the database already would allow async operations. As far as I understand
that would mean that I wrap my database calls at the lowest level in
a Task.
I have a button which is bound to a ReactiveCommand and the command
starts the database query. While the query lasts I want to show some
sort of animation.
The problem is that whatever I tried, the query blocks my UI thread.
Here is part of my code:
public ReactiveCommand<Unit, Unit> StartExportCommand { get; }
//The constructor of my view model
public ExportDataViewModel(IDataRepository dr)
{
this.dr = dr;
//...
StartExportCommand = ReactiveCommand.CreateFromTask(() => StartExport());
//...
}
private async Task StartExport()
{
try
{
Status = "Querying data from database...";
//Interestingly without this call the Status message would not even be shown!
//The delay seems to give the system the opportunity to at least update the
//label in the UI that is bound to "Status".
await Task.Delay(100);
//### This is the call that blocks the UI thread for several seconds ###
var result = await dr.GetValues();
//do something with result...
Status = "Successfully completed";
}
catch(Exception ex)
{
Status = "Failed!";
//do whatever else is necessary
}
}
//This is the GetValues method of the implementation of the IDataRepository.
//The dictionary maps measured values to measuring points but that should not matter here.
//ValuesDto is just some container for the values.
public Task<IDictionary<int, ValuesDto>> GetValues()
{
//...
return Task<IDictionary<int, ValuesDto>>.Factory.StartNew(() =>
{
//### here is where the blocking calls to the database
//### specific APIs take place
return result;
}, TaskCreationOptions.LongRunning);
}
I don't understand why this code is blocking the UI thread although I am wrapping
the long running query in a Task.
Is there something wrong with this pattern or should I go another way with Observables?
Edit 1
I am aware of the fact that async != threads. I thought, however, that Task with the TaskCreationOptions.LongRunning would make the blocking code run on a thread pool thread.
Edit 2
As recommended by Andy I set a breakpoint inside my task and had a look into the Debug Threads window. It tells me that Task is running on a worker thread. Still my UI is blocking.

Codename One RESTfulWebServiceClient Threads

I have a simple programme that calls a rest service and displays the output.
The problem is the display is being updated before the rest method returns.
I've tried to use the invoke and block, however the .find method appears to run in it's own thread? asynchronously
the Sys output goes like this;
Before
After
surname
System.out.println("Before");
userClient = new RESTfulWebServiceClient("http://localhost:8080/MyService/api/person");
Display.getInstance ()
.invokeAndBlock(() -> {
userClient.find(
new Query().id("id"), rowset -> {
for (Map m : rowset) {
person = new JSONObject(m);
System.out.println(person.getString("surname"));
}
}
}
System.out.println("After");
I have found a workaround that works.
As I can see the problem is the .find method of the RESTfulWebServiceClient class. The line NetworkManager.getInstance().addToQueue(req); creates an asynchronies call to the rest network service and returns the method before the call is made.
To get around this I recreated the RESTfulWebServiceClient class in my project and copied the source from github.
I then changed the
NetworkManager.getInstance().addToQueue(req);
to
NetworkManager.getInstance().addToQueueAndWait(req);
This causes the the method to complete the rest call before returning.

Calling a webservice synchronously from a Silverlight 3 application?

I am trying to reuse some .NET code that performs some calls to a data-access-layer type service. I have managed to package up both the input to the method and the output from the method, but unfortunately the service is called from inside code that I really don't want to rewrite in order to be asynchronous.
Unfortunately, the webservice code generated in Silverlight only produces asynchronous methods, so I was wondering if anyone had working code that managed to work around this?
Note: I don't need to execute the main code path here on the UI thread, but the code in question will expect that calls it makes to the data access layers are synchronous in nature, but the entire job can be mainly executing on a background thread.
I tried the recipe found here: The Easy Way To Synchronously Call WCF Services In Silverlight, but unfortunately it times out and never completes the call.
Or rather, what seems to happen is that the completed event handler is called, but only after the method returns. I am suspecting that the event handler is called from a dispatcher or similar, and since I'm blocking the main thread here, it never completes until the code is actually back into the GUI loop.
Or something like that.
Here's my own version that I wrote before I found the above recipe, but it suffers from the same problem:
public static object ExecuteRequestOnServer(Type dalInterfaceType, string methodName, object[] arguments)
{
string securityToken = "DUMMYTOKEN";
string input = "DUMMYINPUT";
object result = null;
Exception resultException = null;
object evtLock = new object();
var evt = new System.Threading.ManualResetEvent(false);
try
{
var client = new MinGatServices.DataAccessLayerServiceSoapClient();
client.ExecuteRequestCompleted += (s, e) =>
{
resultException = e.Error;
result = e.Result;
lock (evtLock)
{
if (evt != null)
evt.Set();
}
};
client.ExecuteRequestAsync(securityToken, input);
try
{
var didComplete = evt.WaitOne(10000);
if (!didComplete)
throw new TimeoutException("A data access layer web service request timed out (" + dalInterfaceType.Name + "." + methodName + ")");
}
finally
{
client.CloseAsync();
}
}
finally
{
lock (evtLock)
{
evt.Close();
evt = null;
}
}
if (resultException != null)
throw resultException;
else
return result;
}
Basically, both recipes does this:
Set up a ManualResetEvent
Hook into the Completed event
The event handler grabs the result from the service call, and signals the event
The main thread now starts the web service call asynchronously
It then waits for the event to become signalled
However, the event handler is not called until the method above has returned, hence my code that checks for evt != null and such, to avoid TargetInvocationException from killing my program after the method has timed out.
Does anyone know:
... if it is possible at all in Silverlight 3
... what I have done wrong above?
I suspect that the MinGatServices thingy is trying to be helpful by ensuring the ExecuteRequestCompleted is dispatched on the main UI thread.
I also suspect that your code is already executing on the main UI thread which you have blocked. Never block the UI thread in Silverlight, if you need to block the UI use something like the BusyIndicator control.
The knee-jerk answer is "code asynchronously" but that doesn't satisfy your question's requirement.
One possible solution that may be less troublesome is to start the whole chunk of code from whatever user action invokes it on a different thread, using say the BackgroundWorker.
Of course the MinGatServices might be ensuring the callback occurs on the same thread that executed ExecuteRequestAsync in which case you'll need to get that to run on a different thread (jumping back to the UI thread would be acceptable):-
Deployment.Current.Dispatcher.BeginInvoke(() => client.ExecuteRequestAsync(securityToken, input));

Resources