Unable to display Stickers from Documents directory in iMessage extension - ios

In my app users are able to create images and then use them as stickers in iMessages.
My problem is that I can't display created images that are stored in the Documents Directory.
My issue is very similar to this question - SO Question but in my case the solution stated there didn't help.
Here's my code for fetching images:
func getImages(finished: () -> Void) {
imageData.removeAll()
let imageNames = keyboardUserDefaults!.stringArray(forKey: "Created stickers")
for imageName in imageNames! {
// let imagePath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let filePath = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent(imageName)
// imageData.append(imagePath + "/" + imageName)
imageData.append(filePath)
}
print("Image Data - ", imageData)
finished()
}
And here is how I apply it to StickerView:
func configure(usingImageName imagePath: String) {
let urlToImage = URL(fileURLWithPath: imagePath)
do {
let description = NSLocalizedString("", comment: "")
let sticker = try MSSticker(contentsOfFileURL: urlToImage, localizedDescription: description)
print("Sicker is here - ", sticker)
stickerView.sticker = sticker
}
catch {
fatalError("Failed to create sticker: \(error)")
}
}
But I'm getting a blank view with no stickers.
Print inside cell shows:
Sicker is here - <MSSticker-<0x280393640> imageFileURL file:///var/mobile/Containers/Data/PluginKitPlugin/32AF9E44-F2F1-4FD1-80B5-E8E4B6C6E338/Documents/F54067B8-18DA-4737-8ED3-B716E368AF6E.png localizedDescription >
Images are showing when I set them from my folder inside Xcode project using Bundle.main.path, but not from the Documents Directory.

I have found source of my problem:
Application Extensions can't access default Documents Directory folder.
Solution:
Use App Groups and make path for image when saving and retrieving it like this:
let url = fileManager.containerURL(forSecurityApplicationGroupIdentifier: "YOUR_APP_GROUP_NAME")?.appendingPathComponent("image.png")
After that you can easily access your images in Application Extensions.
Hope this helps someone in future!

Related

Download and view document in ios from firebase storage

I have a firebase storage link of a document which I want to download in my ios device's Document directory and then view it from there using QuickLook Module.
I am able to download document in iOS device but it is not saving with the name i am providing.
I want to create following directory structure and then need to save document in "doc" folder.
Documents/chat/doc
following code is to create "/chat/doc" inside Iphone's Document directory. Where folderName will contain "/chat/doc".
func createDocument(folderName: String ,completion: #escaping(Bool,String)->()){
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first
if let documentsDirectory = path{
//
let docDirectoryPath = documentsDirectory.appending(folderName)
let fileManager = FileManager.default
if !fileManager.fileExists(atPath: docDirectoryPath) {
do {
try fileManager.createDirectory(atPath: docDirectoryPath,
withIntermediateDirectories: true,
attributes: nil)
//return path on success
completion(true,docDirectoryPath)
return
} catch {
print("Error creating folder in documents dir: \(error.localizedDescription)")
completion(false,error.localizedDescription)
return
}
}
completion(true,docDirectoryPath)
}
}
after creating folder i want to download document and need to store in newly created folder in Document Directory.
Following code will download document.
func downloadDocument(){
createDocument(folderName: "/chat/doc") { (status, path) in
if let docURL = self.documentURL{
if status{
//folder is created now download document
let docDownloadRef = Storage.storage().reference(forURL: docURL)
//get documents metadata from firebase to get document name
docDownloadRef.getMetadata { metadata, error in
if let error = error {
// Uh-oh, an error occurred!
} else {
//get document name from metadata
if let fileName = metadata?.name{
//create file system url to store document
let docsurl = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let myurl = docsurl.appendingPathComponent("chat/\(fileName)")
_ = docDownloadRef.write(toFile: myurl) { url, error in
if let error = error {
print(error.localizedDescription)
} else {
// Local file URL for document is returned
print("doc url \(url?.absoluteString)")
}
}
}
}
}
}else{
//folder is not created show document using online location
}
}
}
}
Ultimate goal is to achieve something like this "Documents/chat/doc/abc.doc"
but my code is not storing document using file name it stores like this "Documents/chat/doc" where doc is considered as a document not a folder.
Please help to let me know what I am doing wrong.
Thank you.
When you are creating url at below line, here you are missing the path for doc folder.
let myurl = docsurl.appendingPathComponent("chat/\(fileName)")
This will save to chat folder. You need to pass the same folder while creating i.e folderName: "/chat/doc" so it should be
let myurl = docsurl.appendingPathComponent("chat/doc/\(fileName)")

How can I show Stickers from Document directory in Swift

DispatchQueue.main.async
{
var x : Int = 0
for item in self.arrGifs
{
let gif : GIF = item as! GIF
let gifName = URL(string: gif.gifImage)?.lastPathComponent
// let ur
let pathUrl = URL.urlInDocumentsDirectory(with: "\(gifName!)").path
print("image path is =====>", pathUrl)
//RawImages.xcassets
// if let url = Bundle.main.url(forResource:"3", withExtension: "gif")
// {
print("url is ------>>>> ",url)
do
{
let sticker = try
// MSSticker(contentsOfFileURL: url,localizedDescription: "")
MSSticker(contentsOfFileURL: URL(string:pathUrl)!,localizedDescription: "")
self.arrSticker.append(sticker)
}
catch
{
print(error.localizedDescription)
}
// }
}
self.createStickerBrowser()
}
Well for the above " MSSticker(contentsOfFileURL: url,localizedDescription: "")" I can get stickers which are saved in Bundle. But If I give path of document directry, no sticker are shown.
I am downloading the imges from api, saving in my sqlite(image name) and images in document directry so that user can view it Offline. I find no solutions . Need help what is wrong and how to do it right.
Thank you.
file:///private/var/containers/Bundle/Application/F8FF754B-F012-4B75-AA2E-9FA71846E6AB/Migos%20Lingo.app/PlugIns/MigosLingo.appex/3.gif === this from Bundle and I can view as Sticker
/var/mobile/Containers/Data/PluginKitPlugin/DE11FB0E-89F9-4A65-917E-B4FEB8CA5470/Documents/111111-1508763742.gif ===> Document directry and I cannot view as Stickers
Can you try this.
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
After this make url because MSSticker create sticker with url path.Don't forget to append your image name with document path. after getting image path just do this.
let pathurl = URL(fileURLWithPath: imagePath!)
now you can creat sticker.
MSSticker(contentsOfFileURL: pathurl, localizedDescription: "hello world")

Swift: can't get array of images from url of folder?

Alright, Im fairly new to the url locating process (I always just use explicit names) and everyone is new to MSMessageStickers. However. I need to pull in an array of image urls to use as MSStickers from a folder of images I have copied into my MessagesExtension target in my project here (it is starterPack):
It may the Swift 3 syntax screwing this up or something else, but I CANT find any way to just get the right url of this folder and get all the images inside of it. The following is successful in making stickers out of PNGS with specific names:
for i in (1..<2) {
if let url = Bundle.main.url(forResource: "test\(i)", withExtension: "png") {
do {
//let sticker = try MSSticker(contentsOfFileURL: url, localizedDescription: "")
let sticker = try MSSticker(contentsOfFileURL: url, localizedDescription: "")
//print("SUCCESS", url)
stickers.append(sticker)
} catch {
print(error)
}
}
}
And I have adapted the following to Swift 3 from similar question How to get array of UIImage, from folder, in Swift? :
if let path = NSBundle.mainBundle().resourcePath {
let imagePath = path + "/images"
let url = NSURL(fileURLWithPath: imagePath)
let fileManager = NSFileManager.defaultManager()
let properties = [NSURLLocalizedNameKey,
NSURLCreationDateKey, NSURLLocalizedTypeDescriptionKey]
do {
let imageURLs = try fileManager.contentsOfDirectoryAtURL(url, includingPropertiesForKeys: properties, options:NSDirectoryEnumerationOptions.SkipsHiddenFiles)
print("image URLs: \(imageURLs)")
// Create image from URL
var myImage = UIImage(data: NSData(contentsOfURL: imageURLs[0])!)
} catch let error1 as NSError {
print(error1.description)
}
}
But because the folder is not technically in my project folder but rather in the messagesExtension folder as you can see, I think that is why it cant find it.
I need to bring in and get the url of all the images contained in my stickers folder. What am I doing wrong?

Save and get image from folder inside Documents folder on iOS wrong path

On application launch I'm creating a folder inside the Documents directory, if there is none there already. This works great!
I'm downloading some images and would like to save them to use them later.
My problem is that my code seems to store the files in a documents directory that is not the same that would be on the next app launch.
I know that since iOS8 the documents directory can change from launch to launch. So I'm always retrieving a path to the Documents folder. Could someone answer me why this code can't get the path to the image correctly?
func requestImage(let url: String,let isbn: String, numberInRow: Int){
var documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
documentsPath.appendContentsOf("/bookImages")
print("Image folder path is: \(documentsPath)")
///var/mobile/Containers/Data/Application/E6B66A15-F166-46FE-A577-9B0D911F5C92/Documents/bookImages
let pathComponent = isbn.stringByAppendingString(".jpg")
print("Suggested filename: \(pathComponent)") //1234567891234.jpg
let imagePath = documentsPath.stringByAppendingString("/\(pathComponent)")
print("Image to be saved at: \(imagePath)")
// /var/mobile/Containers/Data/Application/E6B66A15-F166-46FE-A577-9B0D911F5C92/Documents/bookImages/9788202350420.jpg
if (data != nil){
NSFileManager.defaultManager().createFileAtPath(imagePath, contents: data!, attributes: ["YES" : "NSURLIsExcludedFromBackupKey"])
self.books[numberInRow].setValue(pathComponent, forKey: "imageurl")
}
}
When I would like to display these images I have this in the view controller
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
let imageFolderPath = documentsPath.stringByAppendingString("/bookImages")
if let image = bookData.valueForKey("imageurl") as? String
{
print("Imagepath: \(self.imageFolderPath)/\(image)")
// /var/mobile/Containers/Data/Application/DB1F6FE9-1071-41A6-9E87-2A3D32ECD2B9/Documents/bookImages/9788202350420.jpg
let imagePath = self.imagePath.stringByAppendingString("/\(image)")
reuseableCell.bookCover.image = UIImage(contentsOfFile: imagePath)
}
I removed a lot of code that was not relevant. Why can't the image be displayed be found?
If anyone understand the error code, it is here:
BOMStream BOMStreamWithFileAndSys(int, off_t, size_t, int, char *, BomSys *): read: No such file or directory
Edit:
On application launch I'm searching for files in the Documents/bookImages and the files are there.
let paths: NSArray = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true)
if let documentDirectory = paths.firstObject{
do{
var path = documentDirectory as! String
path.appendContentsOf("/bookImages")
let documents = try NSFileManager.defaultManager().contentsOfDirectoryAtPath(path)
for files in documents {
let urlForm = NSURL.fileURLWithPath((path) + "/" + files)
do{
try print("\(files): \(urlForm.resourceValuesForKeys([NSURLIsExcludedFromBackupKey])), with filepath: \(urlForm)")
//Prints out folder and files in the desired location
} catch let error as NSError{
print("Can't find key: \(error)")
}
}
}catch let error as NSError{
print("Can't retrieve contents: \(error)")
}
}
9788202350420.jpg: ["NSURLIsExcludedFromBackupKey": 0], with filepath: file:///var/mobile/Containers/Data/Application/DB7BA523-6F75-42CF-92E6- ED2AF171D1AA/Documents/bookImages/9788202350420.jpg
9788203193538.jpg: ["NSURLIsExcludedFromBackupKey": 0], with filepath: file:///var/mobile/Containers/Data/Application/DB7BA523-6F75-42CF-92E6-ED2AF171D1AA/Documents/bookImages/9788203193538.jpg
9788203254703.jpg: ["NSURLIsExcludedFromBackupKey": 0], with filepath: file:///var/mobile/Containers/Data/Application/DB7BA523-6F75-42CF-92E6-ED2AF171D1AA/Documents/bookImages/9788203254703.jpg
I suspect the issue is happening because you didn't created the bookImages folder in the document directory. NSFileManager won't create the directories or sub-directories automatically.
// Creating directory
do
{
try NSFileManager.defaultManager().createDirectoryAtPath(documentsPath, withIntermediateDirectories: true, attributes: nil)
}
catch let error as NSError
{
NSLog("\(error.localizedDescription)")
}
// Saving image
if (data != nil)
{
// Also it would be better to check the file creation status
let status = NSFileManager.defaultManager().createFileAtPath(imagePath, contents: data!, attributes: ["NSURLIsExcludedFromBackupKey" : "YES"])
if status
{
// File created
self.books[numberInRow].setValue(pathComponent, forKey: "imageurl")
}
else
{
// File creation failed, update your question on stack overflow, someone will surely help you to find the issue :)
}
}
You can initialize your path variables as global variables at the top of your class, then just modify them within your requestImage method.
Then when you want to retrieve those images, you can just use the same variable name to ensure that it is the exact same path.
EDIT*
I think you may need to be reading some NSData instead. You can use UIImagePNGRepresentation or UIImageJPEGRepresentation to write your file to the documents directory. I found a tutorial here

How to get array of UIImage, from folder, in Swift?

I have an ordinary Xcode project like this ...
notice there's a folder (it is an actual folder - not just a group) named "images". It contains 25 ".png" images.
All I want to do is make an array of UIimage with each of those images.
(Or even, an array of the image names or similar, that would be fine - then could load them UIImage(named:)
How do I get at that folder "images"?? What about a subfolder "images/cars"?
I tried something like this but it finds nothing...
override func viewDidLoad()
{
let imageArray = NSBundle.mainBundle().URLsForResourcesWithExtension(
"png", subdirectory: "images")
print("test...")
for n:NSURL in imageArray!
{ print("found ..." ,n) }
}
We we assume the images are in the app's resource bundle. If not you need to make sure that your images directory is listed in the "Copy Bundle Resources" in the "Build Phases" of the target.
EDIT
This is only going copy the images into the app bundle, if you require the folder to be copied to the app bundle per the code below then please use the follow StackOverflow question to set it up correctly.
This gives us an array of URL's that we can then use with UIImage(data:) and NSData(contentsOfURL:) to create the image when needed.
Get the bundle's resource path and append the image directory then get the contents of the directory.
if let path = NSBundle.mainBundle().resourcePath {
let imagePath = path + "/images"
let url = NSURL(fileURLWithPath: imagePath)
let fileManager = NSFileManager.defaultManager()
let properties = [NSURLLocalizedNameKey,
NSURLCreationDateKey, NSURLLocalizedTypeDescriptionKey]
do {
let imageURLs = try fileManager.contentsOfDirectoryAtURL(url, includingPropertiesForKeys: properties, options:NSDirectoryEnumerationOptions.SkipsHiddenFiles)
print("image URLs: \(imageURLs)")
// Create image from URL
var myImage = UIImage(data: NSData(contentsOfURL: imageURLs[0])!)
} catch let error1 as NSError {
print(error1.description)
}
}
You can follow these steps to download them:
Create a new folder in finder and add all images (or folder, ...
everything).
Change folder name + ".bundle" (for example: YourListImage ->
YourListImage.bundle).
Add folder to project.
Add FileManager extension:
extension FileManager {
func getListFileNameInBundle(bundlePath: String) -> [String] {
let fileManager = FileManager.default
let bundleURL = Bundle.main.bundleURL
let assetURL = bundleURL.appendingPathComponent(bundlePath)
do {
let contents = try fileManager.contentsOfDirectory(at: assetURL, includingPropertiesForKeys: [URLResourceKey.nameKey, URLResourceKey.isDirectoryKey], options: .skipsHiddenFiles)
return contents.map{$0.lastPathComponent}
}
catch {
return []
}
}
func getImageInBundle(bundlePath: String) -> UIImage? {
let bundleURL = Bundle.main.bundleURL
let assetURL = bundleURL.appendingPathComponent(bundlePath)
return UIImage.init(contentsOfFile: assetURL.relativePath)
}
}
Use:
let fm = FileManager.default
let listImageName = fm.getListFileNameInBundle(bundlePath: "YourListImage.bundle")
for imgName in listImageName {
let image = fm.getImageInBundle(bundlePath: "YourListImage.bundle/\(imgName)")
}
Please try the following:
You have to register your images to "Copy Bundle Resources".
You have to add filter module in main Bundle.
enter image description here
It is working well on my side. Maybe you can change filter from "jpg" format into "png" one.
I've tested on iOS 10.x later, Swift 3.0 and xcode 8.1 version.
I would advise against loading all of your images into an array at once. Images tend to be large and it's easy to run out of memory and crash.
Unless you absolutely have to have all the images in memory at once it's better to keep an array of paths or URLs and load the images one at a time as needed.
Assuming the folder full of images is in your app bundle, you can use the NSBundle method URLsForResourcesWithExtension:subdirectory: to get an array of NSURLs to all the files in your images subdirectory, either with a specific filetype, or ALL files (if you pass nil for the extension.)
Once you have an array of file urls you can map it to an array of paths if needed, and then map that to an array of images.
Swift 4
if let path = Bundle.main.resourcePath {
let imagePath = path + "/images"
let url = NSURL(fileURLWithPath: imagePath)
let fileManager = FileManager.default
let properties = [URLResourceKey.localizedNameKey,
URLResourceKey.creationDateKey,
URLResourceKey.localizedTypeDescriptionKey]
do {
let imageURLs = try fileManager.contentsOfDirectory(at: url as URL, includingPropertiesForKeys: properties, options:FileManager.DirectoryEnumerationOptions.skipsHiddenFiles)
print("image URLs: \(imageURLs)")
// Create image from URL
let firstImageURL = imageURLs[0]
let firstImageData = try Data(contentsOf: firstImageURL)
let firstImage = UIImage(data: firstImageData)
// Do something with first image
} catch let error as NSError {
print(error.description)
}
}

Resources