How can my app detect whether another app's window is closed? - objective-c

In Cocoa on the Mac, I'd like to detect when a window belonging to another app is closed. How can I do this?
I only know how to detect other window's resize or minilize using
AXObserverAddNotification(observer,application,kAXWindowMiniaturizedNotification, nil);
AXObserverAddNotification(observer, application, kAXWindowResizedNotification, nil);
But I don't find notification like kAXWindowClosedNotification.

The notification is kAXUIElementDestroyedNotification but it is also sent for other elements. Observe the window or check in the callback if the element is a window.
if (CFStringCompare(notification, kAXUIElementDestroyedNotification, 0) == kCFCompareEqualTo) {
CFTypeRef role;
AXError error = AXUIElementCopyAttributeValue((AXUIElementRef)element, kAXRoleAttribute, &role);
if (error == kAXErrorSuccess) {
if (CFStringCompare(role, kAXWindowRole, 0) == kCFCompareEqualTo) {
NSLog(#"Window is closed");
}
CFRelease(role);
}
}

Related

Is it ok to perform a segue inside a for loop?

Is it ok to perform a segue in a for loop like below or I am setting myself up for trouble?
Or will it simply perform the segue and the rest of the code is never executed?
for(symbol in results) {
if ([symbol.data hasPrefix:#"--"]) {
actualBarCodeStr = [symbol.data substringFromIndex:2];
[self performSegueWithIdentifier:#"trListViewToTrSearchView" sender:self];
} else {
createTransactionResult = [NWBarCodeHelper createTransactionRowFromBarCode:symbol.data];
if ([NWTillHelper isDebug] == 1) {
NSLog(#"Zbar delegate holds barcode: %#", symbol.data);
if(createTransactionResult != 0) {
NSLog(#"TransactionListView:ZBarDelegate:createTransactionFrombarCode failed with errorCode %i", createTransactionResult);
}
}
}
}
As the code is in a method, they will all run in one runloop even if you have performed the first segue. So all the segue will be performed. If it's just a push, I think there will be a bunch of view controller be pushed. For cases like modal present, I think it may crash but I have not tested it.

Changing cocoa bindings value programmatically

I have an NSButton in my preferences to interact with adding the application to the LoginItems. If the adding of the login item fails, I want to uncheck the box so the user doesn't get a false sense that it was added to the login items. However, after doing this, when I click the checkbox again, the bindings is not triggered.
- (void)addLoginItem:(BOOL)status
{
NSURL *url = [[[NSBundle mainBundle] bundleURL] URLByAppendingPathComponent:
#"Contents/Library/LoginItems/HelperApp.app"];
// Registering helper app
if (LSRegisterURL((__bridge CFURLRef)url, true) != noErr) {
NSLog(#"LSRegisterURL failed!");
}
if (!SMLoginItemSetEnabled((__bridge CFStringRef)[[NSBundle mainBundle] bundleIdentifier], (status) ? true : false)) {
NSLog(#"SMLoginItemSetEnabled failed!");
[self willChangeValueForKey:#"startAtLogin"];
[self.startAtLogin setValue:[NSNumber numberWithBool:[self automaticStartup]] forKey:#"state"];
[self didChangeValueForKey:#"startAtLogin"];
}
}
- (void)setAutomaticStartup:(BOOL)state
{
NSLog(#"Set automatic startup: %d", state);
if ([self respondsToSelector:#selector(addLoginItem:)]) {
[self addLoginItem:state];
}
}
- (BOOL)automaticStartup
{
BOOL isEnabled = NO;
// the easy and sane method (SMJobCopyDictionary) can pose problems when sandboxed. -_-
CFArrayRef cfJobDicts = SMCopyAllJobDictionaries(kSMDomainUserLaunchd);
NSArray* jobDicts = CFBridgingRelease(cfJobDicts);
if (jobDicts && [jobDicts count] > 0) {
for (NSDictionary* job in jobDicts) {
if ([[[NSBundle mainBundle] bundleIdentifier] isEqualToString:[job objectForKey:#"Label"]]) {
isEnabled = [[job objectForKey:#"OnDemand"] boolValue];
break;
}
}
}
NSLog(#"Is Enabled: %d", isEnabled);
// if (isEnabled != _enabled) {
[self willChangeValueForKey:#"startupEnabled"];
startupEnabled = isEnabled;
[self didChangeValueForKey:#"startupEnabled"];
// }
return isEnabled;
}
I have my databinding for the checkbox bound to self.automaticStartup. If I remove the line [self.startAtLogin setValue:[NSNumber numberWithBool:[self automaticStartup]] forKey:#"state"]; then the bindings work fine, but it doesn't uncheck, if the adding of the item fails.
How can I change this binding value programmatically so that every other binding event is not ignored?
From your explanation, your bound value is automaticStartup, but you are sending willChangeValueForKey: for startAtLogin. In order for bindings to work correctly, you need to alert on the change to the bound variable at some point. However, since you are in the midst of setAutomaticStartup: at the time, it's not really safe to do that here.
In this case, I would not use bindings to perform the change itself, I would consider the old-style IBAction mechanism and then set the checkbox value manually through an IBOutlet when you can confirm the status.

Detect Enter/Tab/Up/Down keys in NSTextView?

Cocoa noob here. I'm wondering how I can capture the Enter and tab keys onKeyDown whilst the user is typing in an NSTextView?
Thanks!
The easiest way is to implement the - (BOOL)textView:(NSTextView *)aTextView doCommandBySelector:(SEL)aSelector delegate method and look for the insertNewline: and insertTab: selectors.
- (BOOL)textView:(NSTextView *)aTextView doCommandBySelector:(SEL)aSelector
{
if (aSelector == #selector(insertNewline:)) {
// Handle the Enter key
return YES;
} else if (aSelector == #selector(insertTab:)) {
// Handle the Tab key
return YES;
}
return NO;
}
You should handle keyDown:(NSEvent*)theEvent message of NSTextView (i.e. write your own descendant).
In this event you will have key code in [theEvent keyCode].
For return there is a constant kVK_Return, for tab - kVK_Tab, etc.
You should add Carbon framework (and #import Carbon/Carbon.h) to access these constants.

Facebook SessionState always not FBSessionStateCreatedTokenLoaded

I use the latest Facebook SDK (3.1.1).
I wrote a function in my AppDelegate that checks the current session and creates or opens a session according to the state.
The second conditions always return NO and go to show login screen.
I don't understand way.
AppDelegate method:
if (facebook.isOpen == NO)
{
facebook = [[FBSession alloc] initWithPermissions:permission];
if (facebook.state == FBSessionStateCreatedTokenLoaded)
{
[facebook openWithCompletionHandler:^(FBSession *session,FBSessionState status,NSError *error)
{
// load user details
}];
}
else
{
// show login screen
}
}
There is a property access token in the FbSession class.
Use that string in order to check the login status.
If you are getting null in that string it means your session is expired but if you are getting some value in that is means you are still login in the app and you can do what ever you want.
Regards
Abhishek Goyal
if the session state equal to FBSessionStateCreatedTokenLoaded it means that there is a cached accessToken, a call to open* will result in an open session, without UX or app-switching
-(BOOL)validFBSessionExists{
if (FBSession.activeSession.isOpen){
NSLog(#"Facebook accessToken:%#",FBSession.activeSession.accessTokenData.accessToken);
return YES;
}
else if (FBSession.activeSession.state == FBSessionStateCreatedTokenLoaded)
{
// Cached token exist, Session needs to be re-opened
[FBSession.activeSession openWithBehavior:FBSessionLoginBehaviorUseSystemAccountIfPresent completionHandler:nil];
return YES;
}
return NO;
}

How BarAlarm iOS app is made? [duplicate]

I am developing a non-appstore app for iOS. I want to read the cellular signal strength in my code.
I know Apple doesn't provide any API by which we can achieve this.
Is there any private API that can be used to achieve this? I have gone through the various threads regarding this issue but could not find any relevant info.
It is completely possible because there is an app in the app-store for detecting the carrier's signal strength.
Get signalStreght IOS9:
UIApplication *app = [UIApplication sharedApplication];
NSArray *subviews = [[[app valueForKey:#"statusBar"] valueForKey:#"foregroundView"] subviews];
NSString *dataNetworkItemView = nil;
for (id subview in subviews) {
if([subview isKindOfClass:[NSClassFromString(#"UIStatusBarSignalStrengthItemView") class]])
{
dataNetworkItemView = subview;
break;
}
}
int signalStrength = [[dataNetworkItemView valueForKey:#"signalStrengthRaw"] intValue];
NSLog(#"signal %d", signalStrength);
It is not very hard.
Link CoreTelephony.framework in your Xcode project
Add the following lines where you need it
Code:
int CTGetSignalStrength(); // private method (not in the header) of Core Telephony
- (void)aScanMethod
{
NSLog(#"%d", CTGetSignalStrength()); // or do what you want
}
And you are done.
Update May 2016
Apple removed this opportunity.
I briefly looked at the VAFieldTest project located at Github.
There seems to be getSignalStrength() and register_notification() functions in Classes/VAFieldTestViewController.m that might be interesting to you as they call into CoreTelephony.framework.
I am pretty confident that some of the used calls are undocumented in the CoreTelephony framework documentation from Apple and therefore private - any app in the AppStore must have slipped passed inspection.
To get signal streght in iOS 9 or above in Swift 3, without using the private API from CoreTelephony - CTGetSignalStrength(). Just scouring the statusBar view.
func getSignalStrength() -> Int {
let application = UIApplication.shared
let statusBarView = application.value(forKey: "statusBar") as! UIView
let foregroundView = statusBarView.value(forKey: "foregroundView") as! UIView
let foregroundViewSubviews = foregroundView.subviews
var dataNetworkItemView:UIView!
for subview in foregroundViewSubviews {
if subview.isKind(of: NSClassFromString("UIStatusBarSignalStrengthItemView")!) {
dataNetworkItemView = subview
break
} else {
return 0 //NO SERVICE
}
}
return dataNetworkItemView.value(forKey: "signalStrengthBars") as! Int
}
Attention: If the status bar is hidden, the key "statusBar" will return nil.
I haven't tested it, but apparently this is now a method of CTTelephonyNetworkInfo instead of a global/static function.
The return type is id, so I think you get either a NSDictionary (as the _cachedSignalStrength ivar implies) or an NSNumber (as the old function implies).
id signalStrength = [[CTTelephonyNetworkInfo new] signalStrength];
This changed in iOS 8.3, as you can see from the commit.
Note that this is still not documented! So if your app will go in the App Store, take your precautions.
Here's Lucas' answer converted to Xamarin, and tested on iOS 10.2.1:
var application = UIApplication.SharedApplication;
var statusBarView = application.ValueForKey(new NSString("statusBar")) as UIView;
var foregroundView = statusBarView.ValueForKey(new NSString("foregroundView")) as UIView;
UIView dataNetworkItemView = null;
foreach (UIView subview in foregroundView.Subviews)
{
if ("UIStatusBarSignalStrengthItemView" == subview.Class.Name)
{
dataNetworkItemView = subview;
break;
}
}
if (null == dataNetworkItemView)
return false; //NO SERVICE
int bars = ((NSNumber)dataNetworkItemView.ValueForKey(new NSString("signalStrengthBars"))).Int32Value;