简体   繁体   中英

NSData to Data swift 3

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 parse NSRange to Range

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.

Swift 3.0中的一些“小”变化

var dataFile: Data = Data(wav.subdata(with: NSRange(location: currentByte, length: wavDataSize)))

在Swift 3中,您的代码将如下所示:

var dataFile = sourceData.subdata(in: currentByte..<currentByte+wavDataSize)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM