I'm trying to send an iOS push notification with a media attachment (image url) I've OneSignal SDK 2.2.2 for iOS but it doesn't work at all. In the following article it seems you don't have to implement a Service Extension to display the image inside the notification. (iOS 10).
Do I need to create a Notification Service app extension?
Yes it is required to implement a Notification Service Extension to handle the push notification payload before submitting it back to the OS to display to the user.
Example:
if this is the payload received:
{
"aps": {
"alert": {
"title": "My title",
"subtitle": "My title"
},
"mutable-content": 1,
"category": "<your_notification_category>"
},
"data": {
"url": "<img_url>"
}
}
Your service must download the image, then bundle the downloaded local URL as an UNNotificationAttachment before sending it off to the OS
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
// Modify the notification content here...
if (request.content.userInfo[#"data"] && [request.content.userInfo[#"data"] isKindOfClass:[NSDictionary class]]) {
NSDictionary *data = request.content.userInfo[#"data"];
NSLog(#"%#", data);
NSURL *dataURL = [NSURL URLWithString:data[#"url"]];
self.task = [[NSURLSession sharedSession] downloadTaskWithURL:dataURL completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (location) {
// Get the location URL and change directory
NSString *tempDirectory = NSTemporaryDirectory();
NSURL *fileURL = [NSURL fileURLWithPath:tempDirectory];
fileURL = [fileURL URLByAppendingPathComponent:[dataURL lastPathComponent]];
NSError *error;
if ([[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]]) {
[[NSFileManager defaultManager] removeItemAtPath:[fileURL path] error:&error];
}
[[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error];
// Create UNNotificationAttachment for the notification
UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:#"" URL:fileURL options:nil error:nil];
self.bestAttemptContent.attachments = #[attachment];
}
self.contentHandler(self.bestAttemptContent);
}];
[self.task resume];
} else {
self.contentHandler(self.bestAttemptContent);
}
}
}
Related
I am developing a framework that has the functionality to receive messages from FCM and translate them to local notifications, In one of the keys I receive there is a URL to an image. So doing some research in Apple documentation found that the image should be stored in the device (I am using a simulator) and then it can be used, so, I implement a method for downloading a random image (.png). This method works fine, I test it and the image is in the specified location, the problem is that when I print the userInfo Dictionary from NSError* pointer when calling attachmentWithIdentifier:identifier URL: options: error: method I get this
{NSLocalizedDescription = "Invalid attachment file URL";}
I attach my code:
UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
if (settings.alertSetting == UNNotificationSettingEnabled)
{
NSDictionary * remoteMessageData = [remoteMessage appData];
NSString * imageHttpUrl = [remoteMessageData objectForKey:#"im"];
NSURL * imageURL = [self getStorageFilePath:imageHttpUrl];
[self downloadImageFromURL:imageHttpUrl withFullPath:imageURL.absoluteString withCompletitionHandler:^(NSHTTPURLResponse *httpResponse) {
UNMutableNotificationContent * content = [[UNMutableNotificationContent alloc] init];
content.title = [remoteMessageData objectForKey:#"ti"];
content.body = [remoteMessageData objectForKey:#"bd"];
if(httpResponse != nil)
{
if (httpResponse.statusCode == 200)
{
NSString * identifier = #"ImageIdentifier";
NSArray<UNNotificationAttachment*> * attachments = [NSArray arrayWithObjects:[self getAttachments:imageURL withIdentifier:identifier], nil] ;
content.attachments = attachments;
}
}
content.userInfo = remoteMessageData;
UNTimeIntervalNotificationTrigger * trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:10 repeats:NO];
//Create and register a request notification
NSString * uuidString = [[NSUUID UUID] UUIDString];
UNNotificationRequest * request = [UNNotificationRequest requestWithIdentifier:uuidString content:content trigger:trigger];
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
//Handle error
if(error != nil)
{
NSLog(#"Dictionary: %#",[error userInfo]);
}
}];
- (NSURL *) getStorageFilePath : (NSString *)imageStringURL{
if(imageStringURL == nil)
{
return nil;
}
NSURL * imageURL = [NSURL URLWithString:imageStringURL];
NSString * fileName = [imageURL lastPathComponent];
NSLog(#"fileName %#",fileName);
NSArray * systemPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSLog(#"systemPaths %#",systemPaths);
NSString * tempDirectoryStringPath = [[[NSURL URLWithString:[systemPaths objectAtIndex:0]] absoluteString] stringByAppendingString:#"/"];
NSString * fullPath = [tempDirectoryStringPath stringByAppendingString:fileName];
NSLog(#"Full PATH: %#",fullPath);
return [NSURL URLWithString:fullPath];}
-(void) downloadImageFromURL : (NSString *)httpURL withFullPath:(NSString * )fullPath withCompletitionHandler:(void (^) (NSHTTPURLResponse * httpResponse) )taskResult{
if(httpURL == nil || fullPath == nil )
{
taskResult(nil);
return;
}
NSString *strImgURLAsString = httpURL;
strImgURLAsString = [strImgURLAsString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
NSURL * imgURL = [NSURL URLWithString:strImgURLAsString];
NSLog(#"Full PATH inside downloading: %#",fullPath);
NSURLSessionDataTask * downloadPhotoTask = [[NSURLSession sharedSession] dataTaskWithURL:imgURL completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
if (httpResponse.statusCode == 200)
{
[data writeToFile:fullPath atomically:YES];
NSLog(#"Download run successfully");
}else{
NSLog(#"Download could not be completed");
}
taskResult(httpResponse);
} ];
[downloadPhotoTask resume];
}
- (UNNotificationAttachment *) getAttachments: (NSURL *)attachmentURL withIdentifier : (NSString*)identifier
{
NSError * error;
UNNotificationAttachment * icon = [UNNotificationAttachment attachmentWithIdentifier:identifier URL: attachmentURL options:nil error:&error];
NSLog(#"Icon is : %#",[error userInfo]);
UNNotificationAttachment* attachments = icon;
return (attachments);
}
I could make it work. I was using [NSURL URLWithString:fullPath] NSURL but I should´ve use [NSURL fileURLWithPath:fullPath] but in the method downloadImageFromURL keep using an string URL without the format of fileURLWithPath: method (that cause the image not to be downloaded).
I've added UNNotificationServiceExtension and UNNotificationContentExtension in my project for rich push notification. Please refer the code below which i've added for the same.
Code:
#import "NotificationService.h"
#interface NotificationService ()
#property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
#property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
#end
#implementation NotificationService {
NSURLSession *session;
}
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
NSDictionary *userInfo = request.content.userInfo;
if (userInfo == nil) {
[self contentComplete];
return;
}
if ([userInfo objectForKey:#"pic_url"]) {
[self loadAttachmentForUrlString:[userInfo objectForKey:#"pic_url"]
completionHandler: ^(UNNotificationAttachment *attachment) {
self.bestAttemptContent.attachments = [NSArray arrayWithObjects:attachment, nil];
}];
}
}
- (void)loadAttachmentForUrlString:(NSString *)urlString
completionHandler:(void (^)(UNNotificationAttachment *))completionHandler
{
__block UNNotificationAttachment *attachment = nil;
__block NSURL *attachmentURL = [NSURL URLWithString:urlString];
NSString *fileExt = [#"." stringByAppendingString:[urlString pathExtension]];
session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionDownloadTask *task = [session downloadTaskWithURL:attachmentURL
completionHandler: ^(NSURL *temporaryFileLocation, NSURLResponse *response, NSError *error) {
if (error != nil)
{
NSLog(#"%#", error.localizedDescription);
}
else
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *localURL = [NSURL fileURLWithPath:[temporaryFileLocation.path
stringByAppendingString:fileExt]];
[fileManager moveItemAtURL:temporaryFileLocation
toURL:localURL
error:&error];
NSError *attachmentError = nil;
attachment = [UNNotificationAttachment attachmentWithIdentifier:[attachmentURL lastPathComponent]
URL:localURL
options:nil
error:&attachmentError];
if (attachmentError)
{
NSLog(#"%#", attachmentError.localizedDescription);
}
}
completionHandler(attachment);
}];
[task resume];
}
- (void)serviceExtensionTimeWillExpire {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
[self contentComplete];
}
- (void)contentComplete
{
[session invalidateAndCancel];
self.contentHandler(self.bestAttemptContent);
}
#end
I'm using the following payload
{
"to": "9yJUWBA",
"mutable_content": true,
"category": "myNotificationCategory",
"notification":
{
"title":"Realtime Custom Push Notifications",
"subtitle":"Now with iOS 10 support!",
"body":"Add multimedia content to your notifications"
}
}
The problem is i'm not getting the notification. I've used the following tutorial for implementing the rich push notification. I've checked different answers available but none of them worked for me. I've also tried to debug the didReceiveNotificationRequest method by attaching the extension process but the breakpoint not triggered.
https://mobisoftinfotech.com/resources/mguide/ios-10-rich-notifications-tutorial/
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler{
//Called when a notification is delivered to a foreground app.
NSLog(#"Userinfo willPresentNotification%#",notification.request.content.userInfo);
// Print message ID.
/*
NSDictionary *userInfo = notification.request.content.userInfo;
if (userInfo[kGCMMessageIDKey]) {
NSLog(#"Message ID: %#", userInfo[kGCMMessageIDKey]);
}
*/
completionHandler(UNNotificationPresentationOptionAlert);
}
-(void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler{
//Called to let your app know which action was selected by the user for a given notification.
NSLog(#"Userinfo didReceiveNotificationResponse%#",response.notification.request.content.userInfo);
// Print message ID.
/*
NSDictionary *userInfo = notification.request.content.userInfo;
if (userInfo[kGCMMessageIDKey]) {
NSLog(#"Message ID: %#", userInfo[kGCMMessageIDKey]);
}
*/
}
Use above methods and these method available iOS 10 and onwards.
for debugging notification service extension please visit below link
Debug Notification Extensions
if you give support your application iOS 9 onwards then you have to put both methods below are for above iOS 10 and you also wrote in your question
- (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo
{
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// With swizzling disabled you must let Messaging know about the message, for Analytics
// [[FIRMessaging messaging] appDidReceiveMessage:userInfo];
/*
// Print message ID.
if (userInfo[kGCMMessageIDKey]) {
NSLog(#"Message ID: %#", userInfo[kGCMMessageIDKey]);
}
*/
// Print full message.
NSLog(#"Userinfo didReceiveRemoteNotification 1 %#",userInfo);
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// With swizzling disabled you must let Messaging know about the message, for Analytics
// [[FIRMessaging messaging] appDidReceiveMessage:userInfo];
/*
// Print message ID.
if (userInfo[kGCMMessageIDKey]) {
NSLog(#"Message ID: %#", userInfo[kGCMMessageIDKey]);
}
*/
// Print full message.
NSLog(#"Userinfo didReceiveRemoteNotification 2 %#",userInfo);
completionHandler(UIBackgroundFetchResultNewData);
}
let me know if you have query.
#interface NotificationService ()
#property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
#property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
#end
#implementation NotificationService
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler
{
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
// Modify the notification content here...
self.bestAttemptContent.title = #"";
self.bestAttemptContent.subtitle = #"";
// self.bestAttemptContent.body = [NSString stringWithFormat:#"%#", self.bestAttemptContent.body];
NSDictionary *dictPushNotiData = request.content.userInfo;
NSString *imageURL = #"";
NSString *videoURL = #"";
if(dictPushNotiData[#"xxx_details"])
{
NSString *jsonString = [dictPushNotiData objectForKey:#"xxx_details"];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
// If Instagram Notification Informations then call InstagramViewController
if(jsonData)
{
NSMutableDictionary *dictXXX = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil];
NSLog(#"dictXXX : %#",dictXXX);
//self.bestAttemptContent.title = [NSString stringWithFormat:#"%#", [dictXXX objectForKey:#"xxxx"]];
//self.bestAttemptContent.subtitle = [NSString stringWithFormat:#"%#", [dictXXX objectForKey:#"xxxx"]];
NSString *strBody = #"Notification xxxx x xxxx Post";
if([[dictXXX objectForKey:#"xxxx"] length] > 0)
{
strBody = [dictXXX objectForKey:#"xxxx"];
}
self.bestAttemptContent.body = [NSString stringWithFormat:#"%#",strBody];
imageURL = [dictXXX objectForKey:#"xxxx"];
videoURL = [dictXXX objectForKey:#"xxxx"];
}
}
NSString *strAttachment = nil;
// if (videoURL.length > 0)
// { //Prioritize videos over image
// strAttachment = videoURL;
// }
// else
if (imageURL.length > 0)
{
strAttachment = imageURL;
}
else
{
self.contentHandler(self.bestAttemptContent); //Nothing to add to the push, return early.
return;
}
// If there is an image in the payload, this part
// will handle the downloading and displaying of the image.
if (strAttachment) {
NSURL *URL = [NSURL URLWithString:strAttachment];
NSURLSession *LPSession = [NSURLSession sessionWithConfiguration:
[NSURLSessionConfiguration defaultSessionConfiguration]];
[[LPSession downloadTaskWithURL:URL completionHandler: ^(NSURL *temporaryLocation, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(#"Leanplum: Error with downloading rich push: %#",[error localizedDescription]);
self.contentHandler(self.bestAttemptContent);
return;
}
NSString *fileType = [self determineType:[response MIMEType]];
NSString *fileName = [[temporaryLocation.path lastPathComponent] stringByAppendingString:fileType];
NSString *temporaryDirectory = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
[[NSFileManager defaultManager] moveItemAtPath:temporaryLocation.path toPath:temporaryDirectory error:&error];
NSError *attachmentError = nil;
UNNotificationAttachment *attachment =
[UNNotificationAttachment attachmentWithIdentifier:#"" URL:[NSURL fileURLWithPath:temporaryDirectory] options:nil error:&attachmentError];
if (attachmentError != NULL) {
NSLog(#"Leanplum: Error with the rich push attachment: %#",
[attachmentError localizedDescription]);
self.contentHandler(self.bestAttemptContent);
return;
}
self.bestAttemptContent.attachments = #[attachment];
NSLog(#"self.bestAttemptContent.attachments : %#",
self.bestAttemptContent.attachments);
self.contentHandler(self.bestAttemptContent);
[[NSFileManager defaultManager] removeItemAtPath:temporaryDirectory error:&error];
}] resume];
}
}
- (NSString*)determineType:(NSString *) fileType {
// Determines the file type of the attachment to append to NSURL.
if ([fileType isEqualToString:#"image/jpeg"]){
return #".jpg";
}
if ([fileType isEqualToString:#"image/gif"]) {
return #".gif";
}
if ([fileType isEqualToString:#"image/png"]) {
return #".png";
} else {
return #".tmp";
}
}
I'm developing an iPhone application using objective-c. The basic push notification is working properly. Now I want to add rich notification in my app but I cannot get the didReceiveNotificationRequest fired in the NotificationService.
Here is the notification payload I receive on Appdelegate:
https://image.ibb.co/ndA2Qo/grab.png
Here is the NotificationService.m file;
#import "NotificationService.h"
#import "Common.h"
#interface NotificationService ()
#property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
#property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
#end
#implementation NotificationService
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
NSLog(#"didReceiveNotificationRequest");
// Modify the notification content here...
// load the attachment
NSDictionary *userInfo = request.content.userInfo;
NSString *imageURL = [userInfo valueForKey:#"thumbnail_image"];
NSArray *parts = [imageURL componentsSeparatedByString:#"."];
NSString *extension = [parts lastObject];
[self loadAttachmentForUrlString:imageURL
withExtension:extension
completionHandler:^(UNNotificationAttachment *attachment) {
if (attachment) {
self.bestAttemptContent.attachments = [NSArray arrayWithObject:attachment];
}
//self.bestAttemptContent.title = [NSString stringWithFormat:#"%# [modified]", self.bestAttemptContent.title];
self.contentHandler(self.bestAttemptContent);
}];
}
- (void)serviceExtensionTimeWillExpire {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
self.contentHandler(self.bestAttemptContent);
}
- (void)loadAttachmentForUrlString:(NSString *)urlString withExtension:(NSString *)extension completionHandler:(void(^)(UNNotificationAttachment *))completionHandler {
__block UNNotificationAttachment *attachment = nil;
NSURL *attachmentURL = [NSURL URLWithString:urlString];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[[session downloadTaskWithURL:attachmentURL
completionHandler:^(NSURL *temporaryFileLocation, NSURLResponse *response, NSError *error) {
if (error != nil) {
NSLog(#"%#", error.localizedDescription);
} else {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *localURL = [NSURL fileURLWithPath:[temporaryFileLocation.path stringByAppendingString:extension]];
[fileManager moveItemAtURL:temporaryFileLocation toURL:localURL error:&error];
NSError *attachmentError = nil;
attachment = [UNNotificationAttachment attachmentWithIdentifier:#"" URL:localURL options:nil error:&attachmentError];
if (attachmentError) {
NSLog(#"%#", attachmentError.localizedDescription);
}
}
completionHandler(attachment);
}] resume];
}
#end
What am I missing?
Please advice,
Semih
The existing answer states that you should set content-available to 1, that NSLog does not work in extensions and the background thread is an issue. These are not right.
content-available:1 has no relevance to a notification service extension, for that to be fired up you need mutable-content:1.
NSLog works fine in extensions, it is handy to use the OS console when troubleshooting.
There is no problem with downloading an image in a background thread.
Try looking in the OS Console to see what might be happening (launch console.app on your Mac, plug in your device and select it on the left, and see what goes by when you send a push. Even without your NSLog you should see the OS trying to find and launch an extension like:
default 10:38:53.925889 -0400 SpringBoard [com.you.whatever] Remote
notification request 386D-4968 can be modified: 1
default 10:38:53.926227 -0400 SpringBoard [com.you.whatever] Trying to find extension for bundle
info 10:38:53.942366 -0400 pkd Candidate plugin count: 1, info: (
"com.you.whatever.NotificationServiceExtension(1.0.0) 2765CB-8244DD--B83D-20CB148BCEF6")
default 10:38:53.945090 -0400 SpringBoard [com.you.whatever] Extension can modify push notification request 386D-4968? YES
If you don't see success there maybe something is wrong with your project setup, maybe try recreating the target in XCode (do via File -> New -> Target, select Notification Service Extension).
You are downloading the image in background thread which is causing the issue in Rich Push Notification case. If you want you can try with this framework
Also Add "content-available":1 in your aps
OR you can try downloading like this,
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent *))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
self.bestAttemptContent.title = [NSString stringWithFormat:#"%#",request.content.title];
self.bestAttemptContent.body = [NSString stringWithFormat:#"%#",request.content.body];
NSString *attachmentUrlString = [NSString stringWithFormat:#"%#",[request.content.userInfo valueForKey:#"thumbnail_image"]];
if (![attachmentUrlString isKindOfClass:[NSString class]]) {
[self failEarly];
return;
}
NSURL *url = [NSURL URLWithString:attachmentUrlString];
if (!url) {
[self failEarly];
return;
}
NSData *data = [NSData dataWithContentsOfURL:url];
if (!data) {
[self failEarly];
return;
}
NSString *identifierName = [self getIdentifierName:attachmentUrlString];
NSString *tmpSubFolderName = [[NSProcessInfo processInfo] globallyUniqueString];
self.bestAttemptContent.attachments = [NSArray arrayWithObject:[self create:identifierName andData:data withOptions:nil andTmpFolderName:tmpSubFolderName]] ;
self.contentHandler(self.bestAttemptContent);
}
-(void)failEarly {
self.contentHandler(self.bestAttemptContent);
}
-(NSString *)getIdentifierName:(NSString *)fileURL
{
NSString *identifierName = #"image.jpg";
if (fileURL) {
identifierName = [NSString stringWithFormat:#"file.%#",fileURL.lastPathComponent];
}
return identifierName;
}
-(UNNotificationAttachment *)create:(NSString *)identifier andData:(NSData *)data withOptions:(NSDictionary *)options andTmpFolderName:(NSString *)tmpSubFolderName {
NSString *fileURLPath = NSTemporaryDirectory();
NSString *tmpSubFolderURL = [fileURLPath stringByAppendingPathComponent:tmpSubFolderName];
NSError *error;
[[NSFileManager defaultManager] createDirectoryAtPath:tmpSubFolderURL withIntermediateDirectories:TRUE attributes:nil error:&error];
if(!error) {
NSString *fileURL = [tmpSubFolderURL stringByAppendingPathComponent:identifier];
[data writeToFile:fileURL atomically:YES];
UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:identifier URL:[NSURL fileURLWithPath:fileURL] options:options error:&error];
return attachment;
}
return nil;
}
- (void)serviceExtensionTimeWillExpire {
self.contentHandler(self.bestAttemptContent);
}
I am creating an app that uses Google Place. Before I used to use Yahoo's api and had to use a url that was responsible for local search that was provided by yahoo. The url was following:
http://local.yahooapis.com/LocalSearchService/V3/localSearch?appid=SF0DVEvV34G4GnXEDU4SXniaDebJ_UvC1G1IuikVz3vpOJrBpyD.VqCJCVJHMh99He3iFz1Rzoqxb0b7Z.0-
Now since yahoo's api is discontinued I have decided to switch over to Google Place. But I cannot find an Url to use. I just dowlod the framework and use the api key. Where can I find such url for Google Place.
Register for the Google Places API by following the linke provided below:
https://code.google.com/apis/console
Refer Code Link for places Auto Search
https://github.com/AdamBCo/ABCGooglePlacesAutocomplete
NSString *const apiKey = #"*****23xAHRvnOf2BVG8o";
NSString * searchWord = #"search some place "
NSString *urlString = [NSString stringWithFormat:#"https://maps.googleapis.com/maps/api/place/autocomplete/json?input=%#&types=establishment|geocode&radius=500&language=en&key=%#",searchWord,apiKey];
pragma mark - Network Methods
-(void)retrieveGooglePlaceInformation:(NSString *)searchWord withCompletion:(void (^)(BOOL isSuccess, NSError *error))completion {
if (!searchWord) {
return;
}
searchWord = searchWord.lowercaseString;
self.searchResults = [NSMutableArray array];
if ([self.searchResultsCache objectForKey:searchWord]) {
NSArray * pastResults = [self.searchResultsCache objectForKey:searchWord];
self.searchResults = [NSMutableArray arrayWithArray:pastResults];
completion(YES, nil);
} else {
NSString *urlString = [NSString stringWithFormat:#"https://maps.googleapis.com/maps/api/place/autocomplete/json?input=%#&types=establishment|geocode&radius=500&language=en&key=%#",searchWord,apiKey];
NSURL *url = [NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *jSONresult = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
if (error || [jSONresult[#"status"] isEqualToString:#"NOT_FOUND"] || [jSONresult[#"status"] isEqualToString:#"REQUEST_DENIED"]){
if (!error){
NSDictionary *userInfo = #{#"error":jSONresult[#"status"]};
NSError *newError = [NSError errorWithDomain:#"API Error" code:666 userInfo:userInfo];
completion(NO, newError);
return;
}
completion(NO, error);
return;
} else {
NSArray *results = [jSONresult valueForKey:#"predictions"];
for (NSDictionary *jsonDictionary in results) {
}
//[self.searchResultsCache setObject:self.searchResults forKey:searchWord];
completion(YES, nil);
}
}];
[task resume];
}
}
In Below code run so i get a response from url but when i try to get encodedPoints it give me a null value. also i update RegexKitLite but prob. not solve. Any suggestion are welcome Thank you advance.
NSString* saddr = [NSString stringWithFormat:#"%f,%f", f.latitude, f.longitude];
NSString* daddr = [NSString stringWithFormat:#"%f,%f", t.latitude, t.longitude];
NSString* apiUrlStr = [NSString stringWithFormat:#"http://maps.googleapis.com/maps/api/directions/json?origin=%#&destination=%#&sensor=false", saddr, daddr];
// http://maps.googleapis.com/maps/api/directions/json?origin=41.029598,28.972985&destination=41.033586,28.984546&sensor=false%EF%BB%BF%EF%BB%BF
NSURL *apiUrl = [NSURL URLWithString:[apiUrlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSLog(#"api url: %#", apiUrl);
NSString *apiResponse = [NSString stringWithContentsOfURL:apiUrl encoding:nil error:nil];
NSString* encodedPoints = [apiResponse stringByMatching:#"points:\\\"([^\\\"]*)\\\"" capture:1L];
NSLog(#"encodedPoints: %#", encodedPoints);
if (encodedPoints) {
return [self decodePolyLine:[encodedPoints mutableCopy]];
}
else {
return NO;
}
I think its not a good way to do API request synchronously, especially when user' phone has poor internet connection, it will slow down the responsiveness of your application. So you should do an asynchronous API request with NSURLSession.
Also, the Directions API might return more than one routes for your request. So its better to use a NSArray to store your polyline points.
Sample code:
- (void)getPolyline {
NSURL *url = [[NSURL alloc] initWithString:#"https://maps.googleapis.com/maps/api/directions/json?origin=Chicago,IL&destination=Los+Angeles,CA&key=YOUR_API_KEY"];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: url];
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithRequest:request completionHandler:
^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (!error) {
NSError *jsonError;
NSDictionary *dict = (NSDictionary*)[NSJSONSerialization JSONObjectWithData:data options:nil error:&jsonError];
if (!jsonError) {
NSArray *routesArray = (NSArray*)dict[#"routes"];
NSMutableArray *points = [NSMutableArray array];
for (NSDictionary *route in routesArray) {
NSDictionary *overviewPolyline = route[#"overview_polyline"];
[points addObject:overviewPolyline[#"points"]];
}
NSLog(#"%#", points);
}
} else {
//print error message
NSLog(#"%#", [error localizedDescription]);
}
}] resume];
}