Feedback Service
Apps don't live forever. Users add, remove, and replace applications on their iPhones all the time. From an APNS point of view, it's pointless to deliver notifications to iPhones that no longer host your application. As a push provider, it's your duty to remove inactive device tokens from your active support list. As Apple puts it, "APNS monitors providers for their diligence in checking the feedback service and refraining from sending push notifications to nonexistent applications on devices." Big Brother is watching.
Apple provides a simple way to manage inactive device tokens. When users uninstall apps from a device, push notifications begin to fail. Apple tracks these failures and provides reports from its APNS feedback server. The APNS feedback service lists devices that failed to receive notifications. As a provider, you need to fetch this report on a periodic basis and weed through your device tokens.
The feedback server hosts sandbox and production addresses, just like the notification server. You find these at feedback.push.apple.com (port 2196) and feedback.sandbox.push.apple.com. You contact the server with a production SSL certificate and shake hands in the same way you do to send notifications. After the handshake, read your results. The server sends data immediately without any further explicit commands on your side.
The feedback data consists of 38 bytes. This includes the time (4 bytes), the token length (2 bytes), and the token itself (32 bytes). The timestamp tells you when APNS first determined that the application no longer existed on the device. This uses a standard UNIX epoch, namely seconds since Midnight, January 1st, 1970. The device token is stored in binary format. You need to convert it to a hex representation to match it to your device tokens if you use strings to store token data. At the time of writing this book, you can ignore the length bytes. They are always 0 and 32, referring to the 32-byte length of the device token.
// Retrieve message from SSL. size_t processed = 0; char buffer[38]; do { // Fetch the next item result = SSLRead(context, buffer, 38, &processed); if (result) break; // Recover Date from data char *b = buffer; NSTimeInterval ti = ((unsigned char)b[0] << 24) + ((unsigned char)b[1] << 16) + ((unsigned char)b[2] << 8) + (unsigned char)b[3]; NSDate *date = [NSDate dateWithTimeIntervalSince1970:ti]; // Recover Device ID NSMutableString *deviceID = [NSMutableString string]; b += 6; for (int i = 0; i < 32; i++) [ deviceID appendFormat:@"%02x", (unsigned char)b[i]]; // Add dictionary to results [results addObject: [NSDictionary dictionaryWithObject:date forKey:deviceID]]; } while (processed > 0);