Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSData to Data swift 3

Tags:

ios

swift3

var dataFile: NSData = NSMutableData.init(data: wav.subdataWithRange(NSRange.init(location: currentByte, length: wavDataSize)))

How to me convert this code to using Data with Swift 3? Or how to parseNSRange to Range

like image 335
Nguyen van Duc Avatar asked Sep 23 '16 07:09

Nguyen van Duc


2 Answers

Separating Data into smaller Data instances

Assumptions

This answer is the Swift 3 & 4 equivalent of the code in the question. It will produce the same result, dataFile, given the same input values: wav, currentByte and wavDataSize assuming none of the surrounding code changes.

I did not make assumptions about what the variables: wav, dataFile, currentByte or wavDataSize mean. To avoid the variable names implying things not stated in the question, I will use the following names instead: sourceData, subdata, rangeStartByte and subdataLength. I assume the code (not shown in the question) surrounding this would assure that rangeStartByte and subdataLength were in a valid range to avoid an error.

 

Converting NSRange to Range<Int>

The Swift 2 implementation from the question uses an NSRange defined by a start point and a length like this:

NSRange.init(location: rangeStartByte, length: subdataLength)

The Swift 3 & 4 implementation I propose creates an equivalent Range<Int> defined by a start point and end point like this:

rangeStartByte ..< (rangeStartByte + subdataLength)

I converted an app from Swift 2.2 to 3 which used similar code to upload a photo in smaller chunks. During conversion we missed this nuance and used the Swift 2 implementation's length in place of the Swift 3 & 4 implementation's end point. This caused a defect that was tricky to resolve. The first iteration succeeded but subsequent iterations failed.

Another answer implements the issue I just described as the solution. It uses subdataLength from the length of the Swift 2 range as the end point of the Swift 3 & 4 range. This will produce the same result in the special case where currentByte is 0 and subdataLength is <= the length of the NSData instance (which is why the first iteration succeeded in the issue I described). That assumption was not explicitly stated in the question and yields a less flexible solution for others.

 

Swift 3 & 4 equivalent

var subdata = sourceData.subdata(in: rangeStartByte ..< (rangeStartByte + subdataLength))

Swift 2.2

(code from question with updated variable names)

var subdata: NSData = NSMutableData.init(data: sourceData.subdataWithRange(NSRange.init(location: rangeStartByte, length: subdataLength)))

 

Runnable sample code

I've included sample code you can run in a playground demonstrating how this line of code could be used to separate a Data instance into smaller Data instances. The source Data instance is created from a string "ABCDEFGHIJKL". This instance is separated into smaller Data instances of length 5.

Swift 3 & 4 with context

import UIKit

var sourceString = "ABCDEFGHIJKL"
let sourceData = sourceString.data(using: String.Encoding.utf8)!  // sourceData is equivalent to "wav" from question

var rangeStartByte = 0  // rangeStartByte is equivalent to "currentByte" from question
let maxSubdataLength = 5
let dataLength = sourceString.lengthOfBytes(using: String.Encoding.utf8)
precondition(maxSubdataLength <= dataLength, "maxSubdataLength must be <= to dataLength")

while rangeStartByte < dataLength {
    // subdataLength is equivalent to "wavDataSize" from question
    let subdataLength = min(maxSubdataLength, dataLength - rangeStartByte)

    // subdata is equivalent to "dataFile" from question
    let subdata = Data(sourceData.subdata(in: rangeStartByte ..< (rangeStartByte + subdataLength)))

    let subdataString = String(data: subdata, encoding: String.Encoding.utf8) ?? ""
    print("'\(subdataString)'")
    rangeStartByte += subdataLength
}

The result is:

'ABCDE'
'FGHIJ'
'KL'

 

Swift 2.2 with context

import UIKit

var sourceString = "ABCDEFGHIJKL"
let sourceData = sourceString.dataUsingEncoding(NSUTF8StringEncoding)!  // sourceData is equivalent to "wav" from question

var rangeStartByte = 0  // rangeStartByte is equivalent to "currentByte" from question
let maxSubdataLength = 5
let dataLength = sourceString.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
precondition(maxSubdataLength <= dataLength, "maxSubdataLength must be <= to dataLength")

while rangeStartByte < dataLength {
    // subdataLength is equivalent to "wavDataSize" from question
    let subdataLength = min(maxSubdataLength, dataLength - rangeStartByte)

    // subdata is equivalent to "dataFile" from question
    let subdata: NSData = NSMutableData.init(data: sourceData.subdataWithRange(NSRange.init(location: rangeStartByte, length: subdataLength)))

    let subdataString = String(data: subdata, encoding: NSUTF8StringEncoding) ?? ""
    print("'\(subdataString)'")
    rangeStartByte += subdataLength
}

The result is:

'ABCDE'
'FGHIJ'
'KL'

 

Swift 3 & 4 using NSRange

pedrouan's answer uses NSRange like this:

var subdata: Data = Data(sourceData.subdata(with: NSRange(location: rangeStartByte, length: subdataLength)))

I could not get this to compile initially so I disregarded it. Now I realize that it works if sourceData is declared or cast at NSData and not Data

If you want to run this approach within the "Swift 3 & 4 with context" sample code above, replace the corresponding code in that sample with this:

// subdata is equivalent to "dataFile" from question
let sourceNSData = sourceData as NSData
let subdata = sourceNSData.subdata(with: NSRange(location: rangeStartByte, length: subdataLength))

I'm trying not to use "NS" classes like NSRange where possible so I favored the solution using a Swift Range.

like image 153
Mobile Dan Avatar answered Oct 12 '22 23:10

Mobile Dan


Some 'little' changes in Swift 3.0

var dataFile: Data = Data(wav.subdata(with: NSRange(location: currentByte, length: wavDataSize)))
like image 36
pedrouan Avatar answered Oct 12 '22 23:10

pedrouan