How to I hide specific file in File App of Device - ios

First of all I know this is a subjective question, not code based, How ever I need to find solution. Please provide any references
I am working on a task in which I saving the files in Device Document directory. Upto here all is working fine. However when I see these files from :
Files App -> App Folder and the files
I can see all those files.
Now I want to hide few of them, How can I achieve these....?

As per your requirement. You need to change some lines of code in saving file.
Firstly, The FileApp shows all the data that save in documentsDirectory of app whether you put code for hide or not. To hide few files you need to make separate path for them.
First, the file you want to show in documentDirectory:
let documentsDirectory = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)).last! as URL
The file you don’t want to show with user, you need to create path here:
let libraryDirectory = (FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask)).last! as URL
Once you set the library path for saving all your important files. All will be hidden and cannot be accessed by user.
To know more about files, please refer to this link:
https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html

Xcode 9 • Swift 4 or Xcode 8 • Swift 3
extension URL {
var isHidden: Bool {
get {
return (try? resourceValues(forKeys: [.isHiddenKey]))?.isHidden == true
}
set {
var resourceValues = URLResourceValues()
resourceValues.isHidden = newValue
do {
try setResourceValues(resourceValues)
} catch {
print("isHidden error:", error)
}
}
}
}
for more reference see chain here Cocoa Swift, get/set hidden flag on files and directories
or
https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/ManagingFIlesandDirectories/ManagingFIlesandDirectories.html

Related

FileManager can't locate PDF directory in Xcode project

I am trying to list all PDFs inside a folder that is stored in my Xcode project using SwiftUI, but after trying many different methods I found on here I cannot get it working.
Currently I am using file manager to list the items but when I try and print or list the items found it returns nil.
I have added the PDFs by dragging them into the Xcode project. I have also made sure that they are in my Copy Bundle Resource so I can use FileManager. See below:
Here is my Xcode structure, I am trying to list all items inside PDF. As you can see below the PDFs are stored outside the main folder structure in "/Swift/Products/Detail/Tab5/PDF", so when trying to list the files using Bundle.main.bundlePath, it looks at products.app.
Here is the code where I am trying to use FileManager to find all PDFs inside the folder:
struct ProductTab5View: View {
#State var files = getFiles()
var body: some View {
VStack{
ForEach(0..<files.count, id: \.self) { item in
Text(files[item])
}
}
}
}
func getFiles() -> Array<String>{
// Get document directory url
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
var files: [String] = []
do {
// Get the directory contents urls (including subfolders urls)
let directoryContents = try FileManager.default.contentsOfDirectory(at: documentsUrl, includingPropertiesForKeys: nil)
print(directoryContents)
// filter directory contents:
let pdfFiles = directoryContents.filter{ $0.pathExtension == "pdf" }
let pdfFileNames = pdfFiles.map{ $0.deletingPathExtension().lastPathComponent }
files.append(contentsOf: pdfFileNames)
} catch {
print(error)
}
return files
}
In such a way resources are copied flat into root application bundle, not user 's Documents as it was tried in provided code snapshot (and no PDF sub-directory is created for you).
So the solution would be just to iterate internal pdf files, like
func getFiles() -> Array<String> {
return Bundle.main.urls(forResourcesWithExtension: "pdf", subdirectory: nil)?
.compactMap { $0.lastPathComponent } ?? []
}
Tested with Xcode 12.1 / iOS 14.1
There are a few potential problems with your current code.
The first is this line:
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
Unless you have gone through a process separately from what you've shown of adding all of these files to the app's document directory, then there's no reason to believe that these files will be there. In other words: your Xcode project is not the same as your app's document directory. Think of the app's document directory as a place where you may store user-created or perhaps downloaded content as managed by the app -- not Xcode itself.
Next thing to check is whether all of these files are truly added to your target. Check your target's "Build Phases" -> "Copy Bundle Resources" section to see if they appear there.
If they do, you can use FileManager, but you have to access the correct directory, which is inside the main bundle -- not the app's user document directory.
The following answer goes into details about this (including making sure you create folder references): Getting a list of files in the Resources folder - iOS
The gist will be doing something like this:
if let files = try? FileManager.default.contentsOfDirectory(atPath: Bundle.main.bundlePath) { //may need to dig into subdirectories
for file in files {
//manipulate the file
}
}
and then using FileManager to list the documents in that path.
Note that you can also get all the files (including subdirectories) recursively by doing something like this:
let url = URL(fileURLWithPath: Bundle.main.bundlePath)
if let enumerator = FileManager.default.enumerator(at: url, includingPropertiesForKeys: [.isRegularFileKey], options: [.skipsHiddenFiles, .skipsPackageDescendants]) {
for case let fileURL as URL in enumerator {
print(fileURL)
}
}
By using the above code during your debugging process, this should give you an insight into what your directory structure is really like inside your bundle. Once you've figured that out, adjust the previous code sample to be something like:
if let files = try? FileManager.default.contentsOfDirectory(atPath: Bundle.main.bundlePath + mySubPath) {
for file in files {
//manipulate the file
}
}
which will give you just the files in the one subdirectory you want once you fill in mySubPath. Note that if you want recursive search, you can use the code sample above.
You may want to exclude non-file items eventually -- see this answer for more details about recursive directory lists in Swift: listing all files in a folder recursively with swift

SCNParticleSystem load from document Directory

I am trying load SCNParticleSystem from download bundle which i am not able to load.
Path for the resource.
file:///var/mobile/Containers/Data/Application/A91E9970-CDE1-43D8-B822-4B61EFC6149B/Documents/so/solarsystem.bundle/Contents/Resources/
let objScene = SCNParticleSystem(named: "stars", inDirectory: directory)
This object is nil.
This is a legitimate problem since SceneKit does not provide an out-of-the-box solution for initializing particle systems from files that are outside of the main bundle (the only init method SCNParticleSystem.init(named:inDirectory:) implies that SCNParticleSystem.scnp files are in the main bundle).
Luckily for us .scnp files are just encoded/archived SCNParticleSystem instances that we can easily decode/unarchive using NSKeyedUnarchiver:
extension SCNParticleSystem {
static func make(fromFileAt url: URL) -> SCNParticleSystem? {
guard let data = try? Data(contentsOf: url),
let object = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data),
let system = object as? SCNParticleSystem else { return nil }
return system
}
}
If you do not need to support iOS 9 and iOS 10 you can use NSKeyedUnarchiver.unarchivedObject(ofClass: SCNParticleSystem.self, from: data) instead of NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(_:) and type casting, which was introduced in iOS 11.0.
Another issue that you're most likely to encounter is missing particle images. That is because by default SceneKit will look for them in the main bundle. As of current versions of iOS (which is iOS 12) and Xcode (Xcode 10) particle images in .scnp files (particleImage property) are String values which are texture filenames in the main bundle (that might change, but probably won't, however there's not much else we could use).
So my suggestion is to take that filename and look for the texture file with the same name in the same directory where the .scnp file is:
extension SCNParticleSystem {
static func make(fromFileAt url: URL) -> SCNParticleSystem? {
guard let data = try? Data(contentsOf: url),
let object = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data),
let system = object as? SCNParticleSystem else { return nil }
if let particleImageName = system.particleImage as? String {
let particleImageURL = url
.deletingLastPathComponent()
.appendingPathComponent(particleImageName)
if FileManager.default.fileExists(atPath: particleImageURL.path) {
system.particleImage = particleImageURL
}
}
return system
}
}
You can just set the URL of the image file and SceneKit will handle it from there.
As a little side-note, the recommended directory for downloadable content is Application Support directory, not Documents.
Application Support: 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.
(from File System Basics)
Don't have enough reps to add the comment so adding it as the answer.
The answer by Lësha Turkowski works for sure but was had issues with loading the particle images using only NSURL.
All particles were appearing square which meant,
If the value is nil (the default), SceneKit renders each particle as a
small white square (colorized by the particleColor property).
SCNParticleSystem particleImage
In the documentation it says You may specify an image using an
NSImage (in macOS) or UIImage (in iOS) instance, or an NSString or
NSURL instance containing the path or URL to an image file.
Instead of using the NSURL, ended up using the UIImage and it loaded up fine.
extension SCNParticleSystem {
static func make(fromFileAt url: URL) -> SCNParticleSystem? {
guard let data = try? Data(contentsOf: url),
let object = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data),
let system = object as? SCNParticleSystem else { return nil }
if let particleImageName = system.particleImage as? String {
let particleImageURL = url
.deletingLastPathComponent()
.appendingPathComponent(particleImageName)
if FileManager.default.fileExists(atPath: particleImageURL.path) {
// load up the NSURL contents in UIImage
let particleUIImage = UIImage(contentsOfFile: particleImageURL.path)
system.particleImage = particleUIImage
}
}
return system
}
}
I found out, that sometimes when dragging a SCNParticleSystem file into your project (probably form a different project) a silent error can happen due to some bugs in Xcode. As a result you can't get a reference to an instance of your SCNParticleSystem.
Solution: Check your BuildSettings in your target. The SCNPaticleSystem AND the associated ImageFile should be listed there and then you should get it right. (see screenShot below)

Reading/Writing data that is accessible from iOS app extension

I'm working on an iOS Content Blocker, but I would like to have the user choose what lists they want to block (ads, tracks, adult sites etc.). I found that the app extension and the containing app's bundles are separate and have no access to each other's files, so a shared container is needed. I created an app group, but it seems like what I write there does not actually get written. What I am attempting to do is read a .json file from the bundle, and then write it to a sharedJson.json file that the content blocker extension can access.
func writeJsonToSharedJson(arrayOfStrings:[String]) -> Bool {
let composedString = arrayOfStrings.joined(separator: "\n")
let sharedJsonPath = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.alexspear.BlockerTestGroup")?.appendingPathComponent("sharedJson.json")
//let sharedJsonPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("sharedJson.json")
do {
try composedString.write(to: sharedJsonPath!, atomically: true, encoding: .utf8)
}
catch {
print("Could not write to sharedJson.json\n")
return false
}
return verifyJsonWrite()
}
The result is that through the verifyJsonWrite() function, there is nothing there. Am I incorrect in assuming you can create a file in the app group container? I have also tried using FileManager's createFile function with the same result.

Why are the audio files I've added to my xcode project available in the simulator but are not available when i deploy the app on iPhone

I am using an AVQueuePlayer to play local audio files which I have added to my Xcode project by dragging and dropping the .mp3 files into my folder in the project navigator pane. I then use this code to search thru my files and extract the files that I want, which I then use to fill a table view and to create AVPlayerItems for to play in my AVQueuePlayer.
My code works fine when I run the app on simulator but when i run the app on my iPhone, an error occurs and returns
fatal error: unexpectedly found nil while unwrapping an Optional value
Here is the code causing the issue...
var songNameArray: Array<String> = [String]()
let fileManager = NSFileManager.defaultManager()
let enumerator:NSDirectoryEnumerator = fileManager.enumeratorAtPath("/Users/UsersName/Desktop/Xcode Projects Folder/LocalAudioFilePlayer/LocalAudioFilePlayer")!
while let element = enumerator.nextObject() as? String {
if element.hasSuffix("mp3") {
songNameArray.append(element)
print(element)
}
}
Are the files not being properly copied into the bundle which is then deployed to the iPhone? If so, what is the proper way to add the audio files?
I also tried this...
var songNameArray: Array<String> = [String]()
let path = String(NSBundle.mainBundle().resourcePath)
let songFiles = try! NSFileManager.defaultManager().contentsOfDirectoryAtPath(path)
for item in songFiles {
if item.hasSuffix("mp3"){
songNameArray.append(item)
print("appended to songNameArray \(item)")
}
}
But I get the error
The folder “LocalAudioFilePlayer.app")” doesn’t exist
Any help is greatly appreciated
let path = String(NSBundle.mainBundle().resourcePath)
This line is not doing what you might think it does. NSBundle.mainBundle().resourcePath returns String?, which is an optional type. When you create a string from that by wrapping String() around it, it doesn't unwrap the optional, it creates a string describing the optional, which looks like this:
Optional("/Users/markbes/Library/Developer/CoreSimulator/Devices/18D62628-5F8A-4277-9045-C6DE740078CA/data/Containers/Bundle/Application/3DDC064C-0DD1-4BE9-8EA4-C151B65ED1E1/Resources.app")
What you want there is something more like
var path = NSBundle.mainBundle().resourcePath!
That unwraps the String? type, and gives you a string (or throws an error).
Did you select "Copy files if needed" then dragged mp3s into Project?
Try to load files as following:
let path = NSBundle.mainBundle().pathForResource("filename", ofType: "ext")
let url = NSURL.fileURLWithPath(path!)
var audioPlayer: AVAudioPlayer?
do {
try audioPlayer = AVAudioPlayer(contentsOfURL: url)
} catch {
print("Unable to load file")
}
Note: use only filename here, not the whole path.
It sounds like you haven't added your sound file to your target in the Xcode project. If they don't "belong" to a target, they won't get copied into the "resources" folder during building.
Select the sound files you want to use in the project navigator, and check the file inspector (view->Utilities->File Inspector, and make sure that the appropriate target is selected under "target membership".
edit: this is definitely not the problem in this case. Leaving this answer, as it may be useful for others running into a similar issue.

Curious whether fileUrl of CKAsset can change with time?

I am caching CKRecord on client and fileUrl of CKAsset too. Can fileUrl change from time to time? Asset / data itself is not changing.
The fileURL of an asset will not change, but if this is an asset you downloaded from the server the data is only guaranteed to exist at that location until the completion block of the operation is called. After that point the asset's backing file may be cleaned up at any time to free up disk space.
After downloading an asset from the server you should move or copy the backing file to another location in your application's container if you'd like to keep it.
The archived URL is a full path, but since iOS 8 or so the container URL that your app uses to store its files changes everytime the app starts.
So the archived URL is wrong.
This seems like a bug in the way CKAssets are archived to me, they should be archiving and unarchiving the partial path relative to the app's container. Not the full path.
I guess I'll file a radar.
Edit: here's a solution:
extension CKAsset {
var url: URL? {
let path = fileURL.path
if FileManager.default.fileExists(atPath: path) {
return fileURL
} else if let index = path.range(of: "/Library/Caches/") {
// archived CKAssets store full-path URLs but since iOS 8 or so
// the path of our container (home-dir) changes every time we are
// started. So the full path is useless. Try to mangle it.
let suffix = String(path.suffix(from: index.upperBound))
let adjustedUrl = URL.caches/suffix
if FileManager.default.fileExists(atPath: adjustedUrl.path) {
return adjustedUrl
}
}
return nil
}
}
public extension URL {
static var caches: URL {
return FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first ?? URL(fileURLWithPath: "")
}
}

Resources