Embedding C code into Swift


(Christoph K) #1

I'm fighting the problem, that I have a textView in my App that should show output my program is doing. This output would normally be done by printfs at low C level. If I could redirect stdout to that textView I would be done. But I think that would not be possible.

I have seen portions of Swift code that mix kind of C style together with Swift. But whether I can do that with unix network calls with all the #include stuff, I doubt. So I need a means of redirecting all the output my C low level programs are doing into that textView.

I was already considering using a pipe or a fifo but then I run blocking read() when reading from that pipe.

Doing not blocking reads I would build some select() mechanism.

These are program architectural considerations, of which I don't know whether this would be the right place to discuss here. If not, where else could I discuss this?

Thank you,

Christoph

EDIT: I find some examples using NSPipe in stackoverflow, but besides these examples are not covering exactly my needs I'm wondering whether I should dive into Cocoa and use that in my Swift App?


Communicating between threads
#2

Inspiring from this, you can use Pipe's readabilityHandler:

let pipe = Pipe()

// dup2() makes newfd (new file descriptor) be the copy of oldfd (old file descriptor), closing newfd first if necessary.
dup2(pipe.fileHandleForWriting.fileDescriptor, STDOUT_FILENO)

// listening on the readabilityHandler
pipe.fileHandleForReading.readabilityHandler = { [weak self] handle in
    let data = handle.availableData
    let str = String(data: data, encoding: .ascii) ?? "<Non-ascii data of size \(data.count)>\n"
    DispatchQueue.main.async {
        self?.stdOutTextView.string += str
    }
}

Be sure not to let pipe get destroyed.


(Christoph K) #3

Thanks. I exactly found that article too, but following it, it fails already, when trying to incorporate it into my project:

First off, 2 questions:

what is [ weak self ]?

what is stdOutTextView ? If I replace it with my @IBOutlet weak var textView: UITextView there is no .string. it has .text, though.


#4

It fails because it’s a sample code, it’s not tailor-made to your need and you’ll need to adjust it to fit it into your project. Though most of the time it’ll show you just enough to know what is needed to get the job done.

The error is unresolved identifier, that is, the compiler wants a variable (or something similar) but can’t find them.
In this case you can provide them by doing let inputPipe, outputPipe: Pipe in your class.

Answers:

  1. Swift uses Automatic Reference Counting (ARC) to find out which variable to keep, and which to destroy. The side effect is that it may cause some object to be unnecessarily kept indefinitely if there’s a cycle in reference graph.

    • Likely ViewController will hold (have reference to) pipe.
    • pipe is referencing readabilityHandler.
    • readabilityHandler, which is a closure, is capturing self which is the ViewController itself to use self.stdOutTextView.
      The graph will look like this
    ViewController -> pipe -> pipe.readabilityHandler -> ViewController
    

    Which is a cycle alright. In Swift, you can break it by saying that one of the reference won’t count toward this reference graph. It’s very common to do this on self captured by closure hence [weak self]. The new graph will look like this

    ViewController -> pipe -> pipe.readabilityHandler —[weak]-> ViewController
    

    Now the last link is not considered part of the reference graph and so won’t cause reference cycle.

    See the link above to read more about how to properly use it.

  2. I used Scrollable Text View on macOS. iOS doesn’t seem to have it. Attaching ScrollView to TextView shouldn’t be too hard though (you can ask SO for that if you’re stuck).

While bodging is fine for learning new language. It works only to certain extend.
You’re trying to interop C-Swift, Swift-Cocoa and tinkering with stdOut pipe.
It’s quite a lot to just plow your way through the project. You’ll end up asking a lot of question at Every. Single. Step. of the way and end up learning little to nothing.

I do recommend that you at least read Swift’s official guide. You can also ask for book recommendations from this forum, a lot of people are more than willing to help you with that.


(Christoph K) #5

Thanks a lot for this short discourse into Swifts reference graph paradigm. I understand your points. I'm making progress. The pipe() mechanism now works - at least within Xcode on the virtual and physical device. Running the app natively on the iPhone with the cord to Xcode cut, it fails. The pipe() just does nothing, hangs or what. I've joined to the Apple Developers forums already and posted the problem there.

BTW, what is "SO"?

Best

Christoph


(Erik Little) #6

SO is StackOverflow.


(Christoph K) #7

Ouch :) Escaped me for the moment. Thnx.


(Christoph K) #8

Swift language related question:

When I put a

var inputPipe = Pipe()
var outputPipe() = Pipe()

in the class variables sections, does it mean, that there are instances of Pipe() available for the class, right?

So, in my screenshot example above, the inputPipe = Pipe() resp. outputPipe = Pipe() statements are superfluous. Nonetheless I'm wondering about the code in the codesnippet of the original example

guard let inputPipe = inputPipe, let outputPipe = outputPipe    else { 
        return
}

What does this idiom mean? Does it test for one of them being nil?


(Thomas Krajacic) #9

Yes.

guard condition else { … return }

In simple terms this checks if condition is true (in your case, the let binding is true if the variable wasn't nil) and otherwise the else clause is executed. The else clause must leave the current context (return, throw,…) meaning any code after the else clause will not be executed then.
In other words, a guard statement is an elegant way to make sure a certain condition is true from this point on.

And again I really recommend you take the time to read through https://docs.swift.org/swift-book/GuidedTour/GuidedTour.html.
It's all in there with great and simple examples and you'll get up to speed a lot faster!


#10

There are a few parts at play here, shadowing, optional binding and guard statement. I do suggest you check them out since you'll find them very useful (I know I do, to the point that I want such feature in other languages as well).

A few caveat is that, in Swift, shadowing variable doesn't need to be the same type of the shadowed one, and that uses of shadowing name is prohibited before the declaration site.

class SomeClass {
    var someVariable = 0 // a class member of type Int
    func noShadowing() {
        // Following 2 lines ARE equivalent since
        // there's no shadowing of `someVariable`
        someVariable = 1
        self.someVariable = 1
    }
    func shadowing() {
        // You can use `self.someVariable` to refer to Int variable here
        // But can not use `someVariable` here.

        // In some cases, uses of `someVariable` here is allowed,
        // such as when shadowing is done via optional-binding,
        // and refers to the Int variable. Though I would advise against using it.
        <#some code#>

        let someVariable = "SomeString" // a local variable of type String

        print(someVariable) // refers to String local variable
        print(self.someVariable) // refers to Int class member
    }
}

So the example is trying to shadow inputPipe and outputPipe with local, non-optional, variables using optional binding. That is:

// inputPipe *would* refer to class member of type `Pipe?` here
guard let inputPipe = inputPipe, let outputPipe = outputPipe    else { 
        return
}
// inputPipe is a shadowing variable of type `Pipe`

This ensure that later uses of inputPipe and outputPipe are non-optional, clearing up a good amount of boilerplate it may need otherwise.


(Christoph K) #11

Allow me to follow up again on this one. I've been trying to find out, why this isn't working running natively physically on the target device (iPhone) for a week or so. I even sold my soul to Apple subscribing their developer program. Got a certificate and signed the app. Well it eases development now a bit anyway so that I'm not bound to these self signing of Apps.
I put up this problem to Apple developer forum with no answer yet. They take a couple of days for validate the post and since I moved it there from "Swift" to "iOS simulation and devices" it will take another while so that everyone can read it.

I opened an SO thread also which at least lead to confirmation that my posted behaviour can be reproduced.

I need this way of debugging my app (running it natively to for testing it in field) and the essence now is, that I need to debug my debug code.

public func openConsolePipe () {
    
       // dup2(STDOUT_FILENO, outputPipe.fileHandleForWriting.fileDescriptor)
        stdout_fd = Int(dup2(pipe.fileHandleForWriting.fileDescriptor, STDOUT_FILENO))
    stderr_fd = Int(dup2(pipe.fileHandleForWriting.fileDescriptor, STDERR_FILENO))
    

    // listening on the readabilityHandler
    pipe.fileHandleForReading.readabilityHandler = { [weak self] handle in
        let data = handle.availableData
        let str = String(data: data, encoding: .ascii) ?? "<Non-ascii data of size\(data.count)>\n"
        DispatchQueue.main.async {
            self?.textView.text += str
        }
    }
}    

Either of pipe() or DispatchQueue.main.async or something else is not working.
I appended the result of stdout_fd and stderr_fd to the textView.text (the only way of debugging at the moment when running standalone) and their values are 1 and 2 resp..

How can I test whether the code block "listening on the readabilityHandler" returns something meaningful?

Is DispatchQueue.main.async requiring GCD? And would the latter be running in standalone mode or would DispatchQueue.main.async require some initialization in native mode?

What type is "readabilityhandler" ? Can I assign it to something and test it for nil? Or can I guard the code block to see whether is different behaviour in standalone vs. Xcode debugging?

You write "Be sure not to let pipe get destroyed." Would it be kept alive when placed in the scope of ViewController?

And a question regarding the code piece:

// listening on the readabilityHandler
    pipe.fileHandleForReading.readabilityHandler = { ... }

means, a code section (in the curly braces) is established to be the "readabilityHandler" for that fileHandle, right?


#12

I don't think iOS apps have access to a console (tty, in Unix terminology).


(Christoph K) #13

From all what I read so far I believe, they have. How would you interpret this article otherwise?


(Avi Shevin) #14

Interesting. I was not aware that this was possible. I thought print() statements were logged via a different mechanism.


(Quinn “The Eskimo!”) #15

I put up this problem to Apple developer forum with no answer yet.

Please post a link to your DevForums thread. Your question is very specific to Apple platforms, so it’d be better if I responded on DevForums.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple


(Christoph K) #16

Thanks. Here it is