Reputation: 1125
I created an iOS app for iPhone using Swift under the Xcode IDE. For debugging purposes I would like to have everything what print (and printf in C code) prints to the Xcode console normally, redirected to a UItextView
. My code (mainly derived from here) follows:
//
// ViewController.swift
// Scroll View Demo
//
//
//
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var textView: UITextView!
@IBOutlet weak var writeButton: UIButton!
var pipe = Pipe()
var count = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// dup2() makes newfd (new file descriptor) be the copy of oldfd (old file descriptor), closing newfd first if necessary.
openConsolePipe()
print("\npipe started")
}
@IBAction func buttonPressed(_ sender: Any) {
print("\(count). Hello world")
count += 1
}
public func openConsolePipe () {
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?.textView.text += str
}
}
}
}
The code works in simulation mode on the physical device and in the virtual iphone as well. But when I run it natively with the cord to Xcode cut, the pipe() doesn't work. I'm clueless at the moment why the pipe() doesn't work in the standalone app.
Upvotes: 8
Views: 3912
Reputation: 1125
The code change that made the example finally work is to modify the file handle of stdout to unbuffered (setvbuf(stdout, nil, _IONBF, 0)):
//
// ViewController.swift
// Scroll View Demo
//
//
//
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var textView: UITextView!
@IBOutlet weak var writeButton: UIButton!
var pipe = Pipe()
var count = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// dup2() makes newfd (new file descriptor) be the copy of oldfd (old file descriptor), closing newfd first if necessary.
openConsolePipe()
print("\npipe started")
}
@IBAction func buttonPressed(_ sender: Any) {
print("\(count). Hello world")
count += 1
}
public func openConsolePipe () {
setvbuf(stdout, nil, _IONBF, 0) //<--------- !
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?.textView.text += str
}
}
}
That's because with the app running disconnected from Xcode stdout is redirected to something like /dev/null with buffering set to "buffered" and thus never appears in the pipe(). Setting it to unbuffered made things work.
Upvotes: 8