MacOS Mojave UNNotificationAttachment thumbnail not showing up in notifications - macos

This is the (simplified) code I am trying to run. The image file exists and the app does not crash. What happens in that the image is deleted from the location as mentioned in the documentation for UserNotifications. However, the image thumbnail does not show up in the notification that is generated.
I am not sure what else I am missing here.
#import <UserNotifications/UserNotifications.h>
int main(int argc, const char * argv[]) {
UNUserNotificationCenter* notificationCenter = [UNUserNotificationCenter currentNotificationCenter];
[notificationCenter requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound)
completionHandler:^(BOOL granted, NSError * _Nullable error) {}
];
UNMutableNotificationContent *localNotification = [UNMutableNotificationContent new];
localNotification.title = [NSString localizedUserNotificationStringForKey:#"Title" arguments:nil];
localNotification.body = [NSString localizedUserNotificationStringForKey:#"Body Text" arguments:nil];
localNotification.sound = [UNNotificationSound defaultSound];
NSString *path = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES)lastObject];
NSString *testUrl = [path stringByAppendingPathComponent:#"imageFile.jpg"];
NSURL* url = [NSURL fileURLWithPath:testUrl];
CGRect rect = CGRectMake(0.25, 0.25, 0.75, 0.75);
NSDictionary* options = # {
#"UNNotificationAttachmentOptionsTypeHintKey": (__bridge NSString*) kUTTypeJPEG,
#"UNNotificationOptionsThumbnailHiddenKey" : #NO,
#"UNNotificationAttachmentOptionsThumbnailClippingRectKey": [NSValue valueWithRect: rect]
};
UNNotificationAttachment* imageAttachment = [UNNotificationAttachment attachmentWithIdentifier:#""
URL:url
options:options
error:nil];
localNotification.attachments=#[imageAttachment];
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:1 repeats:NO];
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:#"Identifier" content: localNotification trigger:trigger];
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
NSLog(#"Notification created");
}];
[NSThread sleepForTimeInterval:100.0f];
return 0;
}

Check if you set up the key NSUserNotificationAlertStyle
Check the system response((BOOL granted, NSError * _Nullable error)) if you get an error, please provide it.
Add notification in the completion block.
Do not run your code in main, app might haven't been properly initialized, use AppDelegate appDidFinishLaunching instead.
Test with triggerWithTimeInterval:[NSDate date].timeIntervalSince1970+30 (it contradicts documentation but works in my case)
Collapse you app in delivery time, it might be not delivered when app is in focus.
Also go to "System Preferences" and check if your app is allowed to post notifications.(If you do not manage to find your app in the list, please, let us know, it might be the key to the problem)

Related

macOS 10.13: "Scheduling the NSURLDownload loader is no longer supported."

Running my macOS app in macOS 10.13, I see printed to the console:
Scheduling the NSURLDownload loader is no longer supported.
What does this mean?
The Sparkle Updater seems to be the culprit in the instances I have found. I guess the Sparkle dev team will be on to it and hopefully we'll no longer see the message after Sparkle is updated.
It appears to mean You have just created an instance of the deprecated class NSURLDownload.
To show this, create a new Cocoa command-line tool project in Xcode and replace the code in main.m with the following:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSURL* url = [[NSURL alloc] initWithString:#"https://example.com"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:30.0];
NSLog(#"Will print strange sentence to console") ;
[[NSURLDownload alloc] initWithRequest:request
delegate:nil];
NSLog(#"Did print strange sentence to console") ;
}
return 0;
}
Build and run. I get the following result in console (timestamps removed):
Will print strange sentence to console:
Scheduling the NSURLDownload loader is no longer supported.
Did print strange sentence to console
I would say the "fix" is to replace the deprecated NSURLDownload with NSURLSession.
You can correct it directly in the source code for Sparkle. Update SUAppcast.m file at line 82 by replace the NSURLDownload with the following:
NSURLSessionDownloadTask *downloadTask = [[NSURLSession sharedSession] downloadTaskWithRequest:request completionHandler:^(NSURL *location, __unused NSURLResponse *response, NSError *error) {
if (location) {
NSString *destinationFilename = NSTemporaryDirectory();
if (destinationFilename) {
// The file will not persist if not moved, Sparkle will remove it later.
destinationFilename = [destinationFilename stringByAppendingPathComponent:#"Appcast.xml"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *anError = nil;
NSString *fromPath = [location path];
if ([fileManager fileExistsAtPath:destinationFilename])
[fileManager removeItemAtPath:destinationFilename error:&anError];
BOOL fileCopied = [fileManager moveItemAtPath:fromPath toPath:destinationFilename error:&anError];
if (fileCopied == NO) {
[self reportError:anError];
} else {
self.downloadFilename = destinationFilename;
dispatch_async(dispatch_get_main_queue(), ^{
[self downloadDidFinish:[[NSURLDownload alloc] init]];
});
}
}
} else {
[self reportError:error];
}
}];
[downloadTask resume];

non-persistence of object written to documentsDirectory - is

-- a question about how to make an object that is saved to the documents directory persist on the drive and be recoverable after the iDevice is rebooted.
Here's my problem. I make a data object with NSCoding and fill it with data. I write it to the documentsDirectory each time the data in the object are updated. I stop the app and start the app again, and my data object persists, with all of its data. But if I reboot the iPhone the code I wrote to recover and read the data object fails.
The code I wrote originally used only a NSString for the file path. It worked well under ios7 but it fails under ios8.
Reading up on things, I found this clue from the Apple documentation:
"Important: Although they are safe to use while your app is running, file reference URLs are not safe to store and reuse between launches of your app because a file’s ID may change if the system is rebooted. If you want to store the location of a file persistently between launches of your app, create a bookmark as described in Locating Files Using Bookmarks."
So I rewrote my ios7 file open and file close methods so they no longer use strings or urls but get their strings and urls from a bookmark that is saved using NSUserDefaults. Same problem: everything works fine so long as I do not power off the phone, but all is lost once I do. I am not able to solve this.
Here is my current series of steps. First I either determine (or if it already exists in NSUsrDefaults, I recover) the absolute path to the documentsDirectory, using a bookmark:
+ (NSString*) getGeoModelAbsolutePath
{
NSString *path;
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSURL *documentsDirectoryBookmarkURL;
NSData* documentsDirectoryBookmark = [userDefaults objectForKey:#"documentDirectoryBookmark"];
if(documentsDirectoryBookmark == nil)
{
documentsDirectoryBookmarkURL = [self getDocumentsDirectoryURL];
documentsDirectoryBookmark = [self bookmarkForURL:documentsDirectoryBookmarkURL];
}
documentsDirectoryBookmarkURL = [self urlForBookmark:documentsDirectoryBookmark];
path = documentsDirectoryBookmarkURL.path;
path = [path stringByAppendingString:#"/Model.mod"];
return path;
}
using methods modified from my ios7 code (which used only the getDocumentsDirectory method):
+ (NSString *)getDocumentsDirectory
{
NSURL *directory = [self getDocumentsDirectoryURL];
NSString * documentsDirectory = directory.path;
return documentsDirectory;
}
And
+ (NSURL *)getDocumentsDirectoryURL
{
NSURL *directory = [[[NSFileManager defaultManager]
URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask]
lastObject];
return directory;
}
And
+ (NSData*)bookmarkForURL:(NSURL*)url {
NSError* theError = nil;
NSData* bookmark = [url bookmarkDataWithOptions:NSURLBookmarkCreationSuitableForBookmarkFile
includingResourceValuesForKeys:nil
relativeToURL:nil
error:&theError];
if (theError || (bookmark == nil)) {
// Handle any errors.
return nil;
}
return bookmark;
}
So now I have a NSString path with the model filename that I can use to get to the GeoModel
- (GeoModel*) openGeoModel
{
GeoModel *geoModel;
NSString* documentsDirectoryGeoModel =[FileManager getGeoModelAbsolutePath];
if([FileManager fileExistsAtAbsolutePath:documentsDirectoryGeoModel])
{
NSData* data = [NSData dataWithContentsOfFile: documentsDirectoryGeoModel]; //]documentsDirectoryGeoModel];
geoModel = [NSKeyedUnarchiver unarchiveObjectWithData: data];
NSString *unarchivedGeoModelVersion = geoModel.geoModel_VersionID;
if(![unarchivedGeoModelVersion isEqual: currentGeoModelVersion])
{
[FileManager deleteFile:documentsDirectoryGeoModel];
geoModel = [GeoModel geoModelInit];
[Utilities setGeoProjectCounter:0];
}
}
else
{
geoModel = [GeoModel geoModelInit];
}
[FileManager saveGeoModel];
return geoModel;
}
Which I then can save to the documentsDirectory as follows:
+ (BOOL)saveGeoModel
{
NSError *error = nil;
NSString *path = [self getGeoModelAbsolutePath];
[NSKeyedArchiver archiveRootObject:appDelegate.currentGeoModel toFile:path];
NSData* encodedData = [NSKeyedArchiver archivedDataWithRootObject: appDelegate.currentGeoModel];
BOOL success = [encodedData writeToFile: path options:NSDataWritingAtomic error:&error];
return success;
}
Which is always successful -- but is persistent only if I do not turn off the device! I am not making any progress with this: Any help would be much appreciated!
Thanks in advance
Tim Redfield
There. I think it is answered -- unless someone else has a comment on how to improve the above listings, they DO work as they ought to!

iPhone App Crashes When trying to play video

I'm trying to get a simple view based app to play a video, but it crashes, heres my code,
- (IBAction)playButton:(id)sender {
NSString *stringPath = [[NSBundle mainBundle] pathForResource:#"1" ofType:#"mov"];
NSURL *url = [NSURL fileURLWithPath:stringPath];
mpc = [[MPMoviePlayerController alloc]initWithContentURL:url];
[mpc setMovieSourceType:MPMovieSourceTypeFile];
[[self view]addSubview:mpc.view];
[mpc setFullscreen:YES];
[mpc play];
}
#end
and here is where it takes me in xcode when it fails
//
// main.m
// video_play
//
// Created by nathaniel harman on 20/04/2013.
// Copyright (c) 2013 machupicchumobile. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "VideoPlayAppDelegate.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([VideoPlayAppDelegate class]));
}
}
try like this ,
NSString *audio=[[NSBundle mainBundle]pathForResource:#"1" ofType:#"mov"];
NSURL *url=[[NSURL alloc]initFileURLWithPath:audio];
Try this like, where you will find the code I'm using for playing the movie or video.
http://kiranjasvanee.wordpress.com/2013/09/19/play-video-or-movie-in-iphone/?preview=true&preview_id=3&preview_nonce=cf5d01de8d
Let me implement this code here, so can review it,
First you have to import the MediaPlayer header library to use it’s MPMoviePlayer for playing any movie or video.
You can import this library in .h or .m view controller – depends upon where you declaring your MPMoviePlayerViewController object.
Library import:-
#import MediaPlayer/MediaPlayer.h
Object declaration:-
MPMoviePlayerViewController *moviePlayer;
implement this below code in .m file, when play movie pressed:-
below used Movie_URL identifier contains the URL of video or movie.
- (IBAction)BtnVideoShowCalled:(id)sender
{
// Put your Navigation and Tabbar related code here.
Ex :- /* self.navigationController.navigationBarHidden=YES; */
//If you wanna play a video from tableview, then assign tag to _btn and add target this function to that _btn. Ex :-
/*
//Where, record is a object of Messages class.
NSInteger tid = [sender tag];
*/
NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:#"%#",Movie_URL]];
if(URL)
{
Class mplayerControllerClass = NSClassFromString(#"MPMoviePlayerViewController");
if(mplayerControllerClass != nil) {
moviePlayer = [[MPMoviePlayerViewController alloc] initWithContentURL:URL];
moviePlayer.wantsFullScreenLayout = YES;
[moviePlayer shouldAutorotateToInterfaceOrientation:YES];
if(moviePlayer)
{
[self presentMoviePlayerViewControllerAnimated:moviePlayer];
}
[movieplayer readyPlayer];
}
}
}

Storing a PDF generated 'on the fly' for iPad on IOS6.1

I am trying to create a PDF report from an iPad app using xcode 4.6. I know a valid pdf file is being created when run on the simulator, because I can dig it out and preview it. The commented out code does this. The problem is that I can't write it somewhere I can get at it on the iPad.
I've tried using UIGraphicsBeginPDFContextToData instead and trying to write the image out to the PhotoAlbum instead. The problem here is that when I convert the NSMutableData into an image it returns nil.
Here is the code. Thanks for any help you can give me.
- (IBAction)makePDF:(UIButton *)sender
{
CFAttributedStringRef currentText = CFAttributedStringCreate(NULL, (CFStringRef)self.labelCopyright.text, NULL);
if (currentText)
{
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(currentText);
if (framesetter)
{
// NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, //NSUserDomainMask, YES) objectAtIndex:0];
// NSString *pdfPath = [rootPath stringByAppendingPathComponent:#"Nick.pdf"];
// NSLog(#"pdf is at %#",pdfPath);
// UIGraphicsBeginPDFContextToFile(pdfPath, CGRectZero, nil);
NSMutableData *data = [[NSMutableData alloc] initWithCapacity:100000];
UIGraphicsBeginPDFContextToData(data, CGRectZero, nil);
CFRange currentRange = CFRangeMake(0, 0);
NSInteger currentPage = 0;
BOOL done = NO;
do
{
UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, 612, 792), nil);
currentPage++;
// [self drawPageNumber:currentPage];
currentRange = [self renderPage:currentPage withTextRange:currentRange andFramesetter:framesetter];
if (currentRange.location == CFAttributedStringGetLength((CFAttributedStringRef)currentText)) done = YES;
}
while (!done);
UIGraphicsEndPDFContext();
UIImage* image = [UIImage imageWithData:data];
assert(image);
UIImageWriteToSavedPhotosAlbum(image, self, nil, nil);
CFRelease(framesetter);
}
else NSLog(#"Could not create the framesetter needed to lay out the atrributed string.");
CFRelease(currentText);
}
else NSLog(#"Could not create the attributed string for the framesetter");
}
- (CFRange)renderPage:(NSInteger)pageNum withTextRange:(CFRange)currentRange andFramesetter:(CTFramesetterRef)framesetter
{
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSetTextMatrix(currentContext, CGAffineTransformIdentity);
CGRect frameRect = CGRectMake(72, 72, 468, 648);
CGMutablePathRef framePath = CGPathCreateMutable();
CGPathAddRect(framePath, NULL, frameRect);
CTFrameRef frameRef = CTFramesetterCreateFrame(framesetter, currentRange, framePath, NULL);
CGPathRelease(framePath);
CGContextTranslateCTM(currentContext, 0, 792);
CGContextScaleCTM(currentContext, 1.0, -1.0);
CTFrameDraw(frameRef, currentContext);
currentRange = CTFrameGetVisibleStringRange(frameRef);
currentRange.location += currentRange.length;
currentRange.length = 0;
CFRelease(frameRef);
return currentRange;
}
Save the mutable data to your documents directory
[data writeToFile:filePath atomically:YES]
Here's an example:
+(void) saveData: (NSData*) data ToFileName: (NSString*) filename {
// Retrieves the document directories from the iOS device
NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString* documentDirectory = [documentDirectories objectAtIndex:0];
NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent: filename];
// instructs the mutable data object to write its context to a file on disk
[data writeToFile:documentDirectoryFilename atomically:YES];
//NSLog(#"documentDirectoryFileName: %#",documentDirectoryFilename);
}
As for displaying the generated PDF on the device, the UIWebView object supports loading PDF files from NSData. Here is an example:
[self.webView loadData:pdfData MIMEType:#"application/pdf" textEncodingName:#"utf-8" baseURL:nil];
It is possible to attach an NSData object to an email as well. Here is an example:
//Check if we can send e-mails
if ([MFMailComposeViewController canSendMail]) {
//Create the Email view controller
MFMailComposeViewController *controller = [[MFMailComposeViewController alloc] init];
controller.mailComposeDelegate = self;
//Set the subject and body
[controller setSubject:#"Email Subject"];
[controller setMessageBody:#"Email body" isHTML:NO];
//Set the email address
[controller setToRecipients:#"test#test.com"];
//Add the current PDF as an attachment
NSString *fileName = #"file.pdf";
[controller addAttachmentData:self.retrievedPDF mimeType:#"application/pdf" fileName:fileName];
// show the email controller modally
[self.navigationController presentModalViewController: controller animated: YES];
}
Instead of writing the PDF to an NSMutableData object, write it to a file using UIGraphicsBeginPDFContextToFile.
The first argument is the file path. The best place would be the Documents directory. There are then many different ways to get the file out of the app:
iTunes file sharing
Email
iCloud
Sending to a 3rd party server (Dropbox, Box, Google Drive, etc.)
Open in another iOS app using UIDocumentInteractionController.

How can I wait for result from geocodeAddressString iPhone

I know its something to do with locks or dispatch groups, but I just cant seem to code it...
I need to know if the address was a valid address before leaving the method. Currently the thread just overruns and returns TRUE. I've tried locks, dispatchers the works but can't seem to get it correct. Any help appreciated:
- (BOOL) checkAddressIsReal
{
__block BOOL result = TRUE;
// Lets Build the address
NSString *location = [NSString stringWithFormat:#" %# %#, %#, %#, %#", streetNumberText.text, streetNameText.text, townNameText.text, cityNameText.text, countryNameText.text];
// Put a pin on it if it is valid
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:location
completionHandler:^(NSArray* placemarks, NSError* error) {
result = [placemarks count] != 0;
}];
return result;
}
The documentation says that CLGeocoder calls the completionHandler on the main thread. Since you are probably also calling your method from the main thread it cannot wait for the geocoder's answer without giving it the opportunity to deliver the result.
That would be done by polling the runloop, using some API as -[NSRunLoop runMode:beforeDate:].
The disadvantage is that depending on the mode this will also deliver events and fire timers while waiting for the result.
Just use block as parameter:
- (void) checkAddressIsRealWithComplectionHandler:(void (^)(BOOL result))complectionHandler
{
__block BOOL result = TRUE;
// Lets Build the address
NSString *location = [NSString stringWithFormat:#" %# %#, %#, %#, %#", streetNumberText.text, streetNameText.text, townNameText.text, cityNameText.text, countryNameText.text];
// Put a pin on it if it is valid
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:location
completionHandler:^(NSArray* placemarks, NSError* error) {
result = [placemarks count] != 0;
complectionHandler(result);
}];
}

Resources