How to convert UnsafeMutablePointer<UInt8> to UIImage in Swift - ios

I have been trying to send image data from android and receive at iOS using Socket,But the problem is that I am not able to show Image from data which is received from stream.
Here is the complete code:-
Setting Up the network communication
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
ip as CFString,
3003,
&readStream,
&writeStream)
inputStream = readStream!.takeRetainedValue()
outputStream = writeStream!.takeRetainedValue()
inputStream.delegate = self
// outputStream.delegate = self
inputStream.schedule(in: .current, forMode: RunLoop.Mode.common)
outputStream.schedule(in: .current, forMode: RunLoop.Mode.common)
inputStream.open()
outputStream.open()
Receive data through the delegates as shown below:-
extension ChatRoom: StreamDelegate {
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch eventCode {
case Stream.Event.hasBytesAvailable:
print("new message received")
readAvailableBytes(stream: aStream as! InputStream)
case Stream.Event.endEncountered:
print("end received")
stopStreamSession()
case Stream.Event.errorOccurred:
print("error occurred")
case Stream.Event.hasSpaceAvailable:
print("has space available")
case Stream.Event.openCompleted:
print("Stream opened")
default:
print("some other event...")
break
}
}
private func readAvailableBytes(stream: InputStream) {
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: maxReadLength)
while stream.hasBytesAvailable {
let numberOfBytesRead = inputStream.read(buffer, maxLength: maxReadLength)
if numberOfBytesRead < 0 {
if let _ = stream.streamError {
break
}
}
NSLog("number of bytes read = %i", numberOfBytesRead)
if numberOfBytesRead > 0 {
dataOfImage.append(buffer, count: numberOfBytesRead)
}
}
}
}
I have been trying to show image from data received using UIImage(data:dataOfImage), But it is showing nil , when I inspect the dataOfImage it consists of all the data received from stream.
Can anyone suggest a way to get the image out of UnsafeMutablePointer<UInt8> Data.
Also if there is any better approach for data transfer through sockets, Please do Suggest..

Rather than allocate memory declare a buffer variable with the capacity of maxReadLength.
And stream(_:handle:) is called multiple times. You have to check the result of the read function
private var dataOfImage = Data()
private func readAvailableBytes(stream: InputStream) {
var buffer = [UInt8](repeating: 0, count: maxReadLength)
let bytesRead = inputStream.read(&buffer, maxLength: maxReadLength)
switch bytesRead {
case 0: // success, end of file
case -1: // error
default: // append data
dataOfImage.append(contentsOf: buffer)
}
NSLog("number of bytes read = %i", bytesRead)
}

Related

CFStreamCreatePairWithSocketToHost - How to handle the network disconnect?

We have created socket for streaming using the API "CFStreamCreatePairWithSocketToHost".
func openStream() {
if let aUrl = url {
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocketToHost(
kCFAllocatorDefault,
aUrl as CFString,
port!,
&readStream,
&writeStream)
iStream = readStream!.takeRetainedValue()
oStream = writeStream!.takeRetainedValue()
if ssl == true {
let dict = [
kCFStreamSSLValidatesCertificateChain: kCFBooleanFalse, // allow self-signed certificate
kCFStreamSSLLevel: kCFStreamSSLValidatesCertificateChain // don't understand, why there isn't a constant for version 1.2
] as CFDictionary
let sslSetRead = CFReadStreamSetProperty(iStream, CFStreamPropertyKey(kCFStreamPropertySSLSettings), dict)
let sslSetWrite = CFWriteStreamSetProperty(oStream, CFStreamPropertyKey(kCFStreamPropertySSLSettings), dict)
if sslSetRead == false || sslSetWrite == false {
//print("SSL Configuration Failed")
}
}
commonStr = ""
open(iStream)
open(oStream)
print("Open Stream")
}
}
Used the below delegate for getting events.
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
// print("Stream : \(aStream.debugDescription) Event : \(eventCode.rawValue)")
switch eventCode {
case .endEncountered, .errorOccurred:
streamError()
break
case .hasBytesAvailable:
if let iStream = iStream {
if aStream == iStream {
//read data
let bufferSize = 1024
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
var len: Int
while iStream.hasBytesAvailable {
len = iStream.read(buffer, maxLength: bufferSize)
if len > 0 {
let output = Data.init(bytes: buffer, count: len)
bufferTokenize(output: output)
}else {
break
}
}
buffer.deallocate()
}
}
break
case .hasSpaceAvailable:
if aStream == oStream! {
streamReady()
}
break
case .openCompleted:
if aStream == oStream! {
if let del = delegate {
del.connectionCompleted()
}
}
break
default:
break
}
}
Other Supporting functions are
func streamError() {
stopStream()
NSObject.cancelPreviousPerformRequests(withTarget:self)
perform(#selector(callReconnect), with: nil, afterDelay: 2)
}
func open(_ stream: Stream?) {
if let aStream = stream {
aStream.delegate = self
aStream.schedule(in: RunLoop.main, forMode: .common)
aStream.open()
}
}
func stopStream() {
print("Stop Stream")
close(iStream)
close(oStream)
}
func close(_ stream: Stream?) {
if nil != stream {
stream?.close()
stream?.remove(from: RunLoop.main, forMode: .common)
}
}
func resetStream() {
stopStream()
openStream()
}
It is working fine for below steps
Create connection - Open stream
get "openCompleted" event - then Create request
get "hasSpaceAvailable" event - then Send Request
get "hasBytesAvailable" event - then Handle Data
Disconnect connection from server for testing purpose
get "endEncountered" event - then Close the connection and Recreate the connection from step 1
But the below scenario not working properly
Follow the steps 1 - 4 then
Disconnect Internet connection for testing purpose - the event "hasBytesAvailable" stopped
Again reconnect the Internet connection within 30 seconds - now we get "hasBytesAvailable" event
But the reconnect time extent to 5 minute - We don't have any event, and don't get "endEncountered" or "errorOccurred" also. Then how to move to next stop.
Please Help, Thanks
Regards,
Elamvazhuthi K

StreamDelegate stops to receive events after a amount of readings in swift

I have an iOS chat application that receives messages over a socket connection.
When user open the app after a long time and there are more than 50 unread messages, the server send over socket a message telling the number of unread messages, at this point the application show an alert with a progress bar and then the server send each one message.
So, the application get each one message on the StreamDelegate method stream(_ stream: Stream, handle eventCode: Stream.Event) and update the progress bar until the end of messages.
The problem is, when I have a large amount of unread messages (about 300+) at some point the StreamDelegate stops to receive the events with the messages, and none error messages is displayed.
I call the connect method on a global queue:
DispatchQueue.global().async {
self.connect(host, port: port)
}
This is my socket connect code:
fileprivate func connect(_ host: String, port: Int) {
postStatus(.connecting)
self.host = NSString(string: host)
self.port = UInt32(port)
self.log("connect to \(host):\(port)")
var readStream : Unmanaged<CFReadStream>?
var writeStream : Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocketToHost(nil, self.host, self.port, &readStream, &writeStream)
self.inOk = false
self.outOk = false
self.inputStream = readStream!.takeRetainedValue()
self.outputStream = writeStream!.takeRetainedValue()
self.inputStream.delegate = self
self.outputStream.delegate = self
let mainThread = Thread.isMainThread;
let loop = mainThread ? RunLoop.main : RunLoop.current
self.inputStream.schedule(in: loop, forMode: RunLoopMode.defaultRunLoopMode)
self.outputStream.schedule(in: loop, forMode: RunLoopMode.defaultRunLoopMode)
self.inputStream.open()
self.outputStream.open()
self.timer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(connectionTimeout), userInfo: nil, repeats: false)
if(!mainThread) {
loop.run()
}
}
In the StreamDelegate method stream(_ stream: Stream, handle eventCode: Stream.Event) I get the message event and process it on the method read(String)
case Stream.Event.hasBytesAvailable:
if let timer = timer {
timer.invalidate()
self.timer = nil
}
let json = ChatLibSwift.readMessage(self.inputStream)
do {
if StringUtils.isNotEmpty(json) {
try self.read(json)
}
} catch let ex as NSError {
LogUtils.log("ERROR: \(ex.description)")
}
break
case Stream.Event.hasSpaceAvailable:
break
The method that read each one message:
static func readMessage(_ inputStream: InputStream) -> String {
do {
var lenBytes = [UInt8](repeating: 0, count: 4)
inputStream.read(&lenBytes, maxLength: 4)
// header
let i32: Int = Int(UInt32.init(lenBytes[3]) | UInt32.init(lenBytes[2]) << 8 | UInt32.init(lenBytes[1]) << 16 | UInt32.init(lenBytes[0]) << 24 )
var msg = [UInt8](repeating: 0, count: (MemoryLayout<UInt8>.size * Int(i32)))
let bytesRead = inputStream.read(&msg, maxLength: Int(i32))
if bytesRead == -1 {
print("<< ChatLib ERROR -1")
return ""
}
let s = NSString(bytes: msg, length: bytesRead, encoding: String.Encoding.utf8.rawValue) as String?
if let s = s {
if bytesRead == Int(i32) {
return s
}
else {
print("Error: readMessage \(s)")
}
return s
}
return ""
} catch {
return ""
}
}
Anyone has idea how to solve it?
The main idea is to force schedule the reading of the stream after a successful read operation:
let _preallocatedBufferSize = 64 * 1024
var _preallocatedBuffer = [UInt8](repeating: 0, count: MemoryLayout<UInt8>.size * Int(_preallocatedBufferSize))
var message : ....
func readMessage(_ inputStream: InputStream) {
if !inputStream.hasBytesAvailable || message.isCompleted {
return
}
var theBuffer : UnsafeMutablePointer<UInt8>?
var theLength : Int = 0
// try to get buffer from the stream otherwise use the preallocated buffer
if !inputStream.getBuffer(&theBuffer, length:&theLength) || nil == theBuffer
{
memset(&_preallocatedBuffer, 0, _preallocatedBufferSize)
let theReadCount = inputStream.read(&_preallocatedBuffer, maxLength:_preallocatedBufferSize)
if theReadCount > 0 {
theBuffer = _preallocatedBuffer;
theLength = theReadCount;
} else {
theBuffer = nil;
theLength = 0;
}
}
if nil != theBuffer && theLength > 0 {
_message.appendData(theBuffer, length:theLength)
self.perform(#selector(readMessage), with:inputStream, afterDelay:0.0, inModes:[RunLoopMode.defaultRunLoopMode])
}
}

Create a InputStream that receives data through a socket in Swift 3 (iOS)

So my issue is getting a InputStream to work within swift for the iOS platform. I wrote some code and will post it below, but basically I have an Android client that sends data to the IP of the iOS client. Unfortunately the Android client says the connection was refused to connect to iOS client. I'm really not too sure what I'm doing wrong here as I think the code looks solid, so I'm kind of confused. By the way this my first time working with sockets in swift(iOS) so please understand I'm new to this and am here to learn through example code.
ViewControler.swift
var inputstream: InputStream?
var outputstream: OutputStream?
var host: CFString?
var port: UInt32?
override func viewDidLoad() {
super.viewDidLoad()
initNetworkCommunication()
}
func initNetworkCommunication(){
host = "192.168.1.187" as CFString
port = 55555
var readstream : Unmanaged<CFReadStream>?
var writestream : Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host!, port!, &readstream, &writestream)
inputstream? = readstream!.takeRetainedValue()
outputstream? = writestream!.takeRetainedValue()
inputstream?.delegate = self as! StreamDelegate
outputstream?.delegate = self as! StreamDelegate
inputstream?.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
outputstream?.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
inputstream?.open()
Status.text = "Input Stream Opened"
outputstream?.open()
Status.text = "Output Stream Opened"
}
func stream(_ aStream: Stream, handleEvent eventCode: Stream.Event) {
switch (eventCode){
case Stream.Event.errorOccurred:
print("Can not connect to the host!")
Status.text = "Cannot connect to: \(host ?? "Null Host" as CFString)"
break
case Stream.Event.endEncountered:
outputstream?.close()
outputstream?.remove(from: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
outputstream = nil
break
case Stream.Event.hasBytesAvailable:
Status.text = "Recieving data..."
NSLog("HasBytesAvaible")
var buffer = [UInt8](repeating: 0, count: 4096)
if ( aStream == inputstream){
while (inputstream?.hasBytesAvailable)!{
var len = inputstream?.read(&buffer, maxLength: buffer.count)
if(len! > 0){
Status.text = "Collected data..."
var data1 = NSData(bytes: buffer, length: buffer.count)
Status.text = "Converted Data to NSData..."
var str64 = data1.base64EncodedData(options: .lineLength64Characters)
Status.text = "Decode Base64..."
let data: NSData = NSData(base64Encoded: str64 , options: .ignoreUnknownCharacters)!
// turn Decoded String into Data
Status.text = "Turning Base64 data into image..."
let dataImage = UIImage(data: data as Data)
Status.text = "Done"
// pass the data image to image View.:)
Display.image = dataImage
Status.text = "Image Displayed"
}
}
}
break
case Stream.Event.openCompleted:
NSLog("OpenCompleted")
break
case Stream.Event.hasSpaceAvailable:
NSLog("HasSpaceAvailable")
break
default:
print("Unknown event")
}
}
It sounds like the Android device is trying to connect to the iOS device, and from the code you've posted, the iOS device is trying to connect to the Android device. That's not how socket networking works.
One of the devices (or both, if this a peer to peer setup) needs to listen for connections on a particular port. See this guide (https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/UsingSocketsandSocketStreams.html#//apple_ref/doc/uid/CH73-SW8) for information about creating a listening socket from iOS.

iOS - Sending and receiving data simultaneously

thanks for taking the time to read my question.
Basically I'm connecting to another device using MultipeerConnectivity and I want to stream data to the other device while receiving data.
My issue is that one of the devices ends up only sending data, and the other device ends up only receiving it.
Here is my sending method. This method is a delegate method from AVCaptureAudioDataOutputSampleBufferDelegate so it is automatically called.
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) {
//-------------------------------------------------------
// Writing to output stream
var blockBuffer: CMBlockBuffer?
var audioBufferList: AudioBufferList = AudioBufferList.init()
CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBuffer, nil, &audioBufferList, MemoryLayout<AudioBufferList>.size, nil, nil, kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, &blockBuffer)
let buffers = UnsafeMutableAudioBufferListPointer(&audioBufferList)
for buffer in buffers {
let u8ptr = buffer.mData!.assumingMemoryBound(to: UInt8.self)
let output = outputStream!.write(u8ptr, maxLength: Int(buffer.mDataByteSize))
if (output == -1) {
let error = outputStream?.streamError
print("\(#file) > \(#function) > Error on outputStream: \(error!.localizedDescription)")
}
else {
print("\(#file) > \(#function) > Data sent")
}
}
}
And here is my receiving method (I do handle other stream events they are just irrelevant here). You can ignore all of the audio related stuff, it doesn't work right now, but it's not my main issue. This method is a delegate of OutputStream, so it is also automatically called when data is available from the stream.
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
case Stream.Event.hasBytesAvailable:
print("\(#file) > \(#function) > New data has arrived")
readFromStream()
}
func readFromStream() {
while (inputStream!.hasBytesAvailable) {
var buffer = [UInt8](repeating: 0, count: 4096)
let length = inputStream!.read(&buffer, maxLength: buffer.count)
if (length > 0) {
if (audioEngine!.isRunning) {
print("\(#file) > \(#function) > audioEngine is running")
audioEngine!.stop()
audioEngine!.reset()
}
else {
print("\(#file) > \(#function) > audioEngine is NOT running")
}
print("\(#file) > \(#function) > \(length) bytes read")
let audioBuffer = bytesToAudioBuffer(buffer)
let mainMixer = audioEngine!.mainMixerNode
audioEngine!.connect(audioPlayer!, to: mainMixer, format: audioBuffer.format)
audioPlayer!.scheduleBuffer(audioBuffer, completionHandler: nil)
do {
try audioEngine!.start()
}
catch let error as NSError {
print("\(#file) > \(#function) > error: \(error.localizedDescription)")
}
audioPlayer!.play()
}
}
}
From these two methods you can see I have print statements for when data comes in and when data is being sent out. On one of the devices I end up consistently reading data and never sending any, and on the other device I end up constantly sending data and never receiving any.
I imagine this is because I'm not using threads.. But I've never worked with threads on iOS. If this is the case, can anyone guide me on how to do so?
If you need any more information please let me know.
Eventually I did figure out a feasible way to use threads:
let streamReceiverQueue = DispatchQueue(label: "newQueue", qos: DispatchQoS.userInteractive)
streamReceiverQueue.sync {
// code here
}
streamReceiverQueue.async {
// code here
}

NSOutputStream blocking, HasBytesAvailable event was not triggered

I am trying to create custom framework that will deal with ExternalAccessory.framework to do read/write operation with the connected accessory.
I can able to create a session and open the device to do read/write operations.
Problem i am facing was, when i try to write data using NSOutputStream.write from the application, data successfully received by the accessory but after that UI was not responding and data returned byte accessory was not received by the app(HasBytesAvailable was not called)
Here my UIViewController.swift
let sessionHandler = SessionHandler().sharedController()
in ViewDidLoad()
let runLoop = NSRunLoop.currentRunLoop()
sessionHandler.openDeviceWithProtocolString(runLoop)
#IBAction calling sessionHandler._WriteData()
This SessionHandler class was in inside my custom SDK (custom framework)
Here my SessionHandler Class
import UIKit
import ExternalAccessory
public class SessionHandler: NSObject, NSStreamDelegate {
var readData: NSMutableData?
var writeData: NSMutableData?
var _session: EASession?
public func sharedController() -> SessionHandler{
var sessionController: SessionHandler?
if sessionController == nil {
sessionController = SessionHandler()
}
return sessionController!
}
func getConnectedAccessoris() -> Array<EAAccessory>{
let accessories : Array<EAAccessory> = EAAccessoryManager.sharedAccessoryManager().connectedAccessories
return accessories
}
public func openDeviceWithProtocolString(_runLoop: NSRunLoop){
print("Inside openDeviceWithProtocolString")
let _accessories = getConnectedAccessoris()
var _accessory: EAAccessory?
for acsy in _accessories {
if acsy.protocolStrings.contains("my.protocol.string") {
_accessory = acsy
}
}
if _accessory != nil {
_session = EASession(accessory: _accessory!, forProtocol: "my.protocol.string")
print("EASession create :: \(_session)")
if _session != nil {
_session?.inputStream?.delegate = self
_session?.inputStream?.scheduleInRunLoop(_runLoop, forMode: NSDefaultRunLoopMode)
_session?.inputStream?.open()
print("Input stream Opened")
_session?.outputStream?.delegate = self
_session?.outputStream?.scheduleInRunLoop(_runLoop, forMode: NSDefaultRunLoopMode)
_session?.outputStream?.open()
print("Output Stream Opened")
}else {
print("SessionHandler : session nil")
}
}
}
public func _readData() {
print("Trying to read data")
let INPUT_BUFFER_SIZE = 65536
let buf = UnsafeMutablePointer<UInt8>.alloc(INPUT_BUFFER_SIZE)
while ((_session?.inputStream?.hasBytesAvailable) != nil) {
let bytesRead = _session?.inputStream?.read(buf, maxLength: INPUT_BUFFER_SIZE)
if readData == nil {
readData = NSMutableData()
}
readData?.appendBytes(buf, length: bytesRead!)
}
if readData != nil {
let data: NSData = readData!
let count = data.length / sizeof(UInt8)
// create an array of Uint8
var array = [UInt8](count: count, repeatedValue: 0)
// copy bytes into array
data.getBytes(&array, length:count * sizeof(UInt8))
print("Data Received :: \(array)")
validateData(array)
}
}
public func _writeData() {
while _session?.outputStream?.hasSpaceAvailable != nil && writeData?.length > 0 {
print("Writting bytes :: \(writeData?.bytes)")
let bytesWritten = _session?.outputStream?.write(UnsafePointer<UInt8>((writeData?.bytes)!), maxLength: (writeData?.length)!)
print("written bytes : \(bytesWritten)")
if bytesWritten == -1 {
//Write error
print("written error")
break
}else if bytesWritten > 0 {
print("Written success")
validateData(writeData)
writeData?.replaceBytesInRange(NSMakeRange(0, bytesWritten!), withBytes: nil, length: 0)
}
}
}
func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {
switch (eventCode) {
case NSStreamEvent.None:
print("NSStream None")
break
case NSStreamEvent.OpenCompleted:
print("Open Completed")
break
case NSStreamEvent.HasBytesAvailable:
print("Has Bytes Available")
_readData()
break
case NSStreamEvent.HasSpaceAvailable:
print("Hase space Available")
_writeData()
break
case NSStreamEvent.ErrorOccurred:
print("Error occurred")
break
case NSStreamEvent.EndEncountered:
print("End Encountered")
break
default:
print("No stream event")
break
}
}
}
Thanks in Advance..
sessionHandler.openDeviceWithProtocolString(runLoop)
Isn't it supposed to be called on another thread?
I'm sure this is too late, but while ((_session?.inputStream?.hasBytesAvailable) != nil) looks like an infinite loop to me, because true/false will always be != nil.

Resources