"Type of expression is ambiguous without more context" when creating UInt16 array from Bluetooth characteristic - swift

I am using Xcode 9.2 and I don't understand the reason behind the error
Type of expression is ambiguous without more context
I am getting some input when trying to create and wordArray as showed below. If I define it as UInt8 array it does work but not if I do as Uint16 since I get the error.
The original data, Characteristic.value comes from a BLE characteristic
let characteristicData = Characteristic.value
let byteArray = [UInt8](characteristicData!)
print("\(Characteristic.uuid) value as byte is->",byteArray)
let wordArray = [UInt16](characteristicData!)//Type of expression is ambiguous without more context
print("\(Characteristic.uuid) value as word is->",wordArray)
Why does this happen and how I can fix it?

characteristicData has the type Data and that conforms to the
(RandomAccess)Collection protocol with UInt8 as element type, that's why you can
initialize an [UInt8] array from it:
let byteArray = [UInt8](characteristicData)
You could equivalently write
let byteArray = Array(characteristicData)
To interpret the data as an array of a different type, use
the generic
func withUnsafeBytes<ResultType, ContentType>(_ body: (UnsafePointer<ContentType>) throws -> ResultType) rethrows -> ResultType
method:
let wordArray = characteristicData.withUnsafeBytes {
[UInt16](UnsafeBufferPointer(start: $0, count: characteristicData.count/2))
}
Here the ContentType is inferred automatically as UInt16.

Related

How to convert Int8 to Character?

I need to convert Int8 to Character. How can I do it?
I tried use UnicodeScalar
var result += Character(UnicodeScalar(data[i]))
... but got this error:
Cannot invoke initializer for type 'UnicodeScalar' with an argument
list of type (Int8)
Unicode.Scalar can be initalized just with paramaters of certain types which you can find in docs.
I would suggest you using init(_:) which takes UInt8 which tells that given number is positive (this is required for creating UnicodeScalar). So, you can try to cast your Int8 to UInt8 and then your initializer takes parameter of correct type
let int: Int8 = data[i]
if let uint = UInt8(exactly: int) {
let char = Character(UnicodeScalar(uint))
}
Maybe you need just convert whole data to a string:
var result = String(data: data, encoding: .ascii)

crc32 function of zlib fails in swift 3 Cannot invoke initializer for type 'UnsafePointer<Bytef>' with an argument list of type '(UnsafeRawPointer)'

I want to make a CRC32 Hash out of a string and so I got the zlib function crc32.
But in Swift 3 this makes problems.
The code looks like this:
func crc32HashString(_ string: String) {
let strData = string.data(using: String.Encoding.utf8, allowLossyConversion: false)
let crc = crc32(uLong(0), UnsafePointer<Bytef>(strData!.bytes), uInt(strData!.length))
}
The compiler gives me this Error:
Cannot invoke initializer for type 'UnsafePointer<Bytef>' with an argument list of type '(UnsafeRawPointer)'
How do I resolve this error?
best regards and thank you for your help!
Artur
The bytes of a Data value are accessed using the
/// Access the bytes in the data.
///
/// - warning: The byte pointer argument should not be stored and used outside of the lifetime of the call to the closure.
public func withUnsafeBytes<ResultType, ContentType>(_ body: (UnsafePointer<ContentType>) throws -> ResultType) rethrows -> ResultType
method. It calls a closure with a (typed) pointer to the bytes:
let strData = string.data(using: .utf8)! // Conversion to UTF-8 cannot fail
let crc = strData.withUnsafeBytes { crc32(0, $0, numericCast(strData.count)) }
Here the type of $0 is automatically inferred as
UnsafePointer<Bytef> from the context.
Update: As of Swift 5,
public func withUnsafeBytes<ResultType>(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType
calls the closure with a “raw” buffer pointer which must be “bound” to the expected type Bytef (aka UInt8):
let strData = string.data(using: .utf8)! // Conversion to UTF-8 cannot fail
let crc = strData.withUnsafeBytes {
crc32(0, $0.bindMemory(to: Bytef.self).baseAddress, numericCast(strData.count))
}

Subscript range of [UInt8] - Swift

So the issue I'm having is that I have an object with a argument in the init() that requires an [UInt8]. I want to be able to grab a range from another array and use that in the init. See example.
class Test {
init(fromArray: [UInt8]) {
// performs work
}
}
let myStockArray: [UInt8] = [1,2,3,4,5,6,7,8] // reference array
let test = Test(fromArray: myStockArray[1...4]) // doesn't work
How can I get this to work? The error I get is: Cannot subscript a value of type '[UInt8]' with an index of type 'CountableClosedRange'
Subscripting an array with a range doesn't return an array and this is the main issue. You are trying to setArraySlice<UInt8> type data to the constructor that have inside [UInt8] type.
Try this approach:
class Test {
init(fromArray: [UInt8]) {
// performs work
}
}
let myStockArray: [UInt8] = [1,2,3,4,5,6,7,8] // reference array
let test = Test(fromArray: Array(myStockArray[1...4]))

Swift: cannot convert value of type 'Self' to expected argument type 'UnsafePointer<Void>'

I do a bunch of work with [Uint8] arrays. But I often have to recast these as NSData objects to interface with iOS frameworks such as CoreBluetooth. So I have lots of code that might look something like:
var input:[UInt8] = [0x60, 0x0D, 0xF0, 0x0D]
let data = NSData(bytes: input, length: input.count)
Being tired of having to insert this extra let data = ... line, I thought I would just extend those arrays with a computed property to do the work. Then I could just do things like:
aBluetoothPeriperal.write(myBytes.nsdata, ...)
So it's basically just extension sugar. I can't extend Array, but I can extend a protocol:
extension SequenceType where Generator.Element == UInt8 {
var nsdata:NSData {
return NSData(bytes: self, length: self.count)
}
}
Which produces an error that looks like:
Playground execution failed: MyPlayground.playground:3:24: error: cannot convert value of type 'Self' to expected argument type 'UnsafePointer<Void>' (aka 'UnsafePointer<()>')
return NSData(bytes: self, length: self.count)
^~~~
Sadly, the more I use Swift--and I really do like some things about Swift--the more I'm reminded of my negative experiences with trying to understand reams of unhelpful compiler output when I tried my hand at C++ with lots of generics and stuff years ago. So please Obi Wan, help me see the light here!
NSData(bytes:, length:) takes an UnsafePointer<Void> as first parameter, and you cannot pass an arbitrary SequenceType here.
You can pass an Array which would be passed as the address of the
first array element. It is however not guaranteed that Array elements
are stored in contiguous memory.
Therefore:
Define an Array extension instead of a Sequence extension.
Use the withUnsafeBufferPointer() method to get a pointer
to contiguous array storage.
Possible solution:
extension Array where Element : IntegerType {
var nsdata : NSData {
return self.withUnsafeBufferPointer {
NSData(bytes: $0.baseAddress, length: self.count * strideof(Element))
}
}
}
let input:[UInt8] = [0x60, 0x0D, 0xF0, 0x0D]
print(input.nsdata) // <600df00d>
Swift does currently not allow to restrict an array extension
to a concrete type:
extension Array where Element == UInt8 { }
// error: same-type requirement makes generic parameter 'Element' non-generic
therefore it is generalized to any elements conforming to IntegerType.
For a sequence you could then do a conversion to an array first:
let data = Array(myUInt8sequence).nsdata
This seems to do what you want.
protocol ByteOnly: IntegerType {}
extension UInt8: ByteOnly {}
extension Array where Element: ByteOnly {
var n : NSData { return NSData(bytes: self, length: self.count) }
}
// does it work with UInt8
var input5:[UInt8] = [0x61, 0x0D, 0xF1, 0x0D]
let data5 = input5.n // YES
// does it work on ints?
var ints: [Int] = [3,4,5,6,7,8]
let idata5 = ints.n // no

Convert UnsafeMutablePointer<UInt> to UInt in Swift

I got from a function Swift result in type UnsafeMutablePointer<UInt>
Can I cast it to UInt?
Just use the memory property to access the underlying data.
let ptr: UnsafeMutablePointer<UInt> = funcReturningMutablePtr()
let theValue: UInt = ptr.memory
The type annotations are for clarity, but are not necessary.