5 Replies
      Latest reply on Jan 17, 2019 1:11 AM by eskimo
      chriskuku Level 1 Level 1 (0 points)

        (Xcode 10.1, Apple Swift version 4.2.1 (swiftlang-1000.11.42 clang-1000.11.45.1))

         

        I'm developing an App (my first one) and during the development process I'd rather have a debug window into which I can write messages for development and debugging purposes.

         

        I have a UItextView for this and to get all print() into that textView, I'm using the following construct (pipe()) inspired by this :

         

        //
        //  ViewController.swift
        //  Scroll View Demo
        //
        //  Created by chriskuku on 30.12.18.
        //  Copyright © 2018 chriskuku. All rights reserved.
        //
        
        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.
                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) ?? "\n"
                    DispatchQueue.main.async {
                        self?.textView.text += str
                    }
                }
                print("\npipe started")
            }
        
            @IBAction func buttonPressed(_ sender: Any) {
                print("\(count). Hello world")
                count += 1
            }
        }

         

         

        The App works fine when being run within Xcode on either virtual device (iPhone 6s plus in my case) or physially on the device connected to Xcode.

         

        But when I run the app alone on the target device (Apple iPhone 6splus), the pipe() mechanism doesn't seem to work. Nothing appears but the Initial text.

         

        I'm puzzled.

        • Re: pipe ( ) doesn't seem to work when running app natively
          eskimo Apple Staff Apple Staff (11,495 points)

          The problem here is that you’re not flushing stdout.  When your run your app from Xcode, stdout is connected to a pseudo terminal and thus configures itself to be unbuffered.  When you run your app from the Home screen, stdout is connected to something else (/dev/null IIRC) and thus ends up buffered.  So the data you print is never flushed to the pipe, and thus never shows up in your readability handler.

          You can fix your current code by adding the following line to your setup code.

          setvbuf(stdout, nil, _IONBF, 0)

          Share and Enjoy

          Quinn “The Eskimo!”
          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
          let myEmail = "eskimo" + "1" + "@apple.com"

            • Re: pipe ( ) doesn't seem to work when running app natively
              chriskuku Level 1 Level 1 (0 points)

              Fantastic! And to be sure: the setvbuf() should occur *before* the dup2 occurs, on the original filehandle, right? At least it works this way with me here. While trying to make it work with the pipe() filehandle, I'm getting into trouble with the type passed to the setvbuf().

               

               

              setvbuf(pipe.fileHandleForReading, nil, _IONBF, 0)

               

              gives me an error:

               

              Cannot convert value of type 'FileHandle' to expected argument type 'UnsafeMutablePointer<FILE>?' (aka 'Optional<UnsafeMutablePointer<__sFILE>>')

                • Re: pipe ( ) doesn't seem to work when running app natively
                  eskimo Apple Staff Apple Staff (11,495 points)

                  And to be sure: the setvbuf() should occur before the dup2 occurs, on the original filehandle, right?

                  It doesn’t matter.  Buffering is done by the FILE * not the file descriptor.

                  I'm getting into trouble with the type passed to the setvbuf.

                  You’re definitely heading off into the weeds here )-:  Buffering is done by the FILE * — that is, stdout in this example — not by the file descriptor (STDOUT_FILENO) or your file handle.  Thus you need to call setvbuf as I showed in my last post.

                  Here’s how thing progress in that case:

                  1. Your Swift code calls print.

                  2. Internally print calls fwrite passing it stdout.

                  3. fwrite decides to buffer or not based on the setvbuf configuration:

                    • In the unbuffered case, fwrite calls write with STDOUT_FILENO.

                    • If the buffered case, fwrite copies the data to the buffer and, only if it’s full, call write with STDOUT_FILENO.

                  So you only need to change buffering on stdout, the FILE *, and not on the file descriptor, the file handle, the Swift TextOutputStreamable, or anything else.

                  Share and Enjoy

                  Quinn “The Eskimo!”
                  Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                  let myEmail = "eskimo" + "1" + "@apple.com"

                    • Re: pipe ( ) doesn't seem to work when running app natively
                      chriskuku Level 1 Level 1 (0 points)

                      I'm now observing, that I'm also getting kind of  unwanted "debug messages" from iOS in my app, since I redirected stdout and stderr to my textView:

                       

                      2019-01-16 11:38:11.144848+0100 Scroll View Demo[6044:2097913] -[UIWindow endDisablingInterfaceAutorotationAnimated:] called on > without matching -beginDisablingInterfaceAutorotation. Ignoring.

                       

                      I didn't order this message Is it a left over of some Apple Developer having scaffolded the iOS in this place?

                        • Re: pipe ( ) doesn't seem to work when running app natively
                          eskimo Apple Staff Apple Staff (11,495 points)

                          It’s not uncommon for the OS to log messages like this.  In the most egregious cases I recommend that folks file a bug about the log noise.  For the moment, however, you’re going to have to decide how you want to handle this reality, and that boils down to three options:

                          • Change the code you use to print your messages to use something other than just a simple print.  You could, for example, use the to parameter to print to a custom TextOutputStream that redirects your output to the right place.

                          • Continue with your current design but filter out this system output.

                          • Accept the fact that this system output will show up in your debug window.

                          Share and Enjoy

                          Quinn “The Eskimo!”
                          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                          let myEmail = "eskimo" + "1" + "@apple.com"