apple-push-notificationsrubymotion

Getting the iOS device token for push notifciations in RubyMotion


I need the user's device token as a hexadecimal string to send push notifications. I used to do this by using the deviceToken 'description', but this doesn't work anymore in iOS13. I found several solutions for this in Objective-C and Swift.

How can I convert my device token (NSData) into an NSString?

The problem however, is that deviceToken.bytes returns a Pointer in RubyMotion. And calling bytes[index] will give a different result compared to doing it in Objective-C. I've tried a lot of different things, but I can't seem to get it to work. The RubyMotion docs about Pointers are also really barebones, so that doesn't help. I found the following regarding the Pointers on SO:

Pointer handling with RubyMotion

It says that I have to do bytes + index instead of bytes[index]. I currently have the following code:

def application(application, didRegisterForRemoteNotificationsWithDeviceToken: device_token)
  string = token_to_string(device_token)
end

def token_to_string(device_token)
  data_length = device_token.length
  if data_length == 0
    nil
  else
    data_buffer = device_token.bytes
    token = NSMutableString.stringWithCapacity(data_length * 2)

    index = 0
    begin
      buffer = data_buffer + index
      token.appendFormat('%02.2hhx', buffer)
      index += 1
    end while index < data_length

    token.copy
  end
end

It's not giving any errors, but the generated device token doesn't seem to be correct. Any advice would be greatly appreciated!


Solution

  • I've been trying to think of a pure RubyMotion solution, but I just don't understand 'Pointers' enough to get it to work. So I've gone a different way: extending the NSData class.

    1) Create the folder vendor/NSData+Hex inside your project.

    2) Create the file NSData+Hex.h inside the folder and put this inside:

    #import <Foundation/Foundation.h>
    
    @interface NSData (Hex)
    
    @property (nonatomic, copy, readonly) NSString *hexString;
    
    @end
    

    3) Now create NSData+Hex.m in the same folder and put this in the file:

    #import "NSData+Hex.h"
    
    @implementation NSData (Hex)
    
    - (NSString *) hexString
    {
      NSUInteger len = self.length;
      if (len == 0) {
        return nil;
      }
      const unsigned char *buffer = self.bytes;
      NSMutableString *hexString  = [NSMutableString stringWithCapacity:(len * 2)];
      for (int i = 0; i < len; ++i) {
        [hexString appendFormat:@"%02x", buffer[i]];
      }
      return [hexString copy];
    }
    
    @end
    

    4) Add this to your Rakefile:

    app.vendor_project('vendor/NSData+Hex', :static)
    

    5) Now you can simply call deviceToken.hexString within def application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken).

    If anyone can think of a pure RubyMotion solution, feel free to answer. For now, it's at least working. :)