Update Firebase database object with multiple uploads - swift

I have a post object that has two parts (1)photo and (2)video. In the posting flow the user selects and image, then hits next to the next screen where they select a video. Both the photo and the video are uploaded to Firebase as part of a post object. See the simple design.
wireframe design
I'm getting stuck creating the post object with both the photo url and the video url. I've tried:
1- After selecting the photo and moving to the next step, I upload the photo to Firebase storage and create the post object in the database with the photo url and an empty string for the video url.
2-Then after selecting the video, I upload the video to Firebase storage, and try to update the existing post object with the video url...
However I have not been able to update the correct post object, and don't know how I can retrieve the postByAutoID in order to update the correct post.
Here is what the database object looks like:
I tried to pass the photoURL forward to the second part of the post, but the upload and generation of the url is not complete by the time the variable gets passed.
Any help is appreciated!

With web, or JavaScript, uploads to firebase cloud storage return promises, therefore Promise.all an array of individual uploads.
As for swift, or IOS, you will have to push each upload (.putData) to memory then iterate/loop over them.
See Upload Files on iOS which provides a starting point and add your loop/iteration.
// Data in memory
let data = Data()
// Create a reference to the file you want to upload
let riversRef = storageRef.child("images/rivers.jpg")
// Upload the file to the path "images/rivers.jpg"
let uploadTask = riversRef.putData(data, metadata: nil) { (metadata, error) in
guard let metadata = metadata else {
// Uh-oh, an error occurred!
return
}
// Metadata contains file metadata such as size, content-type.
let size = metadata.size
// You can also access to download URL after upload.
riversRef.downloadURL { (url, error) in
guard let downloadURL = url else {
// Uh-oh, an error occurred!
return
}
}
}

Related

How can I upload post without photo in this code? (Flutter&Firebase)

I made code which upload the post on Firebase.
This code is the upload code.
but I don't know how to upload a post with no photo.
When posts include photos, I can upload them to firebase, but I can't upload without a photo.
code link: https://github.com/HighschoolStudentDeveloperYoon/Q1
In your _uploadFile method first check if the file variable has content;
if (_image != null) {
//put file to your storage and retrieve downloadURL
}
& if it doesn't have any content just pass null to your Firestore 'photoUrl' key.

Reading/Writing tags or keywords using Apple Photos SDK

Does anybody know if Apple’s Photos SDK allows us to fetch, or even modify tags on assets stored in the user’s Photos library?
From the official docs I understand that the SDK allows us to change the « favorite » status, show/hide assets, or even find an asset’s location, but I couldn’t find a way to extract a photo’s tags (keywords) stored by the macOS Photos app.
Is it a feature lacking from the current Photos SDK, or is there any way I can access these metadata?
Thanks
Here is a sample how you can get Metadata using CIImage.properties from PHAsset.
fileprivate extension PHAsset {
func printMetadata() {
let options = PHContentEditingInputRequestOptions()
requestContentEditingInput(with: options) { (contentEditingInput: PHContentEditingInput?, _) -> Void in
let img = CIImage(contentsOf: contentEditingInput!.fullSizeImageURL!)
print(img?.properties)
}
}
}
For iCloud stored images add the following line:
options.isNetworkAccessAllowed = true
I haven't found Image metadata in PHAsset as well.
For writing CIImage metadata you can try CIImage.settingProperties(_:) method.

Firebase Offline Support: upload posts while user is offline and sync when user comes online in iOS Swift app

I am using firebase in an iOS-Swift project in which I have to enable offline support for uploading posts, in the post there is a picture and caption just like Instagram, so what I want is when user is offline and he/she wants to upload a post, his/her picture get saved in cache and when user comes online that photo get uploaded and give back a download url that we can use for saving posts-details it in database.
sample code is:
let photoIDString = UUID().uuidString
let storageRef = Storage.storage().reference(forURL: "storage ref URL").child("posts").child(photoIDString)
storageRef.putData(imageData, metadata: nil, completion: { (metadata, error) in
guard let metadata = metadata else {
return
}
if error != nil {
return
}
storageRef.downloadURL(completion: { ( url, error ) in
guard let downloadURL = url else {
return
}
let photoUrl = downloadURL.absoluteString
self.sendDataToDatabase(photoUrl: photoUrl)
})
}
)
I want to know what changes should I make in my code to provide the offline capability. Any code snippet will help more.
The problem is better view as re-send to server when there is an error.
For your offline case, you can check if the error return is a network error, or manually check network connection availability.
You can create a re-send array of object
e.g
var resendList : [YourObjectType]
// when failed to send to server
resendList.append(yourFailedObject)
And then, 2 solutions:
Check the network connectivity manually and reupload in when the app become active in func applicationDidBecomeActive(_ application: UIApplication) in appDelegate. For checking connectivity you can try the method here: Check for internet connection with Swift But this has a problem that, the user has to go out the app and back again with network connected
Keep track(listen to notification) on the connectivity change, using a suggestion method by https://stackoverflow.com/a/27310748/4919289 and reupload it to server
and loop through all objects in resendList and re-upload again.
I am not an iOS developer, but I can share logical flow and some references.
When user clicks on upload: Check if network is available?
if yes: upload the post.
if no:
save the post to app storage or offline database
set broadcast receiver to receive broadcast when device comes online. This link may be helpful.
upload post when device comes online.
If you are looking for solution that is offered by Firebase, you may find more details here.
Firebase offers you plenty of ways to do this in their documentation. https://firebase.google.com/docs/database/ios/offline-capabilities
When uploading to the firebase server, it will queue itself and wait until it has a internet connection again to upload. If this happens to timeout or you want to do it your own way just attempt to upload with a completionHandler on the setValue or updateChild functions - if not successfully and the error message is because of internet, add it to a local cache to the phone with the data and the path to the firebase server.
onLoad, attempt the same upload again until it succeeds, once it succeeds - clear the local cache.

CloudKit CKShare URL Goes Nowhere

I have successfully saved a CKShare URL to CloudKit, and I can see that the user is INVITED in the CloudKit Dashboard. My Mac app emailed the URL to that person, but when they click it, all they see it this screen on icloud.com:
Clicking OK makes everything disappear so all you see is the background on the web page.
My understanding is that the URL is supposed to open my Mac app where it will fire userDidAcceptCloudKitShareWith in my app delegate. But it does nothing.
Could this be because my app is in development and not in the Mac App Store yet? Do I need a custom URL scheme to get it to open my app?
Documentation on this stuff is pretty sparse. I'd love any help someone can provide.
I have since learned that you must specify a fallback URL for your CloudKit container. In cases where the app isn't installed (or isn't recognized, which seems to be the case when doing dev builds in Xcode like I am), CloudKit will forward share URL to somewhere you specify. They append the unique share ID to the URL so that you can process it on your own web page.
In the CloudKit dashboard, go to Environment Settings... and you'll see this popup:
I have it redirect to https://myapp.com/share/?id= and on my web page where it redirects to, I do a $_GET['id'] to grab the id. I then do another redirect to my application using a custom URL scheme and pass the share ID (e.g. myapp://abc123 where abc123 is the share ID).
In my app delegate, I receive the URL like this:
func application(_ application: NSApplication, open urls: [URL]) {
if let url = urls.first, let shareId = url.host{
fetchShare(shareId) //<-- sharedId = abc123
}
}
I then use CKFetchShareMetadataOperation to look up the URL of the share and CKAcceptSharesOperation to accept it like this:
func fetchShare(shareId: String){
if let url = URL(string: "https://www.icloud.com/share/\(shareId)"){
let operation = CKFetchShareMetadataOperation(shareURLs: [url])
operation.perShareMetadataBlock = { url, metadata, error in
if let metadata = metadata{
//:::
acceptShare(metadata: metadata)
}
}
operation.fetchShareMetadataCompletionBlock = { error in
if let error = error{
print("fetch Share error: \(error)")
}
}
CKContainer.default().add(operation)
}
}
func acceptShare(metadata: CKShareMetadata){
let operation = CKAcceptSharesOperation(shareMetadatas: [metadata])
operation.acceptSharesCompletionBlock = { error in
if let error = error{
print("accept share error: \(error)")
}else{
//Share accepted!
}
}
CKContainer.default().add(operation)
}
I think there are easier ways to work through this using NSItemProvider and NSSharingService, but I'm doing a lot of custom UI and wanted to have full control of the share workflow.
I hope this helps someone. :)

Get a PHAsset from ReferenceURL: fetchAssets(withALAssetURLs:options:) DEPRECATED

My goal is to upload original resolution device captured video files to S3 using AWSS3TransferUtility.
User selects a video using UIImagePickerController however, if I pass info[UIImagePickerControllerMEDIAURL] to the transferUtility it always reduces the video to 720p. If I pass the transferutility info[UIImagePickerControllerREFERENCEURL] I get an Error:
The operation couldn’t be completed.
(com.amazonaws.AWSS3TransferUtilityErrorDomain error 4.)
Cannot find an explanation of the error code so I am assuming it is a permissions error because I am not accessing the asset via the Photos framework or PHAsset.fetch
REFERNCE URL -
assets-library://asset/asset.mov?id=5B99DC8E-B94E-4CBF-AFB8-7F82BC72FEE2&ext=mov
MEDIA URL -
file:///private/var/mobile/Containers/Data/Application/76928AD7-F142-4CC9-9708-A58C8CAF8EE5/tmp/trim.82038B6A-222F-4B50-A937-A8C399B02A08.MOV
Now, I have the ReferenceURL, and am trying to get the PHAsset so that I can copy it the Documents Directory and pass the URL of it to TransferUtility from there to get around the AWSS3TransferUtilityErrorDomain error 4.
However, fetchAssets(withALAssetURLs:options:) is now deprecated so I cannot fetch the asset with the ReferenceURL; all the other methods are batch fetching via media type, etc. There is fetchAssets(withLocalIdentifiers:options:) but I am not sure how to get the local identifier of a PHAsset just from the URL.
If you need PHAsset of selected item, you can get it directly from info in UIImagePickerController callback
let asset = info[.phAsset] as? PHAsset
if it is not there, then you did not request authorization before using picker, so do this
let status = PHPhotoLibrary.authorizationStatus()
if status == .notDetermined {
PHPhotoLibrary.requestAuthorization({status in
})
}
Note: localIdentifier is a property of PHObject, and PHAsset is a subclass of PHObject.