Related
I am using this article to communicate with an IoT sensor via BLE. In the article, this quote is mentioned:
The first two bytes do not seem to belong to the data (probably a prefix to denote that it is a data packet), but the remaining ones are more interesting. For the accelerometer, we get three signed 16 bit integers (little endian), which can simply be scaled to the range we set up to get our setup sequence. So the +/-2^15 range of the signed 16bit integer corresponds to the +/-16g, resulting in a factor 1/2048. To get the acceleration in m/s², we apply a factor of 9.81/2048. So, the corresponding bluetooth part reads:
<output char="326a9006-85cb-9195-d9dd-464cfbbae75a" conversion="int16LittleEndian" offset="2" length="2">accXRaw</output>
<output char="326a9006-85cb-9195-d9dd-464cfbbae75a" conversion="int16LittleEndian" offset="4" length="2">accYRaw</output>
<output char="326a9006-85cb-9195-d9dd-464cfbbae75a" conversion="int16LittleEndian" offset="6" length="2">accZRaw</output>
To read this code, I am running this Swift code:
private func sensor(from characteristic: CBCharacteristic) {
guard let characteristicData = characteristic.value,
let _ = characteristicData.first else { return }
let data = characteristic.value!
var values = [UInt8](repeating: 0, count: data.count)
data.copyBytes(to: &values, count: data.count)
print("values = \(values)")
}
The result once I do a print is:
values = [3, 4, 250, 255, 199, 249, 91, 191]
Alike the article mentions, I can confirm that the first two bytes do not belong to any data, and are consistently repeating. Bytes values[2-7] are constantly changing, which makes me more confident that the pairs represent accXRaw, accYRaw, and accZRaw. What I want to do now is convert the pairs to doubles.
For example:
values[2], values[3] = [250 255] (accXRaw)
values[4], values[5] = [199 249] (accYRaw)
values[6], values[7] = [91 191] (accZRaw)
In the article, the author does this via a int16 little endian. I want to do the same with swift 5, but not sure if I am doing it correctly. Here is my code:
let xAxis = Float(bitPattern: UInt32(littleEndian: [values[2], values[3], 0x00, 0x00].withUnsafeBytes { $0.load(as: UInt32.self) }))
let yAxis = Float(bitPattern: UInt32(littleEndian: [values[4], values[5], 0x00, 0x00].withUnsafeBytes { $0.load(as: UInt32.self) }))
let zAxis = Float(bitPattern: UInt32(littleEndian: [values[6], values[7], 0x00, 0x00].withUnsafeBytes { $0.load(as: UInt32.self) }))
print("x=\(xAxis), y=\(yAxis), z=\(zAxis)");
The resulting printout is:
values = [3, 4, 250, 255, 199, 249, 91, 191]
x=9.1827e-41, y=8.9603e-41, z=6.8645e-41
These numbers just look weird, and I suspect I am doing something wrong. Am I reading the byte pairs correctly ( at least in line with the article ) ? If not, what mistakes did I make?
Your issue there is that you are not suppose to initialize your Float using the bitPattern initializer and/or use the UInt32(littleEndian:) initializer. What you need is to convert those 2 bytes to Int16, coerce it to Float and then multiply by the factor of 9.81/2048 to get its acceleration.
Expanding on that, you can create a Numeric initializer that takes an object that conforms to DataProtocol (Data or Bytes [UInt8]):
extension Numeric {
init<D: DataProtocol>(_ data: D) {
var value: Self = .zero
let size = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} )
assert(size == MemoryLayout.size(ofValue: value))
self = value
}
}
Then you can initialize your Int16 object with the subdata (two bytes).
let bytes: [UInt8] = [3, 4, 250, 255, 199, 249, 91, 191]
let xData = bytes[2..<4]
let yData = bytes[4..<6]
let zData = bytes[6..<8]
let factor: Float = 9.81/2048
let xAxis = Float(Int16(xData)) * factor
let yAxis = Float(Int16(yData)) * factor
let zAxis = Float(Int16(zData)) * factor
print("x:", xAxis, "y:", yAxis, "z:", zAxis) // x: -0.028740235 y: -7.6305327 z: -79.27036
I want to convert a standard integer in a list of UInt8 in swift.
var x:Int = 2019
2019 can be written (for example) in hexadecimal 7E3 so i want some kind of function that converts is to a list of UInt8s which looks like this.
var y:[Uint8] = [0x07, 0xE3]
I already found this: Convert integer to array of UInt8 units but he/she is convertign the ascii symbols of the number not the number itself. So his example 94887253 should give a list like [0x05, 0xA7, 0xDD, 0x55].
In the best case the function i'm looking for has some kind of usage so that i can also choose the minimum length of the resulting array so that for example
foo(42, length:2) -> [0x00, 0x2A]
or
foo(42, length:4) -> [0x00, 0x00, 0x00, 0x2A]
You could do it this way:
let x: Int = 2019
let length: Int = 2 * MemoryLayout<UInt8>.size //You could specify the desired length
let a = withUnsafeBytes(of: x) { bytes in
Array(bytes.prefix(length))
}
let result = Array(a.reversed()) //[7, 227]
Or more generally, we could use a modified version of this snippet:
func bytes<U: FixedWidthInteger,V: FixedWidthInteger>(
of value : U,
to type : V.Type,
droppingZeros: Bool
) -> [V]{
let sizeInput = MemoryLayout<U>.size
let sizeOutput = MemoryLayout<V>.size
precondition(sizeInput >= sizeOutput, "The input memory size should be greater than the output memory size")
var value = value
let a = withUnsafePointer(to: &value, {
$0.withMemoryRebound(
to: V.self,
capacity: sizeInput,
{
Array(UnsafeBufferPointer(start: $0, count: sizeInput/sizeOutput))
})
})
let lastNonZeroIndex =
(droppingZeros ? a.lastIndex { $0 != 0 } : a.indices.last) ?? a.startIndex
return Array(a[...lastNonZeroIndex].reversed())
}
let x: Int = 2019
bytes(of: x, to: UInt8.self, droppingZeros: true) // [7, 227]
bytes(of: x, to: UInt8.self, droppingZeros: false) // [0, 0, 0, 0, 0, 0, 7, 227]
I couldn't find a solution to this problem in Swift (all of them are Objective-C, and they deal with pointers which I don't think exist in Swift in the same form). Is there any way to convert a NSData object into an array of bytes in the form of [Uint8] in Swift?
You can avoid first initialising the array to placeholder values, if you go through pointers in a slightly convoluted manner, or via the new Array constructor introduced in Swift 3:
Swift 3
let data = "foo".data(using: .utf8)!
// new constructor:
let array = [UInt8](data)
// …or old style through pointers:
let array = data.withUnsafeBytes {
[UInt8](UnsafeBufferPointer(start: $0, count: data.count))
}
Swift 2
Array(UnsafeBufferPointer(start: UnsafePointer<UInt8>(data.bytes), count: data.length))
Swift 5 Solution
Data to [bytes]
extension Data {
var bytes: [UInt8] {
return [UInt8](self)
}
}
[bytes] to Data
extension Array where Element == UInt8 {
var data: Data {
return Data(self)
}
}
It's funny but exist more simple solution. Works in Swift 3. Surely. I've used this today.
data: Data // as function parameter
let byteArray = [UInt8](data)
That's all! :)
NSData easily bridged to Data.
UPDATE: (due to Andrew Koster comment)
Swift 4.1, Xcode 9.3.1
Just has been rechecked - all works as expected.
if let nsData = NSData(base64Encoded: "VGVzdFN0cmluZw==", options: .ignoreUnknownCharacters) {
let bytes = [UInt8](nsData as Data)
print(bytes, String(bytes: bytes, encoding: .utf8))
Output: [84, 101, 115, 116, 83, 116, 114, 105, 110, 103] Optional("TestString")
You can use the getBytes function of NSData to get the byte array equivalent.
As you did not provide any source code, I will use a Swift String contents that has been converted to NSData.
var string = "Hello World"
let data : NSData! = string.dataUsingEncoding(NSUTF8StringEncoding)
let count = data.length / sizeof(UInt8)
// create an array of Uint8
var array = [UInt8](count: count, repeatedValue: 0)
// copy bytes into array
data.getBytes(&array, length:count * sizeof(UInt8))
println(array)
Swift 3/4
let count = data.length / MemoryLayout<UInt8>.size
// create an array of Uint8
var byteArray = [UInt8](repeating: 0, count: count)
// copy bytes into array
data.getBytes(&byteArray, length:count)
Swift 3/4
let data = Data(bytes: [0x01, 0x02, 0x03])
let byteArray: [UInt8] = data.map { $0 }
You can try
extension Data {
func toByteArray() -> [UInt8]? {
var byteData = [UInt8](repeating:0, count: self.count)
self.copyBytes(to: &byteData, count: self.count)
return byteData
}
}
swift 4 and image data to a byte array.
func getArrayOfBytesFromImage(imageData:Data) ->[UInt8]{
let count = imageData.count / MemoryLayout<UInt8>.size
var byteArray = [UInt8](repeating: 0, count: count)
imageData.copyBytes(to: &byteArray, count:count)
return byteArray
}
I have this output from NSData: <00000100 84000c00 071490fe 4dfbd7e9>
So how could I byte reverse it in Swift and have this output: <00000001 0084000c 1407fe90 fb4de9d7>?
This should work to swap each pair of adjacent bytes in the data.
The idea is to interpret the bytes as an array of UInt16 integers
and use the built-in byteSwapped property.
func swapUInt16Data(data : NSData) -> NSData {
// Copy data into UInt16 array:
let count = data.length / sizeof(UInt16)
var array = [UInt16](count: count, repeatedValue: 0)
data.getBytes(&array, length: count * sizeof(UInt16))
// Swap each integer:
for i in 0 ..< count {
array[i] = array[i].byteSwapped // *** (see below)
}
// Create NSData from array:
return NSData(bytes: &array, length: count * sizeof(UInt16))
}
If your actual intention is to convert data from an (external)
big-endian representation to the host (native) byte order (which happens to be little-endian on all current iOS and OS X devices) then you should replace *** by
array[i] = UInt16(bigEndian: array[i])
Example:
var bytes : [UInt8] = [1, 2, 3, 4, 5, 6, 7, 8]
let data = NSData(bytes: &bytes, length: bytes.count)
print(data)
// <01020304 05060708>
print(swapUInt16Data(data))
// <02010403 06050807>
Update for Swift 3: The generic withUnsafeMutableBytes()
methods allows to obtain a UnsafeMutablePointer<UInt16> to the bytes
and modify them directly:
func swapUInt16Data(data : Data) -> Data {
var mdata = data // make a mutable copy
let count = data.count / MemoryLayout<UInt16>.size
mdata.withUnsafeMutableBytes { (i16ptr: UnsafeMutablePointer<UInt16>) in
for i in 0..<count {
i16ptr[i] = i16ptr[i].byteSwapped
}
}
return mdata
}
Example:
let data = Data(bytes: [1, 2, 3, 4, 5, 6, 7, 8])
print(data as NSData) // <01020304 05060708>
let swapped = swapUInt16Data(data: data)
print(swapped as NSData) // <02010403 06050807>
CoreFoundation has CFSwapInt32BigToHost and CFSwapInt32LittleToHost and other swap functions.
In swift3 it looks like this
struct FileHeader {
var magicNumber: UInt32 = 0
var count: UInt32 = 0
var width: UInt32 = 0
var height: UInt32 = 0
static func create(data: Data) -> FileHeader {
let structSize = MemoryLayout<FileHeader>.size
assert(data.count >= structSize)
var result = FileHeader()
let nsdata = data as NSData
nsdata.getBytes(&result, range: NSRange(location: 0, length: structSize))
result.magicNumber = CFSwapInt32BigToHost(result.magicNumber)
result.count = CFSwapInt32BigToHost(result.count)
result.width = CFSwapInt32BigToHost(result.width)
result.height = CFSwapInt32BigToHost(result.height)
return result
}
}
For someone may want to restrict the byte pattern, it would be a solution:
func swap<U:IntegerType>(data:NSData,_ :U.Type) -> NSData{
var length = data.length / sizeof(U)
var bytes = [U](count: length, repeatedValue: 0)
data.getBytes(&bytes, length: data.length)
// since byteSwapped isn't declare in any protocol, so we have do it by ourselves manually.
var inverse = bytes.enumerate().reduce([U](count: length, repeatedValue: 0)) { (var pre, ele) -> [U] in
pre[length - 1 - ele.index] = ele.element
return pre
}
return NSData(bytes: inverse, length: data.length)
}
for example:
swap(data:data,UInt8.self)
//before <0c20207b 17>
//after <177b2020 0c>
swap(data:anotherData,UInt16.self)
//before <8e004c01 84008f05 0701>
//after <07018f05 84004c01 8e00>
This is an application of Sieve of Eratosthenes written in Swift.
I'm able to write the output on a file, but I'm only able to start by manually passing [primesList].
Instead, I would like to read the file primesList.txt and put it in [primesList].
import Foundation
// set a file called test.txt on Desktop to be used by this program
let dir = "~/Desktop"
let file = "primesList.txt"
let path = dir.stringByExpandingTildeInPath
let filePath: NSString = path.stringByAppendingPathComponent(file)
// set some needed variables and constants
var highestNumberEvaluated = 100
var primesList = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
let readFromFile = String(contentsOfFile: filePath, encoding: NSUTF8StringEncoding, error: nil)
println(readFromFile)
let inputNumber = 100
let range2Evaluate = 100
let last2Evaluate = inputNumber + range2Evaluate
println("Find primes between \(inputNumber) and \(last2Evaluate):\n")
// create a dictionary of numbers to be evaluated true or false
var numbersList = [Int: Bool]()
for i in inputNumber...last2Evaluate {
numbersList[i] = true
}
// mark as not prime (false) all not primes in numbersList
for i in primesList {
if i < Int(sqrt(Double(last2Evaluate))) {
let myMultiplier = inputNumber / i
var bottomValue = i * myMultiplier
for var j = bottomValue; j < (bottomValue + range2Evaluate + 2); j += i {
numbersList[j] = false
}
} else {break}
}
// create an array made by all primes found true, then sort
var primesFoundList = [Int]()
for (myKey, myValue) in numbersList {
if myValue == true {
primesFoundList.append(myKey)
}
}
primesFoundList.sort { $0 < $1 }
// add the primes found to the original primes list and write on the file test.txt
primesList += primesFoundList
let toBeWritten = toString(primesList)
toBeWritten.writeToFile(filePath, atomically: true, encoding: NSUTF8StringEncoding, error: nil)
on line 13-14 I can get the content of the file, how can I write this content in the array [primesList]?
The toString() function is not very well suited to produce output that is to be
read again. You could create a simple comma-separated list with
let toBeWritten = ", ".join(primesList.map { String($0)})
toBeWritten.writeToFile(filePath, atomically: true, encoding: NSUTF8StringEncoding, error: nil)
which can be read again with
if let readFromFile = String(contentsOfFile: filePath, encoding: NSUTF8StringEncoding, error: nil) {
primesList = readFromFile.componentsSeparatedByString(",").map() { ($0 as NSString).integerValue }
}
(Note: This assumes that the file has the correct format, otherwise it may crash.)
Alternatively, use the writeToFile() method from NSArray which writes the
array as a property list (XML) file:
(primesList as NSArray).writeToFile(filePath, atomically: false)
which can be read back with
if let storedList = NSArray(contentsOfFile: filePath) as? [Int] {
primesList = storedList
}