How to get the console logs and display in a textview [Swift] - ios

How can I get the console logs with all the print/Nslog contents and display it on a textview? Thank you very much for your answer.

To accomplish this I modified the OutputListener Class described in this article titled "Intercepting stdout in Swift" by phatblat:
func captureStandardOutputAndRouteToTextView() {
outputPipe = Pipe()
// Intercept STDOUT with outputPipe
dup2(self.outputPipe.fileHandleForWriting.fileDescriptor, FileHandle.standardOutput.fileDescriptor)
outputPipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
NotificationCenter.default.addObserver(forName: NSNotification.Name.NSFileHandleDataAvailable, object: outputPipe.fileHandleForReading , queue: nil) {
notification in
let output = self.outputPipe.fileHandleForReading.availableData
let outputString = String(data: output, encoding: String.Encoding.utf8) ?? ""
DispatchQueue.main.async(execute: {
let previousOutput = self.outputText.string
let nextOutput = previousOutput + outputString
self.outputText.string = nextOutput
let range = NSRange(location:nextOutput.count,length:0)
self.outputText.scrollRangeToVisible(range)
})
self.outputPipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
}
}
}

If you do not want to change existing code, you can;
1 - redirect the output of print to a known file.
see instructions here; How to redirect the nslog output to file instead of console ( answer 4, redirecting)
2 - monitor the file for changes and read them in to display in your textView.

You cannot do that.
You can use some logger, witch allow you to add custom log destination.
You will have to change all print/NSLog calls to e.g. Log.verbose(message).
I'm using SwiftyBeaver. It allows you to define your custom destination. You can later read it and present in some text field.

You can totally do that! Check this out: https://stackoverflow.com/a/13303081/1491675
Basically you create an output file and pipe the stderr output to that file. Then to display in your textView, just read the file and populate your textView.

Related

How can I get selected text from ImageAnalysisInteraction on UIImageView?

I work on an iOS app that displays images that often contain text, and I'm adding support for ImageAnalysisInteraction as described in this WWDC 2022 session. I have gotten as far as making the interaction show up and being able to select text and get the system selection menu, and even add my own action to the menu via the buildMenuWithBuilder API. But what I really want to do with my custom action is get the selected text and do a custom lookup-like thing to check the text against other content in my app.
So how do I get the selected text from an ImageAnalysisInteraction on a UIImageView? The docs show methods to check if there is selected text, but I want to know what the text is.
I was trying to solve the same problem. However, there doesn't currently seem to be any straightforward way to get selected text from ImageAnalysisInteraction. The closest thing seems to be the ImageAnalysis.transcript property, but it contains all the OCR text, not just what the user selected.
My solution was to capture the text whenever the user taps on the copy button on the selection menu. You can do this by observing clipboard changes, which allows you to copy the selected text from the clipboard whenever a change is detected.
See:
Get notified on clipboard change in swift
How to copy text to clipboard/pasteboard with Swift
Hope this help you
// Step -1
import Vision
// Step -2
// converting image into CGImage
guard let cgImage = imageWithText.image?.cgImage else {return}
// Step -3
// creating request with cgImage
let handler = VNImageRequestHandler(cgImage: cgImage, options: [:])
// Step -4
let request = VNRecognizeTextRequest { request, error in
guard let observations = request.results as [VNRecognizedTextObservation],
error == nil else {return}
let text = observations.compactMap({
$0.topCandidates(1).first?.string
}).joined(separator: ", ")
print(text) // text we get from image
}
// step -5
request.recognitionLevel = VNRequestTextRecognitionLevel
try handler.perform([request])
For Reference and more details

Firestore in Swift - Listener producing needless reads

I have an app that uses a snapshot listener to listen to data in a particular document. However, when a field in the document is updated, the data is read 7-10x over. Never read once, and never read the number of fields that are in my document, it always seems to be an arbitrary number. Also, when the read data prints out, it seems like every printout is the same except for a couple of fields that I'm not setting (like an array prints out "<__NSArrayM 0x282d9f240>" but the number changes on each print). As a result, minimal usage of my app is causing 5-10k reads. I'm trying to reduce the number of reads and I don't know exactly how, but the app has to read as data is updated, but my two questions are:
when I print the data from the listener, does each data print out signify a separate read operation? and
is there any way for the listener to be alerted of the update but wait to actually perform the read until the data is updated, then perform one read instead of multiple reads every time any field is updated? Or another strategy to reduce reads when multiple writes occur?
Not sure if this is helpful, but here is the code I'm using to perform the read...its pretty much the standard code from the firestore sdk:
env.db.collection(env.currentSessionCode!).document(K.FStore.docName).addSnapshotListener { [self] documentSnapshot, error in
guard let document = documentSnapshot else {
print("Error fetching snapshot: \(error!)")
return
}
guard let data = document.data() else {
print("Document data was empty.")
return
}
self.env.data1 = data[K.FStore.data1] as? String ?? "????"
self.env.data2 = data[K.FStore.data2] as? String ?? "????"
self.env.data3 = data[K.FStore.data3] as? [String] ?? ["????"]
self.env.data4 = data[K.FStore.data4] as? [String] ?? ["????"]
self.env.data5 = data[K.FStore.data5] as? Double ?? 0
self.env.data6 = data[K.FStore.data6] as? Double ?? 0
self.env.data7 = data[K.FStore.data7] as! Bool
self.env.data8 = data[K.FStore.data8] as! Bool
print("Current data: \(data)")
Update - For clarification, the way I have been updating my data to firebase is with a environment object, and using "didSet" when the new data is changed/updated in the environment to update it on firebase...I think this might be the root of the problem, as the function called on didSet runs 4-5 times each time it is called...
relevant code:
#Published var data1: String {
didSet {
postValuesToFB(fb: K.FStore.data1, string: data1)
}
}
func postValuesToFB(fb: String, string: String) {
guard let code = currentSessionCode else {
fatalError("Error - Connection Check - no value for current session code in Global Env")
}
let docRef = db.collection(code).document(K.FStore.docName)
docRef.getDocument { document, _ in
guard let document = document else {
return
}
if document.exists {
let session = self.db.collection(code).document(K.FStore.docName)
session.updateData([
fb: string,
K.FStore.dateLastAccessed: FieldValue.serverTimestamp(),
])
return
}
}
}
Based on your comments, it sounds as if you've written no code to remove a listener after it's been added. Based on this, it's relatively safe to assume that your code could be adding many listeners over time, and each one is getting called for each change.
You should take a moment to think about the architecture of your app and figure out when is the appropriate time to remove listeners when they're no longer needed. Usually this corresponds with the lifecycle of whatever component is responsible for display of the data from the query. Review the documentation for getting realtime updates, especially the section on detaching a listener. It's up to you to determine the right time to remove your listener, but you definitely don't want to "leak" a listener as you are now.
A common source of unexpected read charges for developers who are new to Firestore is the Firebase console itself. When that console displays Firestore content, you are charged for those read too. To ensure you measure the impact of your code correctly, test it with the Firebase console closed.
when I print the data from the listener, does each data print out signify a separate read operation?
Not really. You get charged for a document read, when the document is read on your behalf on the server. You are not charted for printing the same DocumentSnapshot multiple times.
is there any way for the listener to be alerted of the update but wait to actually perform the read until the data is updated
Nope. To know the document has changed, the server needs to read it. So that requires a charged read operation.

Calculating word count from a url in swift

I'm creating a reading list app, and I'd like to pass the read time of a user added link to a table cell in their reading list - and the only way to get that number is from that page's word count. I've found a few solutions, namely Parsehub, Parse and Mercury but they seem to be geared more towards use cases that need more advanced things to be scraped from a url. Is there a simpler way in Swift to calculate word count of a url?
First of all, you need to parse the HTML. HTML can only be parsed reliably with dedicated HTML parser. Please don't use Regular Expressions or any other search method to parse HTML. You may read it why from this link. If you are using swift, you may try Fuzi or Kanna. After you get the body text with any one of the library, you have to remove extra white spaces and count the words. I have written some basic code with Fuzi library for you to get started.
import Fuzi
// Trim
func trim(src:String) -> String {
return src.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
}
// Remove Extra double spaces and new lines
func clean(src:String) ->String {
return src.replacingOccurrences(
of: "\\s+",
with: " ",
options: .regularExpression)
}
let htmlUrl = URL(fileURLWithPath: ((#file as NSString).deletingLastPathComponent as NSString).appendingPathComponent("test.html"))
do {
let data = try Data(contentsOf: htmlUrl)
let document = try HTMLDocument(data: data)
// get body of text
if let body = document.xpath("//body").first?.stringValue {
let cleanBody = clean(src: body)
let trimmedBody = trim(src:cleanBody)
print(trimmedBody.components(separatedBy: " ").count)
}
} catch {
print(error)
}
If you are fancy, you may change my global functions to String extension or you can combine them in a single function. I wrote it for clarity.

Problems with structs in Swift and making a UIImage from url?

Alright, I am not familiar with structs or the ordeal I am dealing with in Swift, but what I need to do is create an iMessage in my iMessage app extension with a sticker in it, meaning the image part of the iMessage is set to the sticker.
I have pored over Apple's docs and https://www.captechconsulting.com/blogs/ios-10-imessages-sdk-creating-an-imessages-extension but I do not understand how to do this or really how structs work. I read up on structs but that has not helped me accomplishing what Apple does in their sample code (downloadable at Apple)
What Apple does is they first compose a message, which I understood, taking their struct as a property, but I take sticker instead
guard let conversation = activeConversation else { fatalError("Expected a conversation") }
//Create a new message with the same session as any currently selected message.
let message = composeMessage(with: MSSticker, caption: "sup", session: conversation.selectedMessage?.session)
// Add the message to the conversation.
conversation.insert(message) { error in
if let error = error {
print(error)
}
}
They then do this (this is directly from sample code) to compose the message:
fileprivate func composeMessage(with iceCream: IceCream, caption: String, session: MSSession? = nil) -> MSMessage {
var components = URLComponents()
components.queryItems = iceCream.queryItems
let layout = MSMessageTemplateLayout()
layout.image = iceCream.renderSticker(opaque: true)
layout.caption = caption
let message = MSMessage(session: session ?? MSSession())
message.url = components.url!
message.layout = layout
return message
}
}
Basically this line is what Im having the problem with as I need to set my sticker as the image:
layout.image = iceCream.renderSticker(opaque: true)
Apple does a whole complicated function thing that I don't understand in renderSticker to pull the image part out of their stickers, and I have tried their way but I think this is better:
let img = UIImage(contentsOfURL: square.imageFileURL)
layout.image = ing
layout.image needs a UIImage, and I can get the imageFileURL from the sticker, I just cant get this into a UIImage. I get an error it does not match available overloads.
What can I do here? How can I insert the image from my sticker into a message? How can I get an image from its imageFileURL?
I'm not sure what exactly the question is, but I'll try to address as much as I can --
As rmaddy mentioned, if you want to create an image given a file location, simply use the UIImage constructor he specified.
As far as sending just a sticker (which you asked about in the comments on rmaddy's answer), you can insert just a sticker into an iMessage conversation. This functionality is available as part of an MSConversation. Here is a link to the documentation:
https://developer.apple.com/reference/messages/msconversation/1648187-insert
The active conversation can be accessed from your MSMessagesAppViewController.
There is no init(contentsOfURL:) initializer for UIImage. The closest one is init(contentsOfFile:).
To use that one with your file URL you can do:
let img = UIImage(contentsOfFile: square.imageFileURL.path)

How can I parse content from a PDF page with Swift

The documentation is not really clear to me. So far I reckon I need to set up a CGPDFOperatorTable and then create a CGPDFContentStreamCreateWithPage and CGPDFScannerCreate per PDF page.
The documentation refers to setting up Callbacks, but it's unclear to me how. How to actually obtain the content from a page?
This is my code so far.
let pdfURL = NSBundle.mainBundle().URLForResource("titleofdocument", withExtension: "pdf")
// Create pdf document
let pdfDoc = CGPDFDocumentCreateWithURL(pdfURL)
// Nr of pages in this PF
let numberOfPages = CGPDFDocumentGetNumberOfPages(pdfDoc) as Int
if numberOfPages <= 0 {
// The number of pages is zero
return
}
let myTable = CGPDFOperatorTableCreate()
// lets go through every page
for pageNr in 1...numberOfPages {
let thisPage = CGPDFDocumentGetPage(pdfDoc, pageNr)
let myContentStream = CGPDFContentStreamCreateWithPage(thisPage)
let myScanner = CGPDFScannerCreate(myContentStream, myTable, nil)
CGPDFScannerScan(myScanner)
// Search for Content here?
// ??
CGPDFScannerRelease(myScanner)
CGPDFContentStreamRelease(myContentStream)
}
// Release Table
CGPDFOperatorTableRelease(myTable)
It's a similar question to: PDF Parsing with SWIFT but has no answers yet.
Here is an example of the callbacks implemented in Swift:
let operatorTableRef = CGPDFOperatorTableCreate()
CGPDFOperatorTableSetCallback(operatorTableRef, "BT") { (scanner, info) in
print("Begin text object")
}
CGPDFOperatorTableSetCallback(operatorTableRef, "ET") { (scanner, info) in
print("End text object")
}
CGPDFOperatorTableSetCallback(operatorTableRef, "Tf") { (scanner, info) in
print("Select font")
}
CGPDFOperatorTableSetCallback(operatorTableRef, "Tj") { (scanner, info) in
print("Show text")
}
CGPDFOperatorTableSetCallback(operatorTableRef, "TJ") { (scanner, info) in
print("Show text, allowing individual glyph positioning")
}
let numPages = CGPDFDocumentGetNumberOfPages(pdfDocument)
for pageNum in 1...numPages {
let page = CGPDFDocumentGetPage(pdfDocument, pageNum)
let stream = CGPDFContentStreamCreateWithPage(page)
let scanner = CGPDFScannerCreate(stream, operatorTableRef, nil)
CGPDFScannerScan(scanner)
CGPDFScannerRelease(scanner)
CGPDFContentStreamRelease(stream)
}
You've actually specified exactly how to do it, all you need to do is put it together and try until it works.
First of all, you need to setup a a table with callbacks as you state yourself in the beginning of your question (all code in Objective C, NOT Swift):
CGPDFOperatorTableRef operatorTable = CGPDFOperatorTableCreate();
CGPDFOperatorTableSetCallback(operatorTable, "q", &op_q);
CGPDFOperatorTableSetCallback(operatorTable, "Q", &op_Q);
This table contains a list of the PDF operators you want to get called for and associates a callback with them. Those callbacks are simply functions you define elsewhere:
static void op_q(CGPDFScannerRef s, void *info) {
// Do whatever you have to do in here
// info is whatever you passed to CGPDFScannerCreate
}
static void op_Q(CGPDFScannerRef s, void *info) {
// Do whatever you have to do in here
// info is whatever you passed to CGPDFScannerCreate
}
And then you create the scanner and get it going, while passing it the information you just defined.
// Passing "self" is just an example, you can pass whatever you want and it will be provided to your callback whenever it is called by the scanner.
CGPDFScannerRef contentStreamScanner = CGPDFScannerCreate(contentStream, operatorTable, self);
CGPDFScannerScan(contentStreamScanner);
If you want to see a complete example with sourcecode on how to find and process images, check this website.
To understand why a parser works this way, you need to read the PDF specification a bit better. A PDF file contains something close to printing instructions. Such as "move to this coordinate, print this character, move there, change the color, print the character number 23 from the font #23", etc.
The parser gives you callbacks for each instructions, with the possibility to retrieve the instruction parameters. That's all.
So, in order to get the content from a file, you need to rebuild its state manually. Which means, recompute the frames for all characters, and try to reverse-engineer the page layout. This is clearly not an easy task, and that's why people have created libraries to do so.
You may want to have a look at PDFKitten , or PDFParser which is a Swift port with some improvement that i did.

Resources