Let's say 2 different callback of an API request have to execute this same piece of code :
if flag == false {
flag = true // preventing the whole thing to be called multiple times
// Do something
// reset flag to false after 10 sec
}
Sometimes it happens that both threads were so simultaneous, that they both reach "// Do something" instructions.
What is the proper swifty way to resolve this case ?
Related
I am having trouble setting up my In App Purchases on Swift 4 in Xcode.
The purchase goes through successfully, I know this because the following is printed out into the console.
"Adding product to payment queue
purchased com.taptrudel.ZenHangman.Zap
ProductID : com.taptrudel.ZenHangman.Zap
Gave user 10 zaps, new zap count: 13
numberOfZaps = 3"
extension IAPService: SKPaymentTransactionObserver {
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
print(transaction.transactionState.status(), transaction.payment.productIdentifier)
switch transaction.transactionState {
case .purchasing, .deferred: break // do nothing
case .purchased:
queue.finishTransaction(transaction)
givePurchasedProduct(productID: transaction.payment.productIdentifier)
case .restored:
self.restorePurchase()
queue.finishTransaction(transaction)
case .failed:
queue.finishTransaction(transaction)
}
}
}
}
The extension above calls givePurchasedProduct(productId: String) once the purchase state becomes .purchased
That function is just below here, it simply passes in the productId to a function in my game scene which will give the user the consumable he just purchased.
func givePurchasedProduct(productID: String) {
guard let gameScene = GameScene(fileNamed: "GameScene") else { return }
gameScene.givePurchasedProduct(productId: productID)
}
Now if you look below we are in my GameScene.
func givePurchasedProduct(productId: String) {
print("ProductID : \(productId)")
if productId.range(of: "Zap") != nil {
numberOfZaps += 10
print("Gave user 10 zaps, new zap count: \(numberOfZaps)")
// TODO: CALL SAVE DATA HERE
}
}
If you return to the message from the console which I pasted at the top of this post, you will see that
Purchase state becomes .purchased
givePurchasedProduct() is called
We successfully add (10) zaps (user starts with 3) for a total of (13) to the numberOfZaps variable.
Immediately after this, I touch the background anywhere as I made it so this would print the number of zaps to make sure everything was working. As you can see... without any actions in between adding 10 zaps and printing the number of zaps, I somehow seem to have lost them... numberOfZaps is set back to 3 immediately.
I searched my entire project for every time that I set "numberOfZaps" to try and identify the problem although it is only set once at the top of my game scene with the other variables.
I am hopeless as I have been struggling with this issue for days.
Only thing I can think of is that given that these are sandbox test purchases that the variable somehow reverts to its original value given that no real payment has actually been made
THANKS A MILLION FOR TAKING THE TIME YOU ARE AWESOME
ps. I just noticed that if I used (2) "zaps" for example. Leaving me with (1) zap from the original (3) that every user is given.
Initialization shown below.
class GameScene: SKScene {
var level = 1
var gameStarted = false
var numberOfZaps = 3
Although when I buy (10) more zaps.
The expected behaviour would be to see the user now have (11) zaps
Although my console is showing me that we once again have (13) zaps after purchase. So it seems like it it adding 10 to the original value of 3 regardless of where the counter currently is.
But as usual, immediately after I click okay on the confirmation of my new zaps, When I check the numberOfZaps we are back at (3). (not 13, not 11..)
I really dont know what else I can do here.
The only time numberOfZaps is modified is in 3 different places
1) Once when it is initialized at the top of my gameScene
2) When a user buys (10) zaps, I add (10) to numberOfZaps
3) When a user uses a zap during a level, I subtract (1) from numberOfZaps
When I subtract (1) zap, the counter works perfectly and maintains the data, although when I add (10) zaps regardless of where the counter currently is, I end up with (13) zaps for just enough time to print my statement, it seems as soon as I leave the function, my zaps return to (3).
For this reason, I also tried adding "self." before "numberOfZaps" to make sure I was referencing the correct instance of the variable.
Thank you!
In the givePurchasedProduct function, you are creating a new GameScene instance, changing its number of zaps, and then the GameScene object is discarded. Whichever other GameScene object you have, which ostensibly controls the real game scene that your app is showing, is not modified.
I tried to validate 2 conditions inside eventually block... something like this
eventually(timeout(Span(26, Seconds)), interval(Span(2, Seconds))) {
response = executeSomeFunction
response should be = (true)
if (response) {
something = responseResult.get
something should be >= (10)
}
}
What am looking for is eventually should satisfy both the conditions. That is first it should check if response is true and then when response is true, it should validate the condition inside if loop.
I tried executing this but am getting error message
ambiguous reference to overloaded definition" referencing to line
"response should be = (true)"
Am not sure what I am trying to do is even possible inside eventually or not.
The problem is that you write
response should be = (true)
But actually you want to write:
response shouldBe true
In your case you make assignment of response should be: ResultOfBeWordForAny[Boolean] to the value true. Not clear what conversion here you expect.
P.S. Also write response = executeSomeFunction outside of eventually block, otherwise it could be executed multiple times.
P.P.S Moreover you don't need eventual call if you test result of your function, it's anyway in the scope. eventually isn't the best practice and used when function have some async side-effects you would like to test.
I am trying to code around the fact that the messageLostHandler doesn't fire for many minutes after a device is out of range using Audio (or Earshot for Android).
I was hoping that every few secs a message would be received from another device. It fires once. Is this expected? Since I can't rely on the messageLost handler - how do I know when a device is truly out of range of the ultrasonic?
I coded up a timer after receiving the subscriptionWithMessageFoundHandler hoping another message coming in I could just invalidate or restart the timer. If the timer fired, I'd know that x seconds passed and that the other device must be out of range. No such luck.
UPDATE: Here is the code in question:
let strategy = GNSStrategy.init(paramsBlock: { (params: GNSStrategyParams!) -> Void in
params.discoveryMediums = .Audio
})
publication = messageMgr.publicationWithMessage(pubMessage, paramsBlock: { (pubParams: GNSPublicationParams!) in
pubParams.strategy = strategy
})
subscription = messageMgr.subscriptionWithMessageFoundHandler({[unowned self] (message: GNSMessage!) -> Void in
self.messageViewController.addMessage(String(data: message.content, encoding:NSUTF8StringEncoding))
// We only seem to get a 1x notification of a message. So this timer is folly.
print("PING") //Only 1x per discovery.
}, messageLostHandler: {[unowned self](message: GNSMessage!) -> Void in
self.messageViewController.removeMessage(String(data: message.content, encoding: NSUTF8StringEncoding))
}, paramsBlock: { (subParams: GNSSubscriptionParams!) -> Void in
subParams.strategy = strategy
})
Notice that the "PING" only prints once.
When a device goes out of range, Nearby waits for 2 minutes before flushing the other device's token from its cache. So if you wait for 2 minutes, the messageLost handler should be called. Can you verify this? Also, is it safe to assume that you'd like to have a timeout shorter than 2 minutes? This timeout has been a topic of discussion, and there's been some talk of adding a parameter so apps can choose a value that's more appropriate for its use case.
I have a loop where I POST requests to the server:
for (traineeId, points) in traineePointsDict {
// create a new point
let parameters: NSDictionary = [
"traineeId": "\(traineeId)",
"numPoints": points,
"exerciseId": "\(exerciseId)"
]
DataManager.sharedInstance.api.points.request(.POST, json: parameters).success { data in
if data.json["success"].int == 1 {
self.pointCreated()
} else {
self.pointFailToCreate()
}
}.failure { error in
self.pointFailToCreate()
}
}
The problem is that for some reason the last request fails and I am guessing that this is due to posting too many requests to the server at the same time.
Is there a way to chain these requests so they wait for the one before to complete before executing the next?
I have been looking at PromiseKit, but I don't really know how to implement this and I am looking for a quick solution.
Siesta does not control how requests are queued or how many requests run concurrently. You have two choices:
control it on the app side, or
control it in the network layer.
I’d investigate option 2 first. It gives you less fine-grained control, but it give you more robust options on the cheap and is less prone to mistakes. If you are using URLSession as your networking layer (which is Siesta’s default), then investigate whether the HTTPMaximumConnectionsPerHost property of URLSessionConfiguration does what you need. (Here are some examples of passing custom configuration to Siesta.)
If that doesn’t work for you, a simple version of #1 is to use a completion handler to chain the requests:
func chainRequests(_ queue: [ThingsToRequest])
guard let thing = queue.first else { return }
params = makeParamsFor(thing)
resource.request(.POST, json: params)
.onSuccess {
...
}.onFailure {
...
}.onCompletion { _ in
chainRequests(queue[1 ..< queue.count])
}
}
Note that you can attach multiple overlapping handlers to the same request, and they’re called in the order you attached them. Note also that Siesta guarantees that the completion block is always called, no matter the outcome. This means that each request will result in calls to either closures 1 & 3 or closures 2 & 3. That’s why this approach works.
So I have a little sensor in my 'scene' and when it gets blocked I want my program to wait 4 seconds and if the sensor is still blocked I want it to alert the user.
The script works fine if the sensor remains blocked.
However, if you block the sensor for less than the 4 seconds and then remove the part from the sensor (which should result in no alert since the sensor is no longer blocked) the alert still runs even though the condition is no longer true.
Can any buddy see what I am doing wrong here?
The OnBlocked() function runs when the sensor is blocked and the OnCleared() function runs when the sensor is cleared.
The OnisBlockedUpdated() function runs when the isBlocked property is updated.
function OnBlocked( sender : Demo3D.Visuals.PhysicsObject, load : Demo3D.Visuals.Visual )
{
sender.isBlocked = true;
sender.PropertiesUpdated();
}
function OnCleared( sender : Demo3D.Visuals.PhysicsObject, load : Demo3D.Visuals.Visual )
{
sender.isBlocked = false;
sender.PropertiesUpdated();
}
function OnisBlockedUpdated( sender : Demo3D.Visuals.BoxVisual, value : System.Boolean, oldValue )
{
var blockingObj : Demo3D.Visuals.BoxVisual = sender.BlockingLoad;
if (value)
{
print("isBlocked");
wait(4);
sender.PropertiesUpdated();
if(value)
{
alert("The object '" + blockingObj + "' is too tall for this bay.");
}
else
{
return;
}
}
if(!value)
{
print("cleared");
return;
}
}
if you block the sensor for less than the 4 seconds and then remove the part from the sensor (which should result in no alert since the sensor is no longer blocked) the alert still runs even though the condition is no longer true
The function is still running while you're doing your wait(), and the value parameter will be passed in by-value, not by-reference, so it isn't going to change in the middle of the body of the function.
If you want to evaluate that value after the wait has completed, you're going to have to get the current value, rather than the value passed into the function.
Try:
if (value)
{
print("isBlocked");
wait(4);
sender.PropertiesUpdated();
if(sender.isBlocked) // *** This is the line I changed
{
// ...
Although I'm not sure if sender is going to be the same object as in the OnBlocked or OnCleared function, since it is a different type. You may need to figure out some way to get a reference to that same sender : Demo3D.Visuals.PhysicsObject object.