I have some code implemented in my myDocument.m file that simply attempts to load the last used document upon launch. However, upon launching from a fresh install, (or running after deleting the last used file) a 'new' document window does not appear. Does anyone know what to add to my code to do this? Here it is:
- (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender
{
NSURL *lastURL=[[NSDocumentController sharedDocumentController] openDocumentWithContentsOfURL:lastURL display:YES error:nil];
if (lastURL!=nil)
{
[docController openDocumentWithContentsOfURL:lastURL display:YES error:nil];
return NO;
}
return YES;
}
- (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender
{
NSArray* urls = [[NSDocumentController sharedDocumentController] recentDocumentURLs];
if ([urls count] > 0){
NSURL *lastURL= [urls objectAtIndex: 0];
if ([[NSDocumentController sharedDocumentController] openDocumentWithContentsOfURL:lastURL display:YES error:nil]){
return NO;
}
}
return YES;
}
EDIT
I changed it and tried it out it should work now.
What is docController, and why are you sending -openDocumentWithContentsOfURL:display:error: twice? Note that that method returns a document, not a URL, so using the return value as a URL wouldn’t work anyway.
The following is a cleaner, equivalent code:
- (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender
{
id lastDoc = [[NSDocumentController sharedDocumentController]
openDocumentWithContentsOfURL:lastURL
display:YES error:NULL];
return (lastDoc == nil);
}
However, it still doesn’t explain why you don’t get an untitled document. What happens if you comment out -applicationShouldOpenUntitledFile: so that the application follows standard Cocoa behaviour? It could be the case that the problem lies elsewhere.
Related
This method sets the background object.
- (void) downloadWithURL: (NSMutableArray *)urlArray
pathArr: (NSMutableArray *)pathArr
mediaInfo: (MediaInfo *)mInfo
{
bgDownloadMediaInfo = mInfo;
reqUrlCount = urlArray.count;
dict = [NSDictionary dictionaryWithObjects:pathArr
forKeys:urlArray];
mutableDictionary = [dict mutableCopy];
backgroundConfigurationObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"XXXXX"];
backgroundConfigurationObject.sessionSendsLaunchEvents = YES;
backgroundConfigurationObject.discretionary = YES;
backgroundSession = [NSURLSession sessionWithConfiguration: backgroundConfigurationObject
delegate: self delegateQueue: [NSOperationQueue currentQueue]];
self.requestUrl = [urlArray objectAtIndex:0];
download = [backgroundSession downloadTaskWithURL:self.requestUrl];
[download resume];
}
These are the completion handlers.
#pragma Mark - NSURLSessionDownloadDelegate
- (void)URLSession: (NSURLSession *)session
downloadTask: (NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL: (NSURL *)location
{
LogDebug(#"Download complete for request url (%#)", downloadTask.currentRequest.URL);
NSString *temp = [mutableDictionary objectForKey:downloadTask.currentRequest.URL];
NSString *localPath = [NSString stringWithFormat: #"%#",temp];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *destinationURL = [NSURL fileURLWithPath: localPath];
NSError *error = nil;
[fileManager moveItemAtURL:location toURL:destinationURL error:&error];
LogDebug(#"Moving download file at url : (%#) to : (%#)", downloadTask.currentRequest.URL, destinationURL);
reqUrlCount --;
downloadSegment ++;
// Handover remaining download requests to the OS
if ([finalUrlArr count] != 0) {
// remove the request from the array that got downloaded.
[finalUrlArr removeObjectAtIndex:0];
[finalPathArr removeObjectAtIndex:0];
if ([finalUrlArr count] > 0) {
// proceed with the next request on top.
self.requestUrl = [finalUrlArr objectAtIndex:0];
download = [backgroundSession downloadTaskWithURL:self.requestUrl];
[download resume];
}
}
if ([adsArray count] > 0) {
adsArrayCount --;
// delegate back once all the ADs segments have been downloaded.
if (adsArrayCount == 0) {
for (int i = 0; i < [adsArray count]; i++) {
NSArray *ads = [adsArray objectAtIndex: i];
for (int j = 0; j < [ads count]; j++) {
MediaInfo *ad = [ads objectAtIndex: j];
[self setDownloadComplete: ad];
// skip sending downloadFinish delegate if the media is marked as downloadDone
if (!ad.downloadDone) {
[delegate MediaDownloadDidFinish: ad.mediaId error: NO];
}
ad.downloadDone = YES;
}
}
downloadSegment = 0;
}
}
// delegate back once all the main media segments have been downloaded.
if (reqUrlCount == 0) {
[self setDownloadComplete: mediaInfo];
state = DownloadState_Done;
// skip sending downloadFinish delegate if the media is marked as downloadDone
if (!bgDownloadMediaInfo.downloadDone) {
[delegate MediaDownloadDidFinish: bgDownloadMediaInfo.mediaId error: NO];
}
bgDownloadMediaInfo.downloadDone = YES;
[urlArr release];
[pathArr release];
[finalUrlArr release];
[finalPathArr release];
// invalidate the NSURL session once complete
[backgroundSession invalidateAndCancel];
}
}
- (void)URLSession: (NSURLSession *)session
task: (NSURLSessionTask *)task
didCompleteWithError: (NSError *)error
{
if (error) {
NSLog(#"Failure to download request url (%#) with error (%#)", task.originalRequest.URL, error);
}
}
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
// save the total downloaded size
[self downloaderDidReceiveData:bytesWritten];
// enable the log only for debugging purpose.
// LogDebug(#"totalBytesExpectedToWrite %llu, totalBytesWritten %llu, %#", totalBytesExpectedToWrite, totalBytesWritten, downloadTask.currentRequest.URL);
}
With out this code(beginBackgroundTaskWithExpirationHandler) the download stops when the app is pushed into background.
// AppDelegate_Phone.m
- (void)applicationDidEnterBackground: (UIApplication *)application
{
NSLog(#"applicationDidEnterBackground");
UIApplication *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier bgTask;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
}
Have you implemented application:handleEventsForBackgroundURLSession:completionHandler: in your app delegate? That should save the completion handler and start background session with the specified identifier.
If you don't implement that method, your app will not be informed if the download finishes after the app has been suspended (or subsequently terminated in the course of normal app lifecycle). As a result, it might look like the download didn't finish, even though it did.
(As an aside, note that if the user force-quits the app, that not only terminates the download, but obviously won't inform your app that the download was terminated until the user manually restarts the app at some later point and your app re-instantiates the background session. This is a second-order concern that you might not worry about until you get the main background processing working, but it's something to be aware of.)
Also, your URLSessionDidFinishEventsForBackgroundURLSession: must call that saved completion handler (and dispatch this to the main queue).
Also, your design looks like it will issue only one request at a time. (I'd advise against that, but let's just assume it is as you've outlined above.) So, let's imagine that you have issued the first request and the app is suspended before it's done. Then, when the download is done, the app is restarted in the background and handleEventsForBackgroundURLSession is called. Let's assume you fixed that to make sure it restarts the background session so that the various delegate methods can be called. Make sure that when you issue that second request for the second download that you use the existing background session, not instantiating a new one. You can have only one background session per identifier. Bottom line, the instantiation of the background session should be decoupled from downloadWithURL:pathArr:mediaInfo:. Only instantiate a background session once.
Add "Required background modes" in your .plist
There, add the item "App downloads content from the network"
I keep getting this break point appear on my project which then causes the app to stop running.
It doesn't break in the thread results it seems to break on this line:
[urlString appendString: [URLQueryGenerator createSearchURL]];
The method that that line of code is in is as follows:
- (void)loadData {
[_appDelegate reloadFavourites];
if (_appDelegate.properties.count < 1) {
//Reset Results page number to one. Because the results are starting afresh.
[self setCurrentSearchPage:1];
[self.navigationItem setTitle:NSLocalizedString(#"Search Results", nil) ];
//Create URL String
NSMutableString *urlString = [[NSMutableString alloc] initWithString: #""];
[urlString appendString: [URLQueryGenerator createSearchURL]];
NSLog(#"%#", urlString);
[xmlParser parseXMLPage:urlString withDelegate:self];
[v_loadingView setHidden:NO];
//Do not release Parser here, released in dealloc()
//[xmlParser release];
}
else
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
[self loadCells];
else
[table reloadData];
}
}
I have put a break point on the search button that i have which then takes me to the results page to see if i could debug whats happening but its not giving a clear indication as to why i keep getting this error.
I have tried enabling zombie Object on the project but this still doesn't give me any indication.
Its really bugging me because i don't know how i can debug it well enough to find out why its doing this!
Can any one help please?
I'm developing a native application for a website. The application is basically a wrapper around the website, which implements things like push notifications. When a push notification is clicked, the following code is used to go to the appropriate page:
- (NSString *)handlePush:(NSDictionary *)notification
{
if ([[notification objectForKey:#"aps"] objectForKey:#"badge"]) {
int badgeNum = [[[notification objectForKey:#"aps"] objectForKey:#"badge"] integerValue];
NSLog(#"Badge: %i, got from %#", badgeNum, [[notification objectForKey:#"aps"] objectForKey:#"badge"]);
[UIApplication sharedApplication].applicationIconBadgeNumber = badgeNum;
}
if (!active) {
NSLog(#"Got noti while not active, going to that chat!");
NSString *hash;
if ((hash = [notification objectForKey:#"h"])) {
NSLog(#"Hash: %#", [hash stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]);
return [NSString stringWithFormat:#"#%#", hash];
}
return #"";
}
return nil;
}
Active is changed when the application enters the background and after it resumes, to make sure it does not trigger when a push notification arrives when the user is using the app.
The URL is parsed properly, because if i manually paste the exact same URL in the browser, i do go to the correct page.
I am 100% certain the delegate is set, as the UIWebView: ShouldStartLoadWithRequest method is called:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *url = [[request URL] absoluteString];
NSLog(#"shouldStartLoadWithRequest called with url %#", url);
if ([url hasPrefix:BASE_URL]) {
NSLog(#"Yea, i'll load this one for ya");
// remove any get-params / hash suffix
NSRange r = [url rangeOfString:#"?"];
if (r.location == NSNotFound)
r = [url rangeOfString:#"#"];
if (r.location != NSNotFound)
url = [url substringToIndex:r.location];
if (![url isEqualToString:[defaults valueForKey:#"baseUrl"]]) {
NSLog(#"setting new baseUrl %#", url);
[defaults setValue:url forKey:#"baseUrl"];
[defaults synchronize];
}
NSLog(#"Should go and load it now...");
return YES;
}
}
There's some logic for caching versions of the webpage in there. I stepped through with breakpoints, and it reaches the return YES part, and the logs in there are called. However, in that same delegate, the didStartLoad and didFailLoadWithError are not called, which contain purely NSLogs.
On the initial application start, this does work, and it has worded one time too where i stepped through for a long time using the breakpoint, so it seems it's some timing issue. Hoping not having to use any hacky solutions like timers, i was hoping that anyone around here has experience with a similar problem, or has any other valuable input.
I've created an app, containing an ImageView subclass which accepts drag'n'dropping files/folders directly from Finder.
The thing is I'm now trying to make it accept photos, either from iPhoto or Aperture, as well.
Which PboardTypes should I register for?
All I'm currently doing is :
[self registerForDraggedTypes:
[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
Any ideas?
Using Pasteboard Peeker (from Apple) shows me that Aperture gives you file names/URLs as well as "aperture image data" (whatever that is). iPhoto appears only to give "ImageDataListPboardType", which is a PLIST. I'm guessing you could NSLog() that out to see its structure and pull the image information from it. It may possibly include the filename/URL info as well as the actual image as data.
You are correct to register for NSFilenamesPboardType. To complete the task:
1: Make sure you accept the copy operation in draggingEntered. The generic operation is insufficient.
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
NSDragOperation sourceDragMask = [sender draggingSourceOperationMask];
NSPasteboard *pasteboard = [sender draggingPasteboard];
if ( [[pasteboard types] containsObject:NSFilenamesPboardType] ) {
if (sourceDragMask & NSDragOperationCopy) {
return NSDragOperationCopy;
}
}
return NSDragOperationNone;
}
2: There will be one filename per photo. Do something with them.
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
NSPasteboard *pasteboard;
NSDragOperation sourceDragMask;
sourceDragMask = [sender draggingSourceOperationMask];
pasteboard = [sender draggingPasteboard];
if ([[pasteboard types] containsObject:NSFilenamesPboardType])
{
NSData* data = [pasteboard dataForType:NSFilenamesPboardType];
if(data)
{
NSString *errorDescription;
NSArray *filenames = [NSPropertyListSerialization propertyListFromData:data mutabilityOption:kCFPropertyListImmutable format:nil errorDescription:&errorDescription];
for (NSString* filename in filenames)
{
NSImage* image = [[NSImage alloc]initWithContentsOfFile:filename];
//Do something with the image
}
}
}
return YES;
}
I am using the following code to perform drag operation on NSTextView object.
- (BOOL)performDragOperation:(id )sender
{
NSPasteboard *pboard = [sender draggingPasteboard];
if ( [[pboard types] containsObject:NSURLPboardType] )
{
NSURL *fileURL = [NSURL URLFromPasteboard:pboard];
if ([[fileURL path] hasSuffix:#"plist"])
{
NSString *code = [NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:NULL];
int cnt = [[self string] length];
if (cnt) [self setSelectedRange:NSMakeRange(0, cnt)];
[self insertText:code];
return YES;
}
}
return NO;
}
I have declared this method in the .h file as well.
But after running the code it showing following warnings.
warning: 'AppConroller' may not respond to '-string'
(Messages without a matching method signature will be assumed to return 'id' and accept'...' as arguments.)
warning: 'AppConroller' may not respond to '-setSelectedRange:'
warning: 'AppConroller' may not respond to '-insertText:'
You're sending self (the AppController) messages it doesn't support. I think you mean to have [sender string], [sender setSelectedRange:], and [sender insertText:].
Is it possible it's just a typo? All your warnings refer to "AppConroller", but I bet the class is actually named "AppController".