// // UnityNotificationData.m // iOS.notifications // #if TARGET_OS_IOS #import #import #if UNITY_USES_LOCATION #import #endif #import "UnityNotificationData.h" static NSString* ParseNotificationDataObject(id obj) { if ([obj isKindOfClass: [NSString class]]) return obj; else if ([obj isKindOfClass: [NSNumber class]]) { NSNumber* numberVal = obj; if (CFBooleanGetTypeID() == CFGetTypeID((__bridge CFTypeRef)(obj))) return numberVal.boolValue ? @"true" : @"false"; return numberVal.stringValue; } else if ([NSJSONSerialization isValidJSONObject: obj]) { NSError* error; NSData* data = [NSJSONSerialization dataWithJSONObject: obj options: NSJSONWritingPrettyPrinted error: &error]; if (data) { NSString* v = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding]; return v; } else { NSLog(@"Failed parsing notification userInfo value: %@", error); } } else NSLog(@"Failed parsing notification userInfo value"); NSObject* o = obj; return o.description; } NotificationSettingsData UNNotificationSettingsToNotificationSettingsData(UNNotificationSettings* settings) { NotificationSettingsData settingsData; settingsData.alertSetting = (int)settings.alertSetting; settingsData.authorizationStatus = (int)settings.authorizationStatus; settingsData.badgeSetting = (int)settings.badgeSetting; settingsData.carPlaySetting = (int)settings.carPlaySetting; settingsData.lockScreenSetting = (int)settings.lockScreenSetting; settingsData.notificationCenterSetting = (int)settings.notificationCenterSetting; settingsData.soundSetting = (int)settings.soundSetting; settingsData.alertStyle = (int)settings.alertStyle; if (@available(iOS 11.0, *)) { settingsData.showPreviewsSetting = (int)settings.showPreviewsSetting; } else { settingsData.showPreviewsSetting = 2; } return settingsData; } void initiOSNotificationData(iOSNotificationData* notificationData) { notificationData->title = NULL; notificationData->body = NULL; notificationData->badge = 0; notificationData->subtitle = NULL; notificationData->categoryIdentifier = NULL; notificationData->threadIdentifier = NULL; notificationData->soundType = kSoundTypeDefault; notificationData->soundVolume = -1.0f; notificationData->soundName = NULL; notificationData->triggerType = PUSH_TRIGGER; notificationData->userInfo = NULL; } static void parseCustomizedData(iOSNotificationData* notificationData, UNNotificationRequest* request) { NSDictionary* userInfo = request.content.userInfo; NSObject* customizedData = [userInfo objectForKey: @"data"]; // For local notifications, the customzied data is always a string. if (notificationData->triggerType == TIME_TRIGGER || notificationData->triggerType == CALENDAR_TRIGGER || customizedData == nil) { notificationData->userInfo = (__bridge_retained void*)userInfo; return; } // For push notifications, we have to handle more cases. NSString* strData = ParseNotificationDataObject(customizedData); if (strData == nil) NSLog(@"Failed parsing notification userInfo[\"data\"]"); NSMutableDictionary* parsedUserInfo = [NSMutableDictionary dictionaryWithDictionary: userInfo]; [parsedUserInfo setValue: strData forKey: @"data"]; notificationData->userInfo = (__bridge_retained void*)parsedUserInfo; } iOSNotificationData UNNotificationRequestToiOSNotificationData(UNNotificationRequest* request) { iOSNotificationData notificationData; initiOSNotificationData(¬ificationData); UNNotificationContent* content = request.content; notificationData.identifier = strdup([request.identifier UTF8String]); if (content.title != nil && content.title.length > 0) notificationData.title = strdup([content.title UTF8String]); if (content.body != nil && content.body.length > 0) notificationData.body = strdup([content.body UTF8String]); notificationData.badge = [content.badge intValue]; if (content.subtitle != nil && content.subtitle.length > 0) notificationData.subtitle = strdup([content.subtitle UTF8String]); if (content.categoryIdentifier != nil && content.categoryIdentifier.length > 0) notificationData.categoryIdentifier = strdup([content.categoryIdentifier UTF8String]); if (content.threadIdentifier != nil && content.threadIdentifier.length > 0) notificationData.threadIdentifier = strdup([content.threadIdentifier UTF8String]); if ([request.trigger isKindOfClass: [UNTimeIntervalNotificationTrigger class]]) { notificationData.triggerType = TIME_TRIGGER; UNTimeIntervalNotificationTrigger* timeTrigger = (UNTimeIntervalNotificationTrigger*)request.trigger; notificationData.trigger.timeInterval.interval = timeTrigger.timeInterval; notificationData.trigger.timeInterval.repeats = timeTrigger.repeats; } else if ([request.trigger isKindOfClass: [UNCalendarNotificationTrigger class]]) { notificationData.triggerType = CALENDAR_TRIGGER; UNCalendarNotificationTrigger* calendarTrigger = (UNCalendarNotificationTrigger*)request.trigger; NSDateComponents* date = calendarTrigger.dateComponents; notificationData.trigger.calendar.year = (int)date.year; notificationData.trigger.calendar.month = (int)date.month; notificationData.trigger.calendar.day = (int)date.day; notificationData.trigger.calendar.hour = (int)date.hour; notificationData.trigger.calendar.minute = (int)date.minute; notificationData.trigger.calendar.second = (int)date.second; notificationData.trigger.calendar.repeats = (int)calendarTrigger.repeats; } else if ([request.trigger isKindOfClass: [UNLocationNotificationTrigger class]]) { #if UNITY_USES_LOCATION notificationData.triggerType = LOCATION_TRIGGER; UNLocationNotificationTrigger* locationTrigger = (UNLocationNotificationTrigger*)request.trigger; CLCircularRegion *region = (CLCircularRegion*)locationTrigger.region; notificationData.trigger.location.latitude = region.center.latitude; notificationData.trigger.location.longitude = region.center.longitude; notificationData.trigger.location.radius = region.radius; notificationData.trigger.location.notifyOnExit = region.notifyOnEntry; notificationData.trigger.location.notifyOnEntry = region.notifyOnExit; notificationData.trigger.location.repeats = locationTrigger.repeats; #endif } else if ([request.trigger isKindOfClass: [UNPushNotificationTrigger class]]) { notificationData.triggerType = PUSH_TRIGGER; } else notificationData.triggerType = UNKNOWN_TRIGGER; parseCustomizedData(¬ificationData, request); notificationData.attachments = (__bridge_retained void*)request.content.attachments; return notificationData; } void freeiOSNotificationData(iOSNotificationData* notificationData) { if (notificationData->identifier != NULL) free(notificationData->identifier); if (notificationData->title != NULL) free(notificationData->title); if (notificationData->body != NULL) free(notificationData->body); if (notificationData->subtitle != NULL) free(notificationData->subtitle); if (notificationData->categoryIdentifier != NULL) free(notificationData->categoryIdentifier); if (notificationData->threadIdentifier != NULL) free(notificationData->threadIdentifier); if (notificationData->soundName != NULL) free(notificationData->soundName); if (notificationData->userInfo != NULL) { NSDictionary* userInfo = (__bridge_transfer NSDictionary*)notificationData->userInfo; userInfo = nil; } } void* _AddItemToNSDictionary(void* dict, const char* key, const char* value) { NSDictionary* dictionary; if (dict != NULL) dictionary = (__bridge NSDictionary*)dict; else { dictionary = [[NSMutableDictionary alloc] init]; dict = (__bridge_retained void*)dictionary; } NSString* k = [NSString stringWithUTF8String: key]; NSString* v = value ? [NSString stringWithUTF8String: value] : @""; [dictionary setValue: v forKey: k]; return dict; } void* _AddAttachmentToNSArray(void* arr, const char* attId, const char* url, void** outError) { *outError = NULL; NSString* attachmentId = nil; if (attId != NULL) attachmentId = [NSString stringWithUTF8String: attId]; NSURL* uri = [NSURL URLWithString: [NSString stringWithUTF8String: url]]; NSError* error = nil; UNNotificationAttachment* attachment = [UNNotificationAttachment attachmentWithIdentifier: attachmentId URL: uri options: nil error: &error]; if (attachment != nil) { NSMutableArray* array; if (arr != NULL) array = (__bridge NSMutableArray*)arr; else { array = [[NSMutableArray alloc] init]; arr = (__bridge_retained void*)array; } [array addObject: attachment]; return arr; } if (error != nil) *outError = (__bridge_retained void*)error; return NULL; } void _ReadNSDictionary(void* csDict, void* nsDict, void (*callback)(void* csDcit, const char*, const char*)) { NSDictionary* dict = (__bridge NSDictionary*)nsDict; [dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { NSString* k = key; NSString* v = ParseNotificationDataObject(obj); if (v != nil) callback(csDict, k.UTF8String, v.UTF8String); else NSLog(@"Failed to parse value for key '%@'", key); }]; } void _ReadAttachmentsNSArray(void* csList, void* nsArray, void (*callback)(void*, const char*, const char*)) { NSArray* attachments = (__bridge_transfer NSArray*)nsArray; [attachments enumerateObjectsUsingBlock:^(UNNotificationAttachment * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { NSString* idr = obj.identifier; NSString* url = obj.URL.absoluteString; callback(csList, idr.UTF8String, url.UTF8String); }]; } #endif