-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reset local device #1253
Reset local device #1253
Conversation
} | ||
|
||
- (BOOL)isReset { | ||
return [self.id isEqualToString:@""]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lawrence-forooghian I know this looks weird, but is a nullable
id
any better? Also additional flag, as I said earlier, probably also worse than this. Until the example app be able to handle nil
device this is the best I could come up with. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe using extern NSString * const
as a not set value
will be more elegant? You can even compare it using ==
(memory address)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or self.secret == nil
WDYT @lukasz-szyszkowski @lawrence-forooghian @ben-xD
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for me nil
is way better than the empty string
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think my answer to this question probably depends on @maratal's answer to this question.
} | ||
|
||
- (BOOL)isReset { | ||
return [self.id isEqualToString:@""]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe using extern NSString * const
as a not set value
will be more elegant? You can even compare it using ==
(memory address)
@@ -16,6 +16,8 @@ NS_ASSUME_NONNULL_BEGIN | |||
|
|||
- (instancetype)init NS_UNAVAILABLE; | |||
|
|||
- (void)reset; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this should be part of the public interface – let's move it to ARTLocalDevice+Private.h
?
This function is needed to return functioning local device, since after `reset` call | ||
you will end up with empty string as a device's `id` on the server. | ||
*/ | ||
- (ARTLocalDevice *)loadDevice { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this calls device_noSync
, should this also have noSync
in its name to indicate that it's the caller's responsibility to ensure that this method is called on the correct queue? (And then make sure that we respect this requirement in the places where we call this method.)
@@ -742,6 +742,19 @@ - (ARTLocalDevice *)device_nosync { | |||
return _sharedDevice; | |||
} | |||
|
|||
/* | |||
This function is needed to return functioning local device, since after `reset` call |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-[ARTRest device]
is still part of the public interface of the library, so I think that method needs to always return a "functioning" local device without needing to first call loadDevice
(which isn't even a part of the public interface). Could we not move the logic of "if the device has been reset, then create a new one" into -[ARTRest device_noSync]
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried that, but it immediately re-creates the device and thus it's never can be in a isReset
state which was the whole point of these changes and underlying issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that the issue being described by #1177 actually consists of two separate things:
-
When the device deregisters itself for push notifications, it keeps its existing device details (
id
,clientId
, etc), hence violating RSH3g2a. -
Once we've fixed point 1, then we should make sure we respect RSH8b:
The
LocalDevice
id
anddeviceSecret
attributes are generated, and persisted as part of the LocalDevice state, when required by step (RSH3a2b) in the Activation State Machine.That is, after the device has deregistered itself for push notifications, it should have "empty" (whatever that actually means)
id
anddeviceSecret
until the user tries to register the device for push notifications again. This is what @ben-xD showed happening on Android in his video.
So, I think that point 2 above is what you're referring to by "thus it's never can be in a isReset
state", but I disagree that it "was the whole point of these changes" – we're also addressing point 1 above.
Furthermore, I think that trying to address point 2 as part of this PR brings additional complication, because we have never respected RSH8b in our codebase. That is, at the moment, we generate the device id
and deviceSecret
the first time that -[ARTRest device]
is called, instead of waiting until the user tries to register the device for push notifications. That's why we're now finding ourselves trying to answer questions like "should a reset device have an id
of @""
?" If we were already respecting RSH8b, then we'd already have answers to these questions.
So, I think that perhaps respecting RSH8b should be a separate task. And for me (possibly from lack of knowledge of the spec) it raises questions like those you've already identified – if there are API calls that require the device’s ID, which can be called before the push activation process is started, then where are we meant to get the ID from?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that perhaps respecting RSH8b should be a separate task
Agreed
if there are API calls that require the device’s ID, which can be called before the push activation process is started
I don't think so, it wouldn't make any sense. Or maybe I misunderstood what you've meant here. All functions that compose requests with the device id
doesn't do any checks on it though:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[[NSURL URLWithString:@"/push/deviceRegistrations"] URLByAppendingPathComponent:local.id]];
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or maybe I misunderstood what you've meant here.
So, my comment was in response to your code comment on -[ARTRest localDevice]
:
This function is needed to return functioning local device, since after
reset
call
you will end up with empty string as a device'sid
on the server.
I interpreted that comment as meaning the following:
After calling reset
, the localDevice
's id
becomes an empty string. If we make an API request that uses the device's id
whilst in this empty string state, then we'll submit an empty string id
to the server, hence we "will end up with empty string as a device's id
on the server".
Did I misunderstand?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did I misunderstand?
No, all is correct, but:
so I think that method (
device
) needs to always return a "functioning" local device without needing to first call loadDevice
How is it possible to have an empty id
then? It's either this or that ("reset" state or always "functioning"). That's why loadDevice
exist - to load it on demand (when the function needs it). Anyways lets further discuss it in a call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if there are API calls that require the device’s ID, which can be called before the push activation process is started
Or maybe you've meant wether id
might be required somewhere else outside of the scope of push notifications? Then no, AFAIK.
@@ -36,4 +36,10 @@ NS_ASSUME_NONNULL_BEGIN | |||
|
|||
@end | |||
|
|||
@interface NSData (APNSToken) | |||
|
|||
- (NSString *)apnsTokenString; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might it be a good idea to prefix this name with art_
?
@@ -788,5 +788,12 @@ - (void)setAndPersistAPNSDeviceToken:(NSString *)deviceToken { | |||
}); | |||
} | |||
|
|||
- (void)resetDevice { | |||
dispatch_sync(ARTRestInternal.storageQueue, ^{ | |||
ARTLocalDevice *device = [self device_nosync]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding of the *_noSync
methods is that they need to be manually synchronised with the rest of the library – i.e. they need to be called on the clientOptions
’ internalDispatchQueue
. Although you're calling device_noSync
on a queue here, I don't think that's enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Originally the caller in ARTPushActivationStateWaitingForDeregistration
did this:
ARTLocalDevice *local = self.machine.rest.device_nosync;
[local setAndPersistIdentityTokenDetails:nil];
So I thought that
- (void)resetDevice {
dispatch_sync(ARTRestInternal.storageQueue, ^{
ARTLocalDevice *device = [self device]; // <-- ARTRest queue sync call
Would be too much. I'm still not sure about this though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The difference is that the original ARTPushActivationStateWaitingForDeregistration
code
was always called on the ARTRest
internal queue and hence could use the *_noSync
methods. So I think you need to use the device
method here — why do you think it might be "too much"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
storageQueue
is a defense against competing ARTRest
instances. So I guess I can't use other queue then the storage queue here. And not only this, but having dispatch_once
replaced with _sharedDevice
static variable, all access to the device
should now be done via this storage queue.
- (nullable NSString *)apnsDeviceToken; | ||
- (void)setAndPersistAPNSDeviceToken:(nullable NSString *)deviceToken; | ||
|
||
@end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not 100% sure what the purpose of this new, synchronised storage is. But, just going by symmetry, would it not need to also include setAndPersistIdentityTokenDetails:
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what the purpose of this new, synchronised storage is
It was discussed in slack here https://ably-real-time.slack.com/archives/CSQEKCE81/p1638465303293500?thread_ts=1638354865.280100&cid=CSQEKCE81
would it not need to also include setAndPersistIdentityTokenDetails:
Agreed, but it wasn't used anywhere, so I decided to implement only the bare minimum.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, perhaps I'm not understanding - what do you mean by "it wasn't used anywhere"? setAndPersistIdentityTokenDetails
is still called in ARTPushActivationState.m
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was discussed in slack here
OK, thanks for the link. I think I understand the motivation for the new queue a bit better now, but is it directly relevant to this PR, which is about resetting device details? It seems to me like it might be a separate change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to me like it might be a separate change.
Please see this comment #1253 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setAndPersistIdentityTokenDetails
is still called inARTPushActivationState.m
Yes, I should inspect more places where storage queue access should be implemented, thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've spent a while trying to understand this storage queue better, but it might be best if we have a call to talk about it, to prevent this becoming a long back-and-forth. What do you think?
But, here's where my current thinking is at. I still don't understand why it's necessary to introduce a new queue in order to achieve the overall objective of this pull request. (Whether it's necessary for other reasons is a different matter.)
Looking at what's contained in the ARTRestInternal (Storage)
category:
apnsDeviceToken
/setAndPersistAPNSDeviceToken
– access to the APNS device token was not synchronised in the past, why does the fact that we might now reset the device introduce a need to synchronise it?resetDevice
– this method is already being called on a serial queue because it's only called inside a push activation state machine transition method, so why do we need an extra serial queue?
It might be that, if you agree with my analysis that we don't need to put the device into an intermediate "reset" state, then some of this might simplify when you make that change, too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then some of this might simplify
Sure, upon splitting this PR into two, this one is going to contain only the very first commit or so.
we don't need to put the device into an intermediate "reset" state
I agreed with the splitting this PR, we still need something that marks the device as "reset", no? Lets discuss it in a call.
Closed in favor of #1259 |
This PR substitutes #1184 (see explanation why in there).
Closes #1177