Objective C to Swift code conversion, can't seem to treat bytes correctly in Swift

I’m working on a simple Swift bluetooth heart rate monitor iOS App. I found this great tutorial which has objective C code. I’ve converted it to Swift and I’m getting data from my heart rate monitor. My problem is that I can’t seem to correctly access and convert the byte data in Swift.

Here’s the Objective C code:

  • AFNetworking's setImageWithURLRequest sets image in wrong cell after scroll (iOS, Swift)
  • Xamarin iOS IPv6 App Store Rejection
  • Methods for new user registration xmpp framework iOS
  • Detect iOS8 Reachability Gesture
  • How do I auto size a UIScrollView to fit the content
  • How to resize table cell based on textview?
  • // Instance method to get the heart rate BPM information
    - (void) getHeartBPMData:(CBCharacteristic *)characteristic error:(NSError *)error
    {
        // Get the Heart Rate Monitor BPM
        NSData *data = [characteristic value];      // 1
        const uint8_t *reportData = [data bytes];
        uint16_t bpm = 0;
    
        if ((reportData[0] & 0x01) == 0) {          // 2
            // Retrieve the BPM value for the Heart Rate Monitor
            bpm = reportData[1];
        }
        else {
            bpm = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[1]));  // 3
        }
        // Display the heart rate value to the UI if no error occurred
        if( (characteristic.value)  || !error ) {   // 4
            self.heartRate = bpm;
            self.heartRateBPM.text = [NSString stringWithFormat:@"%i bpm", bpm];
            self.heartRateBPM.font = [UIFont fontWithName:@"Futura-CondensedMedium" size:28];
            [self doHeartBeat];
            self.pulseTimer = [NSTimer scheduledTimerWithTimeInterval:(60. / self.heartRate) target:self selector:@selector(doHeartBeat) userInfo:nil repeats:NO];
        }
        return;
    }
    

    Here’s the Swift Code:

    func peripheral(peripheral: CBPeripheral!,
        didUpdateValueForCharacteristic characteristic: CBCharacteristic!,
        error: NSError!) -> String
    {
    
        // Get the Heart Rate Monitor BPM
        var data = characteristic.value
        var reportData = data.bytes
        var bpm : UInt16
        var rawByte : UInt8
        var outputString = ""
        rawByte = UInt8(reportData[0])
        bpm = 0
    
        if ((rawByte & 0x01) == 0) {          // 2
            // Retrieve the BPM value for the Heart Rate Monitor
            bpm = UInt16( reportData[4] )
        }
        else {
            bpm = CFSwapInt16LittleToHost(UInt16(reportData[1]))
        }
    
        outputString = String(bpm)
        return outputString
    }
    

    Solutions Collect From Internet About “Objective C to Swift code conversion, can't seem to treat bytes correctly in Swift”

    const uint8_t *reportData = [data bytes];
    

    translates to

    let reportData = UnsafePointer<UInt8>(data.bytes)
    

    Then reportData has the type UnsafePointer<UInt8> and you can access it as in (Objective-)C:

    if (reportData[0] & 0x01) == 0 { ... }
    

    Next,

    bpm = reportData[1];
    

    is almost identical in Swift. You have to convert explicitly from UInt8 to
    UInt16 because – unlike (Objective-)C – Swift does not implicitly convert between types:

    bpm = UInt16(reportData[1]) 
    

    Putting it together:

    func getHeartBPMData(characteristic: CBCharacteristic!) {
        let data = characteristic.value
        let reportData = UnsafePointer<UInt8>(data.bytes)
        var bpm : UInt16
        if (reportData[0] & 0x01) == 0 {
            bpm = UInt16(reportData[1])
        } else {
            bpm = UnsafePointer<UInt16>(reportData + 1)[0]
            bpm = CFSwapInt16LittleToHost(bpm)
        }
    
        // ...
    }
    

    Note that most of your variables can be declared as constants with let, instead
    of var. Instead of

    bpm = CFSwapInt16LittleToHost(bpm)
    

    you can alternatively use the littleEndian: constructor which is available
    for all integer types:

    bpm = UInt16(littleEndian: bpm)
    

    Update for Swift 3/4:

    func getHeartBPMData(characteristic: CBCharacteristic) {
        guard  let reportData = characteristic.value else {
            return 
        }
    
        let bpm : UInt16
        if (reportData[0] & 0x01) == 0 {
            bpm = UInt16(reportData[1])
        } else {
            bpm = UInt16(littleEndian: reportData.subdata(in: 1..<3).withUnsafeBytes { $0.pointee } )
        }
    
        // ...
    }