// RGBiOSUtilities
//
// Use at your own risk, no guarantees..
//
// Updated (headerdoc added) on 30-01-2018
//
// Updated May 2023
//
//
import Foundation
import UIKit
//MARK: notifyUser(_:,alertTitle: alertMessage:ok:)
/// Shows an alert for the given controller. Requires a title and an alert message; only shows an OK button (no Cancel or other buttons)
///
/// - Parameters:
/// - controller: the ViewController inside which this alert is to be shown
/// - title: The title of the alert, at the top of the dialog
/// - alertMessage: The message to be shown in the alert, right below the title in a smaller script
/// - runOnOK: The function or closure to be called once the dialog ends (because the user taps OK)
public func notifyUser(_ controller:UIViewController,
alertTitle title:String,
alertMessage:String,
runOnOK:@escaping (UIAlertAction) -> Void) {
let alertViewController = UIAlertController(title: title, message: alertMessage, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: runOnOK)
alertViewController.addAction(okAction)
controller.present(alertViewController, animated: true, completion: nil)
}
//public func doSomethingOnOK(alert:UIAlertAction) {
// // Put this function in the class that calls notifyUser()
// // Do whatever is needed
//}
//MARK: userConfirmsAction(_:,alertTitle:, alertMessage:, runOnConfirm:)
/// Shows an user-confirmed alert for this app
/// Shows an alert for the given controller. Requires a title and an alert message; the dialog shows both an OK button and a Cancel button. When one of these is tapped, the dialog closes and ither the runOnConfirm: or the runOnCancel: functions or closures are executed. Both may be { _ in }, that is, do nothing.
///
/// - Parameters:
/// - controller: The ViewController inside which this alert is to be shown
/// - title: The main title of the alert, shown in bold lettering at the top of the dialog
/// - alertMessage: The information shown in the alert, right below the main title
/// - runOnConfirm: A function or closure to be run when the user taps OK. It can be empty, { _ in }
/// - runOnCancel: A function or closure to be run when the user taps Cancel.It can be empt, { _ in }
public func userConfirmsAction(_ controller:UIViewController,
alertTitle title:String,
alertMessage:String,
runOnConfirm: @escaping (UIAlertAction) -> (),
runOnCancel: @escaping (UIAlertAction) -> ()) {
let alertViewController = UIAlertController(title:title,
message: alertMessage,
preferredStyle: UIAlertController.Style.alert)
alertViewController.addAction(UIAlertAction(title: "Confirm",
style: .destructive,
handler: runOnConfirm ))
alertViewController.addAction(UIAlertAction(title: "CANCEL",
style: .cancel,
handler: runOnCancel))
controller.present(alertViewController, animated: true, completion: nil)
}
//func doSomethingOnConfirm(alert:UIAlertAction) {
// // Put this function in the class that calls userConfirmsAction()
// // Do whatever is needed on confirm
//}
//public func doSomethingOnCancel(alert:UIAlertAction) {
// // Put this function in the class that calls userConfirmsAction()
// // Do whatever is needed on cancel
//}
/// Address of the tmp folder as an URL for this app
///
/// - Returns: An URL that points to the tmp directory for this app (inside its sandbox). We are working in iOS.
public func tmpURL() -> URL {
if #available(iOS 10.0, *) {
let dfm = FileManager.default
return dfm.temporaryDirectory
} else {
// Fallback on earlier versions
return URL(string: NSTemporaryDirectory())!
}
}
/// Address of the tmp directory for this app. We are working in iOS.
///
/// - Returns: A String that points to the tmp directory for this app (inside its sandbox) We are working in iOS.
public func tmpPath() -> String{
return tmpURL().path
}
/// Address of the Documents folder as an URL for this app
///
/// - Returns: An URL that points to the Documents folder for this app (inside its sandbox). We are working in iOS.
public func documentsURL() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
/// Address of the Documents path as a String for this app for sandboxing
///
/// - Returns: A String that points to the Documents folder for this app (inside its sandbox). We are working in iOS.
public func documentsPath() -> String {
return documentsURL().path
}
/// Address of the Library folder for this app as an URL
///
/// - Returns: An URL that points to the Library folder for this app (inside its sandbox). We are working in iOS.
public func libraryURL() -> URL {
let dfm = FileManager.default
return dfm.urls(for: FileManager.SearchPathDirectory.libraryDirectory,
in: FileManager.SearchPathDomainMask.userDomainMask).first!
}
/// Address of the Library folder for this app as a String
///
/// - Returns: A String that points to the Library folder for this app (inside its sandbox). We are working in iOS.
public func libraryPath() -> String {
return libraryURL().path
}
/// Returns the name and extension of a given filename
///
/// - Parameter total: The given filename, in the form "name.extension"
/// - Returns: A [String] whose first element is the file name and whose second element is the extension as given in the input parameter.
public func splitIntoNameAndExtension(total:String) -> [String]? {
let parts = total.components(separatedBy: ".")
guard
parts.count == 2,
let name = parts.first,
!name.isEmpty,
let ext = parts.last,
!ext.isEmpty
else
{ return nil }
return [name, ext]
}
/// Is object at the given path a directory?
///
/// - Parameter path: A String path to some object
/// - Returns: True if the object is a directory, false otherwise
public func isDirectory(path: String) -> Bool {
let dfm = FileManager.default
var isDir: ObjCBool = false;
dfm.fileExists(atPath: path, isDirectory: &isDir)
return isDir.boolValue;
}
/// Is the object at the given URL a directory?
///
/// - Parameter url: The URL of some object
/// - Returns: True if the object is a directory, false otherwise
public func isDirectory(url: URL) -> Bool {
let dfm = FileManager.default
var isDir: ObjCBool = false;
dfm.fileExists(atPath: url.path, isDirectory: &isDir)
return isDir.boolValue;
}
/// Is the object with the given URL a directory? Does it exist?
///
/// - Parameter url: The URL to some object
/// - Returns: A tuple whose first value is true if the object exists, or false otherwise, and whose second element is true if the object is a directory, or false otherwise.
public func existsDirAt(url: URL) -> (fileExists:Bool, fileIsDirectory:Bool) {
let dfm = FileManager.default
var isDir: ObjCBool = false;
let exists = dfm.fileExists(atPath: url.path, isDirectory: &isDir)
return (exists, isDir.boolValue);
}
//MARK: isItHidden?
/// Returns whether the given URL belongs to a hidden object (file or directory)
///
/// - Parameter url: The URL to some object
/// - Returns: true if the object is hidden, false otherwise
public func isHidden(url : URL) -> Bool {
return url.lastPathComponent.hasPrefix(".")
}
//MARK: download()?
/// Asynchronously downloads thisFile (a remote URL) and stores it locally toThisURL (a local URL).
/// A callback must be provideded, which receives the UIViewControlelr (for notifyUser()), the error message and the
/// URL of the locally saved file.
/// See an example called completion, below.
///
/// - Parameter onView: The UIViewController from which the download() is called
/// - Parameter thisFile: The URL of the remote object to be downloaded
/// - Parameter toThisURL: The local URL where the remote object is saved after downloading
/// - Parameter callback: A function that is called upon completion; it must give feedback to the user and gets the URL of the downloaded object (will probably update on the main thread)
/// - Returns: Void
func download(_ onView:UIViewController, thisFile remote: URL, toThisURL local:URL, callback: @escaping (UIViewController,String?, URL) -> Void) {
let session = URLSession(configuration: URLSessionConfiguration.default)
let request = URLRequest(url:remote)
let task = session.downloadTask(with: request) { tempLocalUrl, response, error in
if let tmpURL = tempLocalUrl, error == nil {
// State may be useful
// if let status = (response as? HTTPURLResponse)?.statusCode {
// print("Successfully downloaded. Status code: \(status)")
// }
do {
if FileManager.default.fileExists(atPath: local.path()) {
try FileManager.default.removeItem(at: local)
//callback(onView,"File already exists",tmpURL)
//return
}
try FileManager.default.copyItem(at: tmpURL, to: local)
callback(onView, nil, local)
} catch (let writeError) {
callback(onView,"\(local): \(writeError)", local)
}
} else {
callback(onView,error?.localizedDescription, local)
}
}
task.resume()
} // End of download()
/*
func parseAndShow(fileURL theURL:URL) -> Void {
// First parse
do {
let data = try Data(contentsOf: theURL)
let pld = PropertyListDecoder()
let table = try pld.decode([[String]].self, from: data)
let ppl = table.compactMap { Person($0) }
persons = ppl
tableView.reloadData()
} catch {
notifyUser(self, alertTitle: "PARSE ERROR", alertMessage: "DECODE UNSUCCESSFUL", runOnOK: { _ in })
}
// Now show
}
func completion(_ viewController: UIViewController,theErrorMessage:String?, localDownloadURL theURL:URL) {
if let msg = theErrorMessage {
/ UPDATE ON THE MAIN THREAD
OperationQueue.main.addOperation() {
notifyUser(viewController, alertTitle: "DOWNLOAD ERROR", alertMessage: "\(msg)", runOnOK: { _ in })
return
}
}
// If all did go well, do sth with the fle
OperationQueue.main.addOperation() {
self.parseAndShow(fileURL: theURL)
}
} // End of completion()
*/