How can I get a console readout at runtime in an application? - objective-c

For debugging purposes, I'd like to access console printouts at runtime in a way similar to the Console app current on the App Store (that can be found here).
I did some searching of the docs and I can't find anything that's provided by Apple, but I feel like I'm missing something important. Any insight?
Thanks.

You can do so using <asl.h>. Here is an example that I threw together to create an array of console messages.
-(NSArray*)console
{
NSMutableArray *consoleLog = [NSMutableArray array];
aslclient client = asl_open(NULL, NULL, ASL_OPT_STDERR);
aslmsg query = asl_new(ASL_TYPE_QUERY);
asl_set_query(query, ASL_KEY_MSG, NULL, ASL_QUERY_OP_NOT_EQUAL);
aslresponse response = asl_search(client, query);
asl_free(query);
aslmsg message;
while((message = asl_next(response)) != NULL)
{
const char *msg = asl_get(message, ASL_KEY_MSG);
[consoleLog addObject:[NSString stringWithCString:msg encoding:NSUTF8StringEncoding]];
}
if (message != NULL) {
asl_free(message);
}
asl_free(response);
asl_close(client);
return consoleLog;
}

If your device is attached to Xcode, you can see console output (NSLogs and such) in the debug area:
If you're running the app and connecting to Xcode later, I believe you can get console logs in the Organizer.
Edit: to access the log file at runtime, you should try /var/log/system.log — but even better I recommend using a custom debug function, which would write to the system log and/or a text view in your app. (Check out NSLogv, which will be useful when writing a wrapper function.) This also has the advantage of letting you disable all debug logs from one place (just change your debug function).

Related

Firebase OSX simple login with email/pass oddity

I'm setting up a very basic OSX app for an existing firebase with SimpleLogin and email/password authentication.
Here's the code I'm using.
- (void) applicationDidFinishLaunching:(NSNotification *)notification {
Firebase* ref = [[Firebase alloc] initWithUrl:#"https://myapp.firebaseio.com"];
FirebaseSimpleLogin* authClient = [[FirebaseSimpleLogin alloc] initWithRef:ref];
[authClient loginWithEmail:#"myemail#mydomain.com" andPassword:#"mypassword"
withCompletionBlock:^(NSError* error, FAUser* user) {
if (error != nil) {
// There was an error logging in to this account
NSLog(#"authClient login error: %#", error);
} else {
NSLog(#"Login success.");
}
}];
}
Login is successful, and I see the log output. However, "FAUser* user" is nil. How? Why?
Online search / existing SO questions haven't helped..
Any ideas?
** UPDATE **
Same code in iOS works as expected. Is this just an OSX issue?
** UPDATE 2 **
I compiled the source code from the Firebase/Objective-C Simple Login Service (which seems to only reference iOS) directly in my OSX project and found that there is a "duplicate item" error code when the login service tries to store Keychain data on OSX.
The source code after the keychain save operation then proceeds to return a null user. I believe there is a logic error here because the if statement evaluates to true whenever it is not a success code (skipping the special case for duplicate item):
if (status != noErr) {
user = nil;
}
else if (status == errSecDuplicateItem) {
// TODO: log an error?
user = nil;
}
Anyway, I am able to continue working by modifying this small chunk of code to fit my needs.
Yeah, you're right. It's an error with accessing Keychain.
To clear the error on your own machine, you can go to Keychain Access, search for 'firebase' in the upper right. You should get an item with the name https://auth.firebase.com. Click it. Check that it says Firebase_<YOUR_FIREBASE_NAME> for the Account field, and go ahead and delete it. The next time you spin up your app, you'll have to log in again.
This seems to show up when you try to hit the same keychain item many times at once. Accessing the keychain slowly multiple times seems to be fine, so unless someone is authenticating many of the same app on the same machine, you shouldn't get the error. (If you can hit it some other way, please share!)

Enable access for assistive devices programmatically on 10.9

I want to enable access for assistive devices programatically on 10.9. On 10.8 and lower I was using following Applescript to enable access for assistive devices:
tell application "System Events"
if UI elements enabled is false then
set UI elements enabled to true
end if
end tell
With 10.9, Apple has moved the accessibility options to System Preferences ➞ Security & Privacy ➞ Privacy ➞ Accessibility. Unlike previous versions of OS X, which used a universal checkbox for all applications, the new functionality in 10.9 allows users to individually choose which apps can gain control of the system to perform their various scripted functions.
Apple has NOT provided any API to developers to programmatically enable accessibility for an app. So Mac OS 10.9 will prompt a dialog for end user permission to enable Accessibility when application uses accessibility APIs. Additionally User has to Relaunch the application after enabling Accessibility.
Can we enable access for assistive devices programmatically on 10.9 using Applescript or any other APIs? Any help to fix this issue would be greatly appreciated.
This doesn’t answer your question, but it’s good to know about a new API call that appeared in 10.9 and lets you display the authorization screen or bypass it:
NSDictionary *options = #{(id)kAXTrustedCheckOptionPrompt: #YES};
BOOL accessibilityEnabled = AXIsProcessTrustedWithOptions((CFDictionaryRef)options);
Passing YES will force the authorization screen to appear, passing NO will silently skip it. The return value is the same as the one returned by AXAPIEnabled(), which is getting deprecated in 10.9. To make sure that the function is available on your system, just compare it to NULL:
if (AXIsProcessTrustedWithOptions != NULL) {
// 10.9 and later
} else {
// 10.8 and older
}
You'll need to add ApplicationServices.framework to your project, and import to your .m or .h file:
#import <ApplicationServices/ApplicationServices.h>
It’s quite a pity that the authorization screen doesn’t let the user to authorize the app directly, it just opens the right part of the System Preferences. Which, by the way, you can do directly without going through the useless system dialogue:
tell application "System Preferences"
set securityPane to pane id "com.apple.preference.security"
tell securityPane to reveal anchor "Privacy_Accessibility"
activate
end tell
or using Objective C:
NSString *urlString = #"x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility";
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:urlString]];
This can be paired with the first code snippet to test whether accessibilityEnabled by passing #NO to kAXTrustedCheckOptionPrompt while preventing the system pop-up to appear and instead opening the Accessibility preferences pane directly:
NSDictionary *options = #{(id)kAXTrustedCheckOptionPrompt: #NO};
BOOL accessibilityEnabled = AXIsProcessTrustedWithOptions((CFDictionaryRef)options);
if (!accessibilityEnabled) {
NSString *urlString = #"x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility";
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:urlString]];
}
For a native approach I'd recommend against using all the sqlite3 and AppleScript hacks as they might stop working in the future, there's also just a proper api for this.
To add on to this, you can actually monitor if the user clicks the accessibility setting for your app so you can do some actions when the user grants the permission.
(Swift 5, tested on Mojave, Catalina, Big Sur)
reading privileges:
private func readPrivileges(prompt: Bool) -> Bool {
let options: NSDictionary = [kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: prompt]
let status = AXIsProcessTrustedWithOptions(options)
return status
}
Monitoring for changes in accessibility:
DistributedNotificationCenter.default().addObserver(forName: NSNotification.Name("com.apple.accessibility.api"), object: nil, queue: nil) { _ in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.updatePrivileges()
}
}
It is best to read the privileges again after getting the notification as the notification itself doesn't really work in my experience. So inside the updatePrivileges(), run readPrivileges() to get the new status.
You need the delay because it takes some time for the changes to be reflected.
Another thing you need to keep in mind while monitoring is that a notification will be fired for any app that gets different permissions, so if the user grants or revokes a different app you'll still get a notification.
Also, don't forget to remove the observer when you don't need it anymore.
Source: Accessbility Testbench by Piddlesoft
While #user2865860's answer works well, I though I'd post the entire code sample that works perfectly on 10.9 to save others some time. You need to get root privileges, so it will prompt a user to enter the password.
char *command= "/usr/bin/sqlite3";
char *args[] = {"/Library/Application Support/com.apple.TCC/TCC.db", "INSERT or REPLACE INTO access VALUES('kTCCServiceAccessibility','com.yourapp',0,1,0,NULL);", nil};
AuthorizationRef authRef;
OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authRef);
if (status == errAuthorizationSuccess) {
status = AuthorizationExecuteWithPrivileges(authRef, command, kAuthorizationFlagDefaults, args, NULL);
AuthorizationFree(authRef, kAuthorizationFlagDestroyRights);
if(status != 0){
//handle errors...
}
}
You can edit the TCC.db file in directly. I had to do this in order to make Divvy install without user interaction. Just replace com.mizage.divvy with your program.
sudo sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db "INSERT INTO access VALUES('kTCCServiceAccessibility','com.mizage.divvy',0,1,1,NULL);"
To remove the entry:
sudo sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db "delete from access where client='com.mizage.divvy';"
I have found the following code snippet which properly requests Accessibility permissions in OS X 10.9:
if (AXIsProcessTrustedWithOptions != NULL) {
// 10.9 and later
const void * keys[] = { kAXTrustedCheckOptionPrompt };
const void * values[] = { kCFBooleanTrue };
CFDictionaryRef options = CFDictionaryCreate(
kCFAllocatorDefault,
keys,
values,
sizeof(keys) / sizeof(*keys),
&kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
return AXIsProcessTrustedWithOptions(options);
}
// OS X 10.8 and older
I was struggling with this myself and after a bit of a research I found the following:
Hacking the sqlite DB has the major drawback in using authorization services. First this will pop-up a dialog telling user that an application wants to install a utility helper (even though it is just one off launchd submission using SMJobSubmit). Second, it does not work for sandboxed apps and thus no app store.
#Max Al Faeakh uses AuthorizationExecuteWithPrivileges which is deprecated. You need to use launchd with the above SMJobSubmit. Anyway, this still requires authorization. It also requires an auxiliary application like this one.
I guess the best is to use either:
NSDictionary *options = #{(id)kAXTrustedCheckOptionPrompt: #YES};
BOOL accessibilityEnabled = AXIsProcessTrustedWithOptions((CFDictionaryRef)options);
or
NSDictionary *options = #{(id)kAXTrustedCheckOptionPrompt: #NO};
BOOL accessibilityEnabled = AXIsProcessTrustedWithOptions((CFDictionaryRef)options);
and open preference pane manually using for example scripting bridge framework:
SBSystemPreferencesApplication *prefs = [SBApplication applicationWithBundleIdentifier:#"com.apple.systempreferences"];
[prefs activate];
SBSystemPreferencesPane *pane = [[prefs panes] find:^BOOL(SBSystemPreferencesPane *elem) {
return [[elem id] isEqualToString:#"com.apple.preference.security"];
}];
SBSystemPreferencesAnchor *anchor = [[pane anchors] find:^BOOL(SBSystemPreferencesAnchor *elem) {
return [[elem name] isEqualToString:#"Privacy_Accessibility"];
}];
[anchor reveal];
The SBSystemPreferencesPane class comes form a SBSystemPreferences.h file which can be generated:
sdef "/Applications/System Preferences.app" | sdp -fh --basename SBSystemPreferences -o SBSystemPreferences.h
Thanks for this shell script samples from #NightFlight, which are really helpful. I used this with AppleScript in a Python application, like the following:
set sh to "touch /private/var/db/.AccessibilityAPIEnabled && sqlite3 \\"/Library/Application Support/com.apple.TCC/TCC.db\\" \\"INSERT or REPLACE INTO access VALUES('kTCCServiceAccessibility','com.godevnode',0,1,0,NULL);\\""
do shell script sh with administrator privileges
It worked well for me in Python code as a string.
Edit (Nov 7, 2014):
If you want to try this in AppleScript Editor, use a slightly different character escape as below:
set sh to "touch /private/var/db/.AccessibilityAPIEnabled && sqlite3 \"/Library/Application Support/com.apple.TCC/TCC.db\" \"INSERT or REPLACE INTO access VALUES('kTCCServiceAccessibility','com.godevnode',0,1,0,NULL);\""
do shell script sh with administrator privileges
For Mac OS X before 10.9, it's even simpler:
accessibility_api_file = "/private/var/db/.AccessibilityAPIEnabled"
def __enable_accessibility_api():
try:
script = 'do shell script "touch %s" with administrator ' \
'privileges' % accessibility_api_file
result = applescript.AppleScript(script).run()
log.debug("Tried to enable accessibility api, result=" + result)
return True
except applescript.ScriptError as err:
log.error(str(err))
return False
Just need to touch one file. The AppleScript mentioned in the Python code above can also be used in other languages.
Thanks everyone.
I issue the following triggered from the login window to ensure control is given only to the items we want every session:
# Enable Service Accessibility for Textpander and others
# Clear the acess table.
sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db "DELETE FROM access"
# Enter the access we wish to have.
sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db "INSERT INTO access VALUES ('kTCCServiceAccessibility','com.apple.systempreferences',0,1,1,NULL)"
sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db "INSERT INTO access VALUES ('kTCCServiceAccessibility','de.petermaurer.textpanderdaemon',0,1,1,NULL)"
The sqlite3 "hack" is great.
I had to use permissions "1,1,1" (whatever that means) to make this work.
Note that the permission combination, not the client (ie. program name) is the unique database key.

Change Authorization Dialog shown by AuthorizationCreate()

Looking through Apples BetterAuthorizationSample and further Derivatives( http://www.stevestreeting.com/2011/11/25/escalating-privileges-on-mac-os-x-securely-and-without-using-deprecated-methods/ )
I am trying to make a small change to the application and gain better understanding of the whole Security & ServiceManagement framework.. Therefore I proceeded to add an a button which removes the installed Job through the inverse of SMJobBless - SMJobRemove(). Straightforward however the AuthorizationCreate() call displays a dialog that states and requests permission to install a helper and not remove it.
That's the dialog I get (by using kSMRightModifySystemDaemons). As you can see it says that my app tries to add a new helper tool. Which will confuse my users because the app actually tries to remove the installed helper tool.
I'm seeking to find knowledge on how this dialog is changed to reflect my actual action (Job Removal), There are also several other apps which seem to completely customize the dialog - showing their own Custom Label and Buttons..
BOOL doRemoveSystemTool(NSString* label, NSError** error)
{
BOOL result = NO;
AuthorizationItem authItem = { kSMRightModifySystemDaemons, 0, NULL, 0 };
AuthorizationRights authRights = { 1, &authItem };
AuthorizationFlags flags = kAuthorizationFlagDefaults |
kAuthorizationFlagInteractionAllowed |
kAuthorizationFlagPreAuthorize |
kAuthorizationFlagExtendRights;
AuthorizationRef authRef = NULL;
//Obtain authorization
OSStatus status = AuthorizationCreate(&authRights, kAuthorizationEmptyEnvironment, flags, &authRef);
if (status != errAuthorizationSuccess)
{
NSLog(#"Failed to create AuthorizationRef, return code %ld", (long)status);
} else
{
//We have authorization so proceed with removing the Job via SMJobRemove
result = SMJobRemove(kSMDomainSystemLaunchd, (CFStringRef)label, authRef, YES, (CFErrorRef *)error);
}
AuthorizationFree(authRef, kAuthorizationFlagDefaults);
return result;
}
I have experimented with the authItem changing to kSMRightModifySystemDaemons from kSMRightBlessPrivilegedHelper but all this did was change the dialogue to display 'Add' instead of 'Install'
Would greatly appreciate some assistance here...
I haven't used this before but found your question interesting so I did a little reading of Apple's documentation and based on that I wonder if setting up the environment with a kAuthorizationEnvironmentPrompt would do what you want?
From AuthorizationTags.h:
The name of the AuthorizationItem that should be passed into the environment
when specifying a invocation specific additional text. The value should be a
localized UTF8 string.
You'd create an AuthorizationItem with this and then an AuthorizationItemSet containing that, and then pass the set into the AuthorizationCreate call for the environment: parameter.
I'd try that.
The other idea I had reading the documentation was to have a command line tool that does the remove and authorize the execution of the command line tool ("SomethingSomethingHelper") which might be less confusing to the user (so using AuthorizationExecuteWithPrivileges or kAuthorizationRightExecute or whatever).

How is it better to wait an asynchronous method to be finished in iPhone app?

everybody.
I want to understand, how i shoud procceed situations when an asynchronous method has "didFinish:#selector(SEL)" parameter.
My code example is:
//
// Authentication check
- ( void )authenticationSuccess: ( GDataServiceTicket* ) ticket
authenticatedWithError: ( NSError* ) error {
if ( error == nil )
{
NSLog( #"authentication success" );
}
else
{
NSLog( #"authentication error" );
}
}
//
- ( void ) fetchFeedOfSpreadsheets {
//create and authenticate to a google spreadsheet service
if ( !(mService) )
{
GDataServiceGoogleSpreadsheet *service = [self spreadsheetService];
[mService autorelease];
mService = [service retain];
}
// check autentication success ( invoke "authenticationSuccess" method for debug success & error )
[mService authenticateWithDelegate: self
didAuthenticateSelector:#selector(authenticationSuccess:
authenticatedWithError:) ];
// HERE I WANT TO MAKE A PAUSE AND WHAIT THE RESULT, EITHER I AUTHENTICATED OR NOT
// AND MAKE AN "IF" STATEMENT TO CONTINTUE WORKING ON SERVER, OR RETURN ERROR
//fetch retrieves the feed of spreadsheets entries
NSURL *feedURL = [ NSURL URLWithString: kGDataGoogleSpreadsheetsPrivateFullFeed ];
GDataServiceTicket *ticket;
ticket = [mService fetchFeedWithURL: feedURL
delegate: self
didFinishSelector: #selector(spreadsheetsTicket:finishedWithFeed:
error: ) ];
// HERE I WANT TO WAIT SECOND TIME. I WANT "spreadsheetsTicket:
// finishedWithFeed:error:" TO PROCCEED ERROR AND PUT A FEED IN SOME NSARRAY OBJECT
// AND AFTER THAT I WANT TO WORK WITH THAT NSARRAY RIGHT HERE
}
I's clear, that i can push the code i want into the end of "authenticationSuccess" method section, but it's also clear, that it's a wrong a way to solve the proble. There a number of situations like this, where i call an asynchronous method with a selector parameter, and i want to find a solution providing me a flexible code writing.
Thanks in advance.
It's a standard practice in Objective-C to put the code to be executed after the authentication in the authenticationSucess: method. You might not like it, but that is life.
Many people had the same complaint as you, so
on iOS 4 and later, there's something called blocks which allow you to write the code to be executed after the authentication in the method which initiates the authentication, as in
[mService authenticateAndExecute:^{
code to be executed when successfully authenticated ;
} whenError:^{
code to be executed when authentication failed;
} ];
But in this case you need to modify the API, which is possible by using categories. See this blog post by Mike Ash. He has many other posts on blocks on the same blog, which are also very instructive.
If you're going to use a library that works asynchronously (and therefore doesn't block your UI), you should have a good reason for trying to force it to work synchronously.
You should be checking for an authentication error at the end of your authenticationSuccess:authenticatedWithError: method, and calling the next request from there if there's a success. Similarly, in your spreadsheetsTicket:finishedWithFeed:error: check for an error, and continuing processing if there isn't one. It might be a better design to do that continued work in a separate method, but that's up to you.
Is there a specific reason you want to use the GData API in a synchronous fashion?

How to register component interface in wxwebconnect?

I'm doing an experiment with wxWebConnect test application, incorporating the xpcom tutorial at "http://nerdlife.net/building-a-c-xpcom-component-in-windows/"
I adapt MyComponent class as necessary to compile together with testapp.exe (not as separate dll), and on MyApp::OnInit I have the following lines:
ns_smartptr<nsIComponentRegistrar> comp_reg;
res = NS_GetComponentRegistrar(&comp_reg.p);
if (NS_FAILED(res))
return false;
ns_smartptr<nsIFactory> prompt_factory;
CreateMyComponentFactory(&prompt_factory.p);
nsCID prompt_cid = MYCOMPONENT_CID;
res = comp_reg->RegisterFactory(prompt_cid,
"MyComponent",
"#mozilla.org/mycomp;1",
prompt_factory);
Those lines are copied from GeckoEngine::Init(), using the same mechanism to register PromptService, etc. The code compiles well and testapp.exe is running as expected.
I put javascript test as below :
try {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
const cid = "#mozilla.org/mycomp;1";
obj = Components.classes[cid].createInstance();
alert(typeof obj);
// bind the instance we just created to our interface
alert(Components.interfaces.nsIMyComponent);
obj = obj.QueryInterface(Components.interfaces.nsIMyComponent);
} catch (err) {
alert(err);
return;
}
and get the following exception:
Could not convert JavaScript argument arg 0 [nsISupport.QueryInterface]
The first alert says "object", so the line
Components.classes[cid].createInstance()
is returning the created instance.
The second alert says "undefined", so the interface nsIMyComponent is not recognized by XULRunner.
How to dynamically registering nsIMyComponent interface in wxWebConnect environment ?
Thx
I'm not sure what is happening here. The first thing I would check is that your component is scriptable (I assume it is, since the demo you copy from is). The next thing I would check is whether you can instantiate other, standard XULRunner components and get their interface (try something like "alert('Components.interfaces.nsIFile');" - at least in my version of wxWebConnect this shows an alert box with string "nsIFile".
Also, I think it would be worth checking the Error Console to make sure there are no errors or warnings reported. A magic string to do that (in Javascript) is:
window.open('chrome://global/content/console.xul', '', 'chrome,dialog=no,toolbar,resizable');