I would like to create a function that gets the data in my firebase cloud storage. For example, I have 3 separate folders as follows: Movies, Songs and previews. I would like to create a function that will be able to download the url file of the specified folder to get the data of it so I can display it on the selection screen. I have the folders pretty organized. They're in this order, Movies/MovieName/{Movie.jpg (image for the movie), Movie.mp4(video)}. I need my function to open up "Movies" and run through the MovieName file and post each of the contents inside those files. So kinda like a streaming service, I do NOT want to download the url file permanently on the localfile. So kinda think of netflix where they have the image of the movie, with a little description, reviews, etc and it'll stream. You can never download the movie permanently.
"What have I tried?":
I've tried using the firebase link here to guide me but it seems like I'm not understanding it. I've also tried the "list all" list all link but I don't believe it shows the data of each file. Nor was it working for me. Finally I've tried using the URLSession.streamtask()
but I've never used that before and so I'll try to successfully use it now by researching more.
"Some code":
func getAlbums() {
storageref.downloadURL { (url, error) in
if let error = error {
print(error.localizedDescription)
}
else {
//get download url
}
}
According to the firebase link attached this is what I need to do but I don't know how to get the download URL after the else statement.
Related
I have a Flutter app that can view mp4 files from a URL. (Using a video controller playing directly from the URL.) I want the user to be able to share them if they wish. As best I can tell the file has to actually exist on the device so I have broken down the steps for now into download file, invoke share.
I'm using this guide: https://retroportalstudio.medium.com/saving-files-to-application-folder-and-gallery-in-flutter-e9be2ebee92a
I need to work on ios and android. The problem is that on ios neither the filename I get from the dio downloader nor the ImageGallerySaver seem to "work" when passed to the system ShareSheet.
I'm using the flutter extensions dio, share_plus, cross_file, image_gallery_saver as I've seen recommended in various places.
File saveFile = File(directory.path + "/$fileName");
developer.log("starting download...");
await dio.download(url, saveFile.path,
onReceiveProgress: (value1, value2) {
developer.log("got progress " + value1.toString());
setState(() {
downloadProgress = value1 / value2;
});
});
_permaFile = saveFile.path;
if (Platform.isIOS) {
var galleryResult = await ImageGallerySaver.saveFile(saveFile.path,
isReturnPathOfIOS: true);
developer.log("gallery save result = " + galleryResult.toString());
_permaFile = galleryResult['filePath'];
}
After getting a directory we use dio to download the file, do some log chirping, and then save the name to an object member called _permaFile.
Then the share button triggers:
void _shareAction() async {
final box = context.findRenderObject() as RenderBox?;
final files = <XFile>[];
if (_permaFile == null) {
return;
}
developer.log("sharing file: " + _permaFile.toString());
files.add(XFile(_permaFile!));
await Share.shareXFiles(files,
text: "Event",
// subject: "Subject for Event",
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size);
}
This works on android device... after I download I hit share, and I can share the video to a third-party app like WhatsApp.
On ios the ShareSheet is invoked but when I share I only get the text "Event", not the video file that goes along with it.
Note that I have tried both results... setting the _permaFile to be what comes back from ImageGallerySaver but also just using what the dio downloader gives back.
Note also that the ImageGallerySaver seems to work: the video really does land and is there in the ios video lib. If I go into the Photos app I can share from there to WhatsApp and have the video get sent.
In each case I get errors like this:
[ShareSheet] error fetching item for URL:file:/var/mobile/Media/DCIM/100APPLE/IMG_0021.MP4 -- file:/// : (null)
[ShareSheet] error fetching file provider domain for URL:file:/var/mobile/Media/DCIM/100APPLE/IMG_0021.MP4 -- file:/// : (null)
[ShareSheet] error loading metadata for
documentURL:file:/var/mobile/Media/DCIM/100APPLE/IMG_0021.MP4 --
file:/// error:Error Domain=NSFileProviderInternalErrorDomain Code=0
"No valid file provider found from URL
file:/var/mobile/Media/DCIM/100APPLE/IMG_0021.MP4 -- file:///."
UserInfo={NSLocalizedDescription=No valid file provider found from URL
file:/var/mobile/Media/DCIM/100APPLE/IMG_0021.MP4 -- file:///.}
In order to test this further I built the share_plus demo app:
https://github.com/fluttercommunity/plus_plugins/tree/main/packages/share_plus/share_plus
I modified it to share videos to see what was different. The share plus example (sp_example) works for sharing videos that have been selected by the picker.
For this reason I think the problem is something I'm missing about ios video filenames/formats and possibly a built-in conversion step that happens.
Here are what the filenames look like that I see in my app:
dio download result:
file:///var/mobile/Containers/Data/Application/223BF2B9-DDF0-490E-932F-09D5F03B98B3/Library/Caches/test.mp4
ImageGallerySaver result:
file:///var/mobile/Media/DCIM/100APPLE/IMG_0019.MP4
This is what video filenames look like when they are picked and shared in sp_example:
/private/var/mobile/Containers/Data/Application/E5CB4D7C-6CDF-4AA2-8134-C4322ED7C886/tmp/trim.E6633D68-44E3-4853-A29E-A71AC95A0913.MOV
Note that it has been converted to MOV extension and the user gets trim step right in the picker that results in trim in the name.
For my purposes I don't want to go through the picker, the user is on the screen showing the video and they shouldnt have to repick, so where do I get the post-conversion ios filename that references what I just saved?
Summary:
Mail appears to flatten document packages when sending them.
Background:
I'm using subclassed UIDocument and FileWrappers to provide a document package for storing metadata, data and a thumbnail preview for my documents. I've made the necessary UTI incantations to get the document packages working locally.
I've added sharing support via UIActivityViewController. I'm sending the document using its fileURL. In testing with round-tripping my document package everything seems to be working well: export and import work great... almost.
Problem:
With Mail attachments, the document package arrives in a semi-flattened state (import no longer sees it as the document package). On examination of the document (on my Mac in TextWrangler) there is still directory structure and I can see the metadata, data, thumbnail but FileManager doesn't see it.
I've tried sharing the Mail attachment back to my app (via Copy to App) before the email is sent and that works fine.
Question:
Is Mail doing something unusual when moving the document package across a mail server?
I'm hoping to eventually support Open in Place and Files so I don't really want to get into wrapping shared documents in a Data blob etc. Does anyone know what's happening here and how to handle it while keeping my document a package?
Update:
I've confirmed that Apple Mail is zipping the package but not changing the extension. So the file received is a zip of the original. How can I make sure Apple Mail sees this as a package and doesn't zip? And, if I can't, how can I recognize the file being imported is a zip without the zip extension?
Answer:
Mail is indeed zipping the document package.
According to my research, we don't need to alter the document-package approach (i.e. not have a flattened special export type). On import, we test if it's a directory. If it's not a directory then we try unzipping it from its source url instead of just copying it. It should be noted that unzipping needs to be handled through a third-party library.
I wish there was a built-in API for handling this but this seems to be the way to deal with it.
(Inelegant) example code:
// Check if directory
if let _ = try? FileManager.default.contentsOfDirectory(atPath: url.path) {
// It is - try copying to the documents url
do {
try FileManager.default.copyItem(at: url, to: destination)
} catch {
self.delegate?.documentFileManagerDidFail(error: NSLocalizedString("Failed to copy file: \(error)", comment: "Document failure message"))
return
}
} else {
// It's not - try unzipping to the documents url
do {
try FileManager.default.unzipItem(at: url, to: destination)
} catch {
self.delegate?.documentFileManagerDidFail(error: NSLocalizedString("Failed to unzip file: \(error)", comment: "Document failure message"))
return
}
}
I'm building an app for my school which includes a lunch menu, however since the lunch menu changes every month I don't want to keep updating my app just for that. I know you can open PDFs with Xcode either locally or online, but is there a way to change the url path of the pdf manually and have it update real time within the app. Any help would be much appreciated and I'm new to developing IOS apps, so any links that may help would be great.
If you control the website or have a line of communication with the person who does then you could agree that the latest menu always gets returned from example.com/current/lunch.pdf but really this should be set up as a GET request example.com/index.php?lunchmenu=current so that it is down to the website to respond with the file or the file location rather than the app's responsibility to guess the filename. So for example:
if let url = NSURL(string: "http://www.example.com/index.php?lunchmenu=current") {
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let dataTask = session.dataTaskWithURL(url, completionHandler: {(d,_,_) in
if let data = d {
String(data:data, encoding:NSUTF8StringEncoding) // this is could be your file name
}
})
dataTask.resume()
}
It would then be possible to perform other requests like: example.com/index.php?lunchmenu=nextmonth. Working in this way means that if for some reason the naming pattern or location of the files changed your app would still keep working.
If you don't have access to the school website but do know where the file is stored, you could query your own website for the file information and either update this automatically (or if there was no other way - manually). Again, this would prevent an app update if the structure of the school website changed.
Only rely on static file locations and naming systems if you really have to.
What I am trying to do is to save videos to PHPhotoLibrary, and then remove them when upload to clients remote server in the application completes (basically, photo library serves as temporary storage to add additional layer of security in case anything at all fails (I already save my vides it in the applications directory).
Problem:
The problem is for that to work, everything has to work without input from the user. You can write video to photos library like this:
func storeVideoToLibraryForUpload(upload : SMUpload) {
if PHPhotoLibrary.authorizationStatus() != PHAuthorizationStatus.Authorized {
// Don't write to library since this is disallowed by user
return
}
PHPhotoLibrary.sharedPhotoLibrary().performChanges({ () -> Void in
// Write asset
let assetRequest = PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(NSURL(fileURLWithPath: upload.nonsecureFilePath!)!)
let assetPlaceholder = assetRequest.placeholderForCreatedAsset
let localIdentifier = assetPlaceholder.localIdentifier
// Store local identifier for later use
upload.localAssetIdentifier = localIdentifier
}, completionHandler: { (success, error) -> Void in
....
})
}
And that works flawlessly, I get local identifier, I store it for later use.. Unicorns and rainbows.
Now when I want to remove that video immediately after upload finishes, I call following:
func removeVideoFromLibraryForUpload(upload : SMUpload) {
// Only proceed if there is asset identifier (video previously stored)
if let assetIdentifier = upload.localAssetIdentifier {
// Find asset that we previously stored
let assets = PHAsset.fetchAssetsWithLocalIdentifiers([assetIdentifier], options: PHFetchOptions())
// Fetch asset, if found, delete it
if let fetchedAssets = assets.firstObject as? PHAsset {
PHPhotoLibrary.sharedPhotoLibrary().performChanges({ () -> Void in
// Delete asset
PHAssetChangeRequest.deleteAssets([fetchedAssets])
}, completionHandler: { (success, error) -> Void in
...
})
}
}
}
Which successfully deletes the video, BUT user have to confirm deletion first. That is a problem as that backing up won't work.
I obviously know why there is confirmation (so you don't clear entire user library for example, but the thing is, My app made the video - and so I thought there will be way around it, since as an "owner" I should not be doing that, or at least have option to disable confirmation.
Thanks in advance!
TLDR: How can I disable confirmation on delete request, if my application created that content? (I don't want to delete anything else).
Note: Somebody can probably say this is rather strange thing to do but the application is distributed internally and there is good reason to do it like this (the video content is too valuable to be lost, even if user deletes the application for some reason, or there is anything at all that goes wrong, we need to be able to preserve the videos), so please don't question that and just focus your attention on the question :)
I cannot see a way to avoid the delete confirmation. It is an implementation detail of the Photos framework, similar to the way you cannot prevent the device from asking the user's permission to use the microphone when your app tries to use it, and is a matter of security & trust. Once you have saved an asset to the device photo library your app is no longer the owner of that asset, so as you noted in your question the device must of course ensure the app has the user's permission before it goes about deleting such data.
You can never entirely safeguard your users' data against their own unpredictable behaviour - if they decide to remove your app, or delete a particular asset from within Photos, it is up to them. I think your best option is to either put up with the built-in delete confirmation, or to provide a guide to your users that makes it clear that they should be careful to protect this important data by backing up their device, and not deleting the app!
If you did decide to stick to this approach, perhaps the best thing you could do is to prepare the user for the fact that their device may ask them for confirmation to delete a file that is being uploaded to your own servers. For example, put up your own modal alert just before trying to delete the asset. I wouldn't normally suggest that kind of approach for a public shipping app, but since you're only distributing internally it may be acceptable for your team.
I have made an iOS application using built.io. There are several files uploaded in the application. However, i am unable to get the list of all the files uploaded in the app. Could anyone please help?
This can be done via BuiltFile class instance method fetchAllOnSuccess:onError:
It returns all files uploaded in your built application only if the requesting user has permission for it.
Built* builtfileObj = [Built file];
[builtfileObj fetchAllOnSuccess:^(NSArray *allFiles) {
// allFiles contains array of BuiltFiles
} onError:^(NSError *error) {
// there was an error in creating the object
// error.userinfo contains more details regarding the same
}];
To fetch all the uploads, the iOS SDK provides an instance method in the BuiltFile class. It returns an array of all the uploads.
Here's the link to the official documentation
http://static.built.io/downloads/sdk-docs/ios-docs/Classes/BuiltFile.html#//api/name/fetchAllOnSuccess:onError:
I dont know if its available in the iOS SDK but you can use the REST API to fetch it
Headers :
"application_uid: uid"
"application_api_key: api_key"
URL: GET : https://manage.built.io/v1/uploads?skip=0&limit=50&include_count=true
Params : skip and limit for pagination and include_count is for getting the total count.
The above url will fetch all types of files
But if you want only images or videos then you can use the below urls
https://manage.built.io/v1/uploads/images
https://manage.built.io/v1/uploads/videos
I hope i was helpful to you!!
Note : without the headers the API wont work