Writing iOS application logs on a text file - swift

I am using this answer to log messages in my app.
import Foundation
class Log: TextOutputStream {
func write(_ string: String) {
let fm = FileManager.default
let log = fm.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("log.txt")
if let handle = try? FileHandle(forWritingTo: log) {
handle.seekToEndOfFile()
handle.write(string.data(using: .utf8)!)
handle.closeFile()
} else {
do {
try string.data(using: .utf8)?.write(to: log)
} catch {
print(error)
}
}
}
static var log: Log = Log()
private init() {}
}
Used as follows using the Singleton pattern,
print("\(#function) Test Log", to: &Log.log)
This would append the String to the log.txt file. I cannot see the file being created in the Files.app and it doesn't produce an error either. If I print the path of the file where it's being saved it shows,
file:///var/mobile/Containers/Data/Application/00EBA5E5-7132-495E-B90E-E6CF32BA3EA7/Documents/
Where should it be saved? Do I have to do any prior setup? I can't seem to make this work. Do I have to do do something before to create the folder? Nothing shows up in the Files.app.
EDIT: I am not using the Simulator, I need to use a real device.

Okay I got confused and I totally forgot this document is not supposed to show up in the Files.app. It's stored inside the app's container. If you want to share it from the documents sheet and send it to another device via AirDrop or whatever add this action to trigger when you tap a button intended to share the document.
let fm = FileManager.default
let fileUrl = fm.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("log.txt")
var filesToShare = [Any]()
filesToShare.append(fileUrl)
let activityViewController = UIActivityViewController(activityItems: filesToShare, applicationActivities: nil)
self.present(activityViewController, animated: true, completion: nil)

Related

Want to show user a message when no data available in my document directory in swift

I want to check the pdf file with a perticular user name is available in my document directory or not. By using following code i am able to do this. But when there is no data is avalable in the document directory i want to show user a message. But i am unable to show user any message. How do do this? can anyone help me?
private func checkPatientPdfIsPresentOrNot(selectedPatient: String, completion: (_ present: Bool) -> Void){
if let documentsPathString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
let filemanager = FileManager.default
if let files = filemanager.enumerator(atPath: documentsPathString){
while let file = files.nextObject() {
let nameWithDate = (file as! String).components(separatedBy: ".")
let fileName = nameWithDate[0]
let namewithoutDate = fileName.components(separatedBy: "_")
let name = namewithoutDate[0]
if name == selectedPatient.capitalized{
completion(true)
}
else{
completion(false)
}
}
}
else{
completion(false)
}
}
}
First of all there are – in terms of Swift – a lot of outdated APIs in your code.
Second of all as the entire code is synchronous the completion handler is pointless.
The major issue is that the completion handler is called multiple times. You should call it once passing true if the partial file name matches selectedPatient or passing false after the loop.
This is a suggestion with more contemporary code and a boolean return value.
Show the message to the user if method returns false
private func checkPatientPdfIsPresentOrNot(selectedPatient: String) -> Bool {
let fileManager = FileManager.default
do {
let documentsURL = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let fileURLs = try fileManager.contentsOfDirectory(at: documentsURL, includingPropertiesForKeys: [.nameKey], options: .skipsHiddenFiles)
return fileURLs.first(where: { url -> Bool in
let fileName = url.deletingPathExtension().lastPathComponent
return fileName.components(separatedBy: "_").first == selectedPatient.capitalized
}) != nil
} catch { print(error); return false }
}

Is there anyway for me to send a PDF and PNGs by text using MFMessageComposeViewController() in Swift?

Whenever I try to use .addAttachmentURL, it does not attach anything. The ViewController is presented with nothing within the body of the text. The URL is a path to the pdf data (I don't know if that makes a difference) in my file defaults. Is there any way I can send a PDF through text like this? I have not found anything by looking through documentation or StackOverflow. Also, I haven't implemented it yet, but I was wondering if there was a way to also attach PNGs to this message I am sending along with the PDF.
func getFileManager() -> NSString {
let filePath = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString)
return filePath
}
func displayMessageInterface() {
let composeVC = MFMessageComposeViewController()
composeVC.messageComposeDelegate = self
// Configure the fields of the interface.
composeVC.recipients = ["000000000"]
var url = URL(string: self.getFileManager() as String)!
url.appendPathComponent("my_report.pdf")
composeVC.addAttachmentURL(url, withAlternateFilename:
"this file")
// Present the view controller modally.
if MFMessageComposeViewController.canSendText() {
self.present(composeVC, animated: true, completion: nil)
} else {
print("Can't send messages.")
}
}
You are using the wrong URL initializer. URL(string:) initializer expects a scheme, in this case file://. You need to use URL(fileURLWithPath:) initializer or simply get the document directory URL using FileManager urls method:
extension URL {
static let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
}
let url = URL.documentDirectory.appendingPathComponent("my_report.pdf")
I am not sure what you mean when you say "The URL is a path to the pdf data in my file defaults". If you have included your file in your project Bundle you need to use its url(forResource:) method.
let url = Bundle.main.url(forResource: "my_report", withExtension: "pdf")!

Strange error nw_protocol_get_quic_image_block_invoke dlopen libquic failed

I'm new to swift and iOS in general, please keep that in mind.
I get this error when opening the CFReadStream. It does not matter if I open the read or write streams, the app always fails.
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?
let host: CFString = NSString(string: hostIP)
let port: UInt32 = UInt32(self.VNCport)
self.password = password
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host, port, &readStream, &writeStream)
inputStream = readStream!.takeRetainedValue()
outputStream = writeStream!.takeRetainedValue()
if readStream == nil {
print("Erro read")
}
if writeStream == nil {
print("Erro write")
}
inputStream!.delegate = self
outputStream!.delegate = self
inputStream!.schedule(in: RunLoop.main, forMode: RunLoop.Mode.default)
outputStream!.schedule(in: RunLoop.main, forMode: RunLoop.Mode.default)
inputStream!.open()
outputStream!.open()
I made a clean project with just this function and a Button, the result is the same. No quic lib is used in the project.
Can someone help?
I faced the same error in a different context, in XCode 12.0.1 too. It might not be related, but I suspect its an issue with accessing the run loop of the main thread. I was just trying out some introductory code I found online, and faced the same issue, so this is a bug, rather than a problem with your code. Here's how you can get a piece of code that has the same issue:
git clone git#github.com:BestKora/CS193P-Fall-2017-Demo-iOS12.git
cd "CS193P-Fall-2017-Demo-iOS12/Cassini L10"
xed . # this opens XCode (CLI tool stands for XCode editor)
Having said that, by rewriting the code, I was able to prevent this issue. Maybe you can find something amongst the code below to fix your specific issue:
Specifically, instead of using the following (DispatchQueue.global)
private func fetchImage() {
if let url = imageURL {
spinner.startAnimating()
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
let urlContents = try? Data(contentsOf: url)
DispatchQueue.main.async {
if let imageData = urlContents, url == self?.imageURL {
self?.image = UIImage(data: imageData)
}
}
}
}
}
I use URLSession.shared.dataTask, and this error no longer happens:
private func fetchImage() {
print("fetching image")
if let url = imageURL {
let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
guard let data = data else {
return
}
// maybe try dispatch to main
DispatchQueue.main.async {
self.imageView.image = UIImage(data: data)
}
}
task.resume()
}
}
I had same issue in an Widget Target, but solved it by adding "public" to the variables.
Fixed struct is shown below.
struct LastCommitEntry: TimelineEntry {
public let date: Date
public let commit: Commit
}
This is just a Simulator message. It won't appear on a real device, and it doesn't affect the behavior of your app, so ignore it.
I was getting this issue in url session.
Thank you
Restarting my simulator did the trick.
For me, it started working when I encoded the data using String(data: safeData, encoding: .utf8) line:
if let safeData = data {
let dataString = String(data: safeData, encoding: .utf8)
print(dataString!)
}
In my case it was a problem with the model. I was working with Codable Model but it wasn't parsing the data properly. When I used the simulator the error was there but when I used my device it disappeared, neverthelesss, the collection view wasn't showing. When I change my model the error was corrected.
You'll need to adopt that specific delegate you're using in your class.
Like in below example:
class ViewController: UIViewController, UITextFieldDelegate
And don't forget to set your current class as the delegate, like below (for my own case):
IBOutlet weak var searchTextField: UITextField!
searchTextField.delegate = self

How do I allow users to save a file to a specific directory and specify a file name in a macOS app?

I built an iOS app in Swift, and I'm adding some functionality for macOS Catalyst.
In my app I create a .txt file upon clicking a button. I want to present a UIDocumentPickerViewController that allows the user to specify the save directory, and file name. So far I am only able to display the UIDocumentPickerViewController without the option to name the file or save. Is UIDocumentPickerViewController the right view controller to accomplish this? If so, how do I specify the save directory and file name?
Here is the code I'm using to present the UIDocumentPickerViewController
#if targetEnvironment(macCatalyst)
let str = "Hello boxcutter"
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as NSURL
let fileURL = documentsURL.appendingPathComponent("boxCutter.txt")
try! str.write(to: fileURL!, atomically: true, encoding: String.Encoding.utf8)
let types: [String] = [kUTTypeFolder as String]
let documentPicker = UIDocumentPickerViewController(documentTypes: types, in: .open)
documentPicker.delegate = self
documentPicker.allowsMultipleSelection = false
documentPicker.modalPresentationStyle = .formSheet
self.present(documentPicker, animated: true, completion: nil)
#endif
You can use NSSavePanel to ask the user where to save the file. It's a macOS API that isn't directly accessible from Catalyst apps, but you can create a macOS plugin that has access to macOS API as explained here. Or use a library like Dynamic (Full disclosure: I'm the author) to achieve the same thing without a plugin:
let nsWindow = Dynamic.NSApplication.sharedApplication.delegate.hostWindowForUIWindow(view.window)
let panel = Dynamic.NSSavePanel()
panel.nameFieldStringValue = "boxCutter.txt"
panel.beginSheetModalForWindow(nsWindow, completionHandler: { response in
if response == 1 /*OK*/ {
print("file URL: ", panel.URL.asURL)
}
} as ResponseBlock)
typealias ResponseBlock = #convention(block) (_ response: Int) -> Void

open .pkpass file from Wallet using share extension iOS

I am trying to open .pkPass file from wallet using share extension, but its not opening. Basically its returning data and I want URL. but If I try to open .pkpassfile data in PKAddPassesViewController its not open it says Attempt to present on whose view is not in the window hierarchy!.
ShareViewController
override func didSelectPost() { if let item = self.extensionContext?.inputItems[0] as? NSExtensionItem{
print("Item \(item)")
for ele in item.attachments!{
print("item.attachments!======>>> \(ele as! NSItemProvider)")
let itemProvider = ele as! NSItemProvider
print(itemProvider)
if itemProvider.hasItemConformingToTypeIdentifier("com.apple.pkpass"){
imageType = "com.apple.pkpass"
}
if itemProvider.hasItemConformingToTypeIdentifier("com.apple.pkpass"){
imageType = "com.apple.pkpass"
}
print("imageType : \(imageType)")
if itemProvider.hasItemConformingToTypeIdentifier(imageType){
print("True")
itemProvider.loadItem(forTypeIdentifier: imageType, options: nil, completionHandler: { (item, error) in
print(item)
var imgData: Data!
if let url = item as? Data{
imgData = url
//try! Data(contentsOf: url)
self.openPass(withName: url)
}else {
print("sorry it does not work on others")
}
})
}
}
}self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
open PkPass file Functions
func openPass(withName passData: Data?) {
var error: Error? = nil
var newPass: PKPass? = nil
do {
newPass = try PKPass(data: passData as! Data)
} catch {
}
if error != nil {
UIAlertView(title: "Passes error", message: error!.localizedDescription, delegate: nil, cancelButtonTitle: "Ooops", otherButtonTitles: "").show()
return
}
let addController = PKAddPassesViewController(pass: newPass!)
addController!.delegate = self
present(addController!, animated: true)
}
its not opening it says Attempt to present on whose view is not in the window hierarchy!
basically I want to open .pkpass file from wallet app using share extension in my app and then user can print or share the pass using my application. I want to get url of .pkpass file then save it to userdefults, now the issue is this .pkpass file is returning data instead of url
so can anyone help to fix the issue or give suggestion regarding opening .pkpass file from wallet app using share extension thankx