0

I've been having issues converting an Objective-C snippet to Swift that uses NSData and CoreBluetooth. I have looked at this question and a couple others dealing with NSData in Swift but haven't had any success.

Objective-C Snippet:

- (CGFloat) minTemperature
{
    CGFloat result = NAN;
    int16_t value = 0;

    // characteristic is a CBCharacteristic
    if (characteristic) { 
        [[characteristic value] getBytes:&value length:sizeof (value)];
        result = (CGFloat)value / 10.0f;
    }
    return result;
}

What I have so far in Swift (not working):

func minTemperature() -> CGFloat {
    let bytes = [UInt8](characteristic?.value)
    let pointer = UnsafePointer<UInt8>(bytes)
    let fPointer = pointer.withMemoryRebound(to: Int16.self, capacity: 2) { return $0 }
     value = Int16(fPointer.pointee)

    result = CGFloat(value / 10) // not correct value

    return result
}

Does the logic look wrong here? Thanks!

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Surz
  • 984
  • 3
  • 11
  • 36

2 Answers2

0

You should make the return value optional and check if characteristic is nil in the beginning with a guard. You should also explicitly convert the value to CGFloat, then divide it by 10.

func minTemperature() -> CGFloat? {
    guard characteristic != nil else {
        return nil
    }

    let bytes = [UInt8](characteristic!.value)
    let pointer = UnsafePointer<UInt8>(bytes)
    let fPointer = pointer.withMemoryRebound(to: Int16.self, capacity: 2) { return $0 }
    let value = Int16(fPointer.pointee)

    result = CGFloat(value) / 10

    return result
}
Tamás Sengel
  • 55,884
  • 29
  • 169
  • 223
  • Casting value likes that gives the error "Cannot invoke initializer for type 'CGFloat' with an argument list of type '(_)'" – Surz Jul 24 '17 at 18:29
0

One error is in

let fPointer = pointer.withMemoryRebound(to: Int16.self, capacity: 2) { return $0 }

because the rebound pointer $0 is only valid inside the closure and must not be passed to the outside. Also the capacity should be 1 for a single Int16 value. Another problem is the integer division in

result = CGFloat(value / 10)

which truncates the result (as already observed by the4kman).

Creating an [UInt8] array from the data is not necessary, the withUnsafeBytes() method of Data can be used instead.

Finally you could return nil (instead of "not a number") if no characteristic value is given:

func minTemperature() -> CGFloat? {
    guard let value = characteristic?.value else {
        return nil
    }
    let i16val = value.withUnsafeBytes { (ptr: UnsafePointer<Int16>) in
        ptr.pointee
    }
    return CGFloat(i16val) / 10.0
}
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382