Data and String in Swift3

I'm developing a very small iOS application in Swift 3, and I want to interact via wifi with my RPi,using a Simple Socket Server method.


Here in this part of program,


    @IBAction func ButtoniPhone(_ sender: Any) {
  
         let data : Data = "C'est le iPhone".data(using: String.Encoding.utf8)!
         outStream?.write(data, maxLength: data.length)

    }


I give an error here :


        Cannot convert value of type "Data" to expected argument type 'UnsafePointer<UInt8>'

And when I change it in this form :

    @IBAction func ButtoniPhone(_ sender: Any) {
      
        let data : Data = "C'est le iPhone".data(using: String.Encoding.utf8)!
        outStream?.write(UnsafePointer<UInt8>, maxLength: data.count)
    }

I give this error :

    Cannot convert value of type 'UnsafePointer<UInt8>.Type' to expected argument type 'UnsafePointer<UIngt8>'


Is there anyone who knows a solution?

Thanks in advance,

Assuming `outStream` is declared as `OutputStream?`. (You better clarify in your question.)


If you want to output text String to OutputStream, you can write something like this:

        let text = "C'est le iPhone"
        outStream?.write(text, maxLength: text.utf8.count)

When used as a method parameter, Swift Strings are automatically converted to `UnsafePointer<Int8>` or `UnsafePointer<UInt8>`, with temporary UTF-8 representation.


If you really want to write the content of `Data`, this should work:

        let data = "C'est le iPhone".data(using: .utf8)!
        _ = data.withUnsafeBytes {bytes in
            outStream?.write(bytes, maxLength: data.count)
        }

Thanks to replay,


Either outStream or OutputStream, I have no idea which is genuine, and I think little import, cause my goal is to communicate with the Raspberry pi.

But when I try it,


Considering again that my goal is interacting with my Raspberry Pi via Wifi, and my RPi is waiting for my iOS app, in a socket server state, in my terminal :

pi@raspberrypi:~ $ python server.py
host = 172.20.10.12


By this python codes :

import socket

mysocket = socket.socket()
host = socket.gethostbyname(socket.getfqdn())
port = 9876


if host == "127.0.1.1":
    import commands
    host = commands.getoutput("hostname -I")
print "host = " + host


#Prevent socket.error: [Errno 98] Address already in use
mysocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)


mysocket.bind((host, port))
mysocket.listen(5)
c, addr = mysocket.accept()

while True:

    data = c.recv(1024)
    data = data.replace("\r\n", '') #remove new line character
    inputStr = "Received " + data + " from " + addr[0]
    print inputStr
    c.send("Hello from Raspberry Pi!\nYou sent: " + data + "\nfrom: " + addr[0] + "\n")

    if data == "Quit": break


c.send("Server stopped\n")
print "Server stopped"
c.close()


When I click on ConnectNet button, Xcode only print :

NetworkEnable



But I'm wainting for it to print normally, (adapting my case states) :

NetworkEnabled
OpenCompleted
HasSpaceAvalable



And,

When I click on ButtoniPhone, I give an error in my RPi part :

socket.error: [Errno 32] Broken pipe


And a bug in my xcode :

(lldb)



Here is my entire ViewController.swift to verify :

import UIKit
class ViewController: UIViewController, StreamDelegate

     {

    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var labelConnection: UILabel!


    let addr = "172.20.10.12"
    let port = 9876

    var inStream : InputStream?
    var outStream: OutputStream?

    var buffer = [UInt8](repeating: 0, count: 200)


    @IBAction func ConnectNet(_ sender: Any)
     {
        NetworkEnable()
     }

    @IBAction func ButtoniPhone(_ sender: Any)
     {
       let data = "C'est le iPhone".data(using: .utf8)!
        _ = data.withUnsafeBytes {bytes in
            outStream?.write(bytes, maxLength: data.count)
     }


      func NetworkEnable()

    {

        print("NetworkEnable")

        Stream.getStreamsToHost(withName: addr, port: port, inputStream: &inStream, outputStream: &outStream)

        inStream?.delegate = self
        outStream?.delegate = self


        inStream?.schedule( in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
        outStream?.schedule( in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)

        inStream?.open()
        outStream?.open()

        buffer = [UInt8](repeating: 0, count: 200)

     }


    func stream (aStream: Stream, handleEvent eventCode: Stream.Event)

    {

        switch eventCode

        {
  
        case Stream.Event.endEncountered:
            print("EndEncountered")
            labelConnection.text = "Connection arrêté par server"
            inStream?.close()
            inStream?.remove(from: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
            outStream?.close()
            print("Arrêt de outStream currentRunLoop")
            outStream?.remove(from: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
  
        case Stream.Event.errorOccurred:
            print("ErrorOccurred")
            inStream?.close()
            inStream?.remove(from: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
            outStream?.close()
            outStream?.remove(from: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
            labelConnection.text = "Ne peut pas connecter à server"

            label.text = ""
                  
        case Stream.Event.hasBytesAvailable:
            print("HasBytesAvailable")
  
            if aStream == inStream
      
            {
                inStream!.read(&buffer, maxLength: buffer.count)
                let bufferStr = NSString(bytes: &buffer, length: buffer.count, encoding: String.Encoding.utf8.rawValue)
                label.text = bufferStr! as String
                print(bufferStr!)
            }
  
  
        case Stream.Event.hasSpaceAvailable:
            print("HasSpaceAvailable")
  
        case Stream.Event.openCompleted:
            print("OpenCompleted")
            labelConnection.text = "Connecté à server"
        default:
            print("Inconnu")
        }
        }

    override func didReceiveMemoryWarning()

    {
        super.didReceiveMemoryWarning()
    }

    override func viewDidLoad()

    {
        super.viewDidLoad()

    }

}

I'm not good at Python and do not know much about Raspberry Pi, so I can say nothing about your Raspberry Pi side codes or settings.


But your Swift codes has some obvious flaws.


First:

    @IBAction func ButtoniPhone(_ sender: Any) {
        let data = "C'est le iPhone".data(using: .utf8)!
        _ = data.withUnsafeBytes {bytes in
            outStream?.write(bytes, maxLength: data.count)
        } //<- missing in your code
    }

A closing brace is missing in the method above.


Second, this line:

    func stream (aStream: Stream, handleEvent eventCode: Stream.Event)

You need to implement a method of `StreamDelegate`, but the line above is not the right header.

It should be something like this in Swift 3:

func stream(_ aStream: Stream, handle eventCode: Stream.Event)


With fixing two parts above, modifying `addr` and `port` to my test server, your code has output these on ConnectNet:

NetworkEnable

OpenCompleted

OpenCompleted

HasSpaceAvailable


Using `NSInputStream` and `NSOutputStream` correctly is not an easy task even for experienced programmers. Study hard or find something for your Raspberry Pi which is easy to use from iOS.

Thank you very much,


Did you have tried it by WIFI IP adresse or LAN IP adresse for your testing server ? Is there any difference between the configuration of LAN IP addr and WLAN IP addr , which I have to consider in my Swift program ?


Cause it work's for me, when I use my usb0 inet addr:192.168.2.40, (I'm using my USB port in OTG mode, so it's a RNDIS Gadget now), but when I use my wlan0 inet addr:172.20.10.2, not works, and give me an error :


NetworkEnable
2017-07-20 10:27:15.492013+0200 RPiOS_2[26899:814471] [] nw_connection_get_connected_socket_block_invoke 27 Connection has no connected handler
ErrorOccurred

Did you have tried it by WIFI IP adresse or LAN IP adresse for your testing server? Is there any difference between the configuration of LAN IP addr and WLAN IP addr , which I have to consider in my Swift program?

I don’t understand your terminology here. WLAN expands to Wireless Local Area Network, which typically means Wi-Fi. If so, what does LAN mean? On a Mac that would have an obvious explanation (Ethernet) but you started this discussion by saying you’re creating a “small iOS application”. It’s possible, but quite unusual, to have an iOS device on Ethernet.

Can you describe you actual networking setup in more detail?

Just FYI, iOS is quite capable of dealing with multiple network interfaces simultaneously. It’s general routing strategy is pretty straightforward (traffic destined to a locally connected subnet goes direct; all other traffic goes to the default route) but the devil really is in the details here.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I apologize for my unclear description, I'm giong to clear it,


Yes it is, I'm developping a small iOS App, by Swift 3 in xcode 8.3.2.


It's a mobile application project that communicates with embedded electronics installed on a bike via Wi-Fi protocol.

My server ( Raspberry Pi) can be connected to the application by LAN, USB, and Wi-Fi. ( I don't wanna communicate via LAN, or USB, I confirm, is quite unusual, to have an iOS device on Ethernet), but here in my Swift code, I'm trying to communicate through WLAN with my server, that is the Raspberry pi. (By the codes that I put above). Not matter what is a Raspberry Pi, when I test my swift codes by LAN or USB server IP address, it goes well, but when I try with WLAN server IP address , I have an error.

NetworkEnable  
2017-07-20 10:27:15.492013+0200 RPiOS_2[26899:814471] [] nw_connection_get_connected_socket_block_invoke 27 Connection has no connected handler
ErrorOccurred


Here I'm confused, is there any differente between LAN IP addr and WLAN IP addr? Why it work with LAN IP addr, but not with WLAN?


I have to add that I shared the Wi-Fi by using HotSpot in my iPhone, and the iOS application (or my Mac Xcode simulator) and the Raspberry pi are connected to this HotSpot.

Thanks for all the clarifications. This part turned out to be key:

I have to add that I shared the Wi-Fi by using HotSpot in my iPhone, and the iOS application (or my Mac Xcode simulator) and the Raspberry pi are connected to this HotSpot.

Until now it wasn’t clear that you had Personal Hotspot in the mix.

Before I continue I should stress that using Personal Hotspot for this sort of thing is not a good idea. Personal Hotspot was designed solely as a way to connect the client devices to the Internet. It does not guarantee connectivity between apps running on the hotspot device and apps running on the hotspot network. My understanding is that this works right now, but it’s not an essential part of the Personal Hotspot product and so one could easily imagine it being blocked in the future.

So, just for testing purposes, let’s remove Personal Hotspot from the equation. If you put your iOS device and your accessory (the Raspberry Pi) on the same infrastructure Wi-Fi, can they connect successfully?

ps I’ve moved this through to Core OS > Networking because it’s very likely that this has nothing to do with Swift.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
Data and String in Swift3
 
 
Q