iOS - Share video on Facebook with share dialog - facebook

My app had an option to share an online video to Facebook using graph API and was working until August 2018. I just discovered that publish_action got deprecated and that we cannot use graph API in order to share videos on Facebook.
So I tried another solution: to download the video locally, copy it to assets and then use the old FBSDKShareDialog to share it(setting the url on iOS bellow 11 and setting the asset on iOS >= 11), but the post on Facebook appeared without the video, only the text.
Then I tried it using the new ShareDialog class and because it works only with the video URL, after I copied it to assets I could not get its asset URL, but its file URL so I got the following error in the logs:
"Error while sharing video content Error Domain=com.facebook.sdk.share Code=2 "(null)" UserInfo={com.facebook.sdk:FBSDKErrorArgumentValueKey=file:///var/mobile/Media/DCIM/105APPLE/IMG_5953.MP4, com.facebook.sdk:FBSDKErrorDeveloperMessageKey=Must refer to an asset file., com.facebook.sdk:FBSDKErrorArgumentNameKey=videoURL}".
Here is my code. Please tell me what should I change in order to share a video on Facebook. If I share the URL then it shows only the URL in the post and I cannot show the video bellow.
Thanks in advance!
private func downloadVideoForUploadOnFacebook(urlString: String, videoId: Int, isMyVideo: Bool, successHandler: #escaping () -> Void, failureHandler: #escaping (String) -> Void) {
guard let url = URL(string: urlString) else {delegate.stopActivityIndicator(); return}
let request = Alamofire.request(URLRequest(url: url))
request.responseData {[weak self] (response) in
if let data = response.result.value {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let videoURL = documentsURL.appendingPathComponent("video.mp4")
do {
try data.write(to: videoURL)
self?.showShareDialog(localVideoUrl: videoURL, completion: {
print("success")
successHandler()
}, failure: { (error) in
print(error)
failureHandler(error)
})
} catch {
print("Something went wrong!")
}
}
}.downloadProgress { (progress) in
print(progress)
}
}
private func showShareDialog(localVideoUrl: URL, completion: #escaping () -> Void, failure: #escaping (String) -> Void) {
guard let navigationController = UIApplication.shared.keyWindow?.rootViewController as? UINavigationController, let vc = navigationController.viewControllers.last else {return}
if PHPhotoLibrary.authorizationStatus() == .authorized {
showFacebookShareDialog(localVideoUrl: localVideoUrl, vc: vc, completion: completion, failure: failure)
} else {
PHPhotoLibrary.requestAuthorization({[weak self] (status) in
switch status {
case .authorized:
self?.showFacebookShareDialog(localVideoUrl: localVideoUrl, vc: vc, completion: completion, failure: failure)
default:
print(status)
failure("Authorization error")
}
})
}
}
private func showFacebookShareDialog(localVideoUrl: URL, vc: UIViewController, completion: #escaping () -> Void, failure: #escaping (String) -> Void) {
var placeHolder : PHObjectPlaceholder?
do {
try PHPhotoLibrary.shared().performChangesAndWait {
let creationRequest = PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: localVideoUrl )
placeHolder = creationRequest?.placeholderForCreatedAsset
}
}
catch {
print(error)
failure(error.localizedDescription)
}
let result = PHAsset.fetchAssets(withLocalIdentifiers: [placeHolder!.localIdentifier], options: nil)
if let phAsset = result.firstObject {
getURL(ofPhotoWith: phAsset, completionHandler: {[weak self] (url) in
guard let url = url else {return}
self?.showNewDialog(localVideoUrl: url, vc: vc, completion: completion, failure: failure)
// if #available(iOS 11.0, *) {
// self?.showOldDialog(asset: phAsset, vc: vc)
// } else {
// self?.showOldDialog(localVideoUrl: url, vc: vc)
// }
})
} else {
failure("Error at assets")
}
}
func showOldDialog(localVideoUrl: URL, vc: UIViewController) {
let video = FBSDKShareVideo()
video.videoURL = localVideoUrl
let content = FBSDKShareVideoContent()
content.video = video
let shareDialog: FBSDKShareDialog = FBSDKShareDialog()
shareDialog.shareContent = content
shareDialog.fromViewController = vc
DispatchQueue.main.async {
shareDialog.show()
}
}
func showOldDialog(asset: PHAsset, vc: UIViewController) {
let video = FBSDKShareVideo()
video.videoAsset = asset
let content = FBSDKShareVideoContent()
content.video = video
let shareDialog: FBSDKShareDialog = FBSDKShareDialog()
shareDialog.shareContent = content
shareDialog.fromViewController = vc
DispatchQueue.main.async {
shareDialog.show()
}
}
func showNewDialog(localVideoUrl: URL, vc: UIViewController, completion: #escaping () -> Void, failure: #escaping (String) -> Void) {
let video = FacebookShare.Video(url: localVideoUrl)
let content = VideoShareContent(video: video)
let shareDialog = ShareDialog(content: content)
shareDialog.mode = .native
shareDialog.failsOnInvalidData = true
shareDialog.presentingViewController = vc
shareDialog.completion = { result in
switch result {
case .success:
print("Share succeeded")
completion()
case .failed:
print(result)
failure("Error while sharing on facebook")
case .cancelled:
print("Share cancelled")
}
}
DispatchQueue.main.async {
do {
try shareDialog.show()
} catch {
print(error)
failure("Error while sharing video content \(error)")
}
}
}

Related

Download pdf from base64 url webview swift

I am trying to download a pdf from a url in a webview to the device. here is what the base64 endpoint looks like data:application/pdf;base64,JVBERi0xLjMKJf////8KOCAwIG9iago8PAovVHlwZSAvRXh0R1N0YXRlCi9jYSAxCj4+CmVuZG9iagoxMiAwIG9iago8 ... JSVFT0YK
Problem now is getting the data and downloading to show Activity view controller
private func downloadData(from url: URL, fileName: String, completion: #escaping (Bool, URL?) -> Void) {
webView.configuration.websiteDataStore.httpCookieStore.getAllCookies() { cookies in
let session = URLSession.shared
session.configuration.httpCookieStorage?.setCookies(cookies, for: url, mainDocumentURL: nil)
let task = session.downloadTask(with: url) { localURL, urlResponse, error in
if let localURL = localURL {
let destinationURL = self.moveDownloadedFile(url: localURL, fileName: fileName)
completion(true, destinationURL)
}
else {
completion(false, nil)
}
}
task.resume()
}
}
in the navigationResponse I have this but the my break point is not triggered.
if let data = Data(base64Encoded: url.absoluteString, options: .ignoreUnknownCharacters) {
print("THE MINETYPE data: \(data)")
}

using the image assigned to userprofile in firebase and attachine it to profile page

I have been looking online everywhere to help me out here but I cannot figure this out. The code below says that allows for the image to get stored in the firebase database when the user registers:
func uploadProfileImage(_ image: UIImage, completion: #escaping ((_ url: URL?)->())){
guard let uid = Auth.auth().currentUser?.uid else {return}
let storageRef = Storage.storage().reference().child("user/\(uid)")
guard let imageData = image.jpegData(compressionQuality: 0.75 ) else {return}
let metaData = StorageMetadata()
metaData.contentType = "image/jpg."
storageRef.putData(imageData, metadata: metaData) { metaData, error in
if error == nil, metaData != nil {
storageRef.downloadURL { url, error in
completion(url)
// success!
}
} else {
// failed
completion(nil)
}
}
}
func saveProfile(name: String, email: String, profileImageURL: URL, completion: #escaping ((_ success:Bool)->())){
guard let uid = Auth.auth().currentUser?.uid else {return}
let databaseRef = Database.database().reference().child("users/profile/\(uid)")
let userObject = ["name":name, "email":email,
"photoURL":profileImageURL.absoluteString] as [String:Any]
databaseRef.setValue(userObject) {error, ref in completion(error == nil)}
}
}
This code in a different .swift file allows me to get the photo:
static let cache = NSCache<NSString, UIImage>()
static func downloadImage(withURL url:URL, completion: #escaping (_ image:UIImage?, _ url:URL)->()) {
let dataTask = URLSession.shared.dataTask(with: url) { data, responseURL, error in
var downloadedImage:UIImage?
if let data = data {
downloadedImage = UIImage(data: data)
}
if downloadedImage != nil {
cache.setObject(downloadedImage!, forKey: url.absoluteString as NSString)
}
DispatchQueue.main.async {
completion(downloadedImage, url)
}
}
dataTask.resume()
}
static func getImage(withURL url:URL, completion: #escaping (_ image:UIImage?, _ url:URL)->()) {
if let image = cache.object(forKey: url.absoluteString as NSString) {
completion(image, url)
} else {
downloadImage(withURL: url, completion: completion)
}
}
user profile code:
class UserProfile {
var uid:String
var name:String
var photoURL:URL
init(uid:String, name:String, photoURL:URL){
self.uid = uid
self.name = name
self.photoURL = photoURL
}
}
I have added a file that will allow me to get the data from firebase:
class ImageServices{
static let cache = NSCache<NSString, UIImage>()
static func downloadImage(withURL url:URL, completion: #escaping (_ image:UIImage?, _ url:URL)->()) {
let dataTask = URLSession.shared.dataTask(with: url) { data, responseURL, error in
var downloadedImage:UIImage?
if let data = data {
downloadedImage = UIImage(data: data)
}
if downloadedImage != nil {
cache.setObject(downloadedImage!, forKey: url.absoluteString as NSString)
}
DispatchQueue.main.async {
completion(downloadedImage, url)
}
}
dataTask.resume()
}
static func getImage(withURL url:URL, completion: #escaping (_ image:UIImage?, _ url:URL)->()) {
if let image = cache.object(forKey: url.absoluteString as NSString) {
completion(image, url)
} else {
downloadImage(withURL: url, completion: completion)
}
}
}
Now how do I use this in my viewController to actually show the image on the imageView???
The last edit to the question asks this
Now how do I use this in my viewController to actually show the image
on the imageView???
This indicates you have a viewController that contains an imageView as you're asking how to assign the downloaded image to that imageView.
The image is downloaded here in your code
static func downloadImage(withURL...
var downloadedImage:UIImage
if let data = data {
downloadedImage = UIImage(data: data)
so a single line of code following that will take the downloadedImage and display it in the imageView
self.myImageView.image = downloadedImage

Different behavior in a function when I use it in a buttonTapped function or in AV fileOutput to upload a video

I want to upload a video, which was recorded by AV.
It works, when I use my API function inside the func fileOutput().
var urlToUpload: URL?
/// - Tag: DidFinishRecording
func fileOutput(_ output: AVCaptureFileOutput,
didFinishRecordingTo outputFileURL: URL,
from connections: [AVCaptureConnection],
error: Error?) {
urlToUpload = outputFileURL
PostApi.shared.uploadTOFireBaseVideo(url: urlToUpload!, userUid: "huhu", success: {
}) {
}
However, when I call it like this:
// Upload a post to Firestore
#IBAction func buttonSendPostTapped(_ sender: RoundedButton) {
print(urlToUpload!)
-> (Optional(file:///private/var/mobile/Containers/Data/Application/60C494F1-2C08-476A-81C2-052A51987682/tmp/517523BC-B84B-460D-81C9-2D8FDB4051BA.mov))
PostApi.shared.uploadTOFireBaseVideo(url: urlToUpload!, userUid: "huhu", success: {
}) {
}
}
It will NOT work.
How I upload the data:
let data = NSData(contentsOf: ur as URL) <- "data" is nil, when I call it from the buttonTapped and works correctly, if I call it from the fileOutput().
Why is my data nil, when I call it from a buttonTapped?
// Upload media to firestore
func uploadTOFireBaseVideo(url: URL, userUid: String, success : #escaping () -> Void, failure : #escaping () -> Void) {
let db = Firestore.firestore()
let name = "\(Int(Date().timeIntervalSince1970)).mp4"
let dispatchgroup = DispatchGroup()
dispatchgroup.enter()
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let outputurl = documentsURL.appendingPathComponent(name)
var ur = outputurl
print(ur)
self.convertVideo(toMPEG4FormatForVideo: url as URL, outputURL: outputurl) { (session) in
ur = session.outputURL!
dispatchgroup.leave()
}
dispatchgroup.wait()
let data = NSData(contentsOf: ur as URL)
let storageRef = Storage.storage().reference().child("Media").child(name)
if let uploadData = data as Data? {
print("if let uploaded data")
storageRef.putData(uploadData, metadata: nil) { (metadata, error) in
if error != nil {
print(error!)
return
}
storageRef.downloadURL(completion: { (url, error) in
if error != nil {
print(error!)
return
}
let mediaUrl = url?.absoluteString
let dic = ["userUid" : userUid, "postMediaUrl" : mediaUrl ?? "No Media"] as [String : Any]
db.collection("posts-media").document(userUid).setData(dic)
{ err in
if let err = err {
print("Error adding document: \(err)")
failure()
return
} else {
print("Document added")
success()
}
}
})
}
}
}
// Additional function to convert a video to mp4
func convertVideo(toMPEG4FormatForVideo inputURL: URL, outputURL: URL, handler: #escaping (AVAssetExportSession) -> Void) {
let asset = AVURLAsset(url: inputURL as URL, options: nil)
let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality)!
exportSession.outputURL = outputURL
exportSession.outputFileType = .mp4
exportSession.exportAsynchronously(completionHandler: {
handler(exportSession)
})
}
file:///private/var/mobile/Containers/Data/Application/60C494F1-2C08-476A-81C2-052A51987682/tmp/517523BC-B84B-460D-81C9-2D8FDB4051BA.mov
Optional(file:///private/var/mobile/Containers/Data/Application/60C494F1-2C08-476A-81C2-052A51987682/tmp/517523BC-B84B-460D-81C9-2D8FDB4051BA.mov)
Check the implementation of the fileOutput(...) method further down. It's internal cleanup() routine is called when the control flow leaves the method, which deletes the temporary video file. That means in your buttonSendPostTapped method the video file probably no longer exist.
You need to make sure, that fileOutput(...) keeps the file stored at a save location and delete if yourself when you are done with it.

Why doesn't image load on main thread?

I'm using this code to load an image from a URL: Loading/Downloading image from URL on Swift. That is working fine.
I've take the code out of the viewcontroller and put it into a class. Now the image only loads when I step through the code. I'm guessing this has something to do with the code running fine but everything finishes executing before the image has completely downloaded from the web.
What piece of this am I missing so the code waits for the image without holding up any other code executing in the viewcontroller?
class MyClass:NSObject {
private var myImage:UIImage = UIImage()
func getMyImage() -> UIImage {
if let url = URL(string: self.myUrl) {
self.myImage = self.downloadImage(url: url)
}
return self.myImage
}
func getDataFromUrl(url: URL, completion: #escaping (Data?, URLResponse?, Error?) -> ()) {
URLSession.shared.dataTask(with: url) { data, response, error in
completion(data, response, error)
}.resume()
}
func downloadImage(url: URL) {
print("Download Started")
getDataFromUrl(url: url) { data, response, error in
guard let data = data, error == nil else { return }
print(response?.suggestedFilename ?? url.lastPathComponent)
print("Download Finished")
DispatchQueue.main.async() {
self.myImage = UIImage(data: data)!
}
}
}
}
//in ViewController
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DispatchQueue.main.async() {
self.theImageView.image = self.myClass?.getImage()
}
}
You have to set up the imageView's image once the asynchronous call back is completed.
class MyClass: NSObject {
// async function
func getMyImage(completion: #escaping (UIImage?) -> Void) {
// if the url is not available, completion with null
guard let url = URL(string: self.myUrl) else {
completion(nil)
return
}
getDataFromUrl(url: url) { data, response, error in
// if something goes wrong, completion with null
guard let data = data, error == nil else {
completion(nil)
return
}
print(response?.suggestedFilename ?? url.lastPathComponent)
print("Download Finished")
completion(UIImage(data: data)!)
}
}
}
// in ViewController
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
getImage { image in
// async function will get called when image is ready
DispatchQueue.main.async() { // set up your view in main thread
self.theImageView.image = image
}
}
}

Uploading an image into a Web Service from an ImageView

I'm trying to upload an image taken by the device camera, or selected from the device Gallery, later present it at the screen with an ImageView, and at last upload it to a Rest API. I already have a default image stored in the Rest API and my App is already loading this image, but when I try to change the image I have issues.
This is my code after the Post API call, the data of my JSON:
let defaultServiceResponse = data["defaultServiceResponse"] as! NSDictionary
self.idResponse = defaultServiceResponse["idResponse"] as! Int
let loginModel = LoginModel()
if self.idResponse == 0 {
let userInfo = data["userInfo"] as! NSDictionary
loginModel.imageProfileUrl = userInfo["imageProfileUrl"] as! String
let url = URL(string: loginModel.imageProfileUrl)
let data = try? Data(contentsOf: url!)
self.userImage.image = UIImage(data: data!)!
Then I have a second class where I'm trying to upload the image:
class PhotoViewController: UIViewController, UIImagePickerControllerDelegate,
UINavigationControllerDelegate {
#IBOutlet weak var imagePicked: UIImageView!
#IBAction func openCameraButton(sender: AnyObject) {
if UIImagePickerController.isSourceTypeAvailable(.camera) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .camera;
imagePicker.allowsEditing = false
self.present(imagePicker, animated: true, completion: nil)
}
}
#IBAction func openPhotoLibraryButton(sender: AnyObject) {
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .photoLibrary;
imagePicker.allowsEditing = true
self.present(imagePicker, animated: true, completion: nil)
}
}
private func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
imagePicked.image = image
dismiss(animated:true, completion: nil)
}
#IBAction func saveButt(sender: AnyObject) {
let imageData = UIImageJPEGRepresentation(imagePicked.image!, 0.6)
let compressedJPGImage = UIImage(data: imageData!)
UIImageWriteToSavedPhotosAlbum(compressedJPGImage!, nil, nil, nil)
let alert = UIAlertView(title: "Wow",
message: "Your image has been saved to Photo Library!",
delegate: nil,
cancelButtonTitle: "Ok")
alert.show()
}
When I tap the openCamara button and openLibrary button it does the correct function each time, but when I choose the photo (either from the Camera or the Gallery) it doesn't appear anything at the ImageView, and the error I receive is: "Creating an image format with an unknown type is an error
"
And I'm not sending back the image into the Rest API because I haven't any idea of how can I do that.
Can somebody help me showing where is the error that is not letting me show the picture at the screen on the ImageView?
And if it's posible can somebody show me the best way to return that image into my Rest API?
#Zita Noriega Estrada
this code will remove your all errors.
func isValidData(_ result: Result<Any>, completion: #escaping (_ : NSDictionary?, _ : Bool?, _ : NSError?) -> Void) {
self.getValidDict(result, completion: {(dict, error) in
var success = false
var errorNew = error
if dict != nil {
success = ((dict?["dataKey"] as AnyObject).boolValue)!
if !success {
errorNew = NSError(domain: "", code: 400, userInfo: [NSLocalizedDescriptionKey: dict?.value(forKey: "messageKey")! as Any])
}
}
completion (dict, success, errorNew)
})
}
func getValidDict(_ result: Result<Any>, completion: #escaping (_ : NSDictionary?, _ : NSError?) -> Void) {
var dict: NSDictionary!
let errorNew = result.error as NSError?
if let json = result.value {
dict = (json as AnyObject).value(forKey: "responseKey") as! NSDictionary
}
completion (dict, errorNew)
}
Use Alamofire to upload image to server.
Try this code:
func uploadImage {
uploadImage(image: image, completion: { (success, error) in
if success! {
} else {
}
})
}
func uploadImage(_ image: UIImage!, completion: #escaping (_ : Bool?, _: NSError?) -> Void) {
let parameters: Parameters = ["image": image]
Alamofire.upload(multipartFormData: {
multipartFormData in
// For Image
for (key, value) in parameters {
if value != nil {
if let imageData = UIImageJPEGRepresentation(value!, 0.5) {
multipartFormData.append(imageData, withName: key, fileName: "file.png", mimeType: "image/png")
}
}
}
}, to: "apiName", method: .post, encodingCompletion: {
encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.response {
[weak self] response in
guard self != nil
else {
return
}
upload.responseJSON(completionHandler: { response in
self?.isValidData(response.result, completion: {(dict, success, error) in
completion (success, error)
})
})
}
case .failure(let encodingError):
print("error:\(encodingError)")
completion (false, encodingError as NSError?)
}
})
}