[TIMOB-19567] Last change made after useractivitywillsave does not make it to other device
GitHub Issue | n/a |
---|---|
Type | Bug |
Priority | High |
Status | Closed |
Resolution | Fixed |
Resolution Date | 2015-10-29T20:38:40.000+0000 |
Affected Version/s | Release 5.0.0 |
Fix Version/s | Release 5.2.0 |
Components | iOS |
Labels | handoff |
Reporter | Fokke Zandbergen |
Assignee | Chee Kiat Ng |
Created | 2015-09-22T14:22:37.000+0000 |
Updated | 2016-02-23T10:47:24.000+0000 |
Description
While I was working on https://github.com/appcelerator-developer-relations/appc-sample-handoff when our SDK and Apple's were still not GA and since they became GA it seems like the behaviour around
useractivitywillsave
has changed:
* Before it fired directly after setting needsSave:true
but now it only does before the activity is handed off. This is expected behaviour, so I guess OK.
* Before I any change I did to the activity's userInfo
in the event listener for useractivitywillsave
would be received by the continueactivity
event on the other device. But now it is no longer. This is not expected behaviour, so definitely a bug!
To reproduce:
1. Build https://github.com/appcelerator-developer-relations/appc-sample-handoff to 2 devices
2. Make a change to the needsSave
tab's title/message and continue on the other device
3. Check the logs on both and see that while the userInfo
was updated in the useractivitywillsave
on the first, it is not received in continueactivity
on the other.
This seems like a serious bug because this makes handoff not usable for dynamic content.
[~fokkezb], try out my classic app.js sample code
and include in tiapp.xml
It seems to be behaving correctly.
Steps to verify
1. install app on 2 devices 2. on device A, open the app, and press "make activity current" 3. An alert will pop up, indicating will save event with the current activity info 4. on device B, continue the activity via the lock screen 5. alert will pop up in device A, indicating will save event with the new activity info 6. close the alert 7. alert will pop up in device A, indicating new activity is continued on another device 8. alert will pop up in device B, indicating new activity is continuing, with NEW activity infoThough i noticed, i completely did not use 'needSave' property. i'm assuming that's true by default.
[~cng]
useractivitywillsave
will always fire right afterbecomeCurrent()
regardless ofneedsSave
, but indeed I see that in your sample it is also fires when you handover without first settingneedsSave
, while I've also seen it not do that. It seems to be very unpredictable. I've modified your sample to better demonstrates what I can still demonstrate going wrong:*Steps* 1. Make current on first device 2. Handoff on other device 3. See that the activity the other device received does not have the timestamp set by the first in
useractivitywillsave
*Logs Device 1**Log Device 2*
As you can see when I update the activity on device 1 even on that device after setting
activity.title
a get on that same variable returns the old value. So there seems to be something wrong there. Sometimes the get does give the right value, sometimes only for the title, other times only the body. Very odd!Hm. Looking at the code, i think the reason is because of this line: https://github.com/appcelerator/titanium_mobile/blob/master/iphone/Classes/TiAppiOSUserActivityProxy.m#L189 see it fires the event to JS, after which it continues to do what it is supposed to do, to continue activity on another device. So it looks like it won't wait for you to make changes to the activity. that's why you see the race condition. Some thoughts have to be put in on this, this may be a little tricky.
The workaround is to update the activity when you set
needsSave:true
, but that's.. well, ugly. Could we do a callback or something?We could somehow hack it such that the main thread waits for the JS to complete a callback or something, but 1. That sounds like a bad idea 2. i don't think we have ever done something like that in the sdk We might have to consider deprecating and removing this event altogether. users could stick to "useractivitywascontinued", used for remembering the state the app was in before continued on to another device. Because i relate a user activity to a app state, and can only imagine a small use case whereby you want to change the activity content on the last minute before it's handed off.
Well if we remove it we break parity with the native API. *The* example for handoff is starting an email on your iPhone and continue it on your iPad or Mac. For this typical use case it *is* very important that you can update the activity before it is continued on the other device. I really need we do need to fix this. And until we do we should inform people about the workaround, which is to update the activity when they set
needsSave:true
. I will update the blog post that will go out today with that and link to this issue.[~ben.bahrenburg@gmail.com] since you implemented this (right?) any ideas on this issue?
[~fokkezb] what you have as the workaround is the actual behavior. Please reference http://stackoverflow.com/questions/26715531/nsuseractivity-handoff-not-working-for-custom-data If you made any chances you need to update the needsSave property. Sounds like this is just a documentation update. Alternatively we could break with the native API and set the property for the developer. Would suggest the first strategy.
[~ben.bahrenburg@gmail.com] I know you need to set
needsSave:true
when you have changes that need to be saved before another device continues the activity. That's not the point. The point is that Apple is (somewhat) [clear](https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/Handoff/AdoptingHandoff/AdoptingHandoff.html#//apple_ref/doc/uid/TP40014338-CH2-SW14) that you shouldn't actually update the activity until theuseractivitywillsave
event. {quote}To update the activity object’s userInfo dictionary efficiently, configure its delegate and set its needsSave property to YES whenever the userInfo needs updating. At appropriate times, Handoff invokes the delegate’s userActivityWillSave: callback, and the delegate can update the activity state.{quote} And *that* is not (always) working in our Titanium implementation because the Obj-C thread doesn't wait for the JS event to be finished before allowing the other device to continue. Which causes the other device to not always get the updated information but rather the previous state.[~fokkezb] you are not going to be able to have the Obj-C wait for the JS event to finish. This is just a delegate event that is fired in native, that then raises the event in JS. Truthfully you should be re-building your activity on the other side of your application if there is concern over latency.
I don't get what you mean with re-building. Surely we need to be able to get this working as Apple has documented it should work?
[~fokkezb] see [~cng] comment he sums up your options well. Even read your second to last statement with some thought, i.e. you want ObjC to wait for JS in a delegate call.... think about what you just said. I think the key is how do you work around this in your example. Personally when I coded the hand-off part of my application, using this code, I never had to use this event.
I know it works fine if you update the activity before/when you set
needsSave:true
it would just be better if we can make our implementation follow Apple's best practices. If that is technically impossible then we should indeed remove theuseractivitywillsave
event and tell people that in contrast to what Apple says they need to update the activity where they setneedsSave:true
.PR here: https://github.com/appcelerator/titanium_mobile/pull/7357 to deprecate useractivitywillsave to avoid confusion.
[~cng] Added a few comments to the PR. If we don't expect to ever be able to restore
useractivitywillsave
it would be good to look into if it wouldn't be better if we call needsSave automatically whenever the user makes a change to the user activity.[~fokkezb] it sure sounds reasonable. maybe new a ticket for that?
Done: TIMOB-19567
PR approved!
Verified as fixed, This was tested using two iOS9 devices, and an iOS9 to iOS8 device. info made by
useractivitywillsave
returns correct values on multiple tries, and the information received by Device B is reflected correctly. Tested on: iPhone 6Plus device (9.0.2) , iPhone 6S Plus (9.2.1) & iOS8.4 Device Mac OSX El Capitan 10.11.3 (15D21) Ti SDK: 5.2.0.v20160220080449 Appc Studio: 4.5.0.201602170821 Appc NPM: 4.2.3-2 App CLI: 5.2.0-269 Xcode 7.2 Node v4.2.6 production *Closing ticket.*[~htbryant] not sure I get your test report. The event should give a deprecation message now because it does _not_ (always) work as expected.