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 have a function that is supposed to create a batch of files, write them to a directory, zip that directory, and attach it to an email.
When I run it, it tells me that it has written 299 files, but when I open up the zip folder precisely 50 are missing
Here is the code. I thought about trimming it down, but I was afraid of removing something that might help someone spot the problem:
func exportAllData() {
var totalExports = 0
// fetch all the data first
// 1 Describe what you want
let fetchRequest = NSFetchRequest(entityName: "Fish")
let sortDescriptor = NSSortDescriptor(key: "time", ascending: false)
fetchRequest.sortDescriptors = [sortDescriptor]
// 2 Get it!
do {
let allCatches = try managedContext!.executeFetchRequest(fetchRequest) as? [NSManagedObject]
totalExports = allCatches!.count
// Create a folder to store the catches in
// path to where data will be written
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
let applicationDocumentsDirectory:NSURL = urls[urls.count-1]
let destinationURL = applicationDocumentsDirectory.URLByAppendingPathComponent("filesToExport", isDirectory: true)
do { try NSFileManager.defaultManager().createDirectoryAtURL(destinationURL, withIntermediateDirectories: false, attributes: nil)
print("created directory")
var currentFile = 0
// Write each fish to file
for fish in allCatches! {
currentFile+= 1
// convert NSManagedObject into a Dictionary, serialize it to NSData, and write it to file
let keys = fish.entity.propertiesByName.keys.array
let dictionary = fish.dictionaryWithValuesForKeys(keys)
let data = NSKeyedArchiver.archivedDataWithRootObject(dictionary)
let fileName = "catchNumber\(currentFile)"
let fileURL = destinationURL.URLByAppendingPathComponent(fileName)
let success = data.writeToFile(fileURL.path!, atomically: false)
if success { print("successfully exported: "+fileName) } else { print("FAILED to export: "+fileName) }
}
// DEBUG
do {
let contents = try NSFileManager.defaultManager().contentsOfDirectoryAtPath(destinationURL.path!)
print(contents)
} catch {
print(error)
}
// After all the fish have been written to file, zip the file
let zipURL = applicationDocumentsDirectory.URLByAppendingPathComponent("exported.zip")
Main.createZipFileAtPath(zipURL.path!, withContentsOfDirectory: destinationURL.path!)
// Load the zip file up as data and send it out in an email
let zipData = NSData(contentsOfURL: zipURL)
let picker = MFMailComposeViewController()
picker.mailComposeDelegate = self
picker.setSubject("Shared Catch Log! (\(totalExports) entries)")
picker.setMessageBody("Open the attachment to this email on an iOS device with Catch Stats installed on it to import all catch into your Catch Log!", isHTML: false)
picker.addAttachmentData(zipData!, mimeType: "applcation/CatchStats", fileName: "CatchStatsLog.csl")
presentViewController(picker, animated: true, completion: nil)
// If failed to created directory
} catch {
print("directory already existed")
}
// If failed to execute fetch request
} catch {
print(error)
self.navigationController?.popViewControllerAnimated(true)
}
}
Now, at the //Debug when I check the contents of the folder I'm about to zip, it has 299 files numbered from 1 to 299 in it.
But when I email the file out and open it up, 50-99 are all missing. Can anybody offer me some advice?
I plucked this code out of ZipArchive's Main.m file. This is what it's doing.
+ (BOOL)createZipFileAtPath:(NSString *)path
withContentsOfDirectory:(NSString *)directoryPath {
return [self createZipFileAtPath:path withContentsOfDirectory:directoryPath keepParentDirectory:NO];
}
+ (BOOL)createZipFileAtPath:(NSString *)path
withContentsOfDirectory:(NSString *)directoryPath
keepParentDirectory:(BOOL)keepParentDirectory {
BOOL success = NO;
NSFileManager *fileManager = nil;
Main *zipArchive = [[Main alloc] initWithPath:path];
if ([zipArchive open]) {
// Use a local file manager (queue/thread compatibility)
fileManager = [[NSFileManager alloc] init];
NSDirectoryEnumerator *directoryEnumerator = [fileManager enumeratorAtPath:directoryPath];
NSString *fileName;
while ((fileName = [directoryEnumerator nextObject])) {
BOOL isDirectory;
NSString *fullFilePath = [directoryPath stringByAppendingPathComponent:fileName];
[fileManager fileExistsAtPath:fullFilePath isDirectory:&isDirectory];
if (!isDirectory) {
if (keepParentDirectory) {
fileName = [[directoryPath lastPathComponent] stringByAppendingPathComponent:fileName];
}
[zipArchive writeFileAtPath:fullFilePath withFileName:fileName];
} else {
if (0 == [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:fullFilePath error:nil].count) {
NSString *temporaryName = [fullFilePath stringByAppendingPathComponent:#".DS_Store"];
[#"" writeToFile:temporaryName atomically:YES encoding:NSUTF8StringEncoding error:nil];
[zipArchive writeFileAtPath:temporaryName withFileName:[fileName stringByAppendingPathComponent:#".DS_Store"]];
[[NSFileManager defaultManager] removeItemAtPath:temporaryName error:nil];
}
}
}
success = [zipArchive close];
}
return success;
}
I found a solution, but it is more of a work around. After a lot of testing, I realized that ZipArchive would fail to zip a folder with an more than 250 files in it. It didn't matter what the files were named or the order they were in.
Instead of serializing each NSDictionary and writing it to file, I created an array of NSDictionaries, and serialized that instead.That way ZipArchive only had to zip one file, and it seems to be working fine now.
I have an app that saves and uses data from a plist file. I'm working on a WatchKit extension that needs to access the same plist file to display data and save to the file. I know I need to be using app groups but I don't know how to share the plist between the iOS app and the WatchKit extension.
Here is how I'm saving to the plist in the iOS app currently.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [[paths objectAtIndex:0]stringByAppendingPathComponent:#"locations.plist"];
BOOL fileExists = [fileManager fileExistsAtPath:docPath];
NSError *error = nil;
if (!fileExists) {
NSString *strSourcePath = [[NSBundle mainBundle]pathForResource:#"locations" ofType:#"plist"];
[fileManager copyItemAtPath:strSourcePath toPath:docPath error:&error];
}
NSString *path = docPath;
NSMutableArray *plistArray = [[NSMutableArray alloc]initWithContentsOfFile:path];
NSDictionary *locationDictionary = [NSDictionary dictionaryWithObjectsAndKeys:self.locationNameTextField.text, #"locationName", latString, #"latitude", longString, #"longitude", nil];
[plistArray insertObject:locationDictionary atIndex:0];
[plistArray writeToFile:docPath atomically:YES];
Once you've setup your app group (in both your primary iPhone app and the Watch Extension), you can get the path to the shared folder:
NSURL *groupContainerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:#"YourAppGroupSuiteName"];
NSString *groupContainerPath = [groupContainerURL path];
You can then use the groupContainerPath to build your docPath. Otherwise, your code should work as-is.
I was able to successfully use plist data from my main existing iPhone app in my WatchKit app using Swift.
There are two steps:
Enable WatchKit App Extension Target Membership for each plist you want to use. Click on the plist, then:
Here's the Swift code I used to read the plist, which has "id" and "name" fields.
func valueFromPlist(value: Int, file: String) -> String? {
if let plistpath = NSBundle.mainBundle().pathForResource(file as String, ofType: "plist") {
if let entries = NSArray(contentsOfFile: plistpath) as Array? {
var entry = Dictionary<String, Int>()
for entry in entries {
if let id = entry.objectForKey("id") as? Int {
if id == value {
if let name = entry.objectForKey("name") as? String {
return name
}
}
}
}
}
}
return nil
}
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)")
}
}
}
}