APNS令牌冲突,存储在Postgres中



我使用推送通知和存储设备令牌,就像我假设其他人做的那样。首先我把它们转换成一个字符串我的app:

NSString *deviceTokenString = [[[token description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]]
                               stringByReplacingOccurrencesOfString:@" " withString:@""];

然后我把它们放到我的服务器,其中ActiveRecord将它们存储在character varying(255)列:

Device.where(:token => device_token, :username => username).first_or_create!(:model => model)

我有一个验证,确保没有两个令牌是相同的,我理解应该总是这样:

class Device < ActiveRecord::Base
  belongs_to :user
  validates_uniqueness_of :token
end

然而,我已经开始看到令牌唯一性的验证错误:

ActiveRecord::RecordInvalid: Validation failed: Token has already been taken

psql中的手动查询确认设备正在尝试注册表中已经在不同用户下的令牌。这不应该是不可能的吗?我在转换令牌的过程中是否有什么东西截断了它们?当问题第一次出现时,我检查了我能找到的每个代码示例,每个人似乎都使用我在第一个代码示例中列出的方法。

如果有人注销,然后用不同的帐户登录,则可能会发生设备试图用表中已经存在的令牌注册为不同用户的情况。

我将在服务器上为用户user和令牌字符串token执行以下操作(假设一次只能有一个用户登录到一个设备上):

  1. 检查token_string是否有Device
  2. 如果没有,为token_stringuser分别创建一个。
  3. 如果有一个设备,其用户不是user,则更新其用户为user

这样,推送通知将发送给最后一个登录设备的用户。

关于在设备上将NSData转换为十六进制字符串的方式,您不应该依赖-[NSData description]。最好用编程的方式(输入,不测试):

- (NSString *)hexStringForData:(NSData *)data
{
    NSUInteger length = data.length;
    const char *bytes = data.bytes;
    NSMutableString *result = [NSMutableString stringWithCapacity:length * 2];
    for (int i = 0; i < length; i++) {
        [result appendFormat:@"%02x", bytes[i] & 0xff];
    }
    return [result copy];
}

我来猜一猜,但就当我猜一猜吧。

当iOS设备从备份中恢复时,或者当它们被"恢复"到新设备上时,比如,有人从iPhone 4升级到iPhone 5,或者当有人把iPhone送给妻子或在eBay上出售时,你会得到重复/冗余/混乱的设备数据。我肯定看到过这种情况发生,但不是特别针对APNS令牌。

以下是APNS文档对此的说法:

通过请求设备令牌并每次将其传递给提供程序当您的应用程序启动时,您可以帮助确保提供程序具有设备的当前令牌。如果用户将备份恢复到为其创建备份的设备或计算机以外的设备或计算机(例如,用户将数据迁移到新设备或计算机上)或者她必须至少启动应用程序一次才能接收再次通知。如果用户将备份数据恢复到新设备或重新安装计算机,或重新安装操作系统,获得设备令牌的变化。此外,永远不要缓存设备令牌并将其提供给您的提供者;无论何时需要,都要从系统中获取令牌。如果您的申请之前已注册,请致电registerForRemoteNotificationTypes:操作系统的结果立即将设备令牌传递给委托,而不会引发额外开销。

所以,我不是在看你的代码,但你的"重复"令牌似乎与每次不注册、某种缓存和设备恢复的某种组合有关。

了解设备注册时会发生什么是很重要的。它向您的服务器发送以下POST:

/passkit/v1/devices/<deviceID>/registrations/<typeID>/<serial#>

在JSON有效负载中是push_token。重要的是deviceIDpush_token。就苹果而言,你只能通过苹果的push_token系统与设备进行通信。

对于deviceID,它是实际使用的物理设备。设备可能想要注册多次的事实是无关紧要的,您的代码应该根据最近的注册尝试简单地更新DB中的push_token

最新更新