Why does Swift unexpectedly insert “Optional” at the beginning of stringFromData? - swift

I run this code on an iPad to create virtual BLE peripheral.
It starts advertising.
I run the central on iPhone.
Central detects peripheral and connects and subscribes.
Peripheral log unexpectedly has "Optional" in the log, though it's not in stringFromData.
IMAGE SHOWS stringFromData CONTENT AND LOG.........
class PeripheralViewController: UIViewController {
var packet_to_send = Data()
. . .
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
os_log("Central subscribed to characteristic")
// Init 1st sim packet:
packet_to_send = ("antenna data chunk " + String( packet_number )).data(using: .utf8)!
let stringFromData = String(data: packet_to_send, encoding: .utf8)
os_log("initial packet_to_send %d bytes = '%s'.", packet_to_send.count, String( describing: stringFromData))

stringFromData is an Optional. When you use String(describing:) to get the description of an Optional, it will be "Optional(yourDescription)" rather than "yourDescription".
You can avoid this by converting the Optional<String> into a String using optional binding or by providing a default value.
let stringFromData = String(data: packet_to_send, encoding: .utf8) ?? ""
os_log("initial packet_to_send %d bytes = '%s'.", packet_to_send.count, stringFromData)

Related

Can't write JSON to peripheral with CoreBluetooth

I have a paired bluetooth peripheral which I have to send some credentials in a JSON like as follows
{"SSID":"WIFI_SSID", "Password": "WIFI_PASSWORD"}
And after the information has been sent the peripheral should connect to the provided WiFi credentials but I'm not completely sure the process is being performed correctly.
As soon the bluetooth peripheral has connected I start the process,
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
// As soon as the device has connected
peripheral.delegate = self
// Only discover the service I know it's for writing
peripheral.discoverServices([SERVICE_UUID])
}
The discoverServices will call the peripheral(:didDiscoverServices error)
func peripheral( _ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
for service in peripheral.services! {
peripheral.discoverCharacteristics(nil, for: service)
}
}
Calling the following method where I do all the logic
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for characteristic in service.characteristics! {
let characteristic = characteristic as CBCharacteristic
debugPrint(characteristic.properties)
// This prints the following properties, reading + writing
// __C.CBCharacteristicProperties(rawValue: 10)
if characteristic.uuid.isEqual(RX_UUID) {
do {
let msgDictionary = ["SSID": wiFiCredentials.SSID, "Password": wiFiCredentials.Password]
let jsonData = try JSONSerialization.data(withJSONObject: msgDictionary,options:[])
peripheral.writeValue(jsonData, for: characteristic, type: CBCharacteristicWriteType.withResponse)
} catch let error as NSError {
debugPrint("Error in auth message JSON: \(error.description)")
}
}
}
}
Up to this point I think everything it's correct. After calling writeValue and setting the type to CBCharacteristicWriteType.withResponse I should expect something in the peripheral(:didWriteValueFor characteristic: error) method. What I receive in that method is the next error
Error Domain=CBATTErrorDomain Code=3 \"Writing is not permitted.\" UserInfo={NSLocalizedDescription=Writing is not permitted.}"
What I guess is that when I write the JSON value I shouldn't use the .withResponse flag and use .withoutResponse. If I do so I get the following log in the console.
[CoreBluetooth] WARNING: Characteristic <CBCharacteristic: 0x28388a040, UUID = 3E9D2532-2F00-11E9-9602-A44CC81C989A, properties = 0xA, value = (null), notifying = NO> does not specify the "Write Without Response" property - ignoring response-less write
Which confirms to me that I have to use the .writeWithResponse.
Is there something I am missing in the process? The JSON has to be sent using GATT and AFAIK this is the correct approach to do it. Are the printed CBCharacteristicProperties correct?
Things I've done:
The JSON is not the problem. I've tried writing a random variable "1".data(using: .ascii) and still receive the same error.

RSAUtils Decryption , Converting data to it's original value - Swift

i am trying to encrypt and decrypt a string using RSAUtils swift library, i am having a problem returning the decrypted text to it's original value . this is the code:
let PUBLIC_KEY = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJh+/sdLdlVVcM5V5/j/RbwM8SL++Sc3dMqMK1nP73XYKhvO63bxPkWwaY0kwcUU40+QducwjueVOzcPFvHf+fECAwEAAQ=="
let sampleText:String = "WHATS UP"
let encrypted:Data? = RSAUtils.encryptWithRSAPublicKey(sampleText.data(using: String.Encoding.utf8)!, pubkeyBase64: PUBLIC_KEY, keychainTag: "12345")!
let decrypted:Data? = RSAUtils.decryptWithRSAPublicKey(encrypted!, pubkeyBase64: PUBLIC_KEY, keychainTag: "12345")
let encryptedDataText = encrypted!.base64EncodedString(options: NSData.Base64EncodingOptions())
let decryptedDataText = decrypted!.base64EncodedString(options: NSData.Base64EncodingOptions())
I tried to convert the decrypted data to string using this code:
if let string = String(data: decrypted! , encoding: .utf16) {
print(string)
} else {
print("not a valid UTF-16 sequence")
}
But it prints "㺸ꉄ꤈꽹㲞㏯荘뼵鉉큅령嬰ꢤẲ毪쌶⏤ᱼ埡佒�ࡊᩏ⧚㨈؍੯屍" I also tried to decode the base64 value using :
let decodedData = Data(base64Encoded: decryptedDataText)!
let decodedString = String(data: decodedData, encoding: .utf8)!
print(decodedString)
It causes an error
Fatal error: Unexpectedly found nil while unwrapping an O
probably the text is not a valid base64 string.
How can i convert the decrypted data to it's original value.
Thanks.

Parsing NSData is crashes

I get this data from BLE, but is crashes on certain values (I think when data is float )
function to get data :
func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?) {
print(characteristic); // prints a value always
if characteristic.UUID == UUID_CHAR {
if ( characteristic.value == nil)
{return; }
//***crash is here :
let str:NSString = NSString(data: characteristic.value!, encoding: NSUTF8StringEncoding)!
print ("got::",str);
This will crash when the characteristic is :
<CBCharacteristic: 0x157685860, UUID = FFE1, properties = 0x16, value = <00ff00>, notifying = YES>
and not when its this : (value field here is 00 )
<CBCharacteristic: 0x157685860, UUID = FFE1, properties = 0x16, value = <00>, notifying = YES>
I have tried wrapping it with if let , which did not worked.
ERROR: unexpectedly found nil while unwrapping an Optional value
How would you cast the value to string, knowing it could be a float or integer ?
Don't need to cast to NSString, Then you don't have to forcefully unwrap the result. Try this:
let str = NSString(data: characteristic.value!, encoding: NSUTF8StringEncoding)
Now if the value is not a valid UTF-8 Sequence, You won't get a crash

URLSession in command line tool: control multiple tasks and convert HTML to text

In my project for OS X Command Line Tool using Swift 3.0 (Beta 2) I need to convert HTML data to String from multiple URLs. There is a problem in use of such function with many background tasks (it's not working except the main thread, so maybe there is more elegant way to control completion of all tasks and read HTML data in such tool with or without parsers that I need for Swift 3 and Mac OS X (Linux in the near future)):
func html2text (html: String, usedEncoding: String.Encoding) -> String {
let data = html.data(using: usedEncoding)!
if let htmlString = AttributedString(html: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: usedEncoding.rawValue], documentAttributes: nil)?.string {
return htmlString
} else {
return ""
}
}
So I read data first into an Array, waiting when all DataTasks finished and then converting it in the main thread. Also using global variable (Set of urls) to control completion of each task:
import Foundation
import WebKit
var urlArr = [String]()
var urlSet = Set<String>()
var htmlTup : [(url : String, html : String, encoding : String.Encoding)] = []
let session = URLSession.shared
For-in loop with multiple URLSession DataTasks
for myurl in urlArr {
if urlSet.insert(myurl).inserted {
print ("Loading \(myurl)...")
let inputURL = URL(string: myurl)!
let task = session.dataTask(with: inputURL, completionHandler: {mydata, response, error in
Read Encoding from HTML First
var usedEncoding = String.Encoding.utf8
if let encodingName = response!.textEncodingName {
let encoding = CFStringConvertIANACharSetNameToEncoding(encodingName)
if encoding != kCFStringEncodingInvalidId {
usedEncoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(encoding))
}
}
Do some work with HTML String and read data into an Array
if let myString = String(data: mydata!, encoding: usedEncoding) {
htmlTup += [(url: myurl,html: myString, encoding: usedEncoding)]
}
// The end of task removing URL from Set
urlSet.remove(myurl)
})
//Run Task
task.resume()
}
}
}
Waiting for tasks to complete and convert HTML to text
while !urlSet.isEmpty {
// Do nothing
}
for (url,html,encoding) in htmlTup {
print ("Writing data from \(url)...")
print (html2text(html: html, usedEncoding: encoding))
}
Update 1: RunLoop in the main thread from this
Such code to check when each task finished:
var taskArr = [Bool]()
let task = session.dataTask(with: request) { (data, response, error) in
}
taskArr.removeLast()
}
taskArr.append(true)
task.resume()
// Waiting for tasks to complete
let theRL = RunLoop.current
while !taskArr.isEmpty && theRL.run(mode: .defaultRunLoopMode, before: .distantFuture) { }
You can't just spin in a busy loop waiting for results, because you're blocking the main run loop/thread/dispatch queue by doing that.
Instead, return at that point, thus allowing the main run loop to run. Then, in your completion handler, check to see if you've gotten all the responses you're expecting, and if so, do the stuff that you currently have after that busy wait while loop.

unable to print the data after encoding in swift

I'm getting below error when i try to encode the data. If i print directly without encoding its printing properly
fatal error: unexpectedly found nil while unwrapping an Optional value
// get the NSURLRequestSession and get the data
let task = NSURLSession.sharedSession().dataTaskWithRequest(nsurlReq){
(data,response,error) in
// check whether there is no error
if(error==nil)
{
// println("Data \(data) ");
var encodedData = NSString(data: data, encoding: NSUTF8StringEncoding)!;
println(encodedData);
}
} //
// you need to resume the task
task.resume();
I would expect that your data has invalid encoding and that is why you're not able to print out the parsed data.