iOS 7 SDK: Core Bluetooth - Lección práctica
Spanish (Español) translation by Elías Nicolás (you can also view the original English article)
El
marco Core Bluetooth (CB) proporciona los recursos que sus aplicaciones
iOS necesitan para comunicarse con dispositivos que están equipados con
tecnología Bluetooth de baja energía (BTLE). Este
tutorial lo guiará a través de la evolución de CB desde iOS 5 a iOS 7.
Además, aprenderá cómo configurar un Core Bluetooth central y
periférico, cómo comunicarse entre ellos y las mejores prácticas de
programación inherentes cuando trabaje con CB.
Introducción
Los tutoriales Core Bluetooth están divididos en dos partes. El primero cubre el aspecto teórico de Core Bluetooth, mientras que este tutorial es una lección práctica completa. Encontrará el código fuente completo adjunto a esta publicación.
1. Descargue el código fuente de muestra
El objetivo de este tutorial es enseñarle cómo usar el marco Core Bluetooth. Preparamos un código fuente de muestra que hará que su vida sea más fácil y eludirá la configuración del proyecto y las vistas de creación. Debe descargar el código de muestra al principio de esta página.
Suponemos que conoce los conceptos básicos de Xcode e iOS, ya que solo nos centraremos en los datos Core Bluetooth. El código de muestra contiene lo siguiente:
- Una aplicación que usa el controlador de navegación, tres vistas y los controladores inherentes.
- El controlador de vista
inicial
ViewControllercon dos botones - Un
CBCentralManagerViewControllerque crea un iBeacon personalizado - Un
CBPeripheralViewControllerque recibe el iBeacon y la información inherente - Un archivo de encabezado
SERVICEScon algunas variables para usar en la aplicación.
Todas las vistas ya están en su lugar y debidamente definidas. Solo necesita agregar el código para el proceso Core Bluetooth. Abra el proyecto, ejecútelo y juegue con los objetos para familiarizarse con el código.
El archivo SERVICES.h contiene dos
UUID únicos. Esos fueron generados usando el comando terminal uuidgen. Debe generarlos en su aplicación, o puede usar esos.
Tenga en cuenta que
esta lección necesita dos dispositivos iOS para funcionar correctamente. Ejecute Run el proyecto y verá una interfaz similar a esta:



2. Programación de un rol central
En este tutorial,
centrará la clase CBCentralManagerViewController. El primer paso es
agregar los dos protocolos que admiten CBCentralManager y CBPeripheral. La declaración de esos protocolos define métodos (más sobre esto más
adelante). Su interfaz interface debería ser así:
1 |
|
2 |
@interface CBCentralManagerViewController : UIViewController < CBCentralManagerDelegate, CBPeripheralDelegate> |
Ahora debe definir tres propiedades: CBCentralManager, CBPeripheral y NSMutableData. Los dos primeros son obvios y el último se usa para almacenar información que se comparte entre dispositivos.
1 |
|
2 |
@property (strong, nonatomic) CBCentralManager *centralManager; |
3 |
@property (strong, nonatomic) CBPeripheral *discoveredPeripheral; |
4 |
@property (strong, nonatomic) NSMutableData *data; |
En este punto, puede cambiar al archivo de implementación. Verá una advertencia, pero antes de resolver eso, debe iniciar el
administrador central centralManger y los objetos de datos data. Debe iniciar el
centralManager con un auto delegado y sin ninguna cola. Debería
usar el método viewDidLoad y el resultado debería ser similar a esto:
1 |
|
2 |
_centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil]; |
3 |
_data = [[NSMutableData alloc] init]; |
Para
resolver el problema de advertencia, debe agregar el método - (void)centralManagerDidUpdateState:(CBCentralManager *)central.
Es un método de protocolo obligatorio. Comprueba el estado del dispositivo y actúa en consecuencia. Existen varios estados posibles y en su aplicación siempre debe verificarlos. Los estados son:
- CBCentralManagerStateUnknown
- CBCentralManagerStateResetting
- CBCentralManagerStateUnsupported
- CBCentralManagerStateUnauthorized
- CBCentralManagerStatePoweredOff
- CBCentralManagerStatePoweredOn
Por
ejemplo, si ejecuta esta aplicación en un dispositivo que no es
Bluetooth 4.0, obtendrá el código CBCentralManagerStateUnsupported. Aquí
irá a CBCentralManagerStatePoweredOn y cuando ocurra, comenzará a
buscar dispositivos. Para eso, use el método
scanForPeripheralsWithServices. Si pasa nil como primer argumento,
CBCentralManager comienza a buscar cualquier servicio. Aquí usará el
UUID almacenado en SERVICES.h.
El método completo es:
1 |
|
2 |
- (void)centralManagerDidUpdateState:(CBCentralManager *)central { |
3 |
// You should test all scenarios
|
4 |
if (central.state != CBCentralManagerStatePoweredOn) { |
5 |
return; |
6 |
}
|
7 |
|
8 |
if (central.state == CBCentralManagerStatePoweredOn) { |
9 |
// Scan for devices
|
10 |
[_centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]] options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @YES }]; |
11 |
NSLog(@"Scanning started"); |
12 |
}
|
13 |
}
|
En este momento, su aplicación buscará otros dispositivos. Pero a pesar de que ninguno o ninguno está disponible, no obtendrá
ninguna información. Podemos arreglarlo. Debería
agregar el -
(void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber
*)RSSI . Se
llamará cada vez que se descubra un dispositivo. Sin embargo, lo
programará para que reaccione solo a los periféricos que anuncian
TRANSFER_SERVICE_UUID.
Además, utilizaremos el nuevo sistema de caché y almacenaremos el dispositivo para referencia futura y comunicación más rápida. El código fuente completo es el siguiente:
1 |
|
2 |
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI { |
3 |
|
4 |
NSLog(@"Discovered %@ at %@", peripheral.name, RSSI); |
5 |
|
6 |
if (_discoveredPeripheral != peripheral) { |
7 |
// Save a local copy of the peripheral, so CoreBluetooth doesn't get rid of it
|
8 |
_discoveredPeripheral = peripheral; |
9 |
|
10 |
// And connect
|
11 |
NSLog(@"Connecting to peripheral %@", peripheral); |
12 |
[_centralManager connectPeripheral:peripheral options:nil]; |
13 |
}
|
14 |
}
|
La conexión a ese dispositivo puede fallar. Necesitamos
lidiar con ese escenario usando un método específico llamado: - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error. Añádalo e informe al usuario sobre ese error.
1 |
|
2 |
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error { |
3 |
NSLog(@"Failed to connect"); |
4 |
[self cleanup]; |
5 |
}
|
Notará una advertencia, ya que el método de limpieza cleanup todavía no se ha declarado. ¡Vamos a declararlo! En este punto, puede encontrar que el código fuente
del método es complicado. Sin embargo, lo explicaremos más adelante. Debería regresar al final del tutorial para una comprensión
completa.
Este método cancela todas las suscripciones a un dispositivo remoto (si hay alguno) o se desconecta directamente si no es así. Recorre los servicios, luego las características y elimina los enlaces a ellos. El método completo es:
1 |
|
2 |
- (void)cleanup { |
3 |
|
4 |
// See if we are subscribed to a characteristic on the peripheral
|
5 |
if (_discoveredPeripheral.services != nil) { |
6 |
for (CBService *service in _discoveredPeripheral.services) { |
7 |
if (service.characteristics != nil) { |
8 |
for (CBCharacteristic *characteristic in service.characteristics) { |
9 |
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]]) { |
10 |
if (characteristic.isNotifying) { |
11 |
[_discoveredPeripheral setNotifyValue:NO forCharacteristic:characteristic]; |
12 |
return; |
13 |
}
|
14 |
}
|
15 |
}
|
16 |
}
|
17 |
}
|
18 |
}
|
19 |
|
20 |
[_centralManager cancelPeripheralConnection:_discoveredPeripheral]; |
21 |
}
|
Teniendo en
cuenta que nos conectamos con éxito al dispositivo, ahora tenemos que
descubrir los servicios y las características de la misma. Debe declarar el - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral. Una vez establecida
la conexión, detenga el proceso de escaneo. Luego borre los datos que
podamos haber recibido. Luego,
asegúrese de obtener las devoluciones de llamadas de descubrimiento y,
finalmente, buscar servicios que coincidan con su UUID
(TRANSFER_SERVICE_UUID). Aquí está el código:
1 |
|
2 |
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral { |
3 |
NSLog(@"Connected"); |
4 |
|
5 |
[_centralManager stopScan]; |
6 |
NSLog(@"Scanning stopped"); |
7 |
|
8 |
[_data setLength:0]; |
9 |
|
10 |
peripheral.delegate = self; |
11 |
|
12 |
[peripheral discoverServices:@[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]]; |
13 |
}
|
En este punto, el periférico comienza a notificar a su delegado con varias devoluciones de llamada. Una de esas devoluciones es el - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error. Se usa para descubrir
las características de un servicio determinado. No es que siempre deba
verificar si ese método arroja un error. Si no se encuentra ningún
error, debe encontrar las características que necesita, en este caso el
TRANSFER_CHARACTERISTIC_UUID. Aquí está el método completo:
1 |
|
2 |
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error { |
3 |
if (error) { |
4 |
[self cleanup]; |
5 |
return; |
6 |
}
|
7 |
|
8 |
for (CBService *service in peripheral.services) { |
9 |
[peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]] forService:service]; |
10 |
}
|
11 |
// Discover other characteristics
|
12 |
}
|
En este punto, si todo es correcto, se descubrió la característica de transferencia. Una
vez más, se llama un método delegado: - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error. Una vez
que haya encontrado esto, desea suscribirse, lo que le permite a su
CBCentralManager recibir los datos de ese periférico.
Una vez más, debe lidiar con los errores (si los hubiera). Puedes dar un salto de fe y suscribirte directamente a la característica. Sin embargo, debe recorrer el conjunto de características y verificar si la característica es la correcta. Si es así, suscríbete a él. Una vez que esto esté completo, solo necesita esperar a que ingresen los datos (otro método). El método completo está debajo.
1 |
|
2 |
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error { |
3 |
if (error) { |
4 |
[self cleanup]; |
5 |
return; |
6 |
}
|
7 |
|
8 |
for (CBCharacteristic *characteristic in service.characteristics) { |
9 |
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]]) { |
10 |
[peripheral setNotifyValue:YES forCharacteristic:characteristic]; |
11 |
}
|
12 |
}
|
13 |
}
|
Cada vez
que el periférico envía datos nuevos, el delegado periférico utiliza el
método de error -
(void)peripheral:(CBPeripheral *)peripheral
didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error . El segundo argumento contiene la característica que puedes
leer.
Inicialmente, creará un NSString para almacenar el valor
característico. Luego, verificará si los datos recibidos están completos
o si se entregarán más. Simultáneamente, actualizará su vista de texto
textview tan pronto como se reciban nuevos datos. Una
vez completados todos los datos, puede desconectarse de la
característica y desconectarse del dispositivo (aunque puede permanecer
conectado).
Tenga en cuenta que, después de los datos entrantes, puede desconectarse o esperar por otros datos. Esta devolución de llamada nos permite saber si ha llegado más información mediante la notificación de la característica. La fuente completa está a continuación:
1 |
|
2 |
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { |
3 |
if (error) { |
4 |
NSLog(@"Error"); |
5 |
return; |
6 |
}
|
7 |
|
8 |
NSString *stringFromData = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding]; |
9 |
|
10 |
// Have we got everything we need?
|
11 |
if ([stringFromData isEqualToString:@"EOM"]) { |
12 |
|
13 |
[_textview setText:[[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding]]; |
14 |
|
15 |
[peripheral setNotifyValue:NO forCharacteristic:characteristic]; |
16 |
|
17 |
[_centralManager cancelPeripheralConnection:peripheral]; |
18 |
}
|
19 |
|
20 |
[_data appendData:characteristic.value]; |
21 |
}
|
Además, hay un método que asegura que CBCentral sabe cuándo cambia un estado de notificación para una característica dada. Es muy importante rastrearlo para comprender cuándo cambia un estado
característico (actualizar los valores de la aplicación). El
método es: -
(void)peripheral:(CBPeripheral *)peripheral
didUpdateNotificationStateForCharacteristic:(CBCharacteristic
*)characteristic error:(NSError *)error. Debería verificar si la notificación
característica se ha detenido. Si es así, debes desconectarte de él:
1 |
|
2 |
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { |
3 |
|
4 |
if (![characteristic.UUID isEqual:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]]) { |
5 |
return; |
6 |
}
|
7 |
|
8 |
if (characteristic.isNotifying) { |
9 |
NSLog(@"Notification began on %@", characteristic); |
10 |
} else { |
11 |
// Notification has stopped
|
12 |
[_centralManager cancelPeripheralConnection:peripheral]; |
13 |
}
|
14 |
}
|
Si se produce la desconexión entre dispositivos, debe limpiar su copia local del periférico. Para
eso use el método de error - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error. Este método es simple y establece el periférico a cero. Además, puede reiniciar el escaneo del dispositivo o salir de la
aplicación (u otra). En este ejemplo, reiniciará el proceso de escaneo.
1 |
|
2 |
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error { |
3 |
_discoveredPeripheral = nil; |
4 |
|
5 |
[_centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]] options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @YES }]; |
6 |
}
|
Finalmente, se requiere un paso adicional. Cada vez que desaparece la vista, debe detener el proceso de escaneo. En
el método animado viewWillDisappear:(BOOL)animated debe agregar:
1 |
|
2 |
[_centralManager stopScan]; |
Puede Run ejecutar la aplicación, sin embargo, necesita la aplicación periférica para recibir algunos datos. La siguiente imagen presenta la interfaz final de
CBCentralManager.



3. Programando un rol periférico
En este tutorial, se centrará en la clase
CBPeripheralViewController. El primer paso es agregar dos protocolos:
CBPeripheralManagerDelegate y UITextViewDelegate. Su interfaz interface ahora debe
verse así:
1 |
|
2 |
@interface CBPeripheralViewController : UIViewController < CBPeripheralManagerDelegate, UITextViewDelegate> |
Ahora debe definir cuatro propiedades: CBPeripheralManager, CBMutableCharacteristic, NSData, y NSInterger. Los
dos primeros representan el administrador periférico y sus
características, mientras que el tercero son los datos que se enviarán. El último representa el índice de datos.
1 |
|
2 |
@property (strong, nonatomic) CBPeripheralManager *peripheralManager; |
3 |
@property (strong, nonatomic) CBMutableCharacteristic *transferCharacteristic; |
4 |
@property (strong, nonatomic) NSData *dataToSend; |
5 |
@property (nonatomic, readwrite) NSInteger sendDataIndex; |
Ahora cambie al archivo de implementación. Nuestro primer paso es iniciar _peripheralManager y configurarlo para
comenzar a publicitar. La publicidad del servicio debe iniciarse con el
UUID del servicio antes mencionado. Su viewDidLoad debe verse así:
1 |
|
2 |
- (void)viewDidLoad { |
3 |
[super viewDidLoad]; |
4 |
|
5 |
_peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil]; |
6 |
|
7 |
[_peripheralManager startAdvertising:@{ CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]] }]; |
8 |
}
|
Deberías ver una advertencia. Para solucionarlo, declare el método de protocolo periférico - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral. Al igual que CBCentralManager, debe controlar y probar todos los estados de la aplicación. Si
el estado es CBPeripheralManagerStatePoweredOn debe construir y definir
su servicio y características (una de las características reales de iOS
7).
Cada servicio y característica debe identificarse mediante un UUID único. Tenga en cuenta que el tercer argumento del método init está en nada. Al hacerlo, declara que los datos que se intercambiarán se definirán más adelante. Esto generalmente se hace cuando desea crear datos dinámicamente. Si desea tener un valor estático para transmitir, puede declararlo aquí.
Las propiedades determinan cómo se puede usar el valor característico y hay varios valores posibles:
- CBCharacteristicPropertyBroadcast
- CBCharacteristicPropertyRead
- CBCharacteristicPropertyWriteWithoutResponse
- CBCharacteristicPropertyWrite
- CBCharacteristicPropertyWrite
- CBCharacteristicPropertyNotify
- CBCharacteristicPropertyIndicate
- CBCharacteristicPropertyAuthenticatedSignedWrites
- CBCharacteristicPropertyExtendedProperties
- CBCharacteristicPropertyNotifyEncryptionRequired
- CBCharacteristicPropertyIndicateEncryptionRequired
Para una comprensión completa de esas propiedades, debe verificar la Referencia de clase de característica CBC.
El último argumento de init es los permisos de lectura, escritura y encriptación para un atributo. De nuevo, hay varios valores posibles:
- CBAttributePermissionsReadable
- CBAttributePermissionsWriteable
- CBAttributePermissionsReadEncryptionRequired
- CBAttributePermissionsWriteEncryptionRequired
Después
de que la característica lo definió, ahora es el momento de definir el
servicio utilizando CBMutableService. Tenga en cuenta que el servicio
debe definirse con TRANSFER_CHARACTERISTIC_UUID. Agregue la
característica al servicio y luego agréguela al administrador de
periféricos. El método completo está a continuación:
1 |
|
2 |
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral { |
3 |
if (peripheral.state != CBPeripheralManagerStatePoweredOn) { |
4 |
return; |
5 |
}
|
6 |
|
7 |
if (peripheral.state == CBPeripheralManagerStatePoweredOn) { |
8 |
self.transferCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID] properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable]; |
9 |
|
10 |
CBMutableService *transferService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID] primary:YES]; |
11 |
|
12 |
transferService.characteristics = @[_transferCharacteristic]; |
13 |
|
14 |
[_peripheralManager addService:transferService]; |
15 |
}
|
16 |
}
|
Ahora que
tenemos el servicio y sus características (uno en este caso), ahora es
el momento de detectar cuándo un dispositivo se conecta a este y
reacciona en consecuencia. El -
(void)peripheralManager:(CBPeripheralManager *)peripheral
central:(CBCentral *)central
didSubscribeToCharacteristic:(CBCharacteristic *)characteristic el método
característico capta cuando alguien se suscribe a nuestra característica
y luego comienza a enviar datos.
La aplicación envía los datos
disponibles en la vista de texto textview. Si el usuario lo cambia, la aplicación
se lo envía en tiempo real a la suscripción central. El método llama a
un método personalizado llamado sendData.
1 |
|
2 |
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic { |
3 |
|
4 |
_dataToSend = [_textView.text dataUsingEncoding:NSUTF8StringEncoding]; |
5 |
|
6 |
_sendDataIndex = 0; |
7 |
|
8 |
[self sendData]; |
9 |
}
|
SendData es el método que trata con toda la lógica con respecto a la transferencia de datos. Puede hacer varias acciones como:
- Enviar datos
- Enviar el final de la bandera de comunicación
- Prueba si la aplicación envió los datos
- Verificar si se enviaron todos los datos
- Reacciona a todos los temas anteriores
El código fuente completo se presenta a continuación. Varios comentarios se dejaron deliberadamente para facilitar su comprensión.
1 |
|
2 |
- (void)sendData { |
3 |
|
4 |
static BOOL sendingEOM = NO; |
5 |
|
6 |
// end of message?
|
7 |
if (sendingEOM) { |
8 |
BOOL didSend = [self.peripheralManager updateValue:[@"EOM" dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:self.transferCharacteristic onSubscribedCentrals:nil]; |
9 |
|
10 |
if (didSend) { |
11 |
// It did, so mark it as sent
|
12 |
sendingEOM = NO; |
13 |
}
|
14 |
// didn't send, so we'll exit and wait for peripheralManagerIsReadyToUpdateSubscribers to call sendData again
|
15 |
return; |
16 |
}
|
17 |
|
18 |
// We're sending data
|
19 |
// Is there any left to send?
|
20 |
if (self.sendDataIndex >= self.dataToSend.length) { |
21 |
// No data left. Do nothing
|
22 |
return; |
23 |
}
|
24 |
|
25 |
// There's data left, so send until the callback fails, or we're done.
|
26 |
BOOL didSend = YES; |
27 |
|
28 |
while (didSend) { |
29 |
// Work out how big it should be
|
30 |
NSInteger amountToSend = self.dataToSend.length - self.sendDataIndex; |
31 |
|
32 |
// Can't be longer than 20 bytes
|
33 |
if (amountToSend > NOTIFY_MTU) amountToSend = NOTIFY_MTU; |
34 |
|
35 |
// Copy out the data we want
|
36 |
NSData *chunk = [NSData dataWithBytes:self.dataToSend.bytes+self.sendDataIndex length:amountToSend]; |
37 |
|
38 |
didSend = [self.peripheralManager updateValue:chunk forCharacteristic:self.transferCharacteristic onSubscribedCentrals:nil]; |
39 |
|
40 |
// If it didn't work, drop out and wait for the callback
|
41 |
if (!didSend) { |
42 |
return; |
43 |
}
|
44 |
|
45 |
NSString *stringFromData = [[NSString alloc] initWithData:chunk encoding:NSUTF8StringEncoding]; |
46 |
NSLog(@"Sent: %@", stringFromData); |
47 |
|
48 |
// It did send, so update our index
|
49 |
self.sendDataIndex += amountToSend; |
50 |
|
51 |
// Was it the last one?
|
52 |
if (self.sendDataIndex >= self.dataToSend.length) { |
53 |
|
54 |
// Set this so if the send fails, we'll send it next time
|
55 |
sendingEOM = YES; |
56 |
|
57 |
BOOL eomSent = [self.peripheralManager updateValue:[@"EOM" dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:self.transferCharacteristic onSubscribedCentrals:nil]; |
58 |
|
59 |
if (eomSent) { |
60 |
// It sent, we're all done
|
61 |
sendingEOM = NO; |
62 |
NSLog(@"Sent: EOM"); |
63 |
}
|
64 |
|
65 |
return; |
66 |
}
|
67 |
}
|
68 |
}
|
Finalmente,
debe definir una devolución de llamada que se llama cuando el
PeripheralManager está listo para enviar el siguiente fragmento de
datos. Esto asegura que los paquetes lleguen en el orden en que se envían. El
método es el - (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral y solo llama al método sendData. La versión completa está a continuación:
1 |
|
2 |
- (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral { |
3 |
[self sendData]; |
4 |
}
|
Ahora puede ejecutar Run la aplicación y probar la comunicación Bluetooth. La siguiente imagen muestra la interfaz de CBCentralManager.



Conclusión
Al final de este tutorial, debe comprender las especificaciones del marco Core Bluetooth. También debería poder definir y configurar un CBCentralManager y un rol CBPeripheral, y comprender y aplicar algunas de las mejores prácticas cuando se desarrolla con Core Bluetooth.
Si tiene alguna pregunta o comentario, ¡déjela abajo!



