How can I calculate the size of a folder? - ios

I'm creating a folder to cache images inside Documents with my iPhone App. I want to be able to keep the size of this folder down to 1MB, so I need to to check the size in bytes of my folder.
I have code to calculate the size of file, but I need the size of the folder.
What would be the best way to do this?

tl;dr
All the other answers are off :)
Problem
I'd like to add my two cents to this old question as there seem to be many answers that are all very similar but yield results that are in some cases very unprecise.
To understand why we first have to define what the size of a folder is. In my understanding (and probably the one of the OP) it is the amount of bytes that the directory including all of its contents uses on the volume. Or, put in another way:
It is the space becoming available if the directory would be completely removed.
I'm aware that this definition is not the only valid way to interpret the question but I do think it's what most use cases boil down to.
Error
The existing answers all take a very simple approach: Traverse the directory contents, adding up the sizes of (regular) files. This does not take a couple of subtleties into account.
The space used on the volume increments in blocks, not in bytes. Even a one byte file uses at least one block.
Files carry around meta data (like any number of extended attributes). This data must go somewhere.
HFS deploys file system compression to actually store the file using less bytes then its real length.
Solution
All of these reasons make the existing answers produce unprecise results. So I'm proposing this extension on NSFileManager (code on github due to length: Swift 4, Objective C) to remedy the problem. It's also quite a bit faster, especially with directories containing a lot of files.
The core of the solution is to use NSURL's NSURLTotalFileAllocatedSizeKey or NSURLFileAllocatedSizeKey properies to retrieve file sizes.
Test
I've also set up a simple iOS test project, demonstrating the differences between the solutions. It shows how utterly wrong the results can be in some scenarios.
In the test I create a directory containing 100 small files (ranging from 0 to 800 bytes). The folderSize: method copied from some other answer calculates a total of 21 kB while my allocatedSize method yields 401 kB.
Proof
I made sure that the results of allocatedSize are closer to the correct value by calculating the difference of the available bytes on the volume before and after deleting the test directory. In my tests the difference was always exactly equal to the result of allocatedSize.
Please see Rob Napier's comment to understand that there's still room for improvement.
Performance
But there's another advantage: When calculating the size of a directory with 1000 files, on my iPhone 6 the folderSize: method takes about 250 ms while allocatedSize traverses the same hierarchy in 35 ms.
This is probably due to using NSFileManager's new(ish) enumeratorAtURL:includingPropertiesForKeys:options:errorHandler: API to traverse the hierachy. This method let's you specify prefetched properties for the items to be iterated, resulting in less io.
Results
Test `folderSize` (100 test files)
size: 21 KB (21.368 bytes)
time: 0.055 s
actual bytes: 401 KB (401.408 bytes)
Test `allocatedSize` (100 test files)
size: 401 KB (401.408 bytes)
time: 0.048 s
actual bytes: 401 KB (401.408 bytes)
Test `folderSize` (1000 test files)
size: 2 MB (2.013.068 bytes)
time: 0.263 s
actual bytes: 4,1 MB (4.087.808 bytes)
Test `allocatedSize` (1000 test files)
size: 4,1 MB (4.087.808 bytes)
time: 0.034 s
actual bytes: 4,1 MB (4.087.808 bytes)

Cheers for that Alex, you helped a lot, have now written the following function which does the trick...
- (unsigned long long int)folderSize:(NSString *)folderPath {
NSArray *filesArray = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:folderPath error:nil];
NSEnumerator *filesEnumerator = [filesArray objectEnumerator];
NSString *fileName;
unsigned long long int fileSize = 0;
while (fileName = [filesEnumerator nextObject]) {
NSDictionary *fileDictionary = [[NSFileManager defaultManager] fileAttributesAtPath:[folderPath stringByAppendingPathComponent:fileName] traverseLink:YES];
fileSize += [fileDictionary fileSize];
}
return fileSize;
}
It is coming up with the exact number of bytes as Finder does.
As an aside, Finder returns two numbers. One is the size on the disk and the other is the actual number of bytes.
For example, when I run this code on one of my folders, it comes back in the code with a 'fileSize' of 130398. When I check in Finder, it says the size is 201KB on disk (130,398 bytes).
Am a little unsure of what to go with here (201KB or 130,398 bytes) as the actual size. For now, I'll go on the safe side and cut my limit in half until I find out what this means exactly...
If anyone can add any more information to these differing numbers I'd appreciate it.
Cheers,

This is how to get folder and file size in MB, KB and GB ---
1. Folder Size -
-(NSString *)sizeOfFolder:(NSString *)folderPath
{
NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:folderPath error:nil];
NSEnumerator *contentsEnumurator = [contents objectEnumerator];
NSString *file;
unsigned long long int folderSize = 0;
while (file = [contentsEnumurator nextObject]) {
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[folderPath stringByAppendingPathComponent:file] error:nil];
folderSize += [[fileAttributes objectForKey:NSFileSize] intValue];
}
//This line will give you formatted size from bytes ....
NSString *folderSizeStr = [NSByteCountFormatter stringFromByteCount:folderSize countStyle:NSByteCountFormatterCountStyleFile];
return folderSizeStr;
}
Note: In case of sub folders please use subpathsOfDirectoryAtPath: instead of contentsOfDirectoryAtPath:
2. File Size -
-(NSString *)sizeOfFile:(NSString *)filePath
{
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
NSInteger fileSize = [[fileAttributes objectForKey:NSFileSize] integerValue];
NSString *fileSizeStr = [NSByteCountFormatter stringFromByteCount:fileSize countStyle:NSByteCountFormatterCountStyleFile];
return fileSizeStr;
}
---------- Swift 4.0 ----------
1. Folder Size -
func sizeOfFolder(_ folderPath: String) -> String? {
do {
let contents = try FileManager.default.contentsOfDirectory(atPath: folderPath)
var folderSize: Int64 = 0
for content in contents {
do {
let fullContentPath = folderPath + "/" + content
let fileAttributes = try FileManager.default.attributesOfItem(atPath: fullContentPath)
folderSize += fileAttributes[FileAttributeKey.size] as? Int64 ?? 0
} catch _ {
continue
}
}
/// This line will give you formatted size from bytes ....
let fileSizeStr = ByteCountFormatter.string(fromByteCount: folderSize, countStyle: ByteCountFormatter.CountStyle.file)
return fileSizeStr
} catch let error {
print(error.localizedDescription)
return nil
}
}
2. File Size -
func sizeOfFile(_ filePath: String) -> String? {
do {
let fileAttributes = try FileManager.default.attributesOfItem(atPath: filePath)
let folderSize = fileAttributes[FileAttributeKey.size] as? Int64 ?? 0
let fileSizeStr = ByteCountFormatter.string(fromByteCount: folderSize, countStyle: ByteCountFormatter.CountStyle.file)
return fileSizeStr
} catch {
print(error)
}
return nil
}

In iOS 5 the method -filesAttributesAtPath: is deprecated. Here is the version of the first code posted with the new method:
- (unsigned long long int)folderSize:(NSString *)folderPath {
NSArray *filesArray = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:folderPath error:nil];
NSEnumerator *filesEnumerator = [filesArray objectEnumerator];
NSString *fileName;
unsigned long long int fileSize = 0;
while (fileName = [filesEnumerator nextObject]) {
NSDictionary *fileDictionary = [[NSFileManager defaultManager] attributesOfItemAtPath:[folderPath stringByAppendingPathComponent:fileName] error:nil];
fileSize += [fileDictionary fileSize];
}
return fileSize;
}

Something like the following should help get you started. You'll need to modify _documentsDirectory to your specific folder, though:
- (unsigned long long int) documentsFolderSize {
NSFileManager *_manager = [NSFileManager defaultManager];
NSArray *_documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *_documentsDirectory = [_documentPaths objectAtIndex:0];
NSArray *_documentsFileList;
NSEnumerator *_documentsEnumerator;
NSString *_documentFilePath;
unsigned long long int _documentsFolderSize = 0;
_documentsFileList = [_manager subpathsAtPath:_documentsDirectory];
_documentsEnumerator = [_documentsFileList objectEnumerator];
while (_documentFilePath = [_documentsEnumerator nextObject]) {
NSDictionary *_documentFileAttributes = [_manager fileAttributesAtPath:[_documentsDirectory stringByAppendingPathComponent:_documentFilePath] traverseLink:YES];
_documentsFolderSize += [_documentFileAttributes fileSize];
}
return _documentsFolderSize;
}

I used this code to get the directory size of 2 directories, if one directory didnt exist, it would show Zero KB. Otherwise, the second half of the code will display the folder size along with the KB, MB, GB, respectively, and it will also display it in a clean format: 10.02 MB.
Try this something like this:
- (unsigned long long int)folderSize:(NSString *)folderPath {
NSArray *filesArray = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:folderPath error:nil];
NSEnumerator *filesEnumerator = [filesArray objectEnumerator];
NSString *fileName;
unsigned long long int fileSize = 0;
while (fileName = [filesEnumerator nextObject]) {
NSDictionary *fileDictionary = [[NSFileManager defaultManager] fileAttributesAtPath:[folderPath stringByAppendingPathComponent:fileName] traverseLink:YES];
fileSize += [fileDictionary fileSize];
}
return fileSize;
}
-(NSString *)getMPSize
{
NSString*sizeTypeW = #"bytes";
int app = [self folderSize:#"/PathToTheFolderYouWantTheSizeOf/"];
NSFileManager *manager = [NSFileManager defaultManager];
if([manager fileExistsAtPath:#"/AnotherFolder/"] == YES){
int working = [self folderSize:#"/AnotherFolder/"];
if(working<1){
return #"Size: Zero KB";
}else{
if (working > 1024)
{
//Kilobytes
working = working / 1024;
sizeTypeW = #" KB";
}
if (working > 1024)
{
//Megabytes
working = working / 1024;
sizeTypeW = #" MB";
}
if (working > 1024)
{
//Gigabytes
working = working / 1024;
sizeTypeW = #" GB";
}
return [NSString stringWithFormat:#"App: %i MB, Working: %i %# ",app/1024/1024, working,sizeTypeW];
}
}else{
return [NSString stringWithFormat:#"App: %i MB, Working: Zero KB",app/1024/1024];
}
[manager release];
}

Here's a swift 2.1/2.2 answer using extensions and building off of Rok's answer:
extension NSFileManager {
func fileSizeAtPath(path: String) -> Int64 {
do {
let fileAttributes = try attributesOfItemAtPath(path)
let fileSizeNumber = fileAttributes[NSFileSize]
let fileSize = fileSizeNumber?.longLongValue
return fileSize!
} catch {
print("error reading filesize, NSFileManager extension fileSizeAtPath")
return 0
}
}
func folderSizeAtPath(path: String) -> Int64 {
var size : Int64 = 0
do {
let files = try subpathsOfDirectoryAtPath(path)
for i in 0 ..< files.count {
size += fileSizeAtPath((path as NSString).stringByAppendingPathComponent(files[i]) as String)
}
} catch {
print("error reading directory, NSFileManager extension folderSizeAtPath")
}
return size
}
func format(size: Int64) -> String {
let folderSizeStr = NSByteCountFormatter.stringFromByteCount(size, countStyle: NSByteCountFormatterCountStyle.File)
return folderSizeStr
}
}
Usage example:
let fileManager = NSFileManager.defaultManager()
let documentsDirPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
let dirSize: String = fileManager.format(fileManager.folderSizeAtPath(documentsDirPath))

Updated Method using enumeration block
Calculate Folder Size with only files
- (NSString *)sizeOfFolder:(NSString *)folderPath {
NSArray *folderContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:folderPath error:nil];
__block unsigned long long int folderSize = 0;
[folderContents enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[folderPath stringByAppendingPathComponent:obj] error:nil];
folderSize += [[fileAttributes objectForKey:NSFileSize] intValue];
}];
NSString *folderSizeStr = [NSByteCountFormatter stringFromByteCount:folderSize countStyle:NSByteCountFormatterCountStyleFile];
return folderSizeStr;
}
Calculate Folder Size with other sub directories in the folder
NSArray *folderContents = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:folderPath error:nil];
Get File Size
- (NSString *)sizeOfFile:(NSString *)filePath {
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
NSInteger fileSize = [[fileAttributes objectForKey:NSFileSize] integerValue];
NSString *fileSizeString = [NSByteCountFormatter stringFromByteCount:fileSize countStyle:NSByteCountFormatterCountStyleFile];
return fileSizeString;
}

Here is the Swift 3 equivalent of a FileManager extension based off of #vitalii extension:
extension FileManager {
func fileSizeAtPath(path: String) -> Int64 {
do {
let fileAttributes = try attributesOfItem(atPath: path)
let fileSizeNumber = fileAttributes[FileAttributeKey.size] as? NSNumber
let fileSize = fileSizeNumber?.int64Value
return fileSize!
} catch {
print("error reading filesize, NSFileManager extension fileSizeAtPath")
return 0
}
}
func folderSizeAtPath(path: String) -> Int64 {
var size : Int64 = 0
do {
let files = try subpathsOfDirectory(atPath: path)
for i in 0 ..< files.count {
size += fileSizeAtPath(path:path.appending("/"+files[i]))
}
} catch {
print("error reading directory, NSFileManager extension folderSizeAtPath")
}
return size
}
func format(size: Int64) -> String {
let folderSizeStr = ByteCountFormatter.string(fromByteCount: size, countStyle: ByteCountFormatter.CountStyle.file)
return folderSizeStr
}}

I think use Unix C method is better for performance.
+ (long long) folderSizeAtPath: (const char*)folderPath {
long long folderSize = 0;
DIR* dir = opendir(folderPath);
if (dir == NULL) return 0;
struct dirent* child;
while ((child = readdir(dir))!=NULL) {
if (child->d_type == DT_DIR
&& child->d_name[0] == '.'
&& (child->d_name[1] == 0 // ignore .
||
(child->d_name[1] == '.' && child->d_name[2] == 0) // ignore dir ..
))
continue;
int folderPathLength = strlen(folderPath);
char childPath[1024]; // child
stpcpy(childPath, folderPath);
if (folderPath[folderPathLength-1] != '/'){
childPath[folderPathLength] = '/';
folderPathLength++;
}
stpcpy(childPath+folderPathLength, child->d_name);
childPath[folderPathLength + child->d_namlen] = 0;
if (child->d_type == DT_DIR){ // directory
folderSize += [self _folderSizeAtPath:childPath]; //
// add folder size
struct stat st;
if (lstat(childPath, &st) == 0)
folderSize += st.st_size;
} else if (child->d_type == DT_REG || child->d_type == DT_LNK){ // file or link
struct stat st;
if (lstat(childPath, &st) == 0)
folderSize += st.st_size;
}
}
return folderSize;
}

if we want to get the size of any file then here is a method, where we only need to pass path of that file.
- (unsigned long long int) fileSizeAt:(NSString *)path {
NSFileManager *_manager = [NSFileManager defaultManager];
return [[_manager fileAttributesAtPath:path traverseLink:YES] fileSize];
}

I cleaned up a bit the first answer's implementation before using it, so it no longer throws deprecated warnings + using fast enumeration.
/**
* Calculates the size of a folder.
*
* #param folderPath The path of the folder
*
* #return folder size in bytes
*/
- (unsigned long long int)folderSize:(NSString *)folderPath {
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *filesArray = [fm subpathsOfDirectoryAtPath:folderPath error:nil];
unsigned long long int fileSize = 0;
NSError *error;
for(NSString *fileName in filesArray) {
error = nil;
NSDictionary *fileDictionary = [fm attributesOfItemAtPath:[folderPath stringByAppendingPathComponent:fileName] error:&error];
if (!error) {
fileSize += [fileDictionary fileSize];
}else{
NSLog(#"ERROR: %#", error);
}
}
return fileSize;
}

Swift Implementation
class func folderSize(folderPath:String) -> UInt{
// #see http://stackoverflow.com/questions/2188469/calculate-the-size-of-a-folder
let filesArray:[String] = NSFileManager.defaultManager().subpathsOfDirectoryAtPath(folderPath, error: nil)! as [String]
var fileSize:UInt = 0
for fileName in filesArray{
let filePath = folderPath.stringByAppendingPathComponent(fileName)
let fileDictionary:NSDictionary = NSFileManager.defaultManager().attributesOfItemAtPath(filePath, error: nil)!
fileSize += UInt(fileDictionary.fileSize())
}
return fileSize
}

Not sure if this helps anyone, but I wanted to relate some of my findings (some inspired by #zneak's comment above).
I could not find any shortcuts using NSDirectoryEnumerator to avoid enumerating through files to get the total contained size of a directory.
For my tests, using -[NSFileManager subpathsOfDirectoryAtPath:path error:nil] was faster than using -[NSFileManager enumeratorAtPath:path]. This looks to me like it might be a classic time/space tradeoff, as subPaths... creates an NSArray on which it then iterates, where enumerator... might not.
Some background on #1. Assuming:
NSFileManager *fileMan = [NSFileManager defaultManager];
NSString *dirPath = #"/"; // references some directory
Then
[fileMan enumeratorAtPath:dirPath] fileAttributes]
returns nil. The correct attribute accessor is directoryAttributes, but
[fileMan enumeratorAtPath:dirPath] directoryAttributes] fileSize]
returns the size of the directory information, not the recursive sum of the sizes of all contained files (a lá ⌘-I in Finder).

I've created a simple NSFileManager extension:
extension NSFileManager {
func fileSizeAtPath(path: String) -> Int {
return attributesOfItemAtPath(path, error: nil)?[NSFileSize] as? Int ?? 0
}
func folderSizeAtPath(path: String) -> Int {
var size = 0
for file in subpathsOfDirectoryAtPath(path, error: nil) as? [String] ?? [] {
size += fileSizeAtPath(path.stringByAppendingPathComponent(file))
}
return size
}
}
You can get the file size:
NSFileManager.defaultManager().fileSizeAtPath("file path")
and the folder size:
NSFileManager.defaultManager().folderSizeAtPath("folder path")

Related

storage greater than system

I want to simulate a shortage of storage, so I try to copy files util the storage is full, but I found it will never get to that, and I found the occuped storage is greater than system, how can this happen?
the code is as follows:
+ (void)copeFiles
{
NSString *srcPath = [self cloudStorageCacheFolderPath];
NSLog(#"copy: src path:%# ,size:%f", srcPath, [self sizeOfcloudStorageCacheFolder]);
int base = 300;
int i = base;
while (1) {
i++;
if (i > base + 100) {
break;
}
NSInteger freeSizeM = [self freeFileStorageSize];
if (freeSizeM > 1024) {
NSString *newFilePath = [NSString stringWithFormat:#"%#-%d", srcPath, i];
[self copyFolderContentFromSrc:srcPath toDestPath:newFilePath];
NSLog(#"copy: i:%d, freeSizeM:%li", i, (long)freeSizeM);
}else {
break;
}
}
}
+ (void)copyFolderContentFromSrc:(NSString *)srcPath toDestPath:(NSString *)dstPath
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSLog(#"copy: src:%# dst:%#", srcPath, dstPath);
BOOL isDir;
if (![fileManager fileExistsAtPath:dstPath isDirectory:&isDir] || !isDir) {
BOOL sucess = [fileManager createDirectoryAtPath:dstPath withIntermediateDirectories:YES attributes:nil error:nil];
NSLog(#"copy: isDir:%d sucess:%d", isDir, sucess);
}
NSArray* array = [fileManager contentsOfDirectoryAtPath:srcPath error:nil];
int i = 0;
for (NSString *fileName in array) {
i++;
NSString *srcFullPath = [srcPath stringByAppendingPathComponent:fileName];
NSString *toFullPath = [dstPath stringByAppendingPathComponent:fileName];
NSError *error;
BOOL isSucess = [fileManager copyItemAtPath:srcFullPath toPath:toFullPath error:&error];
NSLog(#"copy:%d", i);
if (!isSucess) {
NSLog(#"copy:error:%#", error);
}
}
}
image1
image2
The images may be invisible, so I describe the phenomenon simply in advance.My iphone is 32GB in total, but the available storage is 93GB showed in setting.
Yes, It will also increase if you run it again :)
The reason behind it is that It will store the reference only not an actual file. and I have done the same in my mac system to fill the space for simulator and I have ended up with same 256GB system showing 4.3 TB of space
In Device, you can fill space using record a video and still you can not get that error because Apple will manage it like this way.
1) Delete all temporary files that were stored in All App directory.
2) And then Clear cache memory.
Apple will manage the storage space management very well and there is still some mystery about it.

How to detect total available/free disk space on the iPhone/iPad device on iOS 11

In iOS 11, I am not able to get the correct free size of the device(disk space) from the Dictionary key NSFileSystemFreeSize. Instead giving 34.4 GB it gives 4 GB free space.
Below is the code I am using
pragma mark - Formatter
- (NSString *)memoryFormatter:(long long)diskSpace
{
NSString *formatted;
double bytes = 1.0 * diskSpace;
double megabytes = bytes / MB;
double gigabytes = bytes / GB;
if (gigabytes >= 1.0)
formatted = [NSString stringWithFormat:#"%.2f GB", gigabytes];
else if (megabytes >= 1.0)
formatted = [NSString stringWithFormat:#"%.2f MB", megabytes];
else
formatted = [NSString stringWithFormat:#"%.2f bytes", bytes];
return formatted;
}
#pragma mark - Methods
- (NSString *)totalDiskSpace {
long long space = [[[[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:nil] objectForKey:NSFileSystemSize] longLongValue];
return [self memoryFormatter:space];
}
- (NSString *)freeDiskSpace {
long long freeSpace = [[[[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:nil] objectForKey:NSFileSystemFreeSize] longLongValue];
return [self memoryFormatter:freeSpace];
}
- (NSString *)usedDiskSpace {
return [self memoryFormatter:[self usedDiskSpaceInBytes]];
}
- (CGFloat)totalDiskSpaceInBytes {
long long space = [[[[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:nil] objectForKey:NSFileSystemSize] longLongValue];
return space;
}
- (CGFloat)freeDiskSpaceInBytes {
long long freeSpace = [[[[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:nil] objectForKey:NSFileSystemFreeSize] longLongValue];
return freeSpace;
}
- (CGFloat)usedDiskSpaceInBytes {
long long usedSpace = [self totalDiskSpaceInBytes] - [self freeDiskSpaceInBytes];
return usedSpace;
}
OBJECTIVE C (converted)
- (uint64_t)freeDiskspace
{
uint64_t totalSpace = 0;
uint64_t totalFreeSpace = 0;
__autoreleasing NSError *error = nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSDictionary *dictionary = [[NSFileManager defaultManager] attributesOfFileSystemForPath:[paths lastObject] error: &error];
if (dictionary)
{
NSNumber *fileSystemSizeInBytes = [dictionary objectForKey: NSFileSystemSize];
NSNumber *freeFileSystemSizeInBytes = [dictionary objectForKey:NSFileSystemFreeSize];
totalSpace = [fileSystemSizeInBytes unsignedLongLongValue];
totalFreeSpace = [freeFileSystemSizeInBytes unsignedLongLongValue];
NSLog(#"Memory Capacity of %llu MiB with %llu MiB Free memory available.", ((totalSpace/1024ll)/1024ll), ((totalFreeSpace/1024ll)/1024ll));
}
else
{
NSLog(#"Error Obtaining System Memory Info: Domain = %#, Code = %d", [error domain], [error code]);
}
return totalFreeSpace;
}
So if people have the problem of not getting the correct free size, use NSURL resourceValuesForKeys to get the free space.
[ fileURL resourceValuesForKeys:#[NSURLVolumeAvailableCapacityForImportantUsageKey ] error:&error];
double = availableSizeInBytes = [ results[NSURLVolumeAvailableCapacityForImportantUsageKey] doubleValue ];
Reference Why is `volumeAvailableCapacityForImportantUsage` zero?
Here's how I usually do it
func deviceRemainingFreeSpaceInBytes() -> Int64?
{
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).last!
guard
let systemAttributes = try? FileManager.default.attributesOfFileSystem(forPath: documentDirectory),
let freeSize = systemAttributes[.systemFreeSize] as? NSNumber
else {
// handle failure
return nil
}
return freeSize.int64Value // this returns bytes - scales as required for MB / GB
}
SWIFT 4:
func getFreeDiskspace() -> UInt64
{
let totalSpace: UInt64 = 0
let totalFreeSpace: UInt64 = 0
var error: Error? = nil
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let dictionary = try? FileManager.default.attributesOfFileSystem(forPath: paths.last ?? "")
if dictionary
{
var fileSystemSizeInBytes = dictionary[.systemSize]
var freeFileSystemSizeInBytes = dictionary[.systemFreeSize]
totalSpace = fileSystemSizeInBytes as UInt64? ?? 0
totalFreeSpace = freeFileSystemSizeInBytes as UInt64? ?? 0
print("Memory Capacity of \((totalSpace / 1024) / 1024) MiB with \((totalFreeSpace / 1024) / 1024) MiB Free memory available.")
}
else
{
print("Error Obtaining System Memory Info: Domain = \((error as NSError?)?.domain), Code = \(Int(error.code))")
return totalFreeSpace
}
}

Iterating through files,folders and subfolders in documentDirectory [duplicate]

I'm creating a folder to cache images inside Documents with my iPhone App. I want to be able to keep the size of this folder down to 1MB, so I need to to check the size in bytes of my folder.
I have code to calculate the size of file, but I need the size of the folder.
What would be the best way to do this?
tl;dr
All the other answers are off :)
Problem
I'd like to add my two cents to this old question as there seem to be many answers that are all very similar but yield results that are in some cases very unprecise.
To understand why we first have to define what the size of a folder is. In my understanding (and probably the one of the OP) it is the amount of bytes that the directory including all of its contents uses on the volume. Or, put in another way:
It is the space becoming available if the directory would be completely removed.
I'm aware that this definition is not the only valid way to interpret the question but I do think it's what most use cases boil down to.
Error
The existing answers all take a very simple approach: Traverse the directory contents, adding up the sizes of (regular) files. This does not take a couple of subtleties into account.
The space used on the volume increments in blocks, not in bytes. Even a one byte file uses at least one block.
Files carry around meta data (like any number of extended attributes). This data must go somewhere.
HFS deploys file system compression to actually store the file using less bytes then its real length.
Solution
All of these reasons make the existing answers produce unprecise results. So I'm proposing this extension on NSFileManager (code on github due to length: Swift 4, Objective C) to remedy the problem. It's also quite a bit faster, especially with directories containing a lot of files.
The core of the solution is to use NSURL's NSURLTotalFileAllocatedSizeKey or NSURLFileAllocatedSizeKey properies to retrieve file sizes.
Test
I've also set up a simple iOS test project, demonstrating the differences between the solutions. It shows how utterly wrong the results can be in some scenarios.
In the test I create a directory containing 100 small files (ranging from 0 to 800 bytes). The folderSize: method copied from some other answer calculates a total of 21 kB while my allocatedSize method yields 401 kB.
Proof
I made sure that the results of allocatedSize are closer to the correct value by calculating the difference of the available bytes on the volume before and after deleting the test directory. In my tests the difference was always exactly equal to the result of allocatedSize.
Please see Rob Napier's comment to understand that there's still room for improvement.
Performance
But there's another advantage: When calculating the size of a directory with 1000 files, on my iPhone 6 the folderSize: method takes about 250 ms while allocatedSize traverses the same hierarchy in 35 ms.
This is probably due to using NSFileManager's new(ish) enumeratorAtURL:includingPropertiesForKeys:options:errorHandler: API to traverse the hierachy. This method let's you specify prefetched properties for the items to be iterated, resulting in less io.
Results
Test `folderSize` (100 test files)
size: 21 KB (21.368 bytes)
time: 0.055 s
actual bytes: 401 KB (401.408 bytes)
Test `allocatedSize` (100 test files)
size: 401 KB (401.408 bytes)
time: 0.048 s
actual bytes: 401 KB (401.408 bytes)
Test `folderSize` (1000 test files)
size: 2 MB (2.013.068 bytes)
time: 0.263 s
actual bytes: 4,1 MB (4.087.808 bytes)
Test `allocatedSize` (1000 test files)
size: 4,1 MB (4.087.808 bytes)
time: 0.034 s
actual bytes: 4,1 MB (4.087.808 bytes)
Cheers for that Alex, you helped a lot, have now written the following function which does the trick...
- (unsigned long long int)folderSize:(NSString *)folderPath {
NSArray *filesArray = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:folderPath error:nil];
NSEnumerator *filesEnumerator = [filesArray objectEnumerator];
NSString *fileName;
unsigned long long int fileSize = 0;
while (fileName = [filesEnumerator nextObject]) {
NSDictionary *fileDictionary = [[NSFileManager defaultManager] fileAttributesAtPath:[folderPath stringByAppendingPathComponent:fileName] traverseLink:YES];
fileSize += [fileDictionary fileSize];
}
return fileSize;
}
It is coming up with the exact number of bytes as Finder does.
As an aside, Finder returns two numbers. One is the size on the disk and the other is the actual number of bytes.
For example, when I run this code on one of my folders, it comes back in the code with a 'fileSize' of 130398. When I check in Finder, it says the size is 201KB on disk (130,398 bytes).
Am a little unsure of what to go with here (201KB or 130,398 bytes) as the actual size. For now, I'll go on the safe side and cut my limit in half until I find out what this means exactly...
If anyone can add any more information to these differing numbers I'd appreciate it.
Cheers,
This is how to get folder and file size in MB, KB and GB ---
1. Folder Size -
-(NSString *)sizeOfFolder:(NSString *)folderPath
{
NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:folderPath error:nil];
NSEnumerator *contentsEnumurator = [contents objectEnumerator];
NSString *file;
unsigned long long int folderSize = 0;
while (file = [contentsEnumurator nextObject]) {
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[folderPath stringByAppendingPathComponent:file] error:nil];
folderSize += [[fileAttributes objectForKey:NSFileSize] intValue];
}
//This line will give you formatted size from bytes ....
NSString *folderSizeStr = [NSByteCountFormatter stringFromByteCount:folderSize countStyle:NSByteCountFormatterCountStyleFile];
return folderSizeStr;
}
Note: In case of sub folders please use subpathsOfDirectoryAtPath: instead of contentsOfDirectoryAtPath:
2. File Size -
-(NSString *)sizeOfFile:(NSString *)filePath
{
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
NSInteger fileSize = [[fileAttributes objectForKey:NSFileSize] integerValue];
NSString *fileSizeStr = [NSByteCountFormatter stringFromByteCount:fileSize countStyle:NSByteCountFormatterCountStyleFile];
return fileSizeStr;
}
---------- Swift 4.0 ----------
1. Folder Size -
func sizeOfFolder(_ folderPath: String) -> String? {
do {
let contents = try FileManager.default.contentsOfDirectory(atPath: folderPath)
var folderSize: Int64 = 0
for content in contents {
do {
let fullContentPath = folderPath + "/" + content
let fileAttributes = try FileManager.default.attributesOfItem(atPath: fullContentPath)
folderSize += fileAttributes[FileAttributeKey.size] as? Int64 ?? 0
} catch _ {
continue
}
}
/// This line will give you formatted size from bytes ....
let fileSizeStr = ByteCountFormatter.string(fromByteCount: folderSize, countStyle: ByteCountFormatter.CountStyle.file)
return fileSizeStr
} catch let error {
print(error.localizedDescription)
return nil
}
}
2. File Size -
func sizeOfFile(_ filePath: String) -> String? {
do {
let fileAttributes = try FileManager.default.attributesOfItem(atPath: filePath)
let folderSize = fileAttributes[FileAttributeKey.size] as? Int64 ?? 0
let fileSizeStr = ByteCountFormatter.string(fromByteCount: folderSize, countStyle: ByteCountFormatter.CountStyle.file)
return fileSizeStr
} catch {
print(error)
}
return nil
}
In iOS 5 the method -filesAttributesAtPath: is deprecated. Here is the version of the first code posted with the new method:
- (unsigned long long int)folderSize:(NSString *)folderPath {
NSArray *filesArray = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:folderPath error:nil];
NSEnumerator *filesEnumerator = [filesArray objectEnumerator];
NSString *fileName;
unsigned long long int fileSize = 0;
while (fileName = [filesEnumerator nextObject]) {
NSDictionary *fileDictionary = [[NSFileManager defaultManager] attributesOfItemAtPath:[folderPath stringByAppendingPathComponent:fileName] error:nil];
fileSize += [fileDictionary fileSize];
}
return fileSize;
}
Something like the following should help get you started. You'll need to modify _documentsDirectory to your specific folder, though:
- (unsigned long long int) documentsFolderSize {
NSFileManager *_manager = [NSFileManager defaultManager];
NSArray *_documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *_documentsDirectory = [_documentPaths objectAtIndex:0];
NSArray *_documentsFileList;
NSEnumerator *_documentsEnumerator;
NSString *_documentFilePath;
unsigned long long int _documentsFolderSize = 0;
_documentsFileList = [_manager subpathsAtPath:_documentsDirectory];
_documentsEnumerator = [_documentsFileList objectEnumerator];
while (_documentFilePath = [_documentsEnumerator nextObject]) {
NSDictionary *_documentFileAttributes = [_manager fileAttributesAtPath:[_documentsDirectory stringByAppendingPathComponent:_documentFilePath] traverseLink:YES];
_documentsFolderSize += [_documentFileAttributes fileSize];
}
return _documentsFolderSize;
}
I used this code to get the directory size of 2 directories, if one directory didnt exist, it would show Zero KB. Otherwise, the second half of the code will display the folder size along with the KB, MB, GB, respectively, and it will also display it in a clean format: 10.02 MB.
Try this something like this:
- (unsigned long long int)folderSize:(NSString *)folderPath {
NSArray *filesArray = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:folderPath error:nil];
NSEnumerator *filesEnumerator = [filesArray objectEnumerator];
NSString *fileName;
unsigned long long int fileSize = 0;
while (fileName = [filesEnumerator nextObject]) {
NSDictionary *fileDictionary = [[NSFileManager defaultManager] fileAttributesAtPath:[folderPath stringByAppendingPathComponent:fileName] traverseLink:YES];
fileSize += [fileDictionary fileSize];
}
return fileSize;
}
-(NSString *)getMPSize
{
NSString*sizeTypeW = #"bytes";
int app = [self folderSize:#"/PathToTheFolderYouWantTheSizeOf/"];
NSFileManager *manager = [NSFileManager defaultManager];
if([manager fileExistsAtPath:#"/AnotherFolder/"] == YES){
int working = [self folderSize:#"/AnotherFolder/"];
if(working<1){
return #"Size: Zero KB";
}else{
if (working > 1024)
{
//Kilobytes
working = working / 1024;
sizeTypeW = #" KB";
}
if (working > 1024)
{
//Megabytes
working = working / 1024;
sizeTypeW = #" MB";
}
if (working > 1024)
{
//Gigabytes
working = working / 1024;
sizeTypeW = #" GB";
}
return [NSString stringWithFormat:#"App: %i MB, Working: %i %# ",app/1024/1024, working,sizeTypeW];
}
}else{
return [NSString stringWithFormat:#"App: %i MB, Working: Zero KB",app/1024/1024];
}
[manager release];
}
Here's a swift 2.1/2.2 answer using extensions and building off of Rok's answer:
extension NSFileManager {
func fileSizeAtPath(path: String) -> Int64 {
do {
let fileAttributes = try attributesOfItemAtPath(path)
let fileSizeNumber = fileAttributes[NSFileSize]
let fileSize = fileSizeNumber?.longLongValue
return fileSize!
} catch {
print("error reading filesize, NSFileManager extension fileSizeAtPath")
return 0
}
}
func folderSizeAtPath(path: String) -> Int64 {
var size : Int64 = 0
do {
let files = try subpathsOfDirectoryAtPath(path)
for i in 0 ..< files.count {
size += fileSizeAtPath((path as NSString).stringByAppendingPathComponent(files[i]) as String)
}
} catch {
print("error reading directory, NSFileManager extension folderSizeAtPath")
}
return size
}
func format(size: Int64) -> String {
let folderSizeStr = NSByteCountFormatter.stringFromByteCount(size, countStyle: NSByteCountFormatterCountStyle.File)
return folderSizeStr
}
}
Usage example:
let fileManager = NSFileManager.defaultManager()
let documentsDirPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
let dirSize: String = fileManager.format(fileManager.folderSizeAtPath(documentsDirPath))
Updated Method using enumeration block
Calculate Folder Size with only files
- (NSString *)sizeOfFolder:(NSString *)folderPath {
NSArray *folderContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:folderPath error:nil];
__block unsigned long long int folderSize = 0;
[folderContents enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[folderPath stringByAppendingPathComponent:obj] error:nil];
folderSize += [[fileAttributes objectForKey:NSFileSize] intValue];
}];
NSString *folderSizeStr = [NSByteCountFormatter stringFromByteCount:folderSize countStyle:NSByteCountFormatterCountStyleFile];
return folderSizeStr;
}
Calculate Folder Size with other sub directories in the folder
NSArray *folderContents = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:folderPath error:nil];
Get File Size
- (NSString *)sizeOfFile:(NSString *)filePath {
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
NSInteger fileSize = [[fileAttributes objectForKey:NSFileSize] integerValue];
NSString *fileSizeString = [NSByteCountFormatter stringFromByteCount:fileSize countStyle:NSByteCountFormatterCountStyleFile];
return fileSizeString;
}
Here is the Swift 3 equivalent of a FileManager extension based off of #vitalii extension:
extension FileManager {
func fileSizeAtPath(path: String) -> Int64 {
do {
let fileAttributes = try attributesOfItem(atPath: path)
let fileSizeNumber = fileAttributes[FileAttributeKey.size] as? NSNumber
let fileSize = fileSizeNumber?.int64Value
return fileSize!
} catch {
print("error reading filesize, NSFileManager extension fileSizeAtPath")
return 0
}
}
func folderSizeAtPath(path: String) -> Int64 {
var size : Int64 = 0
do {
let files = try subpathsOfDirectory(atPath: path)
for i in 0 ..< files.count {
size += fileSizeAtPath(path:path.appending("/"+files[i]))
}
} catch {
print("error reading directory, NSFileManager extension folderSizeAtPath")
}
return size
}
func format(size: Int64) -> String {
let folderSizeStr = ByteCountFormatter.string(fromByteCount: size, countStyle: ByteCountFormatter.CountStyle.file)
return folderSizeStr
}}
I think use Unix C method is better for performance.
+ (long long) folderSizeAtPath: (const char*)folderPath {
long long folderSize = 0;
DIR* dir = opendir(folderPath);
if (dir == NULL) return 0;
struct dirent* child;
while ((child = readdir(dir))!=NULL) {
if (child->d_type == DT_DIR
&& child->d_name[0] == '.'
&& (child->d_name[1] == 0 // ignore .
||
(child->d_name[1] == '.' && child->d_name[2] == 0) // ignore dir ..
))
continue;
int folderPathLength = strlen(folderPath);
char childPath[1024]; // child
stpcpy(childPath, folderPath);
if (folderPath[folderPathLength-1] != '/'){
childPath[folderPathLength] = '/';
folderPathLength++;
}
stpcpy(childPath+folderPathLength, child->d_name);
childPath[folderPathLength + child->d_namlen] = 0;
if (child->d_type == DT_DIR){ // directory
folderSize += [self _folderSizeAtPath:childPath]; //
// add folder size
struct stat st;
if (lstat(childPath, &st) == 0)
folderSize += st.st_size;
} else if (child->d_type == DT_REG || child->d_type == DT_LNK){ // file or link
struct stat st;
if (lstat(childPath, &st) == 0)
folderSize += st.st_size;
}
}
return folderSize;
}
if we want to get the size of any file then here is a method, where we only need to pass path of that file.
- (unsigned long long int) fileSizeAt:(NSString *)path {
NSFileManager *_manager = [NSFileManager defaultManager];
return [[_manager fileAttributesAtPath:path traverseLink:YES] fileSize];
}
I cleaned up a bit the first answer's implementation before using it, so it no longer throws deprecated warnings + using fast enumeration.
/**
* Calculates the size of a folder.
*
* #param folderPath The path of the folder
*
* #return folder size in bytes
*/
- (unsigned long long int)folderSize:(NSString *)folderPath {
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *filesArray = [fm subpathsOfDirectoryAtPath:folderPath error:nil];
unsigned long long int fileSize = 0;
NSError *error;
for(NSString *fileName in filesArray) {
error = nil;
NSDictionary *fileDictionary = [fm attributesOfItemAtPath:[folderPath stringByAppendingPathComponent:fileName] error:&error];
if (!error) {
fileSize += [fileDictionary fileSize];
}else{
NSLog(#"ERROR: %#", error);
}
}
return fileSize;
}
Swift Implementation
class func folderSize(folderPath:String) -> UInt{
// #see http://stackoverflow.com/questions/2188469/calculate-the-size-of-a-folder
let filesArray:[String] = NSFileManager.defaultManager().subpathsOfDirectoryAtPath(folderPath, error: nil)! as [String]
var fileSize:UInt = 0
for fileName in filesArray{
let filePath = folderPath.stringByAppendingPathComponent(fileName)
let fileDictionary:NSDictionary = NSFileManager.defaultManager().attributesOfItemAtPath(filePath, error: nil)!
fileSize += UInt(fileDictionary.fileSize())
}
return fileSize
}
Not sure if this helps anyone, but I wanted to relate some of my findings (some inspired by #zneak's comment above).
I could not find any shortcuts using NSDirectoryEnumerator to avoid enumerating through files to get the total contained size of a directory.
For my tests, using -[NSFileManager subpathsOfDirectoryAtPath:path error:nil] was faster than using -[NSFileManager enumeratorAtPath:path]. This looks to me like it might be a classic time/space tradeoff, as subPaths... creates an NSArray on which it then iterates, where enumerator... might not.
Some background on #1. Assuming:
NSFileManager *fileMan = [NSFileManager defaultManager];
NSString *dirPath = #"/"; // references some directory
Then
[fileMan enumeratorAtPath:dirPath] fileAttributes]
returns nil. The correct attribute accessor is directoryAttributes, but
[fileMan enumeratorAtPath:dirPath] directoryAttributes] fileSize]
returns the size of the directory information, not the recursive sum of the sizes of all contained files (a lá ⌘-I in Finder).
I've created a simple NSFileManager extension:
extension NSFileManager {
func fileSizeAtPath(path: String) -> Int {
return attributesOfItemAtPath(path, error: nil)?[NSFileSize] as? Int ?? 0
}
func folderSizeAtPath(path: String) -> Int {
var size = 0
for file in subpathsOfDirectoryAtPath(path, error: nil) as? [String] ?? [] {
size += fileSizeAtPath(path.stringByAppendingPathComponent(file))
}
return size
}
}
You can get the file size:
NSFileManager.defaultManager().fileSizeAtPath("file path")
and the folder size:
NSFileManager.defaultManager().folderSizeAtPath("folder path")

What is NSdocumentdirectory's limitation size?

What is the maximum size of NSDocumentdirectory that can be stored?.
Mostly I am saving images in NSDocumentdirectory, as it is backed up by iCloud.
Now i would like to know what is the maximum size that can be stored in it. Please help.
This code is giving the exact number of bytes.
- (unsigned long long int)folderSize:(NSString *)folderPath {
NSArray *folderArray = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:folderPath error:nil];
NSEnumerator *folderEnumerator = [folderArray objectEnumerator];
NSString *folderName;
unsigned long long int fileSize = 0;
while (folderName = [folderEnumerator nextObject]) {
NSDictionary *folderDictionary = [[NSFileManager defaultManager] fileAttributesAtPath:[folderPath stringByAppendingPathComponent:fileName] traverseLink:YES];
folderSize += [folderDictionary folderSize];
}
return folderSize;
}
Hope it helps...

Calculating documents and data storage used IOS7

I am trying to find the total size of the documents directory of my IOS app. I believe it is the value that I see in the Settings app under usage (documents and data, this value now is 40MB). By using this method:
-(int)sizeOfFolder:(NSString *)folderPath
{
NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:folderPath error:nil];
NSEnumerator *contentsEnumurator = [contents objectEnumerator];
NSString *file;
unsigned long long int folderSize = 0;
while (file = [contentsEnumurator nextObject]) {
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[folderPath stringByAppendingPathComponent:file] error:nil];
folderSize += [[fileAttributes objectForKey:NSFileSize] intValue];
}
//This line will give you formatted size from bytes ....
//NSString *folderSizeStr = [NSByteCountFormatter stringFromByteCount:folderSize countStyle:NSByteCountFormatterCountStyleFile];
NSLog(#"size: %llu", folderSize);
return folderSize;
}
I get always the same value. This is the path I pass onto the method:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* rootPath = [paths objectAtIndex:0];
int space = [self sizeOfFolder:rootPath];
First of all you should be careful with file types. You should use long long to be able to calculate the total size:
unsigned long long folderSize = 0; // remove int here
// ...
folderSize += [[fileAttributes objectForKey:NSFileSize] longLongValue]; // note longLongValue
Next you should skip directories to get more accurate results
if (![fileAttirbutes[NSFileType] equalsToString:NSFileTypeDirectory]) {
folderSize += ...
}
I would recommend you to look at another directory enumeration approach. Using - (NSDirectoryEnumerator *)enumeratorAtURL:(NSURL *)url includingPropertiesForKeys:(NSArray *)keys options:(NSDirectoryEnumerationOptions)mask errorHandler:(BOOL (^)(NSURL *url, NSError *error))handler will allow you to perform deeper directory enumeration and list all directory contents recursively
unsigned long long count = 0;
NSNumber *value = nil;
for (NSURL *url in [[NSFileManager defaultManager] enumeratorAtURL:directoryURL includingPropertiesForKeys:#[NSURLFileSizeKey] options:0 errorHandler:NULL]) {
if ([url getResourceValue:&value forKey:NSURLFileSizeKey error:outError]) {
count += [value longLongValue];
} else {
return nil;
}
}
return #(count);
NSFileManager has no capabilities of returning the size of a folder(a folder + contents). Found this on Internet.
- (unsigned long long) fastFolderSizeAtFSRef:(FSRef*)theFileRef {
FSIterator thisDirEnum = NULL;
unsigned long long totalSize = 0;
// Iterate the directory contents, recursing as necessary
if (FSOpenIterator(theFileRef, kFSIterateFlat, &thisDirEnum) == noErr)
{
const ItemCount kMaxEntriesPerFetch = 256;
ItemCount actualFetched;
FSRef fetchedRefs[kMaxEntriesPerFetch];
FSCatalogInfo fetchedInfos[kMaxEntriesPerFetch];
// DCJ Note right now this is only fetching data fork sizes... if we decide to include
// resource forks we will have to add kFSCatInfoRsrcSizes
OSErr fsErr = FSGetCatalogInfoBulk(thisDirEnum, kMaxEntriesPerFetch, &actualFetched,
NULL, kFSCatInfoDataSizes | kFSCatInfoNodeFlags, fetchedInfos,
fetchedRefs, NULL, NULL);
while ((fsErr == noErr) || (fsErr == errFSNoMoreItems))
{
ItemCount thisIndex;
for (thisIndex = 0; thisIndex < actualFetched; thisIndex++)
{
// Recurse if it's a folder
if (fetchedInfos[thisIndex].nodeFlags & kFSNodeIsDirectoryMask)
{
totalSize += [self fastFolderSizeAtFSRef:&fetchedRefs[thisIndex]];
}
else
{
totalSize += fetchedInfos [thisIndex].dataLogicalSize;
}
}
if (fsErr == errFSNoMoreItems)
{
break;
}
else
{
// get more items
fsErr = FSGetCatalogInfoBulk(thisDirEnum, kMaxEntriesPerFetch, &actualFetched,
NULL, kFSCatInfoDataSizes | kFSCatInfoNodeFlags, fetchedInfos,
fetchedRefs, NULL, NULL);
}
}
FSCloseIterator(thisDirEnum);
}
return totalSize; }

Resources