NSData to Data swift 3 - ios

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

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.

Some 'little' changes in Swift 3.0
var dataFile: Data = Data(wav.subdata(with: NSRange(location: currentByte, length: wavDataSize)))

In Swift 3 your code will be like this one:
var dataFile = sourceData.subdata(in: currentByte..<currentByte+wavDataSize)

Related

How do i fill up the Textbox input reminder bytes with ascii spaces in Swift 4?

Hi I am trying to use a UITextbox and restrict the number of characters input by the user to 10.
I have looked at using the below link,
Max length UITextField
My Questions,
1.Its not working as characters are depreciated in Swift 4 so the string.characters.count is throwing an error so what would be a workaround in Swift 4?
2.After the user enters his x number of characters, I want to make the reminders that is (limitlength - x) into empty spaces (ascii for space = 32 in decimal) so that reminder of the byte array is equal to dec 32.
I have tried doing this,
if let receivedData = rxCharacteristic?.value
let myByteArray = Array(receivedData) {
let b0 = myByteArray[0]
let b1 = myByteArray[1]
let b2 = myByteArray[2]
let b3 = myByteArray[3]
//Now reading data from textbox input
var userdata = textbox.text
let userdataarray: [UInt8] = Array(userdata!.utf8)
//I tried putting values into myByteArray as below
userdataarray[0] = myByteArray[0]
userdataarray[1] = myByteArray[1]
userdataarray[2] = myByteArray[2]
//The last value in myByteArray will remain unchanged so I'm not overwriting it
So from the question when I try to input textbox data less than its length its throwing an index out of range exception. But I went a little extreme to try the below code.
if(userdataarray[0] != 0 && userdataarray[0] != nil)
{
userdataarray[0] = myByteArray[0]
}
else
{
userdataarray[0] = 32 //Which is space in ascii
userdataarray[0] = myByteArray[0]
}
I don't think it worked but wanted to check on how its properly done?
If I understand your question correctly, then your trials are very much overenginering. In Swift you can just add characters to a String (as long as it is declared as var that is). This just boils down to
let orig = "Hello World"
var copy = orig
while copy.count < 15 {
copy.append(" ")
}
let dta = copy.data(using:.isoLatin1)!
let arr = Array(dta)
Since Swift is using some Unicode-encoding internally it is probably crucial to convert your String to data using a specific encoding if you plan to "directly" transfer it to some device that is limited to a certain character set. Still a lot less code than what you provided.

Need to update Swift code to the newest version

Prior to the new Swift version I was using the following code in my app.
Now it launches an exception.
for (i, in 0 ..< len){
let length = UInt32 (letters.length)
let rand = arc4random_uniform(length)
randomString.appendFormat("%C", letters.characterAtIndex(Int(rand)))
}
XCode says:
Expected pattern
Expected "," separator
Expected "in" after for-each pattern
Expected SequenceType expression for for-each loop
Changing the code with the proposed solutions doesn't change the exceptions thrown.
Any help is welcome to update the code to the current Swift version.
For for syntax you are using have been deprecated, and should be changed to
for _ in 0..<len
// rest of your code
the question already has correct answer still i have converted it so posting here may be some get help from it
let len = 5
let letters:NSString = "str"
for i in 0 ..< len {
let length = UInt32 (letters.length)
let rand = arc4random_uniform(length)
let randomString:NSMutableString = ""
randomString.appendFormat("%C", letters.characterAtIndex(Int(rand)))
}
As some of the variable are not shown in the code i have made them based on the parameters

use fscanf() function in swift code

In objective c, I use fscanf to read stream from file and assign the value to variables:
int count;
char type[5];
fscanf(myFile, “count is %d, type is %4s ”, &count, type)
I want to do the same thing in swift code, I tried:
//ERROR: Type annotation missing in pattern
//What type should I use for `count`?
var count
//ERROR: consecutive statement on a line must be separated by ‘;’
var type[5] : char
fscanf(myFile, “count is %d, type is %4s ”, &count, type)
But I got compiler errors showing above. What is the correct way to use fscanf in swift ?
If you know any swift way to achieve the same thing (without using fscanf), it would be great too!
I recommend you use Foundation framework solution for reading/writing file data. A sample code to read contents of files which I used in my app to stream file into NSData:
if let fileHandle = NSFileHandle(forReadingAtPath: "path/to/file") {
fileHandle.seekToFileOffset(0)
var data = fileHandle.readDataOfLength(5)
var chars = [UInt8](count: 5, repeatedValue: 0)
data.getBytes(&chars, length: 5)
fileHandle.closeFile()
}
In case you need read Int64 data from file at a specific location:
if let fileHandle = NSFileHandle(forReadingAtPath: "path/to/file") {
fileHandle.seekToFileOffset(0)
var data = fileHandle.readDataOfLength(500)
var intFetched: Int64 = 0
let location = 100 // start at 101st character of file
data.getBytes(&intFetched, range: NSMakeRange(location, 8))
println(intFetched.littleEndian)
fileHandle.closeFile()
}

Convert NSData bytes to custom object

I am starting a project to create an iOS app to communicate with a device over BLE. Being a new effort, I am trying to do this is Swift if possible. The interface uses GATT and an existing set of custom message structures. I get to a point where I have the data from BLE in an NSData object. I'd like to cast it or directly convert it to my message structure in a fairly generic way.
I know that I can extract the data by hand either directly from the byte array from the NSData object or using an NSInputStream. While that works, it could be a maintenance issue and the interface has a number of different messages in it.
Is there an easier ways to do this?
I'd be willing to create the message structures in Objective-C and do the casting there, but my knowledge of Objective-C is not much better than my knowledge of Swift.
Some sample code of what I've been playing in my playground is shown below. It all works as expected.
func getBytesFromNSData(data: NSData, start: Int) -> [UInt8] {
let count = data.length / sizeof(UInt8)
let remaining = count - start
let range = NSMakeRange(start, remaining )
var dataArray = [UInt8](count: remaining, repeatedValue: 0)
data.getBytes(&dataArray, range: range)
return dataArray
}
class TestObject {
var a: Byte
var b: Byte
init() {
a = 0x01
b = 0x02
}
init(data: NSData) {
let dataBytes = getBytesFromNSData(data, 0)
a = Byte(dataBytes[0])
b = Byte(dataBytes[1])
}
func populateFromStream(data: NSData) {
var stream = NSInputStream(data: data)
stream.open()
var bytesRead = stream.read(&a, maxLength: 1)
println("\(bytesRead)")
bytesRead = stream.read(&b, maxLength: 1)
println("\(bytesRead)")
}
func toArray() -> [Byte] {
var result = [Byte](count: 2, repeatedValue: 0)
result[0] = a
result[1] = b
return result
}
}
let test = TestObject()
let testArray = test.toArray()
let length = testArray.count
let testData = NSData(bytes: testArray, length: length)
println("\(testData)")
let testIn = [ Byte(0x0d), Byte(0x0e) ]
let testDataIn = NSData(bytes: testIn, length: testIn.count)
println("\(testDataIn)")
let testConstructor = TestObject(data: testDataIn)
var testObject = TestObject()
testObject.populateFromStream(testDataIn)
I found a method that is fairly generic that may work is some cases.
Create an Objective-C riding header
Create the data structure as an Objective-C structure
Import the header with the data structure into the bridging header
Assuming that you have a struct called Foo and an NSData object called rawData:
Use the following code to get an cast a pointer.
let structureSize = sizeof(Foo)
var myObject = UnsafeMutablePointer<Foo>.alloc(1).memory
rawData.getbytes(&myObject, length: structureSize)
This will not work in all instances and unfortunately does work in my particular case. The specific problems I have found are:
The Objective-C structure is word aligned. If your structure is not properly aligned to work boundaries, you may have a size that is incorrect. (something I ran into in my particular interface)
If you and dealing with a system that doesn't send the data in the same order you are expecting, this will not handle any byte order conversion, that would still need to be done and the structure would possibly need to be reordered to compensate. That work might negate any saving from this method.
This is the most concise method I have found if it happens to work with your particular message formats.

Swift - converting from ConstUnsafePointer<()>

I'm on beta 3. Consider the following Objective-C line:
const uint8_t *reportData = [data bytes];
where data is a NSData object.
How would this line be re-written in Swift?
data.bytes is of type ConstUnsafePointer<()>, and while there's plenty of documentation on how to create a pointer type in Swift, there isn't much info on how to work with them.
edit:
To add some context, I'm trying to port Apple's HeartRateMonitor sample code to Swift. This code interacts with BLE heart rate monitors. This code I'm working on translates the data received by the Bluetooth system into an int for use in the UI. The data received from BT is expected to be an array of uints, element 0 is used to check for a flag and element 1 contains the value.
Here's the same Objective-C line in context:
const uint8_t *reportData = [data bytes];
uint16_t bpm = 0;
if ((reportData[0] & 0x01) == 0)
{
/* uint8 bpm */
bpm = reportData[1];
}
What you were looking for was how to convert NSData to an array of UInt8. Here's how.
import Foundation
let path = "/etc/csh.cshrc" // something existent
let data = NSData(contentsOfFile: path)
var aofb = [UInt8](count:data.length, repeatedValue:0)
data.getBytes(&aofb, length:data.length)
for c in aofb {
let s = UnicodeScalar(Int(c)).escape(asASCII:true)
println("\(c):\(s)")
}
Just built following code (Note code below works on Beta 3, ConstUnsafePointer<()> needs to be changed to COpaquePointer in order to work on Beta 2, please see edit history for more information)
var dataPath = NSBundle.mainBundle().pathForResource("TestData", ofType: "") // What I have in TestData is "GREETINGS WORLD"
var originalData = NSData(contentsOfFile: dataPath)
var dataLength = originalData.length
println("original data: \(originalData)") // Output original data
// Data to bytes
var reportBytes: ConstUnsafePointer<()> = originalData.bytes
var bytesToString = NSString(bytes: reportBytes, length: dataLength, encoding: NSUTF8StringEncoding)
println("string from bytes: \(bytesToString)")
// Bytes to data
var bytesToData = NSData(bytes: reportBytes, length: dataLength)
println("data from bytes: \(bytesToData)")
Console log
original data: <47524545 54494e47 5320574f 524c44>
string from bytes: GREETINGS WORLD
data from bytes: <47524545 54494e47 5320574f 524c44>
Also found this may help
ConstUnsafePointer<T>
/// This type stores a pointer to an object of type T. It provides no
/// automated memory management, and therefore the user must take care
/// to allocate and free memory appropriately.
Hope this shed light.
Looking at handling bluetooth heart rate monitors in Swift now I found the simplest way to get the NSData byte values to UInt8 format:
let bytes = UnsafePointer<UInt8>(data.bytes)
if bytes[0] & 0x01 == 0 {
NSLog("BPM \(bytes[1]")
}

Resources