I wrote a simple working example, quite simple, and included it in open source Gitub: https://github.com/GitGarage . So far it has been tested only with Android Nexus 9 and iPhone 5, but I believe that it will also work with Nexus 6 and various types of iPhone. So far, it is explicitly configured for communication between one Android and one iPhone, but I believe that it is capable of doing much more.
Here are the basic methods ...
DROID SIDE - sending to iOS:
private void sendMessage() { Thread thread = new Thread(new Runnable() { @Override public void run() { if (mBTAdapter == null) { return; } if (mBTAdvertiser == null) { mBTAdvertiser = mBTAdapter.getBluetoothLeAdvertiser(); } // get the full message from the UI String textMessage = mEditText.getText().toString(); if (textMessage.length() > 0) { // add 'Android' as the user name String message = "Android: " + textMessage; while (message.length() > 0) { String subMessage; if(message.length() > 8) { // add dash to unfinished messages subMessage = message.substring(0,8) + "-"; message = message.substring(8); for (int i = 0; i < 20; i++) // twenty times (better safe than sorry) send this part of the message. duplicate parts will be ignored { AdvertiseData ad = BleUtil.makeAdvertiseData(subMessage); mBTAdvertiser.startAdvertising(BleUtil.createAdvSettings(true, 100), ad, mAdvCallback); mBTAdvertiser.stopAdvertising(mAdvCallback); } } else { // otherwise, send the last part subMessage = message; message = ""; for (int i = 0; i < 5; i++) { AdvertiseData ad = BleUtil.makeAdvertiseData(subMessage); mBTAdvertiser.startAdvertising( BleUtil.createAdvSettings(true, 40), ad, mAdvCallback); mBTAdvertiser.stopAdvertising(mAdvCallback); } } } threadHandler.post(updateRunnable); } } }); thread.start(); }
DROID SIDE - receive from iOS:
@Override public void onLeScan(final BluetoothDevice newDevice, final int newRssi, final byte[] newScanRecord) { int startByte = 0; String hex = asHex(newScanRecord).substring(0,29); // check five times, startByte was used for something else before while (startByte <= 5) { // check if this is a repeat message if (!Arrays.asList(used).contains(hex)) { used[ui] = hex; String message = new String(newScanRecord); String firstChar = message.substring(5, 6); Pattern pattern = Pattern.compile("[ a-zA-Z0-9~!@#$%^&*()_+{}|:\"<>?`\\-=;',\\./\\[\\]\\\\]", Pattern.DOTALL); // if the message is comprised of standard characters... Matcher matcher = pattern.matcher(firstChar); if (firstChar.equals("L")) { firstChar = message.substring(6, 7); pattern = Pattern.compile("[ a-zA-Z0-9~!@#$%^&*()_+{}|:\"<>?`\\-=;',\\./\\[\\]\\\\]", Pattern.DOTALL); matcher = pattern.matcher(firstChar); } if(matcher.matches()) { TextView textViewToChange = (TextView) findViewById(R.id.textView); String oldText = textViewToChange.getText().toString(); int len = 0; String subMessage = ""; // add this portion to our final message while (matcher.matches()) { subMessage = message.substring(5, 6+len); matcher = pattern.matcher(message.substring(5+len, 6+len)); len++; } subMessage = subMessage.substring(0,subMessage.length()-1); Log.e("Address",newDevice.getAddress()); Log.e("Data",asHex(newScanRecord)); boolean enter = subMessage.length() == 16; enter = enter && !subMessage.substring(15).equals("-"); enter = enter || subMessage.length() < 16; textViewToChange.setText(oldText + subMessage.substring(0, subMessage.length() - 1) + (enter ? "\n" : "")); ui = ui == 2 ? -1 : ui; ui++; Log.e("String", subMessage); } break; } startByte++; } }
iOS SIDE - sending to Android:
func startAdvertisingToPeripheral() { var allTime:UInt64 = 0; if (dataToSend != nil) { datastring = NSString(data:dataToSend, encoding:NSUTF8StringEncoding) as String datastring = "iPhone: " + datastring if (datastring.length > 15) { for (var i:Double = 0; i < Double(datastring.length)/15.000; i++) { let delay = i/10.000 * Double(NSEC_PER_SEC) let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay)) allTime = time dispatch_after(time, dispatch_get_main_queue(), { () -> Void in self.sendPart() }); } } else { var messageUUID = StringToUUID(datastring) if !peripheralManager.isAdvertising { peripheralManager.startAdvertising([CBAdvertisementDataServiceUUIDsKey: [CBUUID(string: messageUUID)]]) } } } }
iOS SIDE - receive from Android:
func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) { delegate?.didDiscoverPeripheral(peripheral) var splitUp = split("\(advertisementData)") {$0 == "\n"} if (splitUp.count > 1) { var chop = splitUp[1] chop = chop[0...chop.length-2] var chopSplit = split("\(chop)") {$0 == "\""} if !(chopSplit.count > 1 && chopSplit[1] == "Device Information") { var hexString = chop[4...7] + chop[12...19] + chop[21...26] var datas = hexString.dataFromHexadecimalString() var string = NSString(data: datas!, encoding: NSUTF8StringEncoding) as String if (!contains(usedList,string)) { usedList.append(string) if (string.length == 9 && string[string.length-1...string.length-1] == "-") { finalString = finalString + string[0...string.length-2] } else { lastString = finalString + string + "\n" println(lastString) finalString = "" usedList = newList usedList.append(string) } } } } }