whole code is like this:
var inputStream :NSInputStream?
var outputStream:NSOutputStream?
NSStream.getStreamsToHostWithName(ip, port: port, inputStream: &inputStream, outputStream: &outputStream)
let reader = inputStream
let writer = outputStream
writer?.open()
reader?.open()
var message : UInt8 = 0
while reader!.read(&message, maxLength: 1)>0
{
let wa = NSString(bytes: &message, length: 1, encoding: CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.GB_18030_2000.rawValue))) as! String
}
when the message i receive is a chinese character, the last line throws this:
fatal error:unexpected found nil while unwrapping an optional value, at the mean time, the value of message is 196
does anybody know how to solve this problem?
According to this page, if the byte you read is in the range 0x81-0xfe, the character is encoded using two or four bytes, so trying to decode just the first byte will fail. The NSString constructor will return nil, and attempting to unwrap it (with as! String) will throw that error.
You need to check what byte you read, and read another byte if necessary based on the first byte. Then you need to check the second byte, and possible read two more bytes. Finally, you need to pass all of the one, two, or four bytes to the NSString constructor in a single buffer.
Try to change message type for inputData like this for example:
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue) {
let bufferSize = 1024
var message = Array<UInt8>(count:bufferSize, repeatedValue: 0)
while true {
let bytesRead = self.inputStream!.read(&message, maxLength: bufferSize)
let responseString = NSString(bytes: message, length: message.count, encoding: NSUTF8StringEncoding) as! String
// Do somthing with response
...
}
}
Related
I'm working with a custom network protocol that requires a precise bytes sequence to do a sort of handshake, as example the first call should send a body like:
0003joy that, translated in a [UInt8] should be [0x00,0x00,0x00,0x03,0x6a,0x6f,0x79]
(please note that the first 4 numbers should not be converted to char... I'm sending the numeric value, as per protocol request)
I'm trying to send this sequence to an outputstream but I'm wondering if the steps I'm following are correct, here is my code... do you see anything strange that might prevent this sequence to reach the server?
// Create Bytes sequence
let bytes:[UInt8] = [0x00,0x00,0x00,0x03,0x6a,0x6f,0x79]
// Convert Bytes array do Data
let dt = Data(bytes)
// Send Data to stream
_ = dt.withUnsafeBytes {
guard let pointer = $0.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
return
}
outputStream.write(pointer, maxLength: dt.count)
}
Also, do I need to convert the bytes sequence to Data? is there a way to send bytes sequence directly into the socket without converting it into Data?
I don't see anything wrong with your code but the conversion to Data is not needed:
bytes.withUnsafeBytes {
guard let pointer = $0.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
return
}
outputStream.write(pointer, maxLength: bytes.count)
}
since [UInt8] conforms to ContiguousBytes.
Actually, the following is also possible:
bytes.withUnsafeBufferPointer {
guard let baseAddress = $0.baseAddress else { return }
outputStream.write(baseAddress, maxLength: bytes.count)
}
I'm trying to use NSInputStream for receiving data using TCP socket connection. On the server side I send data size before sending of the data itself. on the iOS client side I need to extract first 4 bytes from the NSInputStream, because I need to check if size of data has received completely, but I have a problem with it:
...
case NSStreamEvent.HasBytesAvailable:
if ( aStream == inputstream){
while (inputstream.hasBytesAvailable){
var readBufferRef = UnsafeMutablePointer<UnsafeMutablePointer<UInt8>>()
var readBufferLengthRef = 0
let readBufferIsAvailable = inputstream.getBuffer(readBufferRef, length: &readBufferLengthRef)
...
}
}
break
After receiving of data readBufferLengthRef always equals to 0.
How it can be?
And how can I get size of the NSInputStream buffer?
UPD:
Code:
case NSStreamEvent.HasBytesAvailable:
NSLog("HasBytesAvaible")
var buffer = [UInt8](count: 1024, repeatedValue: 0)
if ( aStream == inputstream){
while (inputstream.hasBytesAvailable){
var readBufferRef: UnsafeMutablePointer<UInt8> = nil
var readBufferLengthRef = 0
let readBufferIsAvailable = inputstream.getBuffer(&readBufferRef, length: &readBufferLengthRef)
//debugger: readBufferLengthRef = (int)0
}
}
break
In your code, readBufferRef is defined as a "pointer to a pointer"
but never allocated, and therefore it is the NULL pointer.
What you should do is to pass the address of an
UnsafeMutablePointer<UInt8> as an inout argument to the function
(assuming Swift 2):
var readBufferRef: UnsafeMutablePointer<UInt8> = nil
var readBufferLengthRef = 0
let readBufferIsAvailable = inputStream.getBuffer(&readBufferRef, length: &readBufferLengthRef)
On return, readBufferRef is set to the read buffer of the stream (valid until the next read operation), and readBufferLengthRef contains
the number of available bytes.
I'm trying to concatenate two NSData objects into one NSMutableData, and than get them back. For now i'm trying to do it in such way:
Get length of first object.
Write into NSMutableData in such order: first object length, first object, second object.
Code looks like:
let firstString = "first_string";
let secondString = "secondSting";
let firstData = firstString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
let secondData = secondString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
let mutableData = NSMutableData()
var length = firstData.length
mutableData.appendBytes(&length, length: sizeof(Int))
mutableData.appendData(firstData)
mutableData.appendData(secondData)
Then I want to get datas back. So I suppose to read first data length and then get two datas.
var length = 0
mutableData.getBytes(&length, length: sizeof(Int))
But when I'm trying to get data I'm getting crash instead:
var data = NSData()
mutableData.getBytes(&data, range: NSMakeRange(sizeof(Int), length))
Maybe somebody know where is my problem or how to get datas?
You can extract the data using subdataWithRange():
let firstData1 = mutableData.subdataWithRange(NSMakeRange(sizeof(Int), length))
if let firstString1 = NSString(data: firstData1, encoding: NSUTF8StringEncoding) as? String {
println(firstString1)
} else {
// bad encoding
}
Your solution
var data = NSData()
mutableData.getBytes(&data, range: NSMakeRange(sizeof(Int), length))
does not work and crashes because NSData is a reference type and
data a pointer to the object. You are overwriting this pointer
and the following bytes in memory.
This works perfectly without a crash in my storyboard. I just omitted the second var before length in order to avoid redefining it.
Here is the output for each line:
"first_string"
"secondSting"
<66697273 745f7374 72696e67> // let firstData = ...
<7365636f 6e645374 696e67> // let secondData = ...
<> // let mutableData = ...
12 // var length = ...
// appending data
<0c000000 00000000>
<0c000000 00000000 66697273 745f7374 72696e67>
<0c000000 00000000 66697273 745f7374 72696e67 7365636f 6e645374 696e67>
0 // length = 0
<0c000000 00000000 66697273 745f7374 72696e67 7365636f 6e645374 696e67>
12 // length
This means you probably have an error somewhere else. You did not redefine length, right?
I keep getting this error :
fatal error: unexpectedly found nil while unwrapping an Optional value
and cannot figure out how to debug it!
Here's my code :
func readCSV() -> Array<String> {
// Creates a new array of strings
var csvArray : Array<String> = Array<String>()
if let url: NSURL = NSURL(string : "URLFROMCSV" ) {
// Creates an Input Stream that will load the datas from our URL
let data :NSData! = NSData(contentsOfURL: url)!
let stream : NSInputStream! = NSInputStream(data: data)
// Opens the receiving stream
stream.open()
// Sets a buffer with a given size size
let bufferSize = 1024
var buffer = Array <UInt8>(count: bufferSize, repeatedValue: 0)
// String variable initialization
var csvFullString : String = ""
// While the stream receives datas, parses datas, convert them into strings and then concatenate them into one big string
while (stream.hasBytesAvailable) {
let readSize = stream.read(&buffer, maxLength: bufferSize)
let csvRaw = NSString (bytes: &buffer, length: readSize, encoding: NSUTF8StringEncoding)
let csvString = csvRaw as String!
csvFullString = csvFullString + csvString
}
// Fills the array with each strings. Separation between strings is made when a Θ character is parsed
csvArray = csvFullString.componentsSeparatedByString("Θ")
// Delete each null string
for(var i = 0 ; i < csvArray.count; i++) {
if(csvArray[i] == "") {
csvArray.removeAtIndex(i)
}
}
}
return csvArray
}
After searching on the web, I'm pretty sure it has something to do with unwrapping elements but the fact is when I debug it, i don't get any nil value anywhere.
PS: Would like to upload a screen but can't because i don't have 10 reputation, so bad!
Thanks in advance!
EDIT : Line let data :NSData! = NSData(contentsOfURL: url)! got the error.
Terry
You're probably creating the error in one of these two lines (though it may show up later):
let data :NSData! = NSData(contentsOfURL: url)!
let stream : NSInputStream! = NSInputStream(data: data)
You're assigning an optional value to an implicitlyUnwrappedOptional type and then using it without checking if you have a valid value.
This is why if let exists. It's a little funny that you've started to indent as if you're using if let but aren't.
Try this instead:
if let url = NSURL(string : "http://gorillaapplications.com/etablissements.csv" ) {
// Creates an Input Stream that will load the datas from our URL
if let data = NSData(contentsOfURL: url) {
let stream = NSInputStream(data: data)
stream.open()
// rest of your code here
}
else {
println("Didn't get a data object")
}
}
else {
println("Didn't get a URL object")
}
You really need to grasp Optionals for Swift. I'd recommend reading my Optionals chapter in this iBook: https://itunes.apple.com/us/book/swift-optionals-generics-for/id943445214?mt=11&uo=4&at=11lMGu
Update:
Since you added a bit more in your comments above, you're saying you get the error on this line: let data: NSData! = NSData(contentsOfURL: url)!. This is because of the ! at the end, which tells Swift you're sure that this function will return a valid value, so just use it, without checking if it's nil first. In your case, the function is returning nil and so your app crashes. Using the sample code I've provided above, you'll see that you'll no longer get a crash, but your code will execute the "Didn't get a data object" line. You'll need to correctly handle the case where you can't load data from that URL.
We are implementing SQLite in iOS, in Swift, without using wrappers or Objective-C bridging. Everything works fine, except when doing a query and extracting the result. The issue is with the UnsafePointer<UInt8> that is returned from SQLite in Swift as follows:
var querySQL = "SELECT address, phone FROM CONTACTS WHERE NAME = 'myName'"
var cQuery = querySQL.cStringUsingEncoding(NSUTF8StringEncoding)
var statement: COpaquePointer = nil
if sqlite3_prepare_v2(contactsDB, cQuery!, -1, &statement, nil) == SQLITE_OK {
if sqlite3_step(statement) == SQLITE_ROW {
var address : UnsafePointer<UInt8> = sqlite3_column_text(statement, 0)
var data = NSData(bytes: address, length: 10)
var string = NSString(data: data, encoding: NSUTF8StringEncoding)
println(string)
As you can see, we can convert the pointer to String if we know the length of the object (in this case 10)
To dig into this issue, I have the following example
let pointerFromString: UnsafePointer<Int8> = "xyz".cStringUsingEncoding(NSUTF8StringEncoding)
let stringFromPointer = String.fromCString(anotherPointerFromString_Int8) println(stringFromPointer!)
Given that CChar is an alias of Int8, I can convert a String to UnsafePointer<Int8> using .cStringUsingEncoding(), and then back to String using .fromCString(<UnsafePointer_CChar>)
The problem is that my SQLite result is a UnsafePointer_UInt8, that can´t be used with .fromCString()
The bottom line question is: Is it possible to convert or cast a UnsafePointer_UInt8 to UnsafePointer_Int8
This should work:
let address = sqlite3_column_text(statement, 0)
let string = String.fromCString(UnsafePointer<CChar>(address))
Update for Swift 3 (Xcode 8), compare Swift 3: convert a null-terminated UnsafePointer<UInt8> to a string:
let string = String(cString: sqlite3_column_text(statement, 0))
Update:
sqlite3_column_text() returns an implicitly unwrapped optional, and that is why you can pass it to String(cString:) directly.
But according to Column methods, error and NULL handling #41, the return value can be null (in an out-of-memory situation, or if called with a NULL column type). Therefore it is safer to do
if let text = sqlite3_column_text(statement, 0) {
let string = String(cString: text)
// ...
} else {
// sqlite3_column_text() returned NULL
}