Cannot create sqlite file with SQLite.swift "cannot open file at line..." - swift

I'm attempting to create a sqlite DB file within my iOS project. I'm using the code as documented for R/W
let path = NSSearchPathForDirectoriesInDomains(
.documentDirectory, .userDomainMask, true
).first!
let db = try Connection("\(path)/db.sqlite3")
but I end up with a cannot open file at line 45340 of [d24547a13b].
The closest resource I've found is Why do I get Unable to Open Database file? but the code there seems to be the same as what I have.
edit: More logs
[logging-persist] os_unix.c:45340: (0) open
- Undefined error: 0

DB file needs to be created under the documents directory. See Brando Flores' answer: https://stackoverflow.com/a/70514807/346676
do {
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let dbFile = "\(path.first ?? "")/db.sqlite3"
print(dbFile) // show full file path
db = try Connection(dbFile)
}
catch {
print(error)
}

Related

Vapor uploads files to DerivedData instead of the Public folder in the project structure

When I try to upload files via my Vapor server, it always uploads files into the DerivedData folder instead of the Public folder inside the project structure.
I can verify that the file is created in the path, but the path is somewhere in DerivedData directory .. why? How am I going to server such file when it's not in the project's Public folder?
My upload code:
func create(request: Request) async throws -> HTTPStatus {
try CreateDogRequest.validate(content: request)
let createDogRequest = try request.content.decode(CreateDogRequest.self)
guard let userId = try request.auth.require(User.self).id else {
throw Abort(.notFound)
}
let dogId = UUID()
let directory = DirectoryConfiguration.detect()
let publicFolder = "Public"
let folderPath = URL(fileURLWithPath: directory.workingDirectory)
.appendingPathComponent(publicFolder, isDirectory: true)
.appendingPathComponent("dogProfiles", isDirectory: true)
.appendingPathComponent(userId.uuidString, isDirectory: true)
.appendingPathComponent(dogId.uuidString, isDirectory: true)
print("Starting writing to path: \(folderPath)")
let filePath = folderPath.appendingPathComponent("hello" + ".jpg", isDirectory: false)
try FileManager.default.createDirectory(at: folderPath, withIntermediateDirectories: true)
let data = Data(buffer: createDogRequest.dogImage.data)
try data.write(to: filePath, options: .atomic)
print("file uploaded at: \(filePath.relativePath)")
return .ok
}
Now this shows that the file is uploaded here:
/Users/<USERNAME>/Library/Developer/Xcode/DerivedData/<PROJECT_HANDLE>/Build/Products/Debug/Public/dogProfiles/62C340CE-262B-4DE4-9E2A-99B3B3126BB6/hello.jpg
Why? How can I serve such file then?
I debugged the DirectoryConfiguration class and the detect() method checks if the server is running via Xcode and it looks like this:
#if Xcode
if workingDirectory.contains("DerivedData") {
Logger(label: "codes.vapor.directory-config")
.warning("No custom working directory set for this scheme, using \(workingDirectory)")
}
#endif
But the funny thing is, if I put a file inside the Resource folder to read data from, and use the DirectoryConfiguration's detect() method and create a path to that file, it finds the file no problem ??!!! Why? How? :D That is a mystery to me
This is the code that I wrote for reading from the file:
let directory = DirectoryConfiguration.detect()
let configDir = "Resources"
let path = URL(fileURLWithPath: directory.workingDirectory)
.appendingPathComponent(configDir, isDirectory: true)
.appendingPathComponent("file.txt", isDirectory: false)
.relativePath
What am I missing here? How come the file put inside Resources folder gets read, but when I want to put something inside the Public folder it gets put inside DerivedData even tho I am using the same patter when creating the path ??
You need to set a custom working directory in Xcode so Vapor knows where to looks. See https://docs.vapor.codes/getting-started/xcode/#custom-working-directory
Ooookay, once again, I tried to think differently and I googled a bit and found this post:
How to get path of root directory of your project folder in Perfect 2.0?
and the answer from worthbak pointed to the conclusion that I should rather run the server from terminal using swift run and try if the file gets created, and guess what? It does!
This also explains how the file I put in the Resources folder was read, because it is inside a migration that you run from guess where? .. the Terminal :)

How do I write to a local file in Swift/XCTest?

My ultimate question is about saving a screenshot from an AppleTV application using XCTest and Swift4 (running on a MacBook paired to the TV device), but I'm having trouble even writing a simple text string to a local file. If I can get this simple file-save working, I'm hoping I can resolve the screenshot issue. (Apologies for making this look like two questions but they appear to be related and resulted from my troubleshooting efforts.)
First, here's what I'm trying to do with a screenshot, based on sample code I found somewhere online:
let appshot = XCUIApplication().windows.firstMatch.screenshot()
let shotpath = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask)[0].appendingPathComponent("appshot.png")
let shotpathUrl = URL(string: "file://\(shotpath)")
print("Saving to: \(shotpath)")
do {
try appshot.pngRepresentation.write(to: shotpathUrl!)
} catch {
print("Failed saving screenshot due to \(error)")
}
This gives me the following output:
Saving to: file:///var/mobile/Containers/Data/Application/77D52C66-353B-4029-97D5-48E6BAE35C92/Downloads/appshot.png
Failed saving screenshot due to Error Domain=NSCocoaErrorDomain Code=4 "The file “appshot.png” doesn’t exist." UserInfo={NSFilePath=///var/mobile/Containers/Data/Application/77D52C66-353B-4029-97D5-48E6BAE35C92/Downloads/appshot.png, NSUnderlyingError=0x1c405bc60 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
Of course, the file doesn't exist because that's the file I'm trying to create. But /var/mobile doesn't exist on my laptop either -- it looks like the path FileManager is building may exist on the AppleTV device, but I want it on my laptop where my test script is executing.
So I backed out to a much more simple case, and even this is giving me problems:
let str = "This is a test"
let path = "file:///Users/haljor/foo.txt"
let pathUrl = URL(string: path)!
print("Path: \(path)")
print("URL: \(pathUrl)")
do {
try str.write(to: pathUrl, atomically: true, encoding: .utf8)
} catch {
print("Caught error writing to \(pathUrl): \(error)")
}
And here's the output:
Path: file:///Users/haljor/foo.txt
URL: file:///Users/haljor/foo.txt
Caught error writing to file:///Users/haljor/foo.txt: Error Domain=NSCocoaErrorDomain Code=4 "The folder “foo.txt” doesn’t exist." UserInfo={NSURL=file:///Users/haljor/foo.txt, NSUserStringVariant=Folder, NSUnderlyingError=0x1c40553f0 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
Here, it looks like it's trying to write to a folder at the path I specified, not a file. Clearly there's something I'm not understanding in each of these cases.
I don't really have a preference for whether I use a fully-specified path or something using FileManager -- it just needs to land somewhere on my laptop (not the TV device). What am I missing?
You can add an attachment to the test case and save it to disk too. The problem was that Downloads folder may not exist in the container yet. The best way to handle this is via init-once property:
var downloadsFolder: URL = {
let fm = FileManager.default
let folder = fm.urls(for: .downloadsDirectory, in: .userDomainMask)[0]
var isDirectory: ObjCBool = false
if !(fm.fileExists(atPath: folder.path, isDirectory: &isDirectory) && isDirectory.boolValue) {
try! fm.createDirectory(at: folder, withIntermediateDirectories: false, attributes: nil)
}
return folder
}()
func test() {
let appshot = XCUIScreen.main.screenshot()
let attachment = XCTAttachment(screenshot: appshot)
attachment.lifetime = .keepAlways
self.add(attachment)
// Save to container
let url = downloadsFolder.appendingPathComponent("appshot.png")
try! appshot.pngRepresentation.write(to: url)
}
If you want to view the attachment, right-click on the test case, select Jump to Report and expand the tree. You will see the screenshot eventually:

Copy TPK file from AppGroup Container to Documents

I have a file that exists within the AppGroup Shared Container and I was wondering if it was possible to copy the file from the Shared Container into the application bundle.
I am getting the file path as follows :
let filePath = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier("group.com.sharedBasemap")!.URLByAppendingPathComponent("localLayer.tpk")!.path
The reason I am trying to do this is it seems that the ArcGIS SDK will not recognize the TPK file from within the App Group so I am wondering if it will recognize it if I copy it into the app bundle.
EDIT: Based on Leo's comment it appears that you can not copy to the bundle, so I am trying to copy to the App Support folder.Here is my code now, I see the "file exists" message but then it is displaying the Oops message indicating it can not move the file :
let filePath = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier("group.com.sharedBasemap")!.URLByAppendingPathComponent("localLayer.tpk")!.path!
let appSupportFolder = String(NSFileManager.defaultManager().URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask)[0]) + "localLayer.tpk"
let fileManager = NSFileManager.defaultManager()
if NSFileManager.defaultManager().fileExistsAtPath(filePath){
print("File exists at \(filePath)")
do {
try fileManager.copyItemAtPath(filePath, toPath: appSupportFolder)
}
catch let error as NSError {
print("Ooops! Something went wrong: \(error)")
}
} else {
print("File does not exist")
}
EDIT 2: I have modified the code again to just move the TPK file into the documents directory.I believe that piece is working but I receive an error message when trying to load the TPK file into ArcGIS.At this point in time, I am thinking that the issue is related to the ArcGIS SDK and that it does not support loading a TPK file from anywhere except the application bundle.
let destPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first!
let fullDestPath = NSURL(fileURLWithPath: destPath).URLByAppendingPathComponent("localLayer.tpk")
let fullDestPathString = fullDestPath!.path!
im pretty sure the appSupportFolder doesn't exist by default -- nobody creates it unless needed -- try to verify that first and create it if needed
pseudocode if(!fileExists(supportFolder)) { createDirectory(supportFolder) }

Uploading a video to S3 with swift The operation couldn’t be completed. (Cocoa error 260.)"

I am trying to upload a video file to Amazon S3
I constantly get an error 260:
Error in uploading the video: Error
Domain=NSCocoaErrorDomain Code=260 "The operation couldn’t be
completed. (Cocoa error 260.)
I read somewhere that amazon does not support asset library - is it true? and if so what do you suggest
Thanks Eran
func saveVideoToS3 () {
var uploadRequest: AWSS3TransferManagerUploadRequest = AWSS3TransferManagerUploadRequest()
uploadRequest.bucket = "BucketName"
uploadRequest.key = "KeyText"
//Move video file to the application folder so it can be read
var savedVideoURLToBeUsed = NSUserDefaults.standardUserDefaults().objectForKey("ThisIsTheVideoIWantToUse") as! String
println("Video saved in Store: \(savedVideoURLToBeUsed)")
var url: NSURL = NSURL(fileURLWithPath: savedVideoURLToBeUsed)!
uploadRequest.body = url
//uploadRequest.body = NSURL(fileURLWithPath: "file:///\(url)")
println("URL: \(url)")
let transferManager: AWSS3TransferManager = AWSS3TransferManager.defaultS3TransferManager()
transferManager.upload(uploadRequest).continueWithExecutor(AWSExecutor.mainThreadExecutor(), withBlock: { (AWSTask) -> AnyObject! in
//Handle errors
if AWSTask.error != nil {
println("Error in uploading the video: \(AWSTask.error)")
if AWSTask.error.code == 1001 {
self.saveVideoToS3()
}
// Retrive information important for later downloading
} else {
println("Video upload successful..")
var uploadResult: AnyObject! = AWSTask.result
println("Upload result: \(uploadResult)")
//Delete file from the application folder
}
return nil
})
}
Cocoa error 260 is a NSFileReadNoSuchFileError, meaning the path you specified is not valid (file is just not where you say it is), so it probably has nothing with S3 itself. There are three things why this is happening that come to my mind:
you did not use .synchronize() when saving the key to user settings
your file URL contains invalid characters
you did not write the file into filesystem properly
iOS8 Breaking change
Also please note that as of iOS8, due to changes how application work with their assigned sandboxes, you can't save absolute URL to file because next time you open application, it will be different.
Beginning in iOS 8, the Documents and Library directories are no
longer siblings of your application's bundle.
I am using two quick convenience functions that I wrote to get file from cache directory:
func cachePathWithFileName(fileName : String) -> String {
let cacheDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! String
return cacheDirectoryPath.stringByAppendingPathComponent(fileName)
}
and documents directory:
func documentsPathWithFileName(fileName : String) -> String {
let documentsDirectoryPath = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true)[0] as! String
return documentsDirectoryPath.stringByAppendingPathComponent(fileName)
}
Hopefully some of this tips help!

How to read file data Applications document directory in swift?

I am new at Swift programming language. I want to download a movie from some tube servers and want to play offline. I am using Alamofire for downloading part. I can list the file(s) with that:
var file:String?
if let files = NSFileManager.defaultManager().contentsOfDirectoryAtPath(documentsDirectory, error: &error) as? [String] {
for filename in files {
// do stuff with filename
file = filename
println(filename)
}
}
But the problem is how i can use that file for my purpose. Let assume its image file and i want to show in imageview.
myImageView.image = UIImage(contentsOfFile: file) /* doesn't work*/
thank you for any help.
For Swift 2 you have to change something. Note: stringByAppendingPathComponent is not more available on String (only NSString):
var paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString
var getImagePath = paths.stringByAppendingPathComponent("filename")
myImageView.image = UIImage(contentsOfFile: getImagePath)
Try this code:
var paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
var getImagePath = paths.stringByAppendingPathComponent("filename")
myImageView.image = UIImage(contentsOfFile: getImagePath)
I hope this work.
File manage is used for below functionality, Please check once.
1) create folder in document directory or temp directory,
2) copy file from temp to document directory, move path,
3) remove from document directory,
4) list all file from document directory, list file using extension from document directory (ex: if you have .mp3 and .jpg and .text files in document directory and you want only .mp3 file),
5) save file
6) get file path
https://github.com/IosPower/FileManage
At the moment you're just passing in the name of the file to UIImage(contentsOfFile). You need to pass in the entire path, i.e.:
myImageView.image = UIImage(contentsOfFile: "\(documentsDirectory!)/\(filename)") //hopefully does work!