Cannot find file in app extension's shared container - ios

I want to write log file at my extension, and read it at my app.
For this purpose, I'm using shared groups (so both the app and the extension would be able to read from the same file)
I wrote the following code:
Extension:
let fileManager = NSFileManager.defaultManager()
let containerUrl = fileManager.containerURLForSecurityApplicationGroupIdentifier("group.MyCompany.MyProj")
let extensionLogDirectory = containerUrl?.path?.stringByAppendingString("AppExtensionLogs")
let logFileManager = DDLogFileManagerDefault(logsDirectory: extensionLogDirectory)
PacketTunnelProvider.fileLogger = DDFileLogger(logFileManager: logFileManager)
PacketTunnelProvider.fileLogger!.rollingFrequency = 60*60*12
PacketTunnelProvider.fileLogger!.logFileManager.maximumNumberOfLogFiles = 1
DDLog.addLogger(PacketTunnelProvider.fileLogger)
App (just to read the log file):
let fileManager = NSFileManager.defaultManager()
let containerUrl = fileManager.containerURLForSecurityApplicationGroupIdentifier("group.MyCompany.MyProj")
if let extensionLogDirectory = containerUrl?.path?.stringByAppendingString("AppExtensionLogs") {
do {
let directoryContents = try fileManager.contentsOfDirectoryAtPath(extensionLogDirectory)//always fails
for file in directoryContents {
let path = extensionLogDirectory.stringByAppendingString(file)
do {
let fileContents = try NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding)
NSLog("file: \(fileContents)")
}
catch {/* error handling here */
}
}
}
catch {/* error handling here */
NSLog("nope!")
}
But, something now right - it's seems like contentsOfDirectoryAtPath always fails with "no such file" error
What's wrong in this code?

The problem is unrelated to app extensions or CocoaLumberjack.
stringByAppendingString just concatenates strings, so that the path
separator "/" is missing in the generated directory name.
There was a dedicated method stringByAppendingPathComponent, which however
has been deprecated in Objective-C and is no longer available in Swift.
You should operate on the URL by using URLByAppendingPathComponent
instead:
let extensionLogDirectory = containerUrl?.URLByAppendingPathComponent("AppExtensionLogs").path

Related

Get the names of files in an iCloud Drive folder that haven't been downloaded yet

I’m trying to get the names of all files and folders in an iCloud Drive directory:
import Foundation
let fileManager = FileManager.default
let directoryURL = URL(string: "folderPathHere")!
do {
let directoryContents = try fileManager.contentsOfDirectory(at: directoryURL, includingPropertiesForKeys: nil, options: [.skipsSubdirectoryDescendants, .skipsHiddenFiles])
for url in directoryContents {
let fileName = fileManager.displayName(atPath: url.absoluteString)
print(fileName)
}
} catch let error {
let directoryName = fileManager.displayName(atPath: directoryURL.absoluteString)
print("Couldnt get contents of \(directoryName): \(error.localizedDescription)")
}
It appears that any iCloud files that haven’t been downloaded to the device don’t return URLs.
I know I can check if a path contains a ubiquitous item when I already know the path with the code below (even if it isn’t downloaded):
fileManager.isUbiquitousItem(at: writePath)
Is there a way to get the URLs & names of those iCloud files without downloading them first?
The directory URL is a security-scoped URL constructed from bookmark data in case that makes any difference (omitted that code here for clarity).
Thanks
Found the answer. I was skipping hidden files with ".skipsHiddenFiles", but the non-downloaded files are actually hidden files, named: ".fileName.ext.iCloud".
Remove the skips hidden files option now works as expected.
You need to use a NSFileCoordinator to access the directory in iCloud Storage, and then normalize placeholder file names for items that haven't been downloaded yet:
let iCloudDirectoryURL = URL(...)
let fileCoordinator = NSFileCoordinator(filePresenter: nil)
fileCoordinator.coordinate(
readingItemAt: iCloudDirectoryURL,
options: NSFileCoordinator.ReadingOptions(),
error: nil
) { readingURL in
do {
let contents = try FileManager.default.contentsOfDirectory(
at: readingURL, includingPropertiesForKeys: nil
)
for url in contents {
print("\(canonicalURL(url))")
}
} catch {
print("Error listing iCloud directory: '\(error)'")
}
}
func canonicalURL(_ url: URL) -> URL {
let prefix = "."
let suffix = ".icloud"
var fileName = url.lastPathComponent
if fileName.hasPrefix(prefix), fileName.hasSuffix(suffix) {
fileName.removeFirst(prefix.count)
fileName.removeLast(suffix.count)
var result = url.deletingLastPathComponent()
result.append(path: fileName)
return result
} else {
return url
}
}

Access Windows/Mac Shared Folder Locally With smb from iOS

I am trying to build an app where I am able to access(read/write) windows/mac shared folders in my local network with swift.
Is there any possible way to do that with swift?
There is an App in the App Store called "FileExplorer" https://apps.apple.com/de/app/fe-file-explorer-file-manager/id510282524 where you can access these shared folders, but I do not know how they programmed this and with which language.
I also tried to access my shared folders via this App and yes it worked I can see my shared folders on my Phone.
But there needs to be a way to do it with swift...
I already tried different things(code bellow).
In the code bellow I tried to access the shared folder of my second mac and write the Text "Write this text to the fileURL as text in iOS using Swift" into the file named "Test.txt" and after that I want to read the same file again.
#IBAction func Button(_ sender: UIButton)
{
var uc = URLComponents()
uc.scheme = "smb"
uc.user = "user"
uc.password = "password"
uc.host = "ip-adress"
uc.path = "document-directory"
// Save data to file
let fileName = "Test"
let url = uc.url
//let DocumentDirURL = URL(fileURLWithPath: "/Users/f/d/t/App/Assets/Apps/TestApp")
let DocumentDirURL = try! URL(resolvingAliasFileAt: url!)
let fileURL = DocumentDirURL.appendingPathComponent(fileName).appendingPathExtension("txt")
print("FilePath: \(fileURL.path)")
let writeString = "Write this text to the fileURL as text in iOS using Swift"
do {
// Write to the file
try writeString.write(to: fileURL, atomically: true, encoding: String.Encoding.utf8)
} catch let error as NSError {
print("Failed writing to URL: \(fileURL), Error: " + error.localizedDescription)
}
var fullString: String = "" // Used to store the file contents
do {
// Read the file contents
fullString = try String(contentsOf: fileURL, encoding: .utf8)
} catch let error as NSError {
print("Failed reading from URL: \(fileURL), Error: " + error.localizedDescription)
}
print("File Text: \(readString)")
}
If I run the code as shown, he always gives me the error
"smb scheme is not supported" and then some additional errors that he can not write/read the file because he can not access it.
When I change the code and only search on the device I am programming on and run the simulator to search for this file everything works fine. So I have problems with "smb".
Thank you for every helpful answer.
you can use amsmb2 library to do this
you can extend the template class provided to connect to download files, write files, list directories -> on an smb share
everything is asynchronous from memory, with the librarys calls including hooks for progress updates on the ui main thread etc
i believe the amsmb2 library function your after might be uploadItem
iOS 13 includes SMB (server message block protocol) support
https://9to5mac.com/2019/06/17/ios-13-beta-2-enables-smb-server-connectivity-in-the-files-app/

Swift 3.0 FileManager.fileExists(atPath:) always return false

When I use method .fileExists(atPath:)to judge whether the file is exist in file system, the method always return false to me. I checked the file system and the file do exist. Here is my code:
let filePath = url?.path
var isDir : ObjCBool = false
if(self.fileManager.fileExists(atPath: filePath!, isDirectory: &isDir)){
let result = NSData(contentsOfFile: filePath!)
}
or
let filePath = url?.path
if(self.fileManager.fileExists(atPath: filePath!)){
let result = NSData(contentsOfFile: filePath!)
}
the if clause will always be skipped.
I assume your url is an URL type. If so try this out:
let filePath = url?.path // always try to work with URL when accessing Files
if(FileManager.default.fileExists(atPath: filePath!)){ // just use String when you have to check for existence of your file
let result = NSData(contentsOf: url!) // use URL instead of String
}
Saying enough, you should change your implementation like this:
if(FileManager.default.fileExists(atPath: (url?.path)!)){ // just use String when you have to check for existence of your file
let result = NSData(contentsOf: url!) // use URL instead of String
}
EDIT: 1
There is even more better way, you can call it swift-way (:D). You don't have to explicitly check for file existence.
guard let result = NSData(contentsOf: fileURL) else {
// No data in your fileURL. So no data is received. Do your task if you got no data
// Keep in mind that you don't have access to your result here.
// You can return from here.
return
}
// You got your data successfully that was in your fileURL location. Do your task with your result.
// You can have access to your result variable here. You can do further with result constant.
print(result)
Update for Swift 3.0+ without the Objective-Cish NS prefix:
do {
let result = try Data(contentsOf: fileURL)
print(result)
} catch {
print(error)
}
in swift 3
just in case anyone gets confused like i did, here's the full snippets:
let str = "file:///Users/martian2049/Library/Developer/CoreSimulator/Devices/67D744AA-6EEC-4AFD-A840-366F4D78A18C/data/Containers/Data/Application/DD96F423-AF9F-4F4D-B370-94ADE7D6D0A5/Documents/72b8b0fb-7f71-7f31-ac9b-f9cc95dfe90d.mp3"
let url = URL(string: str)
print(url!.path,"\n")
if FileManager.default.fileExists(atPath: url!.path) {
print("FILE Yes AVAILABLE")
} else {
print("FILE NOT AVAILABLE")
}
this prints
/Users/martian2049/Library/Developer/CoreSimulator/Devices/67D744AA-6EEC-4AFD-A840-366F4D78A18C/data/Containers/Data/Application/DD96F423-AF9F-4F4D-B370-94ADE7D6D0A5/Documents/72b8b0fb-7f71-7f31-ac9b-f9cc95dfe90d.mp3
FILE Yes AVAILABLE
notice how the 'file://' got chopped off?
I want to share my experience, in case anyone else gets baffled by this.
Tested on iOS 10-11, Xcode 9.2 and Swift 3.2.
Short answer: if you save a file path to disk, you may solve by not including the Documents directory in it.
Instead, every time you need to retrieve the file with the saved path, get the Documents directory and append the path.
For an iOS app, I was saving an image to .../Documents/Pictures through the relative URL, let's say url.
As the image was saved, a path, let's say url.path, was saved too in a Core Data entity.
When I later tried retrieving the image through FileManager.default.fileExists(atPath: url.path), it always returned false.
I was testing the app on my iPhone. It turned out that, for some reason, every time I ran the app from Xcode, the app identifier folder changed!!
So:
App opened from Xcode -> Image saved -> app closed -> app opened from physical device ->
fileExists -> TRUE
App opened from Xcode -> Image saved -> app closed -> app opened from Xcode -> fileExists -> FALSE
You can check if this is your case by getting and printing the Document folder path (or URL, it doesn't matter) and comparing it with the saved path (or URL). If you get something like this:
/var/mobile/Containers/Data/Application/5D4632AE-C432-4D37-A3F7-ECD05716AD8A/Documents..
/var/mobile/Containers/Data/Application/D09904C3-D80D-48EB-ACFB-1E42D878AFA4/Documents..
you found the issue.
Just use path instead of absoluteString to remove file://
FileManager.default.fileExists(atPath: URL.init(string: "your_url")!.path)
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true);
var path = paths[0] as String;
path = path + "/YourFilePath"
if((NSFileManager.defaultManager().fileExistsAtPath(path))) {
let result = NSData(contentsOfFile: filePath!)}
Try the above code and check again
I had the same problem this worked for me
filePath.replacingOccurrences(of: "file://", with: "")
First, what does your file path looks like? If the path begins with a ~,then it must be expanded with expandingTildeInPath;
Check if the path is inaccessible to your app. iOS App can only visits its sandbox directories.

Read local JSON file in XCUITest

I am writing UI tests for iOS application. I want to read data from a local json file.
I am using the following code to get the path for my json file:
func testExample() {
readJSON()
}
func readJSON(){
let bundle = Bundle(for:myTestClass.self)
if let path = bundle.url(forResource: "myurl", withExtension: "json"){
print("Got the path")
print(path)
}
else{
print("Invalid filename/path")
}
I have tried using the solution for the following stackoverflow question :
Reading in a JSON File Using Swift. Didn't work!
Also, I had a look at the following Apple documentation:
https://developer.apple.com/swift/blog/?id=37
Any help would be greatly appreciated!
First of all, you need to check the .json file is in the same target with the test file. If the file is in the main target, you have to use Bundle.main. However if it is in the same target with your test, use the below code.
let t = type(of: self)
let bundle = Bundle(for: t.self)
let path = bundle.path(forResource: "myurl", ofType: "json")
That's not how you read local json files from Bundle in iOS. It's done in the following way:
// "myUrl" is assumed to be your json file name
if let pathStr: String = Bundle.main.path(forResource: "myurl", ofType: ".json") {
print("json path: \(pathStr)")
}
I just found resources added to XCUITest targets will be placed under Bundle.main.resourcePath + "/PlugIns/<TARGET_NAME>.xctest/". However, I'm not sure if there were better ways to access them rather than hard-coding the sub-directory path.

Uploading a file from AVCapture using AFNetworking

I have a video that is captured with AVCapture, and I'm trying to upload with AFNetworking with Swift.
Code:
let manager = AFHTTPRequestOperationManager()
let url = "http://localhost/test/upload.php"
var fileURL = NSURL.fileURLWithPath(string: ViewControllerVideoPath)
var params = [
"familyId":locationd,
"contentBody" : "Some body content for the test application",
"name" : "the name/title",
"typeOfContent":"photo"
]
manager.POST( url, parameters: params,
constructingBodyWithBlock: { (data: AFMultipartFormData!) in
println("")
var res = data.appendPartWithFileURL(fileURL, name: "fileToUpload", error: nil)
println("was file added properly to the body? \(res)")
},
success: { (operation: AFHTTPRequestOperation!, responseObject: AnyObject!) in
println("Yes thies was a success")
},
failure: { (operation: AFHTTPRequestOperation!, error: NSError!) in
println("We got an error here.. \(error.localizedDescription)")
})
The code above fails, I keep getting
was file added properly to the body? false"
note that ViewControllerVideoPath is a string containing the location of the video which is:
"/private/var/mobile/Containers/Data/Application/1110EE7A-7572-4092-8045-6EEE1B62949/tmp/movie.mov"
using println().... The code above works when I'm uploading a file included in the directory and using:
var fileURL = NSURL.fileURLWithPath(NSBundle.mainBundle().pathForResource("test_1", ofType: "mov")!)
So definitely my PHP code is fine, and the problem lies with uploading that file saved on the device, what am I doing wrong here?
Comments don't allow a full explanation so here is more info;
NSBundle.mainBundle() refers to a path in the bundle file The path in the simulator differs from that of the application ... this is not what you want. There are a number of "folders" you can access based on your needs (private or sharable/files that can get backed up to the cloud). NSPathUtils.h gives a breakdown of the paths available. In keeping with conventions used by most, you should probably create a private path under your application path by doing something like;
- (NSURL *) applicationPrivateDocumentsDirectory{
NSURL *pathURL = [[self applicationLibraryDirecory]URLByAppendingPathComponent:#"MyApplicationName"];
return pathURL;
}
- (NSURL *) applicationLibraryDirecory{
return [[[NSFileManager defaultManager] URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask] lastObject];
}
You can test if it exists, if not, create it ... then store your video files in this path, and pass this to your AVCapture as the location to store the file.
Here are the code that can do following functionality in swift.
1 : Check weather directory exist or not. if not exist then create directory(Directory has given application name) in document directory folder.
2 : Now we have application directory. so all file that from application will write/read in/from this directory.
let file = "file.txt"
let directoryName = “XYZ” // Here “XYZ” is project name.
var error : NSError?
let filemgr = NSFileManager.defaultManager()
let dirPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,
.UserDomainMask, true)
let documentsDirectory = dirPaths[0] as! String
var dataPath = documentsDirectory.stringByAppendingPathComponent(directoryName)
if !NSFileManager.defaultManager().fileExistsAtPath(dataPath) {
NSFileManager.defaultManager().createDirectoryAtPath(dataPath, withIntermediateDirectories: false, attributes: nil, error: &error)
} else {
println("not creted or exist")
}
Now we have Directory so only need to write/read data from directory.
how to write file in document directory in swift
let filePath = dataPath.stringByAppendingPathComponent(file);
let text = "some text"
//writing
text.writeToFile(filePath, atomically: false, encoding: NSUTF8StringEncoding, error: nil);
How to read file from document directory.
let filePath = dataPath.stringByAppendingPathComponent(file);
// Read file
let text2 = String(contentsOfFile: filePath, encoding: NSUTF8StringEncoding, error: nil)
Output :
Hope this will help you.

Resources