I'm trying to make a watchOS 3 app, and I want to update my complication in a background task.
First, I get new data from a server in a background task within handle(). After that, I update my active complications by calling complicationServer.reloadTimeline(for:).
In the console I do see the message "UPDATE COMPLICATION," so the code is executed.
Yet after reloading, the complication still shows the old data. If I switch the watch face and switch back, then the complication sometimes reloads. Do I have to do something else to reload the complication from the background task?
func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
for task : WKRefreshBackgroundTask in backgroundTasks {
if (WKExtension.shared().applicationState == .background) {
if task is WKApplicationRefreshBackgroundTask {
let dataProvider = DataProvider()
dataProvider.getData(station: "Name", completion: { (data, error) in
self.updateComplication()
self.scheduleNextBackgroundRefresh()
task.setTaskCompleted()
})
}
} else {
task.setTaskCompleted()
}
}
}
func updateComplication() {
let complicationServer = CLKComplicationServer.sharedInstance()
for complication in complicationServer.activeComplications! {
print("UPDATE COMPLICATION")
complicationServer.reloadTimeline(for: complication)
}
}
Your current approach:
You've got a mix of watchOS 2 and watchOS 3 approaches there.
WKApplicationRefreshBackgroundTask (new watchOS 3 approach)
DataProvider starts asynchronous transfer (old watchOS 2 approach which assumes it's in the foreground, not the background).
That background thread (of a background task which may or may not be suspended yet not completed) expects to have the background task handle its completion, once the asynchronous transfer completes.
In short, you've expected your background refresh task to wait in the background for an asynchronous transfer. This is bit convoluted (since the refresh task is supposed to be doing work in the background, not waiting on other work to complete).
A better way for watchOS 3:
Since an asynchronous transfer can be suspended, you would be better off using a URLSession background transfer.
Always upload and download data using an URLSession background transfer. Background transfers occur in a separate process. They continue to transfer the data even after your app has terminated. Asynchronous uploads and downloads, on the other hand, are suspended with your app. Given the short run time of watchOS apps, you cannot guarantee that an asynchronous transfer will finish before the app is suspended.
By letting a WKURLSessionRefreshBackgroundTask respond to the background transfer, your extension can be woken in the background once the session completes, hand off that session's data to the data provider, and then update the complication.
An suggestion about the data provider:
It seems to have the responsibility of both transferring data, and providing data. You may want to consider splitting off the network portion into a separate component, and simply let it be a repository of data.
I get the impression that it's meant to be some manner of singleton (behind the scenes) yet you initialize an instance as a DataProvider().
From a readability viewpoint, it's not apparent from the provided code that the complication data source will be using the same data provider as the one who received the data.
You should avoid force unwrapping optionals:
When activeComplications is nil (such as when the complication had been removed from the watch face between the last update and this update), your code will ungraciously crash.
You should use a guard or if let to first check that you still have an active complication.
Related
I have came across a requirement where i want axon to wait untill all events in the eventbus fired against a particular Command finishes their execution. I will the brief the scenario:
I have a RestController which fires below command to create an application entity:
#RestController
class myController{
#PostMapping("/create")
#ResponseBody
public String create(
org.axonframework.commandhandling.gateway.CommandGateway.sendAndWait(new CreateApplicationCommand());
System.out.println(“in myController:: after sending CreateApplicationCommand”);
}
}
This command is being handled in the Aggregate, The Aggregate class is annotated with org.axonframework.spring.stereotype.Aggregate:
#Aggregate
class MyAggregate{
#CommandHandler //org.axonframework.commandhandling.CommandHandler
private MyAggregate(CreateApplicationCommand command) {
org.axonframework.modelling.command.AggregateLifecycle.apply(new AppCreatedEvent());
System.out.println(“in MyAggregate:: after firing AppCreatedEvent”);
}
#EventSourcingHandler //org.axonframework.eventsourcing.EventSourcingHandler
private void on(AppCreatedEvent appCreatedEvent) {
// Updates the state of the aggregate
this.id = appCreatedEvent.getId();
this.name = appCreatedEvent.getName();
System.out.println(“in MyAggregate:: after updating state”);
}
}
The AppCreatedEvent is handled at 2 places:
In the Aggregate itself, as we can see above.
In the projection class as below:
#EventHandler //org.axonframework.eventhandling.EventHandler
void on(AppCreatedEvent appCreatedEvent){
// persists into database
System.out.println(“in Projection:: after saving into database”);
}
The problem here is after catching the event at first place(i.e., inside aggregate) the call gets returned to myController.
i.e. The output here is:
in MyAggregate:: after firing AppCreatedEvent
in MyAggregate:: after updating state
in myController:: after sending CreateApplicationCommand
in Projection:: after saving into database
The output which i want is:
in MyAggregate:: after firing AppCreatedEvent
in MyAggregate:: after updating state
in Projection:: after saving into database
in myController:: after sending CreateApplicationCommand
In simple words, i want axon to wait untill all events triggered against a particular command are executed completely and then return to the class which triggered the command.
After searching on the forum i got to know that all sendAndWait does is wait until the handling of the command and publication of the events is finalized, and then i tired with Reactor Extension as well using below but got same results: org.axonframework.extensions.reactor.commandhandling.gateway.ReactorCommandGateway.send(new CreateApplicationCommand()).block();
Can someone please help me out.
Thanks in advance.
What would be best in your situation, #rohit, is to embrace the fact you are using an eventually consistent solution here. Thus, Command Handling is entirely separate from Event Handling, making the Query Models you create eventually consistent with the Command Model (your aggregates). Therefore, you wouldn't necessarily wait for the events exactly but react when the Query Model is present.
Embracing this comes down to building your application such that "yeah, I know my response might not be up to date now, but it might be somewhere in the near future." It is thus recommended to subscribe to the result you are interested in after or before the fact you have dispatched a command.
For example, you could see this as using WebSockets with the STOMP protocol, or you could tap into Project Reactor and use the Flux result type to receive the results as they go.
From your description, I assume you or your business have decided that the UI component should react in the (old-fashioned) synchronous way. There's nothing wrong with that, but it will bite your *ss when it comes to using something inherently eventually consistent like CQRS. You can, however, spoof the fact you are synchronous in your front-end, if you will.
To achieve this, I would recommend using Axon's Subscription Query to subscribe to the query model you know will be updated by the command you will send.
In pseudo-code, that would look a little bit like this:
public Result mySynchronousCall(String identifier) {
// Subscribe to the updates to come
SubscriptionQueryResult<Result> result = QueryGateway.subscriptionQuery(...);
// Issue command to update
CommandGateway.send(...);
// Wait on the Flux for the first result, and then close it
return result.updates()
.next()
.map(...)
.timeout(...)
.doFinally(it -> result.close());
}
You could see this being done in this sample WebFluxRest class, by the way.
Note that you are essentially closing the door to the front-end to tap into the asynchronous goodness by doing this. It'll work and allow you to wait for the result to be there as soon as it is there, but you'll lose some flexibility.
I have a class with the delegates for a URLSession. I intend to use it with a background configuration. I understand that the handlers are called when a certain event happens, such as didFinishDownloadingTo.
However, I do have the handle function on my ExtensionDelegate class:
func handle( _ handleBackgroundTasks:
Set<WKRefreshBackgroundTask>)
// Sent when the system needs to launch the application in the background
to process tasks. Tasks arrive in a set, so loop through and process each one.
for task in handleBackgroundTasks {
switch task {
case let urlSessionTask as WKURLSessionRefreshBackgroundTask:
I wonder: where should I handle the data I receive after a download? At the didFinishDownloadingTo or at that function on my ExtensionDelegate class, on the appropriate case of the switch statement?
Another question on the same cycle: I read everywhere that one must remember to setTaskCompleted() after going through the background tasks. But I read elsewhere that one should not set a task as completed if the scheduled data transfer hasn't finished. How do I check that?
There is a very good explanation here.enter link description here
It worked when I had an array with my WKURLSessionRefreshBackgroundTask. Then, at the end of my didFinishDownloadingTo, I get the task on that array that has the same sessionIdentifier as the current session.configuration.identifier, and set it as complete.
Is it possible to stop (kill) asynchronous Call?
In my app I have at client side sth like:
Meteor.call('doCalculation', function(err, result) {
//do sth with result
});
'doCalculation' may take long time (this is ok) I dont want user to start new call when he/she has already one running call, I want to allow user to stop current call and submit new one. How correctly do this?
The only idea I have is to communicate between client and server using mongo. In some place in 'doCalculation' function I can observe some mongo document/collection and based on this do sth in the function (e.g. call exception). Do you have any better ideas?
You can use a semaphore for this purpose. When the semaphore is 1, requests are allowed to be sent. When the semaphore is 0, requests are not allowed to be sent. The semaphore should be 1 by default and just before you send the request, you need to set it to 0. When a response is successful, you set the semaphore back to 1.
As about the timeout: You could use a time out using setTimeout after sending the request, like this:
if (semaphore) {
var isTimedOut = false;
var isSuccess = false;
semaphore = 0; //No need to use var keyword, as this should be declared outside of this scope
Meteor.call('doCalculation', function(err, result) {
isSuccess = true;
//do sth with result
});
setTimeout(function() {
if (!isSuccess) {
isTimeout = true;
//do something else, to handle the time out state
}
}, 10000);
}
This is tricky, because you cannot generally set timeouts from the client's point of view. You don't need to, for a bunch of architectural reasons. The most important thing is that if you lose network connectivity or the server crashes (two cases timeouts are designed to manage), the client is aware immediately because it is disconnected. You can use Meteor.status().connected if this happens often.
It sounds like you're running a long calculation on the server. My suggestion is to return a calculationId immediately, and then update a collection with progress, e.g., CalculationProgresses.update(calculationId, {$set: {progress: currentProgress}}) as you calculate. Your UI can then update the progress reactively, in the most convenient way possible.
Note, that when you do run long calculations on the server, you need to occasionally "yield," giving the chance for other work to happen. Node, on which Meteor is based, is tricky for long calculations if you don't master this notion of yielding. In Meteor, you can yield easily by updating a collection (e.g., your progress collection). This will solve lots of problems you're probably experiencing as you write your application.
i think you need a server-side solution for this. if you go with a client-side solution, you don't handle 2 cases:
the user reloads their browser
the user uses 2 browsers
i would create these methods:
isCalculationActive() -- this checks if the user already has a calculation active. on the server, you can either keep that fact in memory or write it to the db. on the client, if this returns false, then you can proceed to call doCalculation(). if true, you can give the user a popup or alert or something to ask if they want to cancel and proceed.
doCalculation() -- this cancels any outstanding calculation by that user and starts a new one.
with these implemented, the user can reload their browser w/o affecting either the running calculation or correct behavior. and if they try a 2nd browser, everything should still work as expected.
if you want to give the user the option to simply stop the job and not start a new one, then you can simply create:
cancelCalculation() -- this cancels any outstanding calculation by that user.
i've been battling for a few days with an autorun function that renders elements on a canvas for two players in a board game. the whole process from when 1 player places a move until its visible for the other player took too long. i managed to cut alot by optimizing the code i have written as a meteor beginner, and also by placing the method that updates the game move inside a (meteor.isServer) so it won't first run the method in the move maker's client (it was already just inside the server's folder).
one delay in time which is beyond my understanding, happens from the moment the server finishes with the move making method until the client's autorun begin to run(about 60 ms delay for chrome, and 150(!) for firefox). i've ran some tests to try and narrow down the options for why it happens, the last one was to subscribe to the games collection from a different page, and see if the response time is slow there too. this is the code:
Template.Watch.onCreated(function(){
this.subscribe('activeGames');
this.autorun(()=>{
if(this.subscriptionsReady()){
console.log(Games.findOne().turn + " " + (new Date).getTime());
}
});
});
Template.Watch.helpers({
games: ()=> {
console.log(Games.findOne().turn + " " + (new Date).getTime());
return Games.find({result: false});
}
});
now, on the client both console.logs appeared whenever a move was played, the weird thing is that the helper's console.log was fired with no delay, 50 ms before the autorun's console.log which had the same delay as in the game page.
by my poor understanding, if the client knows a change was made to the database, the autorun should simply run, just like the helper simply runs.
i would be very grateful to whoever explains to me the behavior of the autorun that causes that delay, and if there is a way to cancel it.
Thanks in advance!
When you call a method on the client using Meteor.call, two things happen in parallel:
The client sends a request to the server to run the method in a secure
environment, just like an AJAX request would work A simulation of the
Method runs directly on the client to attempt to predict the outcome
of the server call using the available information.
So here is the reason why your Helper console print before the autorun.
Your mini-mongo(client side DB) get updated before the server data update according to point number 2.
According to point number 1, the client sends a request to the server, then server updates the server database. Server re-publish the data to the client and autorun reinitialize in your case.
So the point is your mini mongo(client side DB) updated before the subscription ready. that's why you got console log in helper before your autorun.
One more thing happen when your server data to client::
If the result from the server comes back and is consistent with the
simulation on the client, everything remains as is. If the result on
the server is different from the result of the simulation on the
client, the UI is patched to reflect the actual state of the server.
I want to make a tcp connection to a device and keep continously retrieve data from device. I want to start this with a simple request and keep it working background even Page response completed. Is this possible in asp.net?
Can a thread in ASP.NET work keep continue after Response.End?
Yes, you can if you do not care or do not need the result.
For example, in the following code, you call AddLogAsync and insert a log, but you not care whether insert successful or not.
public Task AddLogAsync(Log log)
{
return Task.Run(() => AddLog(log));
}
private void AddLog(TraceLog traceLog)
{
// Do something here.
}
I want to make a tcp connection to a device and keep continously
retrieve data from device. I want to start this with a simple request
and keep it working. Is this possible in asp.net?
I'm not really understanding above question. After Response.End, you cannot return anything, although you can continue work on something in different thread.