0
votes

I recently upgraded from Swift 3 to Swift 4 and iOS 10.3.3 to iOS 11.1.

I'm developing an application that uses BLE to communicate bi-directionally. The workflow is as follows:

  1. PERIPHERAL - Advertise Identity
  2. CENTRAL - Receive Identity (process it...)
  3. CENTRAL - Respond to peripheral
  4. PERIPHERAL - Receive response from central
  5. Done

My code was working perfectly before the update but now it's not. At the end of step 4, I execute the following line:

peripheral.writeValue(encryptedData!, for: characteristic, type: .withResponse)

This should call the following delegate method but it doesn't:

public func peripheral(_ peripheral: CBPeripheral, didWriteValueFor descriptor: CBDescriptor, error: Error?) {
        print("Did Write")
        print("Error=\(error?.localizedDescription)")
    }

It should also (and was calling) the following delegate method on the PERIPHERAL device but it doesn't:

public func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
        print("did receive write request")
}

The service and characteristic are set as follows :

let prefs = Preferences()
            let strServiceUUID = prefs.GetString(key: Preferences.PREF_IDENTITY_SERVICE_UUID, defaultVal: "")!
            let strCharacteristicUUID = prefs.GetString(key: Preferences.PREF_IDENTITY_CHARACTERISTIC_UUID, defaultVal: "")!
            print("ServiceUUID=\(strServiceUUID)")
            print("CharacteristicUUID=\(strCharacteristicUUID)")
            mServiceUUID = CBUUID(string: strServiceUUID)
            mCharacterUUID = CBUUID(string: strCharacteristicUUID)
            mCBBluetoothServices = CBMutableService(type: mServiceUUID, primary: true)
            
            //lets configure the data we want to advertise for
            var characteristics : [CBCharacteristic] = []
            
            //let strData : String = "933911"
            //let data = strData.data(using: .utf8)
            let cbProperties: CBCharacteristicProperties = [.read, .write, .notify]
            let cbPermissions: CBAttributePermissions = [.readable, .writeable]
            mIdentityObjectCharacteristic = CBMutableCharacteristic(type: mCharacterUUID,
                                                                    properties: cbProperties,
                                                                    value: nil,
                                                                    permissions: cbPermissions)
            
            
            characteristics.append(mIdentityObjectCharacteristic)
            mCBBluetoothServices.characteristics = characteristics
            mCBPeripheralManager.add(mCBBluetoothServices)
3

3 Answers

2
votes

I am not sure why upgrading the OS and Swift versions broke your code, however, it looks to me like you may be using the wrong delegate method?

Try using this

func peripheral(CBPeripheral, didWriteValueFor: CBCharacteristic, error: Error?)

instead of this

func peripheral(CBPeripheral, didWriteValueFor: CBDescriptor, error: Error?)
0
votes

Swift 4

For any kind of update characteristic (ex. read/write characteristic), then the didUpdateValueFor delegate will be called.

So, first check in the following delegate methods.

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
    
    print("didUpdateValueForChar", characteristic)
    
    if let error1 = error{
        
        alertMSG(titleString: "Error", subTitleString: "Found error while read characteristic data, Plase try again", buttonTitle: "OK")
        
        print(error1)
    }
    else{
        
        print("Update Characteristic: ", characteristic)
    }
}

func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
    
    print("Write Characteristic :", characteristic)
}
0
votes

Swift 5
iOS 13

Some things to check:

  1. Make sure to set the peripheral's delegate to whichever controller is conforming to the CBPeripheralDelegate protocol (this should also be the same controller that needs to implement the peripheral(_:didWriteValueFor:error:) method).
  2. Make sure you are not specifying a .withoutResponse write type.
  3. As mentioned by this other answer, there are two very similar delegate methods that have the signature peripheral(_:didWriteValueFor:error:). Make sure you are implementing the correct one.

It is easy to confuse the 2 sets of write and delegate methods.

Since you are using:

peripheral.writeValue(encryptedData!, for: characteristic, type: .withResponse) 

The code for the write and delegate pair should be something like this:

class BluetoothController: CBCentralManagerDelegate, CBPeripheralDelegate {

    ...
   
    func writeSomething(to characteristic: CBCharacteristic, of peripheral: CBPeripheral) {
        let something = "1234"
        
        NSLog("Writing \(something) to \(characteristic.uuid.uuidString)")
        
        peripheral.delegate = self  // <===== You may have forgotten this?
        peripheral.writeValue(something.data(using: .utf8)!,
                              for: characteristic,
                              type: .withResponse)
    }

    ...

    func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
        if error != nil {
            NSLog("Write error: \(String(describing: error))")
        } else {
            NSLog("Wrote value to \(characteristic.uuid.uuidString)")
        }
    }
}