Get the content of iCloud Drive - swift

In my viewDidLoad, I need to verify if the files that I saved to the iCloud Drive are still available and have not been deleted. As I have read, I can not use standard FileManager calls:
FileManager.default.fileExists(atPath: filePath)
What would be the alternative. I do use NSMetadataQuery, but I wanted to know if there is an easy way to query the content of the App's UbiquitousDocument prior to the notifications kicking in.
Also, I am using the ios11 facility of sharing files between different users, and again I need to be able to verify if those files are still available when my App comes to the foreground. Using the NSMetadataQuery and searching in NSMetadataQueryUbiquitousDocumentsScope the shared documents are not displayed.
Any suggestions
Best
Reza

Try this:
let DOCUMENTS_DIRECTORY = "Documents"
if let iCloudDocumentsURL = NSFileManager.defaultManager().URLForUbiquityContainerIdentifier(nil)?.URLByAppendingPathComponent(DOCUMENTS_DIRECTORY)?.URLByAppendingPathComponent(fileName) {
if let pathComponent = iCloudDocumentsURL.path {
if (NSFileManager.defaultManager().fileExistsAtPath(pathComponent, isDirectory: nil)) {
}
}
}
This checks the file under the public Document directory.

Related

How to write to a file in a project while the project is running? [duplicate]

Very new to MacOS development (as in, completely new). I've developed a MacOS app (SwiftUI / Swift) and now figuring out a very things related to deployment.
My app generates a text file, but I'd like to save it within the app's folder (or whatever it is called) - and not in a user specified folder, and read it when I want. I can read resources from the Assets.xcassets but I'd like to be able to save as well without making the user choose a location.
Is there a way I can write/read from the app folder (I'm struggling to explain as I'm very unfamiliar with this system)?
Yes you can create a directory inside your application support folder, name it with app’s bundle identifier or your company and store all files that are not accessible to the user there:
Use this directory to store all app data files except those associated
with the user’s documents. For example, you might use this directory
to store app-created data files, configuration files, templates, or
other fixed or modifiable resources that are managed by the app. An
app might use this directory to store a modifiable copy of resources
contained initially in the app’s bundle. A game might use this
directory to store new levels purchased by the user and downloaded
from a server. All content in this directory should be placed in a
custom subdirectory whose name is that of your app’s bundle identifier
or your company.
You should take some time and read the File System Basics documentation
do {
let applicationSupport = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
let bundleID = Bundle.main.bundleIdentifier ?? "company name"
let appSupportSubDirectory = applicationSupport.appendingPathComponent(bundleID,isDirectory: true)
try FileManager.default.createDirectory(at: appSupportSubDirectory, withIntermediateDirectories: true, attributes: nil)
print(appSupportSubDirectory.path) // /Users/.../Library/Application Support/YourBundleIdentifier
} catch {
print(error)
}

read/write database path using sqlite.swift

I'm just starting out trying sqlite.swift and databases with swift. I have prepared a database with tables and preloaded with data. I wish to select data and insert data from within the app.
The problem is that I don't understand where (in my project) to put my database.db file in order for the app to find it.
my connection code:
let path = NSSearchPathForDirectoriesInDomains(
.documentDirectory, .userDomainMask, true
).first!
do {
db = try Connection("\(path)/database.db")
} catch {
db = nil
print ("Unable to open database")
}
In terms of where this file should go, I would suggest the “application support directory”. See File System Programming Guide: Where You Should Put Your App’s Files, which says:
Put user data in Documents/. User data generally includes any files you might want to expose to the user—anything you might want the user to create, import, delete or edit. For a drawing app, user data includes any graphic files the user might create. For a text editor, it includes the text files. Video and audio apps may even include files that the user has downloaded to watch or listen to later.
Put app-created support files in the Library/Application support/ directory. In general, this directory includes files that the app uses to run but that should remain hidden from the user. This directory can also include data files, configuration files, templates and modified versions of resources loaded from the app bundle.
Also see the iOS Storage Best Practices video.
The above is for iOS. If inquiring about macOS, see File System Programming Guide: The Library Directory Stores App-Specific Files.
But regardless of the OS, the technique for referencing the application support directory is largely the same:
do {
let fileURL = try FileManager.default
.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("database.db")
db = try Connection(fileURL.path)
} catch {
db = nil
print("Unable to open database: \(error)")
}

Swift - Grab all the files from a specific folder/remote URL on a server

I am trying to access a specific folder in a remote URL e.g.http://dev.servertest.com/sessions/id/video
from there, I want to grab all the videos from the folder and download them to the device.
I know how to download the videos from a remote URL to the directory but I can't work out to grab all the videos from the specific video folder.
Also, the server may change depending on the user using their own server but the rest of the structure should be the same "/sessions/id/video"
Below is the code I use to upload from a specific folder "ImportVideos" from my app directory if that helps.
func loadVideos(){
let fm = FileManager.default
let dirPaths = fm.urls(for: .documentDirectory, in: .userDomainMask)
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0];
do {
let videoList = try fm.contentsOfDirectory(atPath: "\(documentsPath + "/ImportVideo")")
for filename in videoList {
videoSubtitles.append(filename) //this is used to populate my tableview
}
} catch let error {
print("error: \(error.localizedDescription)")
}
}
If someone could point me in the right direction that would be great. Thanks.
The HTTP protocol does not provide any means of getting a list of files in a directory. What you're trying to do is not generally possible without a manifest — a list of files that you want to download.
The easiest way to do that is to run a script on the server, e.g.
#!/bin/sh
FILES_DIR=/path/to/directory/on/server
cd "$FILES_DIR"
ls > allfiles.txt
Then have your app fetch the file http://example.com/path/to/allfiles.txt, then split it by newline, and fetch each file. This approach also provides the advantage of letting you later replace that text file with a script handler that serves different file lists to different clients, if desired, e.g. for supporting different versions of your app.
Alternatively, if your server supports WebDAV, then it is possible to configure the directory with WebDAV enabled and use a WebDAV library to find out what files are in the directory. However, this is probably not a good idea, because WebDAV is relatively complex and easy to misconfigure.
Finally, some web servers provide a way to enable "directory listings" — a web page that has links to all of the files in the directory. (Many people will tell you that this is a bad idea from a security perspective, though that is debatable.) It is possible, though inherently fragile, to parse such a page and extract the links. This approach is strongly discouraged, however, because it could break completely when you update to a new version of the web server software.

How can local settings be stored on apple watch?

I am aware of the methods listed here to save preferences across Apple Watch and iOS.
But, they mention that settings cannot be changed on the Apple Watch side, and that a WCSession would be needed to change settings from the watch.
I am looking for a method to store preferences locally on the watch. These preferences are just for the watch (so a shared preferences scheme is not what I'm looking for). Also, the method needs to work with or without the phone present.
My end goal is just to have switches on my Apple Watch app retain their state once the user changes them on the watch. I want their state to be retained if the app is closed and reopened.
Any ideas on how to do this? My only idea so far is to save a file locally to the watch and read from that on launch, but I feel like there must be a simpler way.
EDIT: I have since realized that even though Apple discourages the setting of preferences on the watch, it is completely possible (UserDefaults can be used EXACTLY as it is in iOS). This allowed me to have local watch settings. Then, if settings need to be transferred between the phone and the watch, Watch Connectivity (specifically, TransferUserInfo) can do the job.
UserDefaults is just a file-backed dictionary. The file is stored as a plist and UserDefaults is essentially built on top of PropertyListSerialization. For a simple setup, you can roll your own defaults. File storage on the watch is basically the same as in iOS:
Data placement. WatchKit extensions must take an active role in managing your data. The container directory for your WatchKit extension has the same basic structure as the container for your iOS app. Place user data and other critical information in the Documents directory. Place files in the Caches directory whenever possible so that they can be deleted by the system when the amount of free disk space is low.
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
guard let url = urls.first else {
return
}
// The types that can be stored in this dictionary are the same as what NSUserDefaults can do
let preferences = [ "myDefault" : true ]
if let data = try? PropertyListSerialization.data(fromPropertyList: preferences, format: .xml, options: 1) {
do {
try data.write(to: url.appendingPathComponent("mypreferences.plist"))
} catch {
print("Failed to write")
}
}
if let input = try? Data(contentsOf: url.appendingPathComponent("mypreferences.plist")),
let dictionary = try? PropertyListSerialization.propertyList(from: input, options: .mutableContainersAndLeaves, format: nil),
let d = dictionary as? [String : Bool],
let value = d["myDefault"] {
print(value)
}
Alternatively
To share the preferences, you can use the solution here.

How to handle "open in" retrieved file with swift 2

I'm trying to build a quite simple app which uploads files to our server using standard http requests.
I'd like the app to be some sort of hub being able to open an email attachement for example.
That worked out, I added the info to the info.plist.
Now that file is sent to my app successfully (eg a pdf file)...
How can I retrieve that file url to display it for example in a webview ? I googled for hours, seems to fo through the appDelegate but I have no idea how that continues...
Just found this code, now I'm stucked (and don't even know if that actually works !)
func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject?) -> Bool {
let dictionary = NSDictionary(contentsOfURL: url)
return true
}
Any help to point me in the right direction is highly appreciated ! I started using swift 5 days ago, please be gentle ;)
I have not tested this, but it appears that what you want to do is this:
Go to your project settings
Go to the "info" tab
Under "Document Types" add the document types you want to support
To get the added file, you need to look in the Documents/Inbox folder for your app, as shown below:
let filemgr = NSFileManager.defaultManager()
let documentsDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString
let inboxPath = documentsDirectory.stringByAppendingPathComponent("Inbox")
do {
let dirFiles = try filemgr.contentsOfDirectoryAtPath(inboxPath)
} catch {
//Handle error
}
I am not certain what exactly gets passed to application:openURL:sourceApplication:annotation:, but it might help in checking which file was opened.
Additionally, straight from Apple:
Use this directory to access files that your app was asked to open by outside entities. Specifically, the Mail program places email attachments associated with your app in this directory. Document interaction controllers may also place files in it.
Your app can read and delete files in this directory but cannot create new files or write to existing files. If the user tries to edit a file in this directory, your app must silently move it out of the directory before making any changes.
This answer adapted from this tutorial (part 2 & part 3)