I have the following code that loads in the initial ViewController viewDidLoad. It works fine initially. But shouldn't it look for changes every 10 seconds?
When I make an update to a config value in Firebase and publish, I don't see this come through in the app. I am running in debug mode so throttling isn't an issue.
If I restart the app, I see the new value. Since the interval is set to 10 seconds, shouldn't I see the update while the app is running?
let rc = FIRRemoteConfig.remoteConfig()
let interval: TimeInterval = 10
FIRRemoteConfig.remoteConfig().fetch(withExpirationDuration: interval) {
(status, error) in
guard error == nil else {
//handle error here
return
}
FIRRemoteConfig.remoteConfig().activateFetched()
let test = rc["key1"].stringValue //this runs only once
}
Any ideas why this isn't update?
You should use scheduledTimer instead.
/// Fetches Remote Config data and sets a duration that specifies how long config data lasts.
/// Call activateFetched to make fetched data available to your app.
/// #param expirationDuration Duration that defines how long fetched config data is available, in
/// seconds. When the config data expires, a new fetch is required.
/// #param completionHandler Fetch operation callback.
open func fetch(withExpirationDuration expirationDuration: TimeInterval, completionHandler: FirebaseRemoteConfig.FIRRemoteConfigFetchCompletion? = nil)
fetch(withExpirationDuration: interval) is to fetch data with a timeout, that is your interval.
let interval: TimeInterval = 10
Timer.scheduledTimer(timeInterval: interval,
target: self,
selector: #selector(updateConfig),
userInfo: nil,
repeats: true)
func updateConfig() {
let rc = FIRRemoteConfig.remoteConfig()
FIRRemoteConfig.remoteConfig().fetch { (status, error) in
guard error == nil else {
//handle error here
return
}
FIRRemoteConfig.remoteConfig().activateFetched()
let test = rc["key1"].stringValue //this runs only once
}
}
Related
I'm using Durable Functions in a process to collect data from 12 different people. For this I chose the WaitForExternalEvent method. I need to get notified of those external events as they happen. All events must be received in the next 1h.
I have created the following orchestration. The behaviour is odd however. The orchestration neither completes, nor fails. I am using Durable Functions Monitor (dfMon) to inspect the logs. As you can see in the execution history all 12 events are in fact received (before the 1h timeout). Still the orchestrator:
didn't execute the Fxbm_notifyOfNewReport activity function after each received event
also didn't exit the while loop after all 12 events
Also, more than 1h has elapsed and every timeout timer has fired. Still, no exception was thrown. The orchestration is still in a running state.
I took inspiration for this from this doc and this blog.
Does someone know why I am not seeing the expected behaviour?
public class Fxbm_workflow
{
[FunctionName(nameof(Fxbm_workflow))]
public async Task Run([OrchestrationTrigger] IDurableOrchestrationContext ctx, ILogger log)
{
var id = ctx.InstanceId;
var trigger = ctx.GetInput<Trigger<OrchestrationInput2>>();
// sub-orchestration to distribute data to people
// return value is int[], these are the ids of the people
var input = (trigger, id);
var childWorkflowId = $"{id}_{nameof(Fxbm_prep_workflow)}";
var requiredCompanies = await ctx.CallSubOrchestratorAsync<int[]>(nameof(Fxbm_prep_workflow), instanceId: childWorkflowId, input: input);
// to every distributed data package, a string response is expected in 1h at the latest
var expiresIn = TimeSpan.FromHours(1);
var responseTasks = requiredCompanies.Select(id => ctx.WaitForExternalEvent<string>(id.ToString(), expiresIn)).ToList();
// all tasks need to complete or a timeout exception must be thrown
// I want to get notified of responses as they come in
// therefore Task.WhenAll() is not suitable
while (responseTasks.Any())
{
try
{
// responses as they occur
var receivedResponse = await Task.WhenAny(responseTasks);
responseTasks.Remove(receivedResponse);
var stringResponse = await receivedResponse;
// notify via mail
await ctx.CallActivityAsync(nameof(Fxbm_notifyOfNewReport), stringResponse);
}
catch (TimeoutException)
{
// break;
throw;
}
}
}
}
I have an iPhone app that sends data from the iPhone app directly to the watch face to be displayed as a complication.
I use the WatchConnectivity framework to create a WCSession to send the data to the watch from the phone.
My data is stored in a dictionary, and sent to the watch using WCSession's transferCurrentComplicationUserInfo method. (This method can be used something like 50 times a day, and I am aware of this - that is not the issue.)
The transferCurrentComplicationUserInfo method seems to work the first time that I attempt to send data.
My problem is that my iPhone app is meant to call this function several times in a session, and it only reliably works the first time.
When I send a second set of data, the first set remains on the complication. Often, when I send the third set, the second set appears. Sometimes the second set appears permanently, and sometimes it only appears for a brief second before displaying the third set.
It is inconsistent, and that is the issue I am having.
Is there anything that I have set up incorrectly?
Code:
//iPhone code to send data to Apple Watch:
func sendComplication(complication: Complication) {
guard let session = session else {
delegate?.failedToSendComplication(reason: "Could not connect to your Apple Watch.")
return
}
guard let context = convertComplicationToDictionary(complication: complication) else {
delegate?.failedToSendComplication(reason: "Couldn't cast complication to a dictionary.")
return
}
if session.remainingComplicationUserInfoTransfers > 0 {
session.transferCurrentComplicationUserInfo(context)
delegate?.didSendComplication()
} else {
delegate?.failedToSendComplication(reason: "Due to hardware limitations, you can only send a certain amount of complications in a day. You have exceeded that limit for today. You can still set complications from the Apple Watch app.")
}
}
// WatchKit Extension Delegate to receive and handle data sent from iPhone app
import WatchKit
import WatchConnectivity
class ExtensionDelegate: NSObject, WKExtensionDelegate {
var session: WCSession?
override init() {
super.init()
self.session = newWatchConnectivitySession()
}
func newWatchConnectivitySession() -> WCSession? {
if WCSession.isSupported() {
let session = WCSession.default
session.delegate = self
session.activate()
return session
}
return nil
}
func reloadComplicationTimeline() {
let server = CLKComplicationServer.sharedInstance()
guard let activeComplicationFamilies = server.activeComplications else { return }
for comp in activeComplicationFamilies {
server.reloadTimeline(for: comp)
}
}
}
extension ExtensionDelegate: WCSessionDelegate {
func sessionReachabilityDidChange(_ session: WCSession) {
if session.activationState != .activated {
self.session = newWatchConnectivitySession()
}
}
// Receive info from iPhone app
func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
// Parse dictionary and update data source
reloadComplicationTimeline()
}
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
guard let error = error else { return }
print(error.localizedDescription)
}
}
// UPDATE //
Upon further inspection, I now see that steps are happening out of order.
This is the sequence of events:
sendComplication is called from the iPhone app
ExtensionDelegate is initialized on the Watch app, setting up the WCSession
The complication is updated (too early - this is before the WCSession receives the new data)
The WCSession didReceiveUserInfo delegate method is called, data is parsed, and the data source is updated (too late)
The complication is told to reload, but nothing happens (possible budgeting issue?)
Try the following:
func reloadComplicationTimeline() {
#if os(watchOS)
let server = CLKComplicationServer.sharedInstance()
if let activeComplicationFamilies = server.activeComplications {
for comp in activeComplicationFamilies {
server.reloadTimeline(for: comp)
}
#endif
}
func sendComplication(complication: Complication) {
guard WCSession.default.activationState == .activated else {
delegate?.failedToSendComplication(reason: "Could not connect to your Apple Watch.")
return
}
guard let context = convertComplicationToDictionary(complication: complication) else {
delegate?.failedToSendComplication(reason: "Couldn't cast complication to a dictionary.")
return
}
#if os(iOS)
if WCSession.default.isComplicationEnabled {
let userInfoTranser = WCSession.default.transferCurrentComplicationUserInfo(context)
delegate?.didSendComplication()
} else {
delegate?.failedToSendComplication(reason: "Due to hardware limitations, you can only send a certain amount of complications in a day. You have exceeded that limit for today. You can still set complications from the Apple Watch app.")
}
#endif
}
Here is a good example from Apple that could help you more: source
As you describe it in your update, your iPhone app calls session.transferCurrentComplicationUserInfo(context) before the watch sets up its WCSession. But the docs say:
[transferCurrentComplicationUserInfo(_:)] can only be called while the
session is active - that is, the activationState property is set to
WCSessionActivationState.activated. Calling this method for an
inactive or deactivated session is a programmer error.
Thus I suggest that you implement (if you haven’t done so already) the WCSessionDelegate function session(_:activationDidCompleteWith:error:) (see here), and transfer complication data only after the session has been activated.
Resetting the iPhone, Apple Watch, and Mac fixed the problem.
It seems that Ignite(2.0) Messaging's send function works in sync mode, it will be blocked be the listener. And below is my testing code.
ignite.message().localListen("TEST", (nodeId, Msg) -> {
try {
Thread.sleep(500);
} catch (Exception ex) {
}
return true;
});
for (int i = 0; i < 100; i++) {
ignite.message().send("TEST", "Hello World");
}
It cost about 50 seconds to send 100 messages, and it is almost equals the sleep time 500 ms * 100. seems the send function in sync mode not in async mode.
Does anybody know how to change the send function in async mode?
Thanks in advance.
Seems async listener invocation was missed while adding new API, but you still have two options:
Use deprecated withAsync(), unless sendAsync() will be added.
Pass your own Executor in predicate, if you always return true, for example.
I've just opened a ticket for that IGNITE-5570
It seems that you're testing within one node. In this case there is no message sent and listener is invoked synchronously. Network communication is asynchronous in Ignite, so if you do the testing on two nodes, you should not see such behavior.
I wanna make a web crawling, currently i am reading a txt file with 12000 urls, i wanna use concurrency in this process, but the requests don't work.
typealias escHandler = ( URLResponse?, Data? ) -> Void
func getRequest(url : URL, _ handler : #escaping escHandler){
let session = URLSession(
configuration: .default,
delegate: nil,
delegateQueue: nil)
var request = URLRequest(url:url)
request.httpMethod = "GET"
let task = session.dataTask(with: request){ (data,response,error) in
handler(response,data)
}
task.resume()
}
for sUrl in textFile.components(separatedBy: "\n"){
let url = URL(string: sUrl)!
getRequest(url: url){ response,data in
print("RESPONSE REACHED")
}
}
If you have your URLSessions working correctly, all you need to go is create separate OperationQueue create a Operation for each of your async tasks you want completed, add it to your operation queue, and set your OperationQueue's maxConcurrentOperationCount to control how many of your tasks can run at one time. Puesdo code:
let operationQueue = OperationQueue()
operationQueue.qualityOfService = .utility
let exOperation = BlockOperation(block: {
//Your URLSessions go here.
})
exOperation.completionBlock = {
// A completionBlock if needed
}
operationQueue.addOperation(exOperation)
exOperation.start()
Using a OperationQueue subclass and Operation subclass will give you additional utilities for dealing with multiple threads.
I'm using Long Polling with Atmosphere JS for session timeout, so the server tells me when the user has been logged out.
The issue is that once subscribed, Atmosphere JS keeps sending a get request every 60 seconds, which restarts the user session and never logs them out.
I've read the documentation and searched around, but can't see any way to stop this happening. Here is my code:
var socket = atmosphere;
var subSocket;
// subscribe
function subscribe() {
var request = {
url : "/web-service/notifier",
transport: 'long-polling'
};
request.onMessage = function (response) {
var jsonStringArray = response.responseBody.split('|');
// go through each notification and convert from string to object
$.each(jsonStringArray, function(index, elem){
if (elem != ""){
var parsedObject = JSON.parse(elem);
// if notification states user is logged out, log them out
if (parsedObject.action === 'LOGGED_OUT'){
// DO LOGOUT STUFF
}
}
});
};
subSocket = socket.subscribe(request);
}
Thanks for any help.
That's how long-polling works, e.g the connection will be closed/resumed by the server after 60 seconds (you have set that value server side...check your code)
-- Jeanfrancois