APNS令牌冲突,存储在Postgres中

时间:2013-03-02 18:05:52

标签: ios objective-c postgresql push-notification apple-push-notifications

我使用推送通知和存储设备令牌,就像我假设其他人一样。首先,我将它们转换为我的应用程序字符串:

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中的手动查询确认设备正在尝试使用其他用户下的表中已有的令牌进行注册。这不应该是不可能的吗?我正在改变令牌截断它们的方式吗?我检查了问题首次出现时我能找到的每个代码示例,每个人似乎都使用我在第一个代码示例中列出的方法。

3 个答案:

答案 0 :(得分:7)

如果有人注销,然后使用其他帐户登录,设备可能会尝试使用其他用户下的表中的令牌进行注册。

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

  1. 检查Device是否有token_string
  2. 如果没有设备,请为token_stringuser创建一个。
  3. 如果有设备且其用户不是user,请将其用户更新为user
  4. 这样,推送通知将发送给登录设备的最后一个用户。

    关于您在设备上将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];
    }
    

答案 1 :(得分:1)

我会猜测这个,但是把它当作它的用途,一个猜测。

当iOS设备从备份中恢复,或者当它们“恢复”到新设备上时,例如,有人从iPhone 4升级到iPhone 5,或者当有人将iPhone交给他们的妻子或在eBay上销售时,将获得重复/冗余/混淆的设备数据。我肯定已经看到了这种情况,但不是专门针对APNS令牌。

以下是APNS docs对此所说的内容:

  

通过请求设备令牌并将其传递给提供者   您的应用程序启动时,您可以帮助确保提供程序   拥有设备的当前令牌。如果用户将备份还原到   设备或计算机,而不是为其创建备份的设备或计算机   (例如,用户将数据迁移到新设备或计算机),他   或者她必须至少启动一次申请才能收到   再次通知。如果用户将备份数据还原到新设备   或计算机,或重新安装操作系统,设备令牌   变化。此外,永远不要缓存设备令牌并将其提供给您   供应商;始终在需要时从系统获取令牌。   如果您的申请已经注册,请致电   registerForRemoteNotificationTypes:操作系统中的结果   将设备令牌立即传递给代理人而不会产生任何影响   额外的开销。

所以,我不是在看你的代码,但似乎你的“重复”代币可能与某些组合没有每次注册,某种缓存和设备恢复有关。

答案 2 :(得分:0)

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

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

在JSON有效负载内是push_token。 重要的是deviceIDpush_token。就Apple而言,您通过Apple系统使用push_token与设备进行通信,而不是其他任何内容。

对于deviceID,它是使用的实际物理设备。设备可能想要多次注册这一事实无关紧要,您的代码应该根据最新的注册尝试更新数据库中的push_token。就是这样。