NSLog ignored on devices - objective-c

I am trying to redirect all NSLog output to be able to see all my logs in a UITextView inside the app (for debug purposes, and maybe later to log everything using TestFlight). I can't use the well-known DLog because I want to catch logs from frameworks I don't own.
I'm doing this by 'redirecting' stderr to a pipe and reading from this pipe.
Anyway, this works well on the simulator and on the device when the app is launched from within Xcode.
But, this does not work when the app is launched "by hand" on the device.
It seems that NSLog calls have no effect on the device: I don't read anything from the pipe. However, if I manually write on stderr : fprintf(stderr, "test") I can read it from the pipe, so the problem is not with stderr but rather with the NSLog function.
Why does it do nothing on devices ? Is there a way to make it write on stderr even on the device ?
If it is not possible to use NSLog on devices, is there another way to gather logs from external frameworks ?
Thank you.

NSLog goes to the system log on devices. You can access the system log using the C API asl (Apple System Log); also check out this blog post.
However if you're using TestFlight it's much easier to just replace/append those NSLog statements with the TFLog statement.
TFLog(#"This comment is live on TestFlight.");

If you want some UILogging, you could consider using ULog, a UIAlertView based Logging #define directive.
// ULog will show the UIAlertView only when the DEBUG variable is set
#ifdef DEBUG
# define ULog(fmt, ...) { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:#"%s\n [Line %d] ", __PRETTY_FUNCTION__, __LINE__] message:[NSString stringWithFormat:fmt, ##__VA_ARGS__] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil]; [alert show]; }
#else
# define ULog(...)
#endif
(source: http://overbythere.co.uk/blog/2012/01/alternatives-nslog)

Related

Asking a user for elevated privileges and elevating the application without an Apple developer certificate

Apparently, as of 10.7, AuthorizationExecuteWithPrivileges is deprecated. The general gist of the information I've gathered on this seems to suggest using ServiceManagement.framework's SMJobBless() function to have a helper application deployed.
My understanding of it though, is that this will need a developer certificate to be purchased from Apple to code sign both my application and the helper process - or this will not work. Is this correct?
I originally used AuthorizationExecuteWithPrivileges to ask a user for elevated privileges, since they are needed to access another running process. Without that, my app can't work as the unofficial plugin it's intended to. Is the code signing way really the only way to go from here? I'm trying to avoid purchasing a developer certificate due to the sheer cost of it.
Has anyone found any alternative ways to relaunch an application with elevated privileges, with user permission of course?
#CarlosP's answer with code to escape the path & arguments:
- (BOOL)runProcessAsAdministrator:(NSString*)scriptPath
withArguments:(NSArray*)arguments
output:(NSString**)output
errorDescription:(NSString**)errorDescription {
//Check path.
if (![scriptPath hasPrefix:#"/"]) {
#throw [NSException exceptionWithName:
NSInvalidArgumentException reason:#"Absolute path required." userInfo:nil];
}
//Define script.
static NSAppleScript* appleScript = nil;
if (!appleScript) {
appleScript = [[NSAppleScript alloc] initWithSource:
#"on run commandWithArguments\n"
" activate\n"
" repeat with currentArgument in commandWithArguments\n"
" set contents of currentArgument to quoted form of currentArgument\n"
" end repeat\n"
" set AppleScript's text item delimiters to space\n"
" return do shell script (commandWithArguments as text) with administrator privileges\n"
"end run"];
}
//Set command.
NSAppleEventDescriptor* commandWithArguments = [NSAppleEventDescriptor listDescriptor];
[commandWithArguments insertDescriptor:
[NSAppleEventDescriptor descriptorWithString:scriptPath] atIndex:0];
//Set arguments.
for (NSString* currentArgument in arguments) {
[commandWithArguments insertDescriptor:
[NSAppleEventDescriptor descriptorWithString:currentArgument] atIndex:0];
}
//Create target & event.
ProcessSerialNumber processSerial = {0, kCurrentProcess};
NSAppleEventDescriptor* scriptTarget =
[NSAppleEventDescriptor descriptorWithDescriptorType:typeProcessSerialNumber bytes:&processSerial length:sizeof(ProcessSerialNumber)];
NSAppleEventDescriptor* scriptEvent =
[NSAppleEventDescriptor appleEventWithEventClass:kCoreEventClass
eventID:kAEOpenApplication
targetDescriptor:scriptTarget
returnID:kAutoGenerateReturnID
transactionID:kAnyTransactionID];
[scriptEvent setParamDescriptor:commandWithArguments forKeyword:keyDirectObject];
//Run script.
NSDictionary* errorInfo = [NSDictionary dictionary];
NSAppleEventDescriptor* eventResult = [appleScript executeAppleEvent:scriptEvent error:&errorInfo];
//Success?
if (!eventResult) {
if (errorDescription)
*errorDescription = [errorInfo objectForKey:NSAppleScriptErrorMessage];
return NO;
} else {
if (output)
*output = [eventResult stringValue];
return YES;
}
}
Update
In Yosemite, do shell script just calls a version of AuthorizationExecuteWithPrivileges embedded in StandardAdditions.osax.
It's conceivable that the with administrator privileges option for do shell script will go away when AuthorizationExecuteWithPrivileges does.
Personally, I would just continue calling AuthorizationExecuteWithPrivileges directly.
do shell script does have the advantage of reaping the process automatically. That requires a little extra work with AuthorizationExecuteWithPrivileges.
Is the code signing way really the only way to go from here?
To my knowledge, there is no secure alternative to AuthorizationExecuteWithPrivileges.
It still works fine under Yosemite. Haven't tried out El Capitan yet.
You can try to fail gracefully if the call goes away in the future.
I'm trying to avoid purchasing a developer certificate due to the sheer cost of it.
Well, if it helps, the code signing certificate will be valid for several years.
I'm pretty sure I've let my developer account lapse without any issues.
So it's basically $99 every five years.

Developing jailbreak tweak

I have searched everywhere for a solution to this but cannot find one.
My tweak, developed in Theos is very simple, I want to disable the mute button in the phone app. I have found the private framework I think I need and the code that is called.
I have added a message so i can see if this routine is called, but is not.
Although my code compiles and installs on the phone, it never runs.
I have tried a basic springboard tweak and that is fine, so I must be doing something wrong with the framework or subroutine called?
(I have also added com.apple.mobilephone to my plist as suggested somewhere)
Here is my MAKEFILE
include theos/makefiles/common.mk
TARGET_IPHONEOS_DEPLOYMENT_VERSION=7.0
TWEAK_NAME = Disablemute
TARGET = iphone:7.0:7.0
ARCHS = armv7
Disablemute_FILES = Tweak.xm
Disablemute_FRAMEWORKS = UIKit
Disablemute_PRIVATE_FRAMEWORKS = TelephonyUtilities CoreTelephony
include $(THEOS_MAKE_PATH)/tweak.mk
Here is the tweak.mk code
#import <PrivateFrameworks/TelephonyUtilities/TUCall.h>
#import <UIKit/UIKit.h>
%hook TUCall
- (void)setMuted:(BOOL)arg1 {
//%orig;
%log;
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Welcome"
message:#"Welcome to your iPhone Burt!"
delegate:nil
cancelButtonTitle:#"Thanks"
otherButtonTitles:nil];
[alert show];
[alert release];
}
%end
So the private framework is TelephonyUtilities and the header file is TUCall.h, the function is -void SetMuted:(BOOL)arg1;
Can anyone help please.

How to detect microphone input permission refused in iOS 7

I would like to detect when a user refused the microphone permission on my iOS application.
I only get this value when I try to record the microphone: -120.000000 db
But before to get this I have to set up an AVAudioSession. Is there another function?
And I got this message in the output:
Microphone input permission refused - will record only silence
Thanks.
If you are still compiling with iOS SDK 6.0 (as I am) you have to be a bit more indirect than #Luis E. Prado, as the requestRecordPermission method doesn't exist.
Here's how I did it. Remove the autorelease bit if you're using ARC. On iOS6 nothing happens, and on iOS7 either the 'microphone is enabled' message is logged or the alert is popped up.
AVAudioSession *session = [AVAudioSession sharedInstance];
if ([session respondsToSelector:#selector(requestRecordPermission:)]) {
[session performSelector:#selector(requestRecordPermission:) withObject:^(BOOL granted) {
if (granted) {
// Microphone enabled code
NSLog(#"Microphone is enabled..");
}
else {
// Microphone disabled code
NSLog(#"Microphone is disabled..");
// We're in a background thread here, so jump to main thread to do UI work.
dispatch_async(dispatch_get_main_queue(), ^{
[[[[UIAlertView alloc] initWithTitle:#"Microphone Access Denied"
message:#"This app requires access to your device's Microphone.\n\nPlease enable Microphone access for this app in Settings / Privacy / Microphone"
delegate:nil
cancelButtonTitle:#"Dismiss"
otherButtonTitles:nil] autorelease] show];
});
}
}];
}
EDIT: It turns out that the withObject block is executed in a background thread, so DO NOT do any UI work in there, or your app may hang. I've adjusted the code above. A client pointed this out on what was thankfully a beta release. Apologies for the mistake.
Please note that this will only work if built with Xcode 5, and not with 4.6
Add the AVFoundation Framework to your project
Then import the AVAudioSession header file, from the AVFoundation framework, where you intend to check if the microphone setting is enabled
#import <AVFoundation/AVAudioSession.h>
Then simply call this method
[[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
if (granted) {
// Microphone enabled code
}
else {
// Microphone disabled code
}
}];
The first time this method runs, it will show the prompt to allow microphone access and based on the users response it will execute the completion block. From the second time onwards it will just act based on the stored setting on the device.
Swift answer:
if AVAudioSession.sharedInstance().recordPermission() == .Denied {
print("Microphone permission refused");
}
Or you can use framework like PermissionScope which permit to easily check permissions. https://github.com/nickoneill/PermissionScope
Edit: Swift 3 answer:
import AVFoundation
...
if AVAudioSession.sharedInstance().recordPermission() == .denied {
print("Microphone permission refused");
}
I'm not 100% certain if we're allowed to talk about iOS 7 outside of Apple's devforums, but I found the answer you're looking for there.
In short, you'll find your solution in the AVAudioSession.h header file in the SDK. And if you want to make use of it while still supporting iOS 6, make certain to use "respondsToSelector:" to check for the API availability.

iOS: My Alert message is displayed twice, but the code is only executed once

Here is my code that calls "displayAlert". The problem is not only do I get an error message (wait_fences: failed to receive reply: 10004003) but the "alert" is displayed twice!
if(gSiteID.globalSiteID.length == 0) { // user didn't choose site
[self displayAlert:NSLocalizedString(#"Missing Site ID", nil) andData:NSLocalizedString(#"You must choose a site from the View Sites page",nil)];
return;
}
This the code for "displayAlert":
- (void) displayAlert: (NSString *) title andData: (NSString *) errorMsg {
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle: title
message: errorMsg
delegate:nil
cancelButtonTitle: #"OK"
otherButtonTitles: nil];
[alert show];
return;
}
I have searched SO and Google and found nothing that is specific to my issue. What am I doing wrong?
Are you testing this on a real device or the simulator? wait_fences: failed to receive reply usually means something bad happened with the debugger's connection to your device, or that you sat at a breakpoint for a really long time and it timed out. Are you sure that the code only executes once, and that nothing else could call that method? Stick breakpoints in your if statement and in your displayAlert:andData: method and see what happens. Run through your logic and find all the cases when that display alert method can be called and stick breakpoints on all of them.
I found the problem: indeed I was calling it twice from different .cs files (do you see the egg on my face?). Jack Lawrence please post your answer to the question, since you hit it on the head.

iOS Reachability Not Recognizing When Host is Removed

I am building an application and trying to check and see if a device is still available on the network (by connecting to the devices IPAddress). I am using reachability to confirm that it is available.
When I network access for the iOS device (turn on airplane mode for example) everything works properly, but if I remove the device from the network, reachability does not seem to notice the change.
It seems like reachability is caching the results, and not seeing the update.
Don't use reachability then!
Use this bit of code instead which works a treat;
NSString *connected = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://www.google.com"]];
wait(25000);
if (connected == NULL) {
NSLog(#"Not Connected");
//Code to show if not connected
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:#"Oops! You aren't connected to the Internet."
delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
} else {
NSLog(#"Connected Successfully!");
//Any other code for successful connection
}
The SCReachability API only checks if the local hardware is configured such that it could reach the specified address; it does not actually attempt to reach it. To determine if the target is alive and kicking, you must attempt to open a connection to it.
Check out this answer. While the pixelbit's answer is viable, burtlo is right to bring up that just waiting 25 seconds isn't a great idea. Using the NSURLConnection is much cleaner.
Apple updated the Reachabilty class file. You can test it with latest Xcode.
here is link:
Apple Reachabilty Sample Code
I tested with Xcode 8.3.1 on iPhone 6 version 10.3.1.
It will notify user for change in network status.