Set image file from documents to UIImage

I'm trying to set a JPG or PNG image in my IOS documents folder. I've been trying to use the initializers "contentsOfFile" and "named." Here's my script at the moment:


            let myPng = filterd.first
            print (myPng!)        //the file path and name of the png
            let nameIndex = myPng?.characters.index(of: "V")        
            let nameWithPng = String(myPng!.characters.suffix(from: nameIndex!))         //returns the name of the file with the extension
            print (nameWithPng)         
            let pngIndex = nameWithPng.characters.index(of: ".")         
            let nameWOPng = String(nameWithPng.characters.prefix(upTo: pngIndex!))      //returns only the name of the file, no extension   
            print (nameWOPng)         
            let photo = UIImage.init(named: nameWOPng)         
            print (photo)         
            firstFrame.image = photo


Photo returns nil. The file it's trying to make a UIImage out of is a test PNG on my test phone. When I've tried 'contentsOfFile" I replace "nameWOPng" constant with "myPng." What am I doing wrong?

Answered by QuinceyMorris in 231377022

You don't use UIImage(named:) for this, because that's for loading assets (from your app bundle).


Don't use UIImage(contentsOfFile:) either, because it's a path-based API. There's no URL-based equivalent, which is an Apple clue that should be doing something else. In this case, use Data(contentsOf:options:) with a URL, which properly throws file system errors. Then use UIImage(data:) to convert the raw data to an image.


Generally, in modern iOS apps, you should avoid path-based APIs. If you use them, avoid using substrings to locate file system parts (such as file names and extensions). There are specific String methods for appending or removing file names and extensions.


Assuming "myPng" is a proper path to the file, URL(fileURLWithPath:myPng) will give you a URL for the file.

Accepted Answer

You don't use UIImage(named:) for this, because that's for loading assets (from your app bundle).


Don't use UIImage(contentsOfFile:) either, because it's a path-based API. There's no URL-based equivalent, which is an Apple clue that should be doing something else. In this case, use Data(contentsOf:options:) with a URL, which properly throws file system errors. Then use UIImage(data:) to convert the raw data to an image.


Generally, in modern iOS apps, you should avoid path-based APIs. If you use them, avoid using substrings to locate file system parts (such as file names and extensions). There are specific String methods for appending or removing file names and extensions.


Assuming "myPng" is a proper path to the file, URL(fileURLWithPath:myPng) will give you a URL for the file.

Thank you Quincy! Here's what I did:


            let myJpg = filterd.first         
            print (myJpg!)         
            let pngURL = URL.init(string: myJpg!)
            let data = try! Data.init(contentsOf: pngURL!)          
            let photo = UIImage.init(data: data)          
            print (photo)          
            firstFrame.image = photo


Now, you did put 'options' in the Data initializer, although I don't understand what that is. I looked for it in the Swift Documentation, but it didn't return anything. Do you think it's OK to do it the way I did things, or would it be beter to do it your way? What is "options?"

1. This isn't going to work, because "URL(string:)" creates a generalized URL, not a file URL. Since your path string is just a path, the URL has no explicit scheme, and when it's used it's probably going to default to "http", which isn't what you want.


2. Why are you writing out the initializer method name ("init") each time? It's at least unnecessary, and it may have undesirable side effects. (I don't know of any, but it's not the usual way of doing it.)


3. The data file reading options are described here (developer.apple.com/reference/foundation/nsdata.readingoptions), and I suggest you specify both ".mappedIfSafe" and ".uncached". Mapping the data avoids having to copy the data from permanent storage to RAM, and not caching avoids evicting other data from file system caches. In your code, the data is used exactly once and just for a short period of time (to create the UIImage), so you don't need any of the normal optimizations that keep file data hanging around in memory.


So:


     let myJpg = filterd.first!        // unwrap it here, to crash your app at the point of the error
     print (myJpg)         
     let pngURL = URL(fileURLWithPath: myJpg)
     let data = try Data(contentsOf: pngURL, options: [.mappedIfSafe, .uncached])      // propagate or handle this exception   
     let photo = UIImage(data: data)          
     print (photo)          
     firstFrame.image = photo


Note that I took out all the "!" operators except the first. I recommend you don't let them propagate even from one line to the next (as in lines 01-02 of your code). Also, you should really handle the exception in line 4, because reading from a file can fail for reasons outside your app. It's not a programming error within your app, so crashing is not the right choice.

Set image file from documents to UIImage
 
 
Q