Data (contentsOf: url, options: .alwaysMapped) doesn't 'map' in Xcode 10.2/Swift 5

I've found that when I try to access a huge (several Gigabyte ,eg. a movie) file in an application compiled with Swift 5 / XCode 10.2, it no longer 'maps' the file. Instead, it loads the entire thing - which causes my application to hang for several seconds.


The file is a local file, so it should be able to map it fine.


I was so suprised that I created a test app, the guts of it being...


func openMappedFile (url: URL) throws {

let data = try Data (contentsOf: url, options: .alwaysMapped)

print (data.count)

}


Sure enough, when I compile the test app with XCode 10.1/Swift 4.2, it behaves as expected, and immediately prints the count of the number of bytes in the file - without having to load the entire thing.


But when I compile the same app with XCode 10.2/Swift 5, it hangs for several seconds, with lots of disk activity while it loads the file before printing 'count'


Has anyone else been affected by this? Does anyone know of a workaround (while still using 'Data' - I don't want to have to fiddle round with Darwin APIs, mmap, etc.). Or should I just go back to using XCode 10.1 until it's (hopefully) fixed. I've already raised a bug for this in Bug Reporter.

Did you try using .mappedIfSafe instead, to see what happens ?


See a discussion here (but not related to XCode 10.2):

https://forums.swift.org/t/what-s-the-recommended-way-to-memory-map-a-file/19113


Did you try to call the API in objc ?

Thanks for replying. I just tried .mappedIfSafe. Exactly the same behaviour as I'm seeing with .alwaysMapped - so it 'maps' in Swift 4.2 but not in Swift 5


I'll try ObjC later- I'm not great at ObjC, but a tiny test program shouldn't be too hard 🙂


Update: I tried in ObjC using...


- (void)openMappedFile:(NSURL *) url {

NSData *data = [NSData dataWithContentsOfURL:url options:NSDataReadingMappedAlways error:nil];

printf("%lu\n", (unsigned long)[data length]);

}


It worked fine, and printed the data length instantly. So it seems to be a Swift 5 issue, rather than an XCode one 😟

So I tried again on Swift 5 - this time using NSData instead of Data. It worked fine and mapped as expected.


So this works...


func openMappedFile (url: URL) throws {

let data = try NSData (contentsOf: url, options: .alwaysMapped)

// let data = try Data (contentsOf: url, options: .alwaysMapped)

print (data.count)

}


... and this doesn't...


func openMappedFile (url: URL) throws {

// let data = try NSData (contentsOf: url, options: .alwaysMapped)

let data = try Data (contentsOf: url, options: .alwaysMapped)

print (data.count)

}


Looks like a bug in 'bridging' ? Many thanks for your suggestion about trying in ObjC - it put me on the right track 🙂

Great.


So you could complement your bug report.


And don't forget to mark this thread as closed.

Data(contentsOf: url, options: .alwaysMapped)
doesn't ‘map’

I’m glad you found a workaround for this.

I suspect that this is related to the issue described in this Swift Forums post, where

NSData
is no longer bridging to
Data
. That issues seems like a minor inconvenience, but if it’s breaking
.alwaysMapped
that’s a serious issue. I can reproduce the problem myself, and so I filed my own bug about it (r. 49603450).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I've added details of the workaround to my bug report (r. 49561849

Thanks for looking at this - I'm glad you could replicate the issue. Hopefully it'll get fixed quickly 🙂

Unfortunately not fixed in XCode 10.2.1

Unfortunately not fixed in Xcode 10.2.1

Right. Now that Swift’s runtime is included in the OS, a fix for this will most likely ship as part of an OS update rather than part of Xcode release.

ps After some spelunking through the Swift open source, I think I found the pull request for this fix.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

"Right. Now that Swift’s runtime is included in the OS, a fix for this will most likely ship as part of an OS update rather than part of Xcode release."


Not fixed in macOS 10.14.5 😟

Today the condition is April 12, 2022, macOS 12.3.1, Xcode 13.3.1 just installed and Swift 5.6.1. Is there a work around for this?

I have been working on this for a few days, why is this problem NOT fixed by now? My "User Experience" is not very good, for whoever cares.

I'm having the same issue too. I need to get the MP4 file path from a local file.

It worked for me: change URL(string: path) to URL(fileURLWithPath: path)

example: if let path = filePath { let URL = URL(fileURLWithPath: path) // let URL = URL(string: path) do { let data = try Data (contentsOf: URL, options: .alwaysMapped) print(" data: (data.count)") } catch let error { print(" data nil") } }

Data (contentsOf: url, options: .alwaysMapped) doesn't 'map' in Xcode 10.2/Swift 5
 
 
Q