I am experimenting with macOS xcode objective-C file handling - or having my app open files of a specific extension.
So far, I have used this doc to register file types (even though iOS and I'm using Mac, same process it seems) and that worked perfectly. That associates file extensions with my app and allows double-clicking of the files to launch my app.
Now I want to add handling of the file once it opens. I created a new Cocoa NSObject Subclass called FileOpen.
In my MainMenu.xib I created an Object (Blue Cube) and changed the class to FileOpen. I set it as delegate to File Owner. This was described in the selected answer to this stack question
Now, in FileOpen.m I have this - just to basically show a NSAlert when the file is opened:
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
NSLog(#"Application Finished Launching");
}
-(BOOL)application:(NSApplication*)sharedApplication openFile:(NSString*) fileName
{
[[NSAlert alertWithMessageText:#"Opening File"
defaultButton:#"OK"
alternateButton:nil
otherButton:nil
informativeTextWithFormat:#"Testing file opening handle code."] runModal];
return;
}
This all seems to work fine. If I run my app, I get the NSLog that the application finished launching. If I double-click a file, it runs the app and gives the NSAlert.
The problem I am running into is that my app has a splash screen. This shows until the user closes it and then the main app starts. If I disable my splash screen, the above all works fine as described. If I enable my splash screen, the NSLog saying application finished launching does run, after the splash screen, but then the application:openfile never runs (and no NSAlert).
I assume I need to add some code that waits for applicationWillFinishLaunching before running the
application:open file? Is there a way to wait until the application is fully initialized to be able to open/handle the file correctly? It seems that by having a splash screen is interrupting the application:openfile
Thanks to red_menace I was able to get this working. The key was moving the handling of my splash screen to the NSObject Subclass FileOpen. I did this by just adding a notification to open the splash screen in the FileOpen class instead of where it was originally invoked (the main view).
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
NSLog(#"applicationWillFinishLaunching");
[[NSNotificationCenter defaultCenter] postNotificationName:#"WhatsNewOpen" object:self];
}
-(BOOL)application:(NSApplication*)sharedApplication openFile:(NSString*) fileName
{
[[NSAlert alertWithMessageText:#"Opening Files"
defaultButton:#"OK"
alternateButton:nil
otherButton:nil
informativeTextWithFormat:#"Testing File Open"] runModal];
return;
}
Related
I need a way to hide sensitive information in a react native app so if you minimize the app and leave your phone unlocked the snapshot of the app in the multitasking view would be blurred and the navigation stack would be switched back to login screen when the app becomes active again.
Even just showing the Login screen just before the app becomes inactive->background would be sufficient but it seems that the AppState's change event is called after the state is already changed from active to inactive and at this point the snapshot is already made and the screen changing occurs after the app is restored. This way the screen with the sensitive data is visible in the multitasking.
I know how to make this with ease in native iOS environment but it seems it's not quite that trivial in React Native.
You can't avoid here from the native code.
in iOS:
In your AppDelegate.m file these 2:
1.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Your application can present a full screen modal view controller to
// cover its contents when it moves into the background. If your
// application requires a password unlock when it retuns to the
// foreground, present your lock screen or authentication view controller here.
UIViewController *blankViewController = [UIViewController new];
blankViewController.view.backgroundColor = [UIColor blackColor];
// Pass NO for the animated parameter. Any animation will not complete
// before the snapshot is taken.
[self.window.rootViewController presentViewController:blankViewController animated:NO completion:NULL];
}
And this
2.
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// This should be omitted if your application presented a lock screen
// in -applicationDidEnterBackground:
[self.window.rootViewController dismissViewControllerAnimated:NO completion:NO];
}
Source:
https://developer.apple.com/library/content/qa/qa1838/_index.html
Found this also:
https://github.com/kayla-tech/react-native-privacy-snapshot
My Xcode NSDocument-based app contains a floating panel. On each launch of the app the default doc window and panel appear in their previous locations on the screen. However, if I save a document and quit the app then double click the saved file, the document window is positioned at the same origin as the panel.
Turning off cascading gets around the problem but of course I lose cascading.
A minimum Xcode example showing the issue can be downloaded here.
Run the example project.
Do a File/Save.
Quit the app. << important
Double click the saved file.
Any help appreciated.
If you want to store the state of your window before the app terminates try this:
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
// If you want to save your window position
// you can use [window saveFrameUsingName:#"someWindowName"];
//
// then use [window setFrameAutosaveName:#"someWindowName"]; at the app launch.
return NSTerminateNow;
}
I knew it had something to do with document windows cascading from the panel. Adding this to the panel controller seems to have fixed it
- (void)windowDidLoad {
[super windowDidLoad];
[self setShouldCascadeWindows:NO];
}
I have a Mac app with a preferences window. The preferences window is opened modally
-(IBAction)displayPreferencesWindow:(id)sender{
if (!pc) {
pc = [[PreferencesController alloc] initWithWindowNibName:#"PreferencesController"];
pc.delegate = self;
}
NSWindow *pcWindow = [pc window];
[NSApp runModalForWindow: pcWindow];
[NSApp endSheet: pcWindow];
[pcWindow orderOut: self];
}
In the preferences Window I have a button that opens the account preferences panel
- (IBAction)openSystemPrefs:(id)sender {
[[NSWorkspace sharedWorkspace] openFile:#"/System/Library/PreferencePanes/Accounts.prefPane"];
}
The problem is that the account preferences panel does not open in front of the actual Window. How can I achieve this?
This is kind of weird, and goes against the comments in the header, but try using this instead:
- (BOOL)openFile:(NSString *)fullPath withApplication:(NSString *)appName andDeactivate:(BOOL)flag;
From the comments there:
Open a file at some path. If you use the variant without the
withApplication: parameter, or if you pass nil for this parameter, the
default application is used. The appName parameter may be a full path
to an application, or just the application's name, with or without the
.app extension. If you pass YES for andDeactivate:, or call a variant
without this parameter, the calling app is deactivated before the new
app is launched, so that the new app may come to the foreground unless
the user switches to another application in the interim. Passing YES
for andDeactivate: is generally recommended.
So it sounds like your app should be deactivating (since you're calling a variant without the andDeactivate: parameter) but I would try explicitly using the variant with that parameter, just to be sure.
Empirically, it appears that the launched application will not activate if the launching application is presenting modal UI, at least not when using the NSWorkspace API. I was able to hack something up using AppleScript that appears to achieve the desired results:
- (IBAction)doStuff:(id)sender
{
[[NSWorkspace sharedWorkspace] openFile:#"/System/Library/PreferencePanes/Accounts.prefPane"];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSString* s = #"tell application \"System Preferences\" to activate";
NSAppleScript* as = [[[NSAppleScript alloc] initWithSource: s] autorelease];
NSDictionary* error = nil;
if ([as compileAndReturnError: &error])
{
(void)[as executeAndReturnError: &error];
}
});
}
I dispatched it to the background queue because it takes a few hundred milliseconds to compile and run the AppleScript and it's a little conspicuous if done synchronously (the button stays highlighted longer than you expect).
If you were feeling really masochistic, you could probably get the script compiling phase out of it (i.e. quicker) by conjuring up the equivalent AppleEvents and sending them directly, but this appeared to achieve the desired effect, even while presenting modal UI in the launching application.
I've just added iCloud support to an app that I am working on. Its working great, except that when I open the application without a document in focus the iCloud open file dialog appears and I don't want it to!
In my app delegate I have:
- (BOOL) applicationShouldOpenUntitledFile:(NSApplication *)sender
{
[mainWindowController.window makeKeyAndOrderFront:self];
return NO;
}
Which I use to show my own custom window. However now, both the iCloud open file dialog and my own dialog are displayed. Any ideas on how I can get rid of the iCloud dialog?
https://developer.apple.com/library/prerelease/content/releasenotes/AppKit/RN-AppKitOlderNotes/index.html
NSDocument Support for iCloud
In 10.8, NSDocument-based applications with a ubiquity-container-identifiers entitlement gain new functionality and UI to facilitate iCloud document management.
When iCloud is enabled and an application is first launched or re-activated and no windows are visible or being restored, instead of creating a new Untitled document, NSDocumentController will display a non-modal open panel showing the user's iCloud library.
...
Applications that do not wish to use these features for any or all of their NSDocument subclasses can override +[NSDocument usesUbiquitousStorage] and return NO. If all of the application's declared NSDocument subclasses return NO from this method, then NSDocumentController will never show the new non-modal open panel.
So if you can give up using the features listed in this release note, return NO at +[NSDocument usesUbiquitousStorage].
I confirmed you can still open/save your file into iCloud storage from the normal dialog.
Putting below codes in your App Delegate lets you bypass that iCloud pop up New Document screen. Tested for High Sierra.
-(void)applicationDidFinishLaunching:(NSNotification *)notification
{
// Schedule "Checking whether document exists." into next UI Loop.
// Because document is not restored yet.
// So we don't know what do we have to create new one.
// Opened document can be identified here. (double click document file)
NSInvocationOperation* op = [[NSInvocationOperation alloc]initWithTarget:self selector:#selector(openNewDocumentIfNeeded) object:nil];
[[NSOperationQueue mainQueue] addOperation: op];
}
-(void)openNewDocumentIfNeeded
{
NSUInteger documentCount = [[[NSDocumentController sharedDocumentController] documents]count];
// Open an untitled document what if there is no document. (restored, opened).
if(documentCount == 0){
[[NSDocumentController sharedDocumentController]openUntitledDocumentAndDisplay:YES error: nil];
}
}
- (BOOL) applicationShouldOpenUntitledFile:(NSApplication *)sender
{
[mainWindowController.window makeKeyAndOrderFront:self];
return NO;
}
This part is correct. I've just tested it.
Just make sure your that this class is really your app delegate.
Make a new class called prefixAppDelegate
In your MainMenu.xib, drag a new object to the side and set it's custom class to the app delegate class
Right click Application and drag from Delegate down to your app delegate object.
Now just paste the code above into your app delegate class
If that still doesn't help, try logging something in applicationShouldOpenUntitledFile:.
Also, I recommend not to set [mainWindowController.window makeKeyAndOrderFront:self]; in this method. You should rather use the app delegate method applicationDidFinishLaunching: method.
My observation and fix:
[applicationShouldOpenUntitledFile:] won't be executed except you remove Key NSDocumentClass from *-info.plist. But this is harmful if your app is document based application, it won't open the document type you linked.
My fix is open my customised window directly in -(void)applicationWillFinishLaunching:(NSNotification *)notification method (Application delegate)
ETDocumentWindowController *windowController = (ETDocumentWindowController*)get your own window controller here...;
[windowController.window makeKeyAndOrderFront:nil];
I thought I would share my solution to this issue as I see others still looking for an answer. Its not a great solution but it does the trick.
Subclass NSDocumentController and add the following:
+ (void) setCanOpenUntitledDocument: (BOOL) _canOpenUntitledDocument
{
canOpenUntitledDocument = _canOpenUntitledDocument;
} // End of setCanOpenUntitledDocument:
- (void) openDocument: (id) sender
{
// With iCloud enabled, the app keeps trying to run openDocument: on first launch (before apphasfinishedlaunching gets set.
// This method lets us check and see if the app has finished launching or not. If we try to open a document before
// its finished, then don't let it.
if(!canOpenUntitledDocument)
{
return;
} // End of appHasFinishedLaunching not set
[super openDocument: sender];
} // End of openDocument:
Add the following to your app delegate:
- (void) applicationDidFinishLaunching: (NSNotification *) aNotification
{
// Finished launching. Let us open untitled documents.
[SQLProDocumentController setCanOpenUntitledDocument: true];
...
}
And the reasoning -- By setting a breakpoint in openDocument I've found that its called before applicationDidFinishLaunching, applicationShouldOpenUntitledFile or applicationShouldHandleReopen:hasVisibleWindows: get called, meaning adding those methods is useless. Again, it's not great code but it works and does the trick. (None of the other solutions have worked for me).
I ran into a similar problem -- it turned out that in my case, I had to remove the NSDocumentClass key and value from my Info.plist in the CFBundleDocumentTypes array. Only then would the applicationShouldOpenUntitledFile: method get called and thus allow me to prevent the iCloud/Document window from opening.
i have a button in my app a button that submit score to gamecenter and works.
this is the code:
-(void)subScore{
GKScore *scoreRepoter = [[[GKScore alloc] initWithCategory:#"123456"] autorelease];
scoreRepoter.value=100;
[scoreRepoter reportScoreWithCompletionHandler:^(NSError *error) {
if (error!=nil) {
NSLog(#"errr submitting");
}else
NSLog(#"ok!");
}];
now i'd like to submit score before app is closed with home button.
i thought to customize an action of home button (if it is possible)
or perhaps i make the same line of code in viewDidUload...or something like that...
will i be sure that that action will be performed before unloading the app?
i should make that code in dealloc method?
thanks
You can't customize behaviour of Home button directly, but iOS provides some methods in your application's delegate, by which you can control lifecycle of the application.
Method called right before the application goes to background is applicationWillResignActive: in your application's delegate (usually this method is located in AppDelegate.m file).
I think you can get needed effect by calling your method like that:
- (void)applicationWillResignActive:(UIApplication *)application {
[mygame subScore];
}
Also please note that iOS has time limit of execution for this method: you must do all saving-the-game work in less that five seconds or your application will be killed.