How to get resource path for PNG files from Bundle? - ios

I'm trying to get a resource path of image files added to the project. The following code works fine for jpeg files. But it gives nil for PNG files. The name of the images are correct and added to the project.
Image files are added with the puppy.png and car.jpeg names.
let carPathURL = Bundle.main.path(forResource: "car", ofType: "jpeg") // Gives right path
let puppyPathURL = Bundle.main.path(forResource: "puppy", ofType: "png") // Gives nil
Below is the screenshot of the Application bundle contents.
The screenshot for the project is below showing the added files.
Why it is giving nil for PNG files? How to get the resource path for PNG files?

If extension is uppercased (ie, PNG, not png) you have to specify this explicitly
Bundle.main.path(forResource: "puppy", ofType: "PNG")

Related

Xcode 10: Load the same .xml file in project target and unit test target

I try to load the same .xml file in project target and unit test target.
It seems to be a similar problem like this question: Xcode. Image resources added to a test target are not copied into the tests bundle
In project target, this code snipped worked fine. I just have to add the "xmlString.xml" file to "Copy Files" (see image 1).
func parseFile() {
let stringPath = Bundle.main.path(forResource: "xmlString", ofType: "xml")
let url = NSURL(fileURLWithPath: stringPath!)
...
}
If I run the code snipped in unit test target, I get an error because the .xml file can not be found. I tried to add the .xml file to the "Copy Bundle Resources" without any success (see image 2).
The only way to get it working is to use the absolute path
func parseFile() {
let stringPath: String? = "/Users/.../Documents/Git/.../.../.../.../xmlString.xml"
let url = NSURL(fileURLWithPath: stringPath!)
...
}
Is there a way to use the Bundle.main.path(forResource: "xmlString", ofType: "xml") function instead of the absolute path?
Thanks in advance!
You can get the bundle for your project like this:
let projectBundle = Bundle(for: AnyClassInMyProject.self)
where you replace AnyClassInMyProject with the name of an actual class in your project.
You can then get the path to your .xml file like this:
let stringPath = projectBundle.path(forResource: "xmlString", ofType: "xml")

WKWebView load files from both Bundle and Document directory

I am running a webapp using WKWebView which load content and assets (html, js, img, css...) from a Bundle directory called Web_Assets.
I proceed like this to load index.html, and set Web_assets as baseURL:
if let filePath = Bundle.main.path(forResource:"index", ofType:"html", inDirectory: "Web_Assets") {
let html = try String(contentsOfFile: filePath, encoding: .utf8)
webView.loadHTMLString(html, baseURL: Bundle.main.resourceURL?.appendingPathComponent("Web_Assets"))
}
This works perfectly: index.html is loaded and Web_Assets serve as base path for other ressources without any problem.
I could also use the loadFileURL method which is equivalent (to my understanding).
BUT, now i'd like to use other ressources (video in my case) inside my WKWebView from the Document directory which is static and manageable via iTunes File Sharing, while of course keeping the present architecture as it is (Web assets inside Bundle directory).
For example my page index.html would load his style.css and other assets from Web_Assets and display an HTML5 video located in Document
Is it possible to do this ?
Ideas (which i don't know how to achieve):
Symlink of Document in Web_assets ?
Different baseURL for different file types in WKWebView ?
(route all requests to Web_Assets except for mp4 files requests which are routed to Document)
Thanks for your help !
EDIT 1:
Idea 1 is not possible since Bundle directories are read-only.
Document can't be symlinked in Web_assets.
I can't manage to symlink Web_assets into Document either (weird error).
So i decided for now to copy Web_assets inside Document on each boot, so my Document folder can act as baseURL with my videos and web assets altogether.
It's a bit dirty since all my web assets are now visible in my Document folder (but not modifiable since the folder is erased and copied from Bundle uppon App restart)
I finally end up as EDIT 1 and #ngbaanh suggested:
on viewDidLoad:
let filemgr = FileManager.default
docURL = filemgr.urls(for: .documentDirectory, in: .userDomainMask)[0]
destPath = docURL.path+"/www/"
sourcePath = Bundle.main.resourceURL!.appendingPathComponent("Web_Assets").path
//COPY Web_Assets content from Bundle to Documents/www
do {
try filemgr.removeItem(atPath: destPath)
} catch {
print("Error: \(error.localizedDescription)")
}
do {
try filemgr.copyItem(atPath: sourcePath, toPath: destPath)
} catch {
print("Error: \(error.localizedDescription)")
}
// Configure Webview
webView.navigationDelegate = self
webView.configuration.userContentController.add(self, name: "teleco")
do {
let baseURL = try filemgr.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:false)
let fileURL = baseURL.appendingPathComponent("www/index.html")
self.loadFileURL(fileURL, allowingReadAccessTo: baseURL)
} catch {
print(error)
}
This way my baseURL is Documents/www/
So in my index.html i can include ressources like style.css
AND i can also access files in Documents/ by using relative path like ../video.mp4
So index.html and style.css are distributed via the Bundle, copied to Documents at runtime, which allows me to access Documents files too.
A bit twisted, but works great.
Downside: it exposes Web_assets files in user accessible Documents folder.
But they can't be altered since they are replaced on App start.

Bundle.main.path(forResource:ofType:inDirectory:) returns nil when directory and filename are correct

let audioPath = Bundle.main.path(forResource: songs[indexPath.row], ofType: ".mp3", inDirectory: "Songs")!
Returns nil. songs[indexPath.row] returns the correct name, as it is exactly in the folder named "Songs". The songs folder is blue and shows up correctly in my build phases->copy bundle resources
I replied in the comment above but I should go to this place to upload the image and recap my opinion again:
make sure you removed the dot .: mp3 instead of .mp3
songs[indexPath.row] value must not be optional, because it can cause a string like this: Optional(MyFileName), and the value must not contains the extension .mp3, just filename only.
Look at the picture below, if you Bundle.main equivalence is not a selected target in the list of File Inspector, the audioPath may be nil. So you have to check them for all of your mp3 files.

pathForResource returns nil

I have two files, both named GoogleService-Info.plist. One is inside the folders Resources -> Staging and another is in the folders Resources -> Production. They're both added to Copy Bundle Resources. My code to access one of them is this:
if let path = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist", inDirectory: "Resources/Staging") {
print("this should work but doesn't")
}
It always returns nil. Why can it not find the file?
The resources will be put in the root of your app bundle. The only time you will get folders inside your app bundle is when you create folder references in your project. These will be blue folders, not yellow.
Change your code to (remove the inDirectory part):
if let path = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist") {
print("this should work but doesn't")
}

Read file in Xcode project with Swift

I made a method to read a file from the temporary folder. I use the following lines to get the document's path:
path = NSTemporaryDirectory().stringByAppendingPathComponent(folder)
path = path.stringByAppendingPathComponent(fileName + "." + fileType)
And it works great. My question is: how do I get the path for a file located in the main folders of the Xcode project? They are two text files that I drag and drop from a folder to the Xcode project. I selected the option "copy files if needed" when dropping the files. Thanks!
What you are looking for is app's main bundle. You get a path of a file in it like this:
if let path = NSBundle.mainBundle().pathForResource(fileName, ofType:fileType) {
// use path
}
Keep in mind this folder is read-only so you will need to copy the files to temp or documents folders if you want to make changes.
Update the Swift 3.1 - Thanks #Filip Radelic
if let path = Bundle.main.path(forResource: fileName, ofType: fileType) {
// use path
print(path)
}
Swift 4
if let path = Bundle.main.path(forResource: "imagename.png", ofType: nil) {
print("[check] FILE AVAILABLE \(path)")
}
You would use NSBundle to get your main bundle, then pathForResource(_ name: String?, ofType extension: String?) -> String?.

Resources