You are here:    Home  > Blog  

On Device Console for iOS Developers

iOS Application DeveloperXcode gives us a bunch of tools to keep the nasties at bay.
There are so many situations where you want to see same Xcode console log output while you are not actually debugging you application using Xcode.

Your tester reported one issue ( which is not easily reproducible ) . And you wanted to see the console output to resolve the issue.
You might have think lots of times :- ‘Wish i was able to see console output of application right their.

Lets make a control which will you show console output on device itself.

Resource:

There is a file in application directory which actually saves the every log output you see in Xcode console while debugging.
The smarter way is to simply read the file and show on device using some good user interface.

//
// AKSDeviceConsole.h
//
#import <Foundation/Foundation.h>
@interface AKSDeviceConsole : NSObject
+ (void)startService;
@end

//
// AKSDeviceConsole.m
//

#import “AKSDeviceConsole.h”
#import “AppDelegate.h”
#import <QuartzCore/QuartzCore.h>

#define AKS_LOG_FILE_PATH [[AKSDeviceConsole documentsDirectory] stringByAppendingPathComponent:@”ns.log”]
#define APPDELEGATE                                     ((AppDelegate *)[[UIApplication sharedApplication] delegate])

@interface AKSDeviceConsole () {
UITextView *textView;
}
@end

@implementation AKSDeviceConsole

+ (id)sharedInstance {
static id __sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
__sharedInstance = [[AKSDeviceConsole alloc]init];
});
return __sharedInstance;
}

+ (NSMutableString *)documentsDirectory {
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0];
}

– (id)init {
if (self = [super init]) {
[self initialSetup];
}
return self;
}

– (void)initialSetup {
[self resetLogData];
[self addGestureRecogniser];
}

+ (void)startService {
double delayInSeconds = 0.1;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
[AKSDeviceConsole sharedInstance];
});
}

– (void)resetLogData {
[NSFileManager.defaultManager removeItemAtPath:AKS_LOG_FILE_PATH error:nil];
freopen([AKS_LOG_FILE_PATH fileSystemRepresentation], “a”, stderr);
}

– (void)addGestureRecogniser {
UISwipeGestureRecognizer *recogniser = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(showConsole)];
[recogniser setDirection:UISwipeGestureRecognizerDirectionRight];
[APPDELEGATE.window addGestureRecognizer:recogniser];
}

– (void)showConsole {
if (textView == nil) {
CGRect bounds = [[UIScreen mainScreen] bounds];
CGRect viewRectTextView = CGRectMake(30, bounds.size.height – bounds.size.height / 3 – 60, bounds.size.width – 30, bounds.size.height / 3);

textView = [[UITextView alloc]initWithFrame:viewRectTextView];
[textView setBackgroundColor:[UIColor blackColor]];
[textView setFont:[UIFont systemFontOfSize:10]];
[textView setEditable:NO];
[textView setTextColor:[UIColor whiteColor]];
[[textView layer]setOpacity:0.6];

[APPDELEGATE.window addSubview:textView];
[APPDELEGATE.window bringSubviewToFront:textView];

UISwipeGestureRecognizer *recogniser = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(hideWithAnimation)];
[recogniser setDirection:UISwipeGestureRecognizerDirectionLeft];
[textView addGestureRecognizer:recogniser];

[self moveThisViewTowardsLeftToRight:textView duration:0.30];
[self setUpToGetLogData];
[self scrollToLast];
}
}
– (void)hideWithAnimation {
[self moveThisViewTowardsLeft:textView duration:0.30];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self hideConsole];
});
}
– (void)hideConsole {
[textView removeFromSuperview];
[NSNotificationCenter.defaultCenter removeObserver:self];
textView  = nil;
}
– (void)scrollToLast {
NSRange txtOutputRange;
txtOutputRange.location = textView.text.length;
txtOutputRange.length = 0;
textView.editable = YES;
[textView scrollRangeToVisible:txtOutputRange];
[textView setSelectedRange:txtOutputRange];
textView.editable = NO;
}
– (void)setUpToGetLogData {
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:AKS_LOG_FILE_PATH];
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(getData:) name:@”NSFileHandleReadCompletionNotification” object:fileHandle];
[fileHandle readInBackgroundAndNotify];
}
– (void)getData:(NSNotification *)notification {
NSData *data = notification.userInfo[NSFileHandleNotificationDataItem];
if (data.length) {
NSString *string = [NSString.alloc initWithData:data encoding:NSUTF8StringEncoding];
textView.editable = YES;
textView.text = [textView.text stringByAppendingString:string];
textView.editable = NO;
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
[self scrollToLast];
});
[notification.object readInBackgroundAndNotify];
}
else {
[self performSelector:@selector(refreshLog:) withObject:notification afterDelay:1.0];
}
}
– (void)refreshLog:(NSNotification *)notification {
[notification.object readInBackgroundAndNotify];
}
– (void)moveThisViewTowardsLeft:(UIView *)view duration:(float)dur; {
[UIView animateWithDuration:dur animations: ^{
[view setFrame:CGRectMake(view.frame.origin.x – [[UIScreen mainScreen]bounds].size.width, view.frame.origin.y, view.frame.size.width, view.frame.size.height)];
} completion: ^(BOOL finished) {}];
}
– (void)moveThisViewTowardsLeftToRight:(UIView *)view duration:(float)dur; {
CGRect original = [view frame];
[view setFrame:CGRectMake(view.frame.origin.x – [[UIScreen mainScreen]bounds].size.width, view.frame.origin.y, view.frame.size.width, view.frame.size.height)];
[UIView animateWithDuration:dur animations: ^{
[view setFrame:original];
} completion: ^(BOOL finished) {}];
}

@end

The above class is a complete control to show/hide on device console window on top of the application screen dynamically.
simply drop the above class in your project.

And initiate on device console like

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
[AKSDeviceConsole startService];
return YES;
}

How to use show and hide console

Swipe left and right to hide and show the console

The Complete Demo Example Project is available here

On CocoaControls
https://www.cocoacontrols.com/controls/aksdeviceconsole

On Github
https://github.com/aryansbtloe/AksDeviceConsole

By: Vipin Jain

About Vipin Jain

(CEO / Founder of Konstant Infosolutions Pvt. Ltd.) Mobile App Provider (A Division of Konstant Infosolutions Pvt. Ltd.) has an exceptional team of highly experienced & dedicated mobile application and mobile website developers, business analysts and service personnels, effectively translating your business goals into a technical specification and online strategy. Read More