Swift - converting from ConstUnsafePointer<()> - ios

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]")
}

Related

Get input from UITextField(HEX) data and send out via bluetooth LE

i'm currently trying to get the data(HEX) from UITextfield, and i would want to store the data in UInt8 i'm currently doing this.
let incomingdata = UInt8(textfield.text!)
by doing this it returns nil. The purpose i'm doing this because after i gets the data from UITextField, i would send out the data in UInt8 format via bluetooth. Can someone suggest me how can i do that?Thank you
I update my question, in short i input 72AE in UITextField, i get the text in string format, but in the end i wan to convert it to UInt8 and it is in 0x72, 0xAE
In short, i'm converting HexString to UInt8
You can convert a hex value to an Int with this code.
let hex2int = String(format:"%2X", hex)
Quite easy.
I might be misunderstanding your question, but if you would like to covert string into array of Int, this is how you might go about it.
let stringFromTextField = "anything"
// Convert stringFromTextField to NSString and then then convert it to NSData
// in encoding of your choosing
if let data = NSString(string: stringFromTextField).dataUsingEncoding(NSUTF8StringEncoding) where data.length > 1 {
// Create buffer
var buffer: Int = 0
let bufferSize = 1
let adjustedDataLenght = data.length / bufferSize
var yourInt = [Int]()
// Loop over data and get bytes
for i in 0..<adjustedDataLenght {
data.getBytes(&buffer, range: NSRange(location: i, length: bufferSize))
yourInt.append(buffer)
}
// Here are your ints
print(yourInt)
}
Works in playground. Hope it helped.

How to create a NSData from hex value in swift

I'm a swift/iOS newbie and I have a problem to solve.
I'm trying to get data from Texas Instrument SensorTag 2. To activate a sensor, following the instructions, I have to write a binary string in the configuration bank of my sensor.
I have this snippet of code:
if SensorTag.validConfigCharacteristic(thisCharacteristic) {
// Enable Sensor
let enableByte = SensorTag.getEnableByteFor(thisCharacteristic)
self.sensorTagPeripheral.writeValue(enableByte, forCharacteristic: thisCharacteristic, type: CBCharacteristicWriteType.WithResponse)
}
and I write the function to get the value to write. enableByte type is NSData.
class func getEnableByteFor(thisCharacteristic: CBCharacteristic) -> NSData {
print(thisCharacteristic.UUID)
var enableValue = 0
if thisCharacteristic.UUID == MovementConfigUUID {
enableValue = ...
} else { // any other activation
enableValue = 1
}
return NSData(bytes: &enableValue, length: sizeof(UInt8))
}
For every sensor I have to write a 1 if I want to enable the sensor and 0 if I want to disable it, but with the movement sensor I have to write according to this guide 16 bits (2 byte). For my config I have to write a binary value of 0000000001111111, 0x007F. How can I initialize a NSData object with value 0x007F?
Try this:
let bytes : [CChar] = [0x0, 0x7F]
let data = NSData(bytes: bytes, length: 2)
NSData(bytes:length:) creates an NSData object from a byte stream. In Objective-C, this byte stream is of type char *. The Swift equivalent is [CChar]. The question (and another answer) use an Int to represent this byte stream. This is wrong and dangerous.
var enableValue = 0 // enableValue is a 64-bit integer
NSData(bytes: &enableValue, length: sizeof(UInt8)) // this trims it to the first 8 bits
It works because x86 uses Little Endian encoding, which puts the least significant byte first. It will fail on PowerPC, which uses Big Endian. ARM uses switchable endianness so it may or may not fail there. When the situation call for exact bit layout, you should not rely on the architecture's endianness:
class func getEnableByteFor(thisCharacteristic: CBCharacteristic) -> NSData {
print(thisCharacteristic.UUID)
let enableValue : [CChar]
if thisCharacteristic.UUID == MovementConfigUUID {
enableValue = [0x0, 0x7F]
} else { // any other activation
enableValue = [0x1]
}
return NSData(bytes: enableValue, length: enableValue.count)
}
Much shorter solution taking in account byte order:
NSData(bytes: [UInt16(0x007F).bigEndian], length: 2)
Now there is nothing wrong with using [UInt16] as byte stream because UInt16 has bigEndian property that returns the big-endian representation of the integer changing byte order if necessary.

NSData from UInt8

I have recently found a source code in swift and I am trying to get it to objective-C. The one thing I was unable to understand is this:
var theData:UInt8!
theData = 3;
NSData(bytes: [theData] as [UInt8], length: 1)
Can anybody help me with the Obj-C equivalent?
Just to give you some context, I need to send UInt8 to a CoreBluetooth peripheral (CBPeripheral) as UInt8. Float or integer won't work because the data type would be too big.
If you write the Swift code slightly simpler as
var theData : UInt8 = 3
let data = NSData(bytes: &theData, length: 1)
then it is relatively straight-forward to translate that to Objective-C:
uint8_t theData = 3;
NSData *data = [NSData dataWithBytes:&theData length:1];
For multiple bytes you would use an array
var theData : [UInt8] = [ 3, 4, 5 ]
let data = NSData(bytes: &theData, length: theData.count)
which translates to Objective-C as
uint8_t theData[] = { 3, 4, 5 };
NSData *data = [NSData dataWithBytes:&theData length:sizeof(theData)];
(and you can omit the address-of operator in the last statement,
see for example How come an array's address is equal to its value in C?).
In Swift 3
var myValue: UInt8 = 3 // This can't be let properties
let value = Data(bytes: &myValue, count: MemoryLayout<UInt8>.size)
In Swift,
Data has a native init method.
// Foundation -> Data
/// Creates a new instance of a collection containing the elements of a
/// sequence.
///
/// - Parameter elements: The sequence of elements for the new collection.
/// `elements` must be finite.
#inlinable public init<S>(_ elements: S) where S : Sequence, S.Element == UInt8
#available(swift 4.2)
#available(swift, deprecated: 5, message: "use `init(_:)` instead")
public init<S>(bytes elements: S) where S : Sequence, S.Element == UInt8
So, the following will work.
let values: [UInt8] = [1, 2, 3, 4]
let data = Data(values)

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.

Resources