How to fix leak on [NSString availableStringEncodings]? - ios

When profiling the project, leaks were found and located to [NSString availableStringEncodings] in below function. No idea how to fix it :
- (NSString *)stringFromArchive:(NSString *)fileName inDirectory:(NSString *)folderName
{
NSString *rStr = #"";
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *folderPath = [documentsDirectory stringByAppendingPathComponent:folderName];
NSString *filePath = [folderPath stringByAppendingPathComponent:fileName];
NSFileManager * fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:filePath]) {
NSError *error;
const NSStringEncoding *encodings = [NSString availableStringEncodings];
NSStringEncoding encoding1;
while ((encoding1 = *encodings++) != 0)
{
rStr = [NSString stringWithContentsOfFile:filePath encoding:encoding1 error:&error];
if (rStr == nil) {
NSLog(#"error: %#", error);
}else{
break;
}
}
}
return rStr;
}
enter image description here

As seen often and it is very common to find Object declarations without initial value but found out in hard learning curve that a lot of leaks I found where bound to the easy peasy practice of unitialised declarations...
in Short: proper declaration helps the debugger/instruments as well.
NSError *error;   very often seen. Can even leak, at least instruments finds it suspicious
NSError *error = nil;   better, and also not NULL (which leaks for sure).
NSStringEncoding encoding1;
is an ENUM of NSUInteger and you seem to try to iterate thru all encodings from the bottom up until you find a match that properly reads the file.. then you may want to start not with an undefined value but with 1 which is the most simple one (NSASCIIStringEncoding). Which is why you use break; and compare against !=0, so it iterates up to the end of the zero-terminated list (tells you also why the enum does not start with 0 but NSASCIIStringEncoding==1).
and also when you use const NSStringEncoding *var and then *var++ what is increased? The pointers address or the value that was stored at that address.., *(var++) vs (*var)++. Both could work fine but one is more risky the other iterates thru encodings that do not exist. So guessing you want to go thru the list of encodings up to its zero-termination, take the values and try reading...
NSString *rStr = #"";
NSError *error = nil;
const NSStringEncoding *a = [NSString availableStringEncodings];
NSStringEncoding b = NSASCIIStringEncoding;
// increase addr of a, take value to b
// and check for b not 0==end-of-list
while ( (b=*(a++))!=0 ) {
//iterate until encoding allows reading successful..
rStr = [NSString stringWithContentsOfFile:#"yourfile.." encoding:b error:&error];
if (rStr != nil) break; // rStr has content, so end loop
// print reason on any missed trial
NSLog(#"error: %#", error);
}
edit: made a leak on purpose just for fun if Instruments will detect it, the leaking code i used...
extern "C" int leakingCppFunction(const char * s)
{
Example *e = new Example(s); //e is never deleted, so it leaks.
return e->getLen();
}
clearly finds this leak and none in the encoding iteration discussed above. So guessing your extension (NSStringOtherEncodings) is causing availableStringEncodings to leak.

Related

Is there a more efficient way to append a log to a log file than this?

I don't like what I have written here. The entire contents of the log file is pulled into memory, and I was wondering if there is a way to append to whatever is there, without pulling it out.
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *filePath = [documentsPath stringByAppendingPathComponent:#"Log"];
NSMutableString *content = [#"" mutableCopy];
NSMutableString *existing = [[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil] mutableCopy];
[content appendString:existing];
[content appendString:#"\n"];
[content appendString:[user logDetails]];
[[content copy] writeToFile:filePath atomically:NO encoding:NSUTF8StringEncoding error:nil];
That code is very inefficient, but this is because file manipulation in general is poor when using the Foundation classes.
I would recommend using the C-API functions fopen(), fprintf(), etc instead. Simply write the UTF-8 strings from NSString objects and logging will be much quicker and won't endanger the memory footprint of the app.
If you mean that you want to write custom stuff to the end of a file, then yes, see the comments from #Amadan. If you mean that you want a proper logging system, with log rotation and asynchronous logging and all sorts of other things, then check out https://github.com/CocoaLumberjack/CocoaLumberjack. More love has gone into that than you will ever dream of investing yourself.
Ignore #trojanfoe's answer -- you're just making more work for yourself if you drop to the C APIs, for very little gain.
I use this in my FileSystem class:
+ (BOOL)appendData:(NSData *)_data toFile:(NSString *)_path
{
if( !_path || !_data )
return NO;
// TODO : check before for file existence
// open for binary append
FILE *_file = fopen([_path UTF8String], "ab");
if( !_file )
return NO;
size_t _nbObjToWrite = [_data length];
size_t _nbObjWritten = fwrite([_data bytes], 1, _nbObjToWrite, _file); // write [_data length] objectq of length 1 from [_data bytes] buffer in _file
if( _nbObjWritten != _nbObjToWrite ) {
fclose( _file );
return NO;
}
fclose( _file ); // will automatically flush the stream
return YES;
}
And then, you can call:
[MyClass appendData:[<myLogStringToAdd> dataUsingEncoding:NSUTF8StringEncoding] toFile:#"<pathToMyFile>"];

Proper way to giving name to file when [NSData writeToFile:] used

I'm using that code to save downloaded videos from internet to application document folder:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *save_it = [documentsDirectory stringByAppendingPathComponent:video_filename];
NSError *error;
BOOL success = [fileData writeToFile:save_it options:0 error:&error];
if (!success) {
NSLog(#"writeToFile failed with error %#", error);
}
it works, but if there is a slash "/" in the video_filename it breaks because of slash is directory seperator, I know.
For example when video_filename is : Best Video / Best Song Ever.3gpp , log says:
{NSFilePath=/Users/Apple/Library/Developer/CoreSimulator/Devices/5A7D36F5-6EDB-495D-9E8E-B9EB22E5357C/data/Containers/Data/Application/B1D0AC48-D84C-4A0D-9F09-08BF4C45DD32/Documents/Best Video / Best Song Ever.3gpp, NSUnderlyingError=0x7d339430 "The operation couldn’t be completed. No such file or directory"}
I don't know is there any other special character that will make crashing,
So what is the best way of cleaning these special characters from nsstring ?
We can make SEO friendly urls in PHP, I'm searching a function like that to do this.
The first problem I see here is that your file path includes some spaces. in the example you gave, the value of video_filename variable is "Best Video / Best Song Ever.3gpp" which includes spaces around the slash. You first have to delete the spaces, this might help you do that:
NSArray *components = [video_filename componentsSeparatedByString:#"/"];
for (NSInteger i = 0, i < components.count, ++i) {
NSString *string = components[i];
string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
components[i] = string;
}
NSString *path = [components componentsJoinedByString:#"/"];
If I understood correctly, your video_filename might be either in the form xxx.3gpp or yyy/xxx.3gpp. If it's the format of yyyy/xxxx.3gpp, you first have to create a directory named yyyy and then save the file to that directory.
This might help you do that:
- (void)createDirectory:(NSString *)directoryName
atFilePath:(NSString *)filePath
{
NSString *filePathAndDir = [filePath
stringByAppendingPathComponent:directoryName];
NSError *error;
if (![[NSFileManager defaultManager] createDirectoryAtPath:filePathAndDir
withIntermediateDirectories:NO
attributes:nil
error:&error]) {
NSLog(#"Create directory error: %#", error);
}
}
and the way you would use this is
[self createDirectory:components[0] atFilePath:documentsDirectory];
hope this helps!
So if your filename is actually "Best Video / Best Song Ever.3gpp" I am sorry but nothing easy comes to mind.
Now if Best Video is a folder where you will save your file you can use :
+(NSString*) getPathToFolder:(NSString*) folderName {
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *folderPath = [documentsPath stringByAppendingPathComponent:folderName];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:folderPath]) {
NSLog(#"Creating a new folder at\n%#", folderPath) ;
[fileManager createDirectoryAtPath:folderPath withIntermediateDirectories:NO attributes:nil error:nil];
}
return folderPath ;
}
This will check if your folder exist or not, if it does not exists then it will create it.
it will return the path you will want to use to save your file.
Now regarding the naming of the files, using spaces is highly unadvisable, I suggest using :
NSString* pathWITHSpaces ;
NSString* pathWithoutSpaces = [pathWITHSpaces stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
Hope this helps a bit

iOS - App crash without error during loop process

during a loop process, my App crash without error. The array count is equal to 175260. With profiler I don't have leaks, so I don't know why the App exit, maybe the CPU usage 100% during a lot of time?
Thank you for your help.
Just this code following crash the App :
for(unsigned int i = 0; i <14;i++)
{
if(findSensor[i]==YES)
{
for(unsigned int j = 1; j <[array count];j++)
{
#autoreleasepool {
if([[[[array objectAtIndex:j] componentsSeparatedByString:#";"] objectAtIndex:0] isEqualToString:[NSString stringWithFormat:#"%d",10*(i+1)]])
{
//Code here
}
}
}
}
}
The full code is :
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fileName = [NSString stringWithFormat:#"%#/%#",documentsDirectory,[ibNavSettings interfaceSettings].selectedFileToDataBase];
NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:fileName];
NSFileHandle *output = [NSFileHandle fileHandleForReadingAtPath:[NSString stringWithFormat:#"%#/%#10",documentsDirectory,[ibNavSettings interfaceSettings].selectedFileToDataBase]];
if(output == nil)
{
NSManagedObjectContext *context = [self managedObjectContext];
_recordlocal = [NSEntityDescription insertNewObjectForEntityForName:#"RECORD" inManagedObjectContext:context];
_recordlocal.date = [ibNavSettings interfaceSettings].selectedFileToDataBase;
NSData *inputData = [NSData dataWithData:[fh readDataToEndOfFile]];
NSString *inputString = [[NSString alloc] initWithData:inputData encoding:NSUTF8StringEncoding];
NSArray *array = [[NSArray alloc] initWithArray:[inputString componentsSeparatedByString:#"\n"]];
for(unsigned int i = 0; i <14;i++)
{
if(findSensor[i]==YES)
{
[[NSFileManager defaultManager] createFileAtPath:[NSString stringWithFormat:#"%#/%#%d",documentsDirectory,[ibNavSettings interfaceSettings].selectedFileToDataBase,10*(i+1)] contents:nil attributes:nil];
NSMutableString *saveString = [[NSMutableString alloc] init];
int count = 0;
for(unsigned int j = 1; j <[array count];j++)
{
#autoreleasepool {
if([[[[array objectAtIndex:j] componentsSeparatedByString:#";"] objectAtIndex:0] isEqualToString:[NSString stringWithFormat:#"%d",10*(i+1)]])
{
[saveString appendString:[array objectAtIndex:j]];
[saveString appendString:#"\n"];
if(i == 0)
count++;
progress++;
pourcent = progress/total;
load = pourcent*100;
if(load%5==0)
[self performSelectorInBackground:#selector(changeUI:)withObject:[NSNumber numberWithFloat:(pourcent)]];
}
}
}
[saveString writeToFile:[NSString stringWithFormat:#"%#/%#%d",documentsDirectory,[ibNavSettings interfaceSettings].selectedFileToDataBase,10*(i+1)] atomically:YES encoding:NSUTF8StringEncoding error:nil];
if(i == 0)
_recordlocal.count = [[NSNumber alloc] initWithInt:(count/50)];
}
}
_recordlocal.load = [[NSNumber alloc] initWithBool:YES];
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Core data error %#, %#", error, [error userInfo]);
abort();
}
I would guess that your app is crashing without a readable exception because it is running out of available RAM, especially since you indicated that it is running through a large number of iterations.
For a test, I would recommend doing what Rikkles suggests with the autorelease pool. In addition, since the value of i (and as a result the comparison string) rarely changes, I would create that string outside the j loop as well. This would avoid the creation of a lot of extra strings laying around.
Beyond that, since it appears that you are looking for a string at the beginning of a string that is delimited by a semicolon, I would recommend instead of doing componentsSeparatedByString and then examining element zero that you use the NSString method hasPrefix to check for the condition you are looking for.
Here is an example:
for(unsigned int i = 0; i <14;i++)
{
NSString *searchString = [NSString stringWithFormat:#"%d;", 10*(i+1)];
if(findSensor[i]==YES)
{
for(unsigned int j = 1; j <[array count];j++)
{
if([[array objectAtIndex:j] hasPrefix:searchString])
{
//Code here
}
}
}
}
(I hope this compiles and runs, if it doesn't it should require more than minor tweaks. I am away from my Mac right now.)
If this doesn't help, then something going on inside //Code here must be the culprit.
Why are you creating [array count] autoreleasepools? What's the point of creating so many of them? It could crash because of that. Put the #autoreleasepool outside the for loop.
The only reason I could think that you would do that is if you create so many transient objects inside each iteration of the for loop that you'd want to get rid of them as soon as you got out of the iteration. But there are other ways to do that, including reusing those objects within each iteration.
First suggestion
Just use fast enumeration for the inner loop, you aren't actually using the index 'j' for anything
https://developer.apple.com/library/mac/documentation/General/Conceptual/DevPedia-CocoaCore/Enumeration.html
Second suggestion
Put some NSLog's in place, it will slow everything down, but you need to figure out what point you are failing at. That will help point everyone in the right direction.
Third suggestion
Actually use NSError objects and output their value if an error is thrown:
NSError *writeError = nil;
[saveString writeToFile:[NSString stringWithFormat:#"%#/%#%d",documentsDirectory,[ibNavSettings interfaceSettings].selectedFileToDataBase,10*(i+1)]
atomically:YES
encoding:NSUTF8StringEncoding
error:&writeError];
if(error != nil) NSLog(#"error writing file: %#", [[writeError userInfo]description]);
Fourth suggestion
You appear to try to be updating the UI from a background thread. This will not work or will cause a crash. UI code can only be called from a main thread. So dont do this:
[self performSelectorInBackground:#selector(changeUI:)withObject:[NSNumber numberWithFloat:(pourcent)]];
If you are already on a background thread this will probably crash because you are creating threads on threads on threads. You instead would want to call:
[self performSelectorOnMainThread:#selector(changeUI:)withObject:[NSNumber numberWithFloat:(pourcent)]];
Fifth suggestion
You may be going over the maximum length for NSString (it's big but I did it once on accident before). You should probably just be appending the file on each iteration of the loop instead, so you don't have an ever growing NSMutableString:
NSString *path = [NSString stringWithFormat:#"%#/%#%d",documentsDirectory,[ibNavSettings interfaceSettings].selectedFileToDataBase,10*(i+1)]
NSFileHandle *fh = [NSFileHandle fileHandleForWritingAtPath:filePath];
NSData *newLine = [#"\n" dataUsingEncoding:NSUTF8StringEncoding];
for(NSString *rowString in array)
{
if([[[rowString componentsSeparatedByString:#";"] objectAtIndex:0] isEqualToString:[NSString stringWithFormat:#"%d",10*(i+1)]])
{
NSData *stringData = [rowString dataUsingEncoding:NSUTF8StringEncoding];
[fh truncateFileAtOffset:[fh seekToEndOfFile]];
[fh writeData:stringData];
[fh truncateFileAtOffset:[fh seekToEndOfFile]];
[fh writeData:newLine];
if(i == 0)
count++;
progress++;
pourcent = progress/total;
load = pourcent*100;
if(load%5==0)
[self performSelectorOnMainThread:#selector(changeUI:)withObject:[NSNumber numberWithFloat:(pourcent)]];
}
}
}
And this has the added benefit of helping you ditch the autoreleasepools
This was invalid
If your array does in fact have 175260 rows, that is probably your issue. You are looping using unsigned int as your index var. Unsigned ints in c only have a max value of 65535. Use an unsigned long int, max 4294967295.

Getting a -1 from open on iOS, why?

The code is copied below. I am using the documents directory because I know you can write outside of your apps sandbox.
I also compare the string I was using to determine the path to one created by the NSFileManager and they are the same. What do you all think?
- (NSString *)documentsDirectory
{
NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
return [paths objectAtIndex:0];
}
- (void) whateverFunction{
NSString *memfileName = #"memmapfile.map";
NSString *filePath = [[self documentsDirectory] stringByAppendingPathComponent:memfileName];
NSLog(#"Here is the filePath: %#", filePath);
NSLog(#"Other version: %s", [[NSFileManager defaultManager] fileSystemRepresentationWithPath:filePath]);
int memFD = open([filePath cStringUsingEncoding:NSASCIIStringEncoding], O_RDWR);
NSLog(#"I am getting -1 for memFD: %d", memFD);
}
I'd guess your file doesn't exist yet, so of course you can't open it. You should add O_CREAT to your open flags so that it gets created if it doesn't exist:
int memFD = open([filePath cStringUsingEncoding:NSASCIIStringEncoding], O_RDWR|O_CREAT);

Getting a list of files in a directory with a glob

For some crazy reason I can't find a way to get a list of files with a glob for a given directory.
I'm currently stuck with something along the lines of:
NSString *bundleRoot = [[NSBundle mainBundle] bundlePath];
NSArray *dirContents = [[NSFileManager defaultManager]
directoryContentsAtPath:bundleRoot];
..and then stripping out the stuff I don't want, which sucks. But what I'd really like is to be able to search for something like "foo*.jpg" instead of asking for the entire directory, but I've not been able to find anything like that.
So how the heck do you do it?
You can achieve this pretty easily with the help of NSPredicate, like so:
NSString *bundleRoot = [[NSBundle mainBundle] bundlePath];
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *dirContents = [fm contentsOfDirectoryAtPath:bundleRoot error:nil];
NSPredicate *fltr = [NSPredicate predicateWithFormat:#"self ENDSWITH '.jpg'"];
NSArray *onlyJPGs = [dirContents filteredArrayUsingPredicate:fltr];
If you need to do it with NSURL instead it looks like this:
NSURL *bundleRoot = [[NSBundle mainBundle] bundleURL];
NSArray * dirContents =
[fm contentsOfDirectoryAtURL:bundleRoot
includingPropertiesForKeys:#[]
options:NSDirectoryEnumerationSkipsHiddenFiles
error:nil];
NSPredicate * fltr = [NSPredicate predicateWithFormat:#"pathExtension='jpg'"];
NSArray * onlyJPGs = [dirContents filteredArrayUsingPredicate:fltr];
This works quite nicely for IOS, but should also work for cocoa.
NSString *bundleRoot = [[NSBundle mainBundle] bundlePath];
NSFileManager *manager = [NSFileManager defaultManager];
NSDirectoryEnumerator *direnum = [manager enumeratorAtPath:bundleRoot];
NSString *filename;
while ((filename = [direnum nextObject] )) {
//change the suffix to what you are looking for
if ([filename hasSuffix:#".data"]) {
// Do work here
NSLog(#"Files in resource folder: %#", filename);
}
}
What about using NSString's hasSuffix and hasPrefix methods? Something like (if you're searching for "foo*.jpg"):
NSString *bundleRoot = [[NSBundle mainBundle] bundlePath];
NSArray *dirContents = [[NSFileManager defaultManager] directoryContentsAtPath:bundleRoot];
for (NSString *tString in dirContents) {
if ([tString hasPrefix:#"foo"] && [tString hasSuffix:#".jpg"]) {
// do stuff
}
}
For simple, straightforward matches like that it would be simpler than using a regex library.
Very Simplest Method:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSFileManager *manager = [NSFileManager defaultManager];
NSArray *fileList = [manager contentsOfDirectoryAtPath:documentsDirectory
error:nil];
//--- Listing file by name sort
NSLog(#"\n File list %#",fileList);
//---- Sorting files by extension
NSArray *filePathsArray =
[[NSFileManager defaultManager] subpathsOfDirectoryAtPath:documentsDirectory
error:nil];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF EndsWith '.png'"];
filePathsArray = [filePathsArray filteredArrayUsingPredicate:predicate];
NSLog(#"\n\n Sorted files by extension %#",filePathsArray);
Unix has a library that can perform file globbing operations for you. The functions and types are declared in a header called glob.h, so you'll need to #include it. If open up a terminal an open the man page for glob by typing man 3 glob you'll get all of the information you need to know to use the functions.
Below is an example of how you could populate an array the files that match a globbing pattern. When using the glob function there are a few things you need to keep in mind.
By default, the glob function looks for files in the current working directory. In order to search another directory you'll need to prepend the directory name to the globbing pattern as I've done in my example to get all of the files in /bin.
You are responsible for cleaning up the memory allocated by glob by calling globfree when you're done with the structure.
In my example I use the default options and no error callback. The man page covers all of the options in case there's something in there you want to use. If you're going to use the above code, I'd suggest adding it as a category to NSArray or something like that.
NSMutableArray* files = [NSMutableArray array];
glob_t gt;
char* pattern = "/bin/*";
if (glob(pattern, 0, NULL, &gt) == 0) {
int i;
for (i=0; i<gt.gl_matchc; i++) {
[files addObject: [NSString stringWithCString: gt.gl_pathv[i]]];
}
}
globfree(&gt);
return [NSArray arrayWithArray: files];
Edit: I've created a gist on github that contains the above code in a category called NSArray+Globbing.
You need to roll your own method to eliminate the files you don't want.
This isn't easy with the built in tools, but you could use RegExKit Lite to assist with finding the elements in the returned array you are interested in. According to the release notes this should work in both Cocoa and Cocoa-Touch applications.
Here's the demo code I wrote up in about 10 minutes. I changed the < and > to " because they weren't showing up inside the pre block, but it still works with the quotes. Maybe somebody who knows more about formatting code here on StackOverflow will correct this (Chris?).
This is a "Foundation Tool" Command Line Utility template project. If I get my git daemon up and running on my home server I'll edit this post to add the URL for the project.
#import "Foundation/Foundation.h"
#import "RegexKit/RegexKit.h"
#interface MTFileMatcher : NSObject
{
}
- (void)getFilesMatchingRegEx:(NSString*)inRegex forPath:(NSString*)inPath;
#end
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// insert code here...
MTFileMatcher* matcher = [[[MTFileMatcher alloc] init] autorelease];
[matcher getFilesMatchingRegEx:#"^.+\\.[Jj][Pp][Ee]?[Gg]$" forPath:[#"~/Pictures" stringByExpandingTildeInPath]];
[pool drain];
return 0;
}
#implementation MTFileMatcher
- (void)getFilesMatchingRegEx:(NSString*)inRegex forPath:(NSString*)inPath;
{
NSArray* filesAtPath = [[[NSFileManager defaultManager] directoryContentsAtPath:inPath] arrayByMatchingObjectsWithRegex:inRegex];
NSEnumerator* itr = [filesAtPath objectEnumerator];
NSString* obj;
while (obj = [itr nextObject])
{
NSLog(obj);
}
}
#end
I won't pretend to be an expert on the topic, but you should have access to both the glob and wordexp function from objective-c, no?
stringWithFileSystemRepresentation doesn't appear to be available in iOS.
Swift 5
This works for cocoa
let bundleRoot = Bundle.main.bundlePath
let manager = FileManager.default
let dirEnum = manager.enumerator(atPath: bundleRoot)
while let filename = dirEnum?.nextObject() as? String {
if filename.hasSuffix(".data"){
print("Files in resource folder: \(filename)")
}
}
Swift 5 for cocoa
// Getting the Contents of a Directory in a Single Batch Operation
let bundleRoot = Bundle.main.bundlePath
let url = URL(string: bundleRoot)
let properties: [URLResourceKey] = [ URLResourceKey.localizedNameKey, URLResourceKey.creationDateKey, URLResourceKey.localizedTypeDescriptionKey]
if let src = url{
do {
let paths = try FileManager.default.contentsOfDirectory(at: src, includingPropertiesForKeys: properties, options: [])
for p in paths {
if p.hasSuffix(".data"){
print("File Path is: \(p)")
}
}
} catch { }
}

Resources