I downloaded .plist file with Swift and Alamofire and I want to read values of .plist file.
if let url = URL(string: urlString) {
Alamofire.download(url).responseData(completionHandler: { response in
if response.result.isSuccess {
if let plistData = response.result.value {
if let plistXml = String(data: data, encoding: .utf8) {
// plistXml contains the actual plist contents as String object.
}
}
}
})
}
I have to objects containing my downloaded .plist file:
plistData: the downloaded Data object
plistXml: String object from plistData
Using any of these objects, I want to convert the plist fie to NSDictionary or Dictionary.
You can use PropertyListSerialization from the Foundation framework to create a NSDictionary
from the given property list data. Swift 3 Example:
do {
if let plist = try PropertyListSerialization.propertyList(from: plistData, format: nil)
as? NSDictionary {
// Successfully read property list.
print(plist)
} else {
print("not a dictionary")
}
} catch let error {
print("not a plist:", error.localizedDescription)
}
And with
if let plist = try PropertyListSerialization.propertyList(from: plistData, format: nil)
as? [String: Any] { ... }
you'll get the result as a Swift Dictionary.
Use SWXMLHash
Read plistXml and parse xml step by step using that documentation.
Save the element that you want to inside plistXml into Dictionary
Good Luck!
Here is the objective-C version of code. You can convert it very easily.
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"register.plist"];
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath]) {
plistPath = [[NSBundle mainBundle] pathForResource:#"register" ofType:#"plist"];
}
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
I am trying to create a custom directory using following snippet, that I essentially translated from a working Obj-C code for the same app.
class func pathToConfigFolder() -> String {
let urls: [String] = NSSearchPathForDirectoriesInDomains(.LibraryDirectory, .UserDomainMask, true)
let libraryPath: String = urls.last!
let configFolder: String = NSURL(fileURLWithPath: libraryPath).URLByAppendingPathComponent(".conf").absoluteString
var directory: ObjCBool = ObjCBool(true)
if !NSFileManager.defaultManager().fileExistsAtPath(configFolder, isDirectory: &directory) {
do {
try NSFileManager.defaultManager().createDirectoryAtPath(configFolder, withIntermediateDirectories: false, attributes: nil)
} catch let error {
print(" Error thrown... \(error)")
}
}
return configFolder
}
But the code fails with following error.
Error thrown... Error Domain=NSCocoaErrorDomain Code=4 "The operation couldn’t be completed. (Cocoa error 4.)" UserInfo=0x17526f4c0 {NSFilePath=file:///var/mobile/Containers/Data/Application/44D90AEB-DD7A-4C8C-9AD0-2665147BAAEC/Library/conf, NSUnderlyingError=0x174054c40 "The operation couldn’t be completed. No such file or directory"}
I have tried both on device and simulator running iOS 9.
EDIT
Objective-C Code
+ (NSString*) pathToConfigFolder
{
NSArray *urls = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
NSUserDomainMask, YES);
NSString *libraryPath = [urls lastObject];
NSString *configFolder = [libraryPath stringByAppendingPathComponent:#".conf"];
NSError *error = nil;
if ( ! [[NSFileManager defaultManager] fileExistsAtPath:configFolder]) {
[[NSFileManager defaultManager] createDirectoryAtPath:configFolder
withIntermediateDirectories:NO
attributes:nil
error:&error];
}
return configFolder;
}
Your problem is here:
let libraryPath: String = urls.last!
let configFolder: String = NSURL(fileURLWithPath: libraryPath).URLByAppendingPathComponent(".conf").absoluteString
The absoluteString method returns the entire URL as a string, so your configFolder is file:///var/mobile/Containers/Data/Application/44D90AEB-DD7A-4C8C-9AD0-2665147BAAEC/Library/conf. That is, it includes the file:// and is therefore not a valid file path suitable for use with the NSFileManager methods you use it with.
Changing those lines to look like this would fix the problem:
let libraryPath: NSString = urls.last!
let configFolder = libraryPath.stringByAppendingPathComponent(".conf")
I'm using NSString here because that's where stringByAppendingPathComponent is defined.
I have a UIImageView that allows a user to place and hold an image until it can be saved. The problem is, I can't figure out how to actually save and retrieve the image I've placed in the view.
I have retrieved and placed the image in the UIImageView like this:
//Get Image
- (void) getPicture:(id)sender {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = (sender == myPic) ? UIImagePickerControllerSourceTypeCamera : UIImagePickerControllerSourceTypeSavedPhotosAlbum;
[self presentModalViewController:picker animated:YES];
[picker release];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage (UIImage *)image editingInfo:(NSDictionary *)editingInfo {
myPic.image = image;
[picker dismissModalViewControllerAnimated:YES];
}
It displays the selected image in my UIImageView just fine, but I have no idea how to save it. I'm saving all the other pieces of the view (mostly UITextfield) in Core Data. I've searched and searched, and tried many bits of code that people have suggested, but either I'm not entering the code correctly, or those suggestions don't work with the way I have my code set up. It's likely the former. I'd like to save the image in the UIImageView using the same action (a save button) I'm using to save the text in the UITextFields. Here's how I'm saving my UITextField info:
// Handle Save Button
- (void)save {
// Get Info From UI
[self.referringObject setValue:self.myInfo.text forKey:#"myInfo"];
Like I said earlier, I have tried several methods to get this to work, but can't get a grasp on it. For the first time in my life I've wanted to cause physical harm to an inanimate object, but I've managed to restrain myself.
I'd like to be able to save the image the user places into the UIImageView in the application's documents folder, and then be able to retrieve it and place it in another UIImageView for display when the user pushes that view onto the stack. Any help is greatly appreciated!
It's all good, man. Don't harm yourself or others.
You probably don't want to store these images in Core Data, since that can impact performance if the data set grows too large. Better to write the images to files.
NSData *pngData = UIImagePNGRepresentation(image);
This pulls out PNG data of the image you've captured. From here, you can write it to a file:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0]; //Get the docs directory
NSString *filePath = [documentsPath stringByAppendingPathComponent:#"image.png"]; //Add the file name
[pngData writeToFile:filePath atomically:YES]; //Write the file
Reading it later works the same way. Build the path like we just did above, then:
NSData *pngData = [NSData dataWithContentsOfFile:filePath];
UIImage *image = [UIImage imageWithData:pngData];
What you'll probably want to do is make a method that creates path strings for you, since you don't want that code littered everywhere. It might look like this:
- (NSString *)documentsPathForFileName:(NSString *)name
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
return [documentsPath stringByAppendingPathComponent:name];
}
Hope that's helpful.
Swift 3.0 version
let documentDirectoryPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
let img = UIImage(named: "1.jpg")!// Or use whatever way to get the UIImage object
let imgPath = URL(fileURLWithPath: documentDirectoryPath.appendingPathComponent("1.jpg"))// Change extension if you want to save as PNG
do{
try UIImageJPEGRepresentation(img, 1.0)?.write(to: imgPath, options: .atomic)//Use UIImagePNGRepresentation if you want to save as PNG
}catch let error{
print(error.localizedDescription)
}
Swift 4 with extension
extension UIImage{
func saveImage(inDir:FileManager.SearchPathDirectory,name:String){
guard let documentDirectoryPath = FileManager.default.urls(for: inDir, in: .userDomainMask).first else {
return
}
let img = UIImage(named: "\(name).jpg")!
// Change extension if you want to save as PNG.
let imgPath = URL(fileURLWithPath: documentDirectoryPath.appendingPathComponent("\(name).jpg").absoluteString)
do {
try UIImageJPEGRepresentation(img, 0.5)?.write(to: imgPath, options: .atomic)
} catch {
print(error.localizedDescription)
}
}
}
Usage example
image.saveImage(inDir: .documentDirectory, name: "pic")
This is Fangming Ning's answer for Swift 4.2, updated with a recommended and more Swifty method for retrieving the document directory path and with better documentation. Credits to Fangming Ning for the new method as well.
guard let documentDirectoryPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
return
}
//Using force unwrapping here because we're sure "1.jpg" exists. Remember, this is just an example.
let img = UIImage(named: "1.jpg")!
// Change extension if you want to save as PNG.
let imgPath = documentDirectoryPath.appendingPathComponent("1.jpg")
do {
//Use .pngData() if you want to save as PNG.
//.atomic is just an example here, check out other writing options as well. (see the link under this example)
//(atomic writes data to a temporary file first and sending that file to its final destination)
try img.jpegData(compressionQuality: 1)?.write(to: imgPath, options: .atomic)
} catch {
print(error.localizedDescription)
}
Check out all the possible Data writing options here.
#pragma mark - Save Image To Local Directory
- (void)saveImageToDocumentDirectoryWithImage:(UIImage *)capturedImage {
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents folder
NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:#"/images"];
//Create a folder inside Document Directory
if (![[NSFileManager defaultManager] fileExistsAtPath:dataPath])
[[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:NO attributes:nil error:&error]; //Create folder
NSString *imageName = [NSString stringWithFormat:#"%#/img_%#.png", dataPath, [self getRandomNumber]] ;
// save the file
if ([[NSFileManager defaultManager] fileExistsAtPath:imageName]) {
// delete if exist
[[NSFileManager defaultManager] removeItemAtPath:imageName error:nil];
}
NSData *imageDate = [NSData dataWithData:UIImagePNGRepresentation(capturedImage)];
[imageDate writeToFile: imageName atomically: YES];
}
#pragma mark - Generate Random Number
- (NSString *)getRandomNumber {
NSTimeInterval time = ([[NSDate date] timeIntervalSince1970]); // returned as a double
long digits = (long)time; // this is the first 10 digits
int decimalDigits = (int)(fmod(time, 1) * 1000); // this will get the 3 missing digits
//long timestamp = (digits * 1000) + decimalDigits;
NSString *timestampString = [NSString stringWithFormat:#"%ld%d",digits ,decimalDigits];
return timestampString;
}
In Swift:
let paths: [NSString?] = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .LocalDomainMask, true)
if let path = paths[0]?.stringByAppendingPathComponent(imageName) {
do {
try UIImagePNGRepresentation(image)?.writeToFile(path, options: .DataWritingAtomic)
} catch {
return
}
}
I'm using this piece of code to try to retrieve the last modified date of a file:
NSError *error = nil;
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath: myFilePath error:&error];
if (attributes != nil) {
NSDate *date = (NSDate*)[attributes objectForKey: NSFileModificationDate];
NSLog(#"Date modiifed: %#", [date description]);
}
else {
NSLog(#"Not found");
}
This works well for files in the main bundle but not if the file is located in a subdirectory of the app's document folder, with myFilePath like this:
/Users/User/Library/Application Support/iPhone Simulator/6.0/Applications/The App ID Number/Documents/mySubdirectory/My Saved File
It keeps returning "not found".
I know the file is there, as I can view it with finder. I also tried removing the spaces in the file name but this had no effect.
The error log says no such file or directory, so it looks like something must've gone wrong when I tried to copy the file to the document directory.
Weird thing is, iterating through the document sub directory with contentsOfDirectoryAtPath shows the file as being present.
I've tried hard-coding the path and retrieving it programmatically, with:
*myFolder = [documentsDirectory stringByAppendingPathComponent:#"myFolder"];
*myFilePath = [myFolder stringByAppendingPathComponent:theFileName];
Can anyone see where I'm going wrong?
Swift 3 solution:
func fileModificationDate(url: URL) -> Date? {
do {
let attr = try FileManager.default.attributesOfItem(atPath: url.path)
return attr[FileAttributeKey.modificationDate] as? Date
} catch {
return nil
}
}
Try this. I had same problem and solved with something like next:
NSURL *fileUrl = [NSURL fileURLWithPath:myFilePath];
NSDate *fileDate;
[fileUrl getResourceValue:&fileDate forKey:NSURLContentModificationDateKey error:&error];
if (!error)
{
//here you should be able to read valid date from fileDate variable
}
hope it helped ;)
Here is a Swift like solution of #zvjerka24 answer:
func lastModified(path: String) -> NSDate? {
let fileUrl = NSURL(fileURLWithPath: path)
var modified: AnyObject?
do {
try fileUrl.getResourceValue(&modified, forKey: NSURLContentModificationDateKey)
return modified as? NSDate
} catch let error as NSError {
print("\(#function) Error: \(error)")
return nil
}
}
If you get the error:
"CFURLCopyResourcePropertyForKey failed because it was passed this URL which has no scheme"
You can try to solve this by appending "file:///" to your NSString file path before converting it to NSURL, it worked in my case.
Can also do:
NSURL* file = ...
NSError* error;`
NSDate *creationDate = [[NSFileManager defaultManager] attributesOfItemAtPath:file.path error:&error].fileCreationDate;
For any file in macOS system we can easily get modification date by using any of below mentioned options:
Way 1:
NSString *path = #"path to file";
NSError *err = nil;
NSDictionary *dic2 = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&err];
NSLog(#"File modification Date:%#", dic2[NSFileModificationDate]);
Way 2:
MDItemRef itemRef = MDItemCreate(kCFAllocatorDefault, (__bridge CFStringRef)path);
NSArray *attributeNames = (__bridge NSArray *)MDItemCopyAttributeNames(itemRef);
NSDictionary *attributes = (__bridge NSDictionary *) MDItemCopyAttributes(itemRef, (__bridge CFArrayRef) attributeNames);
CFDateRef modifDate = MDItemCopyAttribute(itemRef, kMDItemContentModificationDate);
NSDate* modificationDate = (__bridge NSDate*) modifDate;
NSLog(#"Modification Date%#", modificationDate);
You can also print various other attributes provided by MDItem :
NSLog(#"All attributes%#", attributes);
Is it possible to write every NSLog not only into console, but into a file too? I want to prepare this without replacing NSLog into someExternalFunctionForLogging.
It will be real problem to replace all NSLog. Maybe there is possibility for parsing data from console or catching messages?
Option 1: Use ASL
NSLog outputs log to ASL (Apple's version of syslog) and console, meaning it is already writing to a file in your Mac when you use the iPhone simulator. If you want to read it open the application Console.app, and type the name of your application in the filter field. To do the same in your iPhone device, you would need to use the ASL API and do some coding.
Option 2: write to a file
Let's say you are running on the simulator and you don't want to use the Console.app. You can redirect the error stream to a file of your liking using freopen:
freopen([path cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);
See this explanation and sample project for details.
Or you can override NSLog with a custom function using a macro. Example, add this class to your project:
// file Log.h
#define NSLog(args...) _Log(#"DEBUG ", __FILE__,__LINE__,__PRETTY_FUNCTION__,args);
#interface Log : NSObject
void _Log(NSString *prefix, const char *file, int lineNumber, const char *funcName, NSString *format,...);
#end
// file Log.m
#import "Log.h"
#implementation Log
void _Log(NSString *prefix, const char *file, int lineNumber, const char *funcName, NSString *format,...) {
va_list ap;
va_start (ap, format);
format = [format stringByAppendingString:#"\n"];
NSString *msg = [[NSString alloc] initWithFormat:[NSString stringWithFormat:#"%#",format] arguments:ap];
va_end (ap);
fprintf(stderr,"%s%50s:%3d - %s",[prefix UTF8String], funcName, lineNumber, [msg UTF8String]);
[msg release];
}
#end
And import it project wide adding the following to your <application>-Prefix.pch:
#import "Log.h"
Now every call to NSLog will be replaced with your custom function without the need to touch your existing code. However, the function above is only printing to console. To add file output, add this function above _Log:
void append(NSString *msg){
// get path to Documents/somefile.txt
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"logfile.txt"];
// create if needed
if (![[NSFileManager defaultManager] fileExistsAtPath:path]){
fprintf(stderr,"Creating file at %s",[path UTF8String]);
[[NSData data] writeToFile:path atomically:YES];
}
// append
NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:path];
[handle truncateFileAtOffset:[handle seekToEndOfFile]];
[handle writeData:[msg dataUsingEncoding:NSUTF8StringEncoding]];
[handle closeFile];
}
and add this line below fprintf in the _Log function:
append(msg);
File writing also works in your iPhone device, but the file will be created in a directory inside it, and you won't be able to access unless you add code to send it back to your mac, or show it on a view inside your app, or use iTunes to add the documents directory.
There is a far easier approach. Here is the method that redirects NSLog output into a file in application’s Documents folder. This can be useful when you want to test your app outside your development studio, unplugged from your mac.
ObjC:
- (void)redirectLogToDocuments
{
NSArray *allPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [allPaths objectAtIndex:0];
NSString *pathForLog = [documentsDirectory stringByAppendingPathComponent:#"yourFile.txt"];
freopen([pathForLog cStringUsingEncoding:NSASCIIStringEncoding],"a+",stderr);
}
Swift:
// 1. Window > Devices and Simulators
// 2. Select the device
// 3. Select your app and click gear icon
// 4. Download container
// 5. Right click and "view contents"
// 6. Find "yourfile.log" under Downloads
//
// redirectLogToDocuments()
func redirectLogToDocuments() {
let allPaths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentsDirectory = allPaths.first!
let pathForLog = "\(documentsDirectory)/yourfile.log"
freopen(pathForLog.cString(using: String.Encoding.ascii)!, "a+", stdout)
}
After executing this method all output generated by NSLog (ObjC) or print (Swift) will be forwarded to specified file. To get your saved file open Organizer, browse application’s files and save Application Data somewhere in your file system, than simply browse to Documents folder.
I found the simplest solution to the problem: Logging to a file on the iPhone . No need to change any NSLog code or change logger itself, just add these 4 lines to your didFinishLaunchingWithOptions and make sure in your build settings that live release will not have this activated (I added LOG2FILE flag for this).
#ifdef LOG2FILE
#if TARGET_IPHONE_SIMULATOR == 0
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *logPath = [documentsDirectory stringByAppendingPathComponent:#"console.log"];
freopen([logPath cStringUsingEncoding:NSASCIIStringEncoding],"a+",stderr);
#endif
#endif
Translated the answer of JaakL to Swift, posting it here in any case someone else needs it as well
Run this code somewhere in your app, from that moment it stores all NSLog() output to a file, in the documents directory.
let docDirectory: NSString = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0] as NSString
let logpath = docDirectory.stringByAppendingPathComponent("YourFileName.txt")
freopen(logpath.cStringUsingEncoding(NSASCIIStringEncoding)!, "a+", stderr)
Extra: How to find the log-file with Xcode:
You can simply acces the log from Xcode: Windows > Devices > Choose your app > InfoWheelButton > download container.
View the file with finder: click right mouse button on file > show package content > appdata > documents > And there the files are
Swift 4 version
let docDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let logpathe = docDirectory.appendingPathComponent("Logerr.txt")
freopen(logpathe.path.cString(using: .ascii)!, "a+", stderr)
let logpatho = docDirectory.appendingPathComponent("Logout.txt")
freopen(logpatho.path.cString(using: .ascii)!, "a+", stdout)
Output from Swift print() will be in stdout
Ok! firstly, I want to thank Evan-Mulawski.
Here is my solution, maybe it will be helpful for someone:
In AppDelegate I add Function:
void logThis(NSString* Msg, ...)
{
NSArray* findingMachine = [Msg componentsSeparatedByString:#"%"];
NSString* outputString = [NSString stringWithString:[findingMachine objectAtIndex:0]];
va_list argptr;
va_start(argptr, Msg);
for(int i = 1; i < [findingMachine count]; i++) {
if ([[findingMachine objectAtIndex:i] hasPrefix:#"i"]||[[findingMachine objectAtIndex:i] hasPrefix:#"d"]) {
int argument = va_arg(argptr, int); /* next Arg */
outputString = [outputString stringByAppendingFormat:#"%i", argument];
NSRange range;
range.location = 0;
range.length = 1;
NSString* tmpStr = [[findingMachine objectAtIndex:i] stringByReplacingCharactersInRange:range withString:#""];
outputString = [outputString stringByAppendingString:tmpStr];
}
else if ([[findingMachine objectAtIndex:i] hasPrefix:#"#"]) {
id argument = va_arg(argptr, id);
// add argument and next patr of message
outputString = [outputString stringByAppendingFormat:#"%#", argument];
NSRange range;
range.location = 0;
range.length = 1;
NSString* tmpStr = [[findingMachine objectAtIndex:i] stringByReplacingCharactersInRange:range withString:#""];
outputString = [outputString stringByAppendingString:tmpStr];
}
else if ([[findingMachine objectAtIndex:i] hasPrefix:#"."]) {
double argument = va_arg(argptr, double);
// add argument and next patr of message
outputString = [outputString stringByAppendingFormat:#"%f", argument];
NSRange range;
range.location = 0;
range.length = 3;
NSString* tmpStr = [[findingMachine objectAtIndex:i] stringByReplacingCharactersInRange:range withString:#""];
outputString = [outputString stringByAppendingString:tmpStr];
}
else if ([[findingMachine objectAtIndex:i] hasPrefix:#"f"]) {
double argument = va_arg(argptr, double);
// add argument and next patr of message
outputString = [outputString stringByAppendingFormat:#"%f", argument];
NSRange range;
range.location = 0;
range.length = 1;
NSString* tmpStr = [[findingMachine objectAtIndex:i] stringByReplacingCharactersInRange:range withString:#""];
outputString = [outputString stringByAppendingString:tmpStr];
}
else {
outputString = [outputString stringByAppendingString:#"%"];
outputString = [outputString stringByAppendingString:[findingMachine objectAtIndex:i]];
}
}
va_end(argptr);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString * filePath = [[paths objectAtIndex:0]stringByAppendingPathComponent:#"logFile.txt"];
NSError* theError = nil;
NSString * fileString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&theError];
if (theError != nil||[fileString length]==0) {
fileString = [NSString stringWithString:#""];
}
fileString = [fileString stringByAppendingFormat:#"\n%#",outputString];
if(![fileString writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&theError])
{
NSLog(#"Loging problem");
}
NSLog(#"%#",outputString);
}
and, then use "replace for all" NSLog -> logThis.
This code is adapted for my app. It can be expand for different needs.
Thnks for help.
This is what I use and works well:
http://parmanoir.com/Redirecting_NSLog_to_a_file
Hope it helps.
I'll just post it here for the sake of the content
- (BOOL)redirectNSLog {
// Create log file
[#"" writeToFile:#"/NSLog.txt" atomically:YES encoding:NSUTF8StringEncoding error:nil];
id fileHandle = [NSFileHandle fileHandleForWritingAtPath:#"/NSLog.txt"];
if (!fileHandle) return NSLog(#"Opening log failed"), NO;
[fileHandle retain];
// Redirect stderr
int err = dup2([fileHandle fileDescriptor], STDERR_FILENO);
if (!err) return NSLog(#"Couldn't redirect stderr"), NO; return YES;
}
Swift 2.0 :
Add these to Appdelegate didFinishLaunchWithOptions.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
var paths: Array = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let documentsDirectory: String = paths[0]
let logPath: String = documentsDirectory.stringByAppendingString("/console.log")
if (isatty(STDERR_FILENO) == 0)
{
freopen(logPath, "a+", stderr)
freopen(logPath, "a+", stdin)
freopen(logPath, "a+", stdout)
}
print(logPath)
return true
}
Accessing console.log :
When the log path is printed on Xcode Log Area, select the path, right click, choose Services- Reaveal in Finder and open the file console.log
I worked a little bit with the answer of Alvin George.
To keep the log file sizes under control I implemented (quick and dirty) a "10 generations of log files" solution and add a func to delete them later on
Every time the app starts, it will generate a new log file with an index "0". The exiting file(s) will be renamed with an index higher than before. Index "10" will be deleted.
So, each start gives you a new log file, maximum 10 generations
Might not be the most elegant way to do it, but works for me during the last weeks very good, as I need some longtime logging "off the mac"
// -----------------------------------------------------------------------------------------------------------
// redirectConsoleToFile()
//
// does two things
// 1) redirects "stderr", "stdin" and "stdout" to a logfile
// 2) deals with old/existing files to keep up to 10 generations of the logfiles
// tested with IOS 9.4 and Swift 2.2
func redirectConsoleToFile() {
// Instance of a private filemanager
let myFileManger = NSFileManager.defaultManager()
// the path of the documnts directory of the app
let documentDirectory: String = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first!
// maximum number of logfiles
let maxNumberOfLogFiles: Int = 10
// look if the max number of files already exist
var logFilePath : String = documentDirectory.stringByAppendingString("/Console\(maxNumberOfLogFiles).log")
var FlagOldFileNoProblem: Bool = true
if myFileManger.fileExistsAtPath(logFilePath) == true {
// yes, max number of files reached, so delete the oldest one
do {
try myFileManger.removeItemAtPath(logFilePath)
} catch let error as NSError {
// something went wrong
print("ERROR deleting old logFile \(maxNumberOfLogFiles): \(error.description)")
FlagOldFileNoProblem = false
}
}
// test, if there was a problem with the old file
if FlagOldFileNoProblem == true {
// loop over all possible filenames
for i in 0 ..< maxNumberOfLogFiles {
// look, if an old file exists, if so, rename it with an index higher than before
logFilePath = documentDirectory.stringByAppendingString("/Console\((maxNumberOfLogFiles - 1) - i).log")
if myFileManger.fileExistsAtPath(logFilePath) == true {
// there is an old file
let logFilePathNew = documentDirectory.stringByAppendingString("/WayAndSeeConsole\(maxNumberOfLogFiles - i).log")
do {
// rename it
try myFileManger.moveItemAtPath(logFilePath, toPath: logFilePathNew)
} catch let error as NSError {
// something went wrong
print("ERROR renaming logFile: (i = \(i)), \(error.description)")
FlagOldFileNoProblem = false
}
}
}
}
// test, if there was a problem with the old files
if FlagOldFileNoProblem == true {
// No problem so far, so try to delete the old file
logFilePath = documentDirectory.stringByAppendingString("/Console0.log")
if myFileManger.fileExistsAtPath(logFilePath) == true {
// yes, it exists, so delete it
do {
try myFileManger.removeItemAtPath(logFilePath)
} catch let error as NSError {
// something went wrong
print("ERROR deleting old logFile 0: \(error.description)")
}
}
}
// even if there was a problem with the files so far, we redirect
logFilePath = documentDirectory.stringByAppendingString("/Console0.log")
if (isatty(STDIN_FILENO) == 0) {
freopen(logFilePath, "a+", stderr)
freopen(logFilePath, "a+", stdin)
freopen(logFilePath, "a+", stdout)
displayDebugString(DEBUG_Others, StringToAdd: "stderr, stdin, stdout redirected to \"\(logFilePath)\"")
} else {
displayDebugString(DEBUG_Others, StringToAdd: "stderr, stdin, stdout NOT redirected, STDIN_FILENO = \(STDIN_FILENO)")
}
}
// -----------------------------------------------------------------------------------------------------------
// cleanupOldConsoleFiles()
//
// delete all old consolfiles
func cleanupOldConsoleFiles() {
// Instance of a private filemanager
let myFileManger = NSFileManager.defaultManager()
// the path of the documnts directory of the app
let documentDirectory: String = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first!
// maximum number of logfiles
let maxNumberOfLogFiles: Int = 10
// working string
var logFilePath: String = ""
// loop over all possible filenames
for i in 0 ... maxNumberOfLogFiles {
// look, if an old file exists, if so, rename it with an index higher than before
logFilePath = documentDirectory.stringByAppendingString("/Console\(i).log")
if myFileManger.fileExistsAtPath(logFilePath) == true {
// Yes, file exist, so delete it
do {
try myFileManger.removeItemAtPath(logFilePath)
} catch let error as NSError {
// something went wrong
print("ERROR deleting old logFile \"\(i)\": \(error.description)")
}
}
}
}