Search code examples
swiftuploadchunks

Splitting Data into chunks in Swift 3


I need to send images read from the Photo Library over the wire - in chunks of 5MB.

I read an image from the library using: PHImageManager.requestImageData(for:options:resultHandler:) and get a Data object. I would then like to efficiently split the data into chunks (without copying the memory). What would be the best way to do that?

This is what I have so far:

    imageData.withUnsafeBytes { (unsafePointer: UnsafePointer<UInt8>) -> Void in

        let totalSize = data.endIndex
        var offset = 0

        while offset < totalSize {
            let chunkSize = offset + uploadChunkSize > totalSize ? totalSize - offset : uploadChunkSize
            let chunk = Data(bytesNoCopy: unsafePointer, count: chunkSize, deallocator: Data.Deallocator.none)

            // send the chunk...

           offset += chunkSize
        }
    }

However I get this error at compile time:

Cannot convert value of type 'UnsafePointer' to expected argument type 'UnsafeMutableRawPointer'

If I use mutableBytes:

data.withUnsafeMutableBytes { (unsafePointer: UnsafeMutablePointer<UInt8>) -> Void in... }

Then I get the compile-time error:

Cannot use mutating member on immutable value: 'data' is a 'let' constant

Which is correct, since I do not really want to make changes to the image data. I only want to send one chunk of it at a time.

Is there a better way to do this?


Solution

  • Hi there!

    I need the same behaviour and came up with this solution. You pointed the error right and the solution is just to create the UnsafeMutableRawPointer with the UnsafePointer address. It's the fastest solution I found yet.

    One other thing is to add the offset to the base address of the mutRawPointer when you create a chunk.

    50MB data in 2MB chunks takes ~ 0.009578s

    func createChunks(forData: Data) {
    
        imageData.withUnsafeBytes { (u8Ptr: UnsafePointer<UInt8>) in
            let mutRawPointer = UnsafeMutableRawPointer(mutating: u8Ptr)
            let uploadChunkSize = 2097152
            let totalSize = imageData.count
            var offset = 0
    
            while offset < totalSize {
    
                let chunkSize = offset + uploadChunkSize > totalSize ? totalSize - offset : uploadChunkSize
                let chunk = Data(bytesNoCopy: mutRawPointer+offset, count: chunkSize, deallocator: Data.Deallocator.none)
                offset += chunkSize
            }
        }
    }