I am trying to share a video from a URL using UIActivityViewController.
If I try with an Image I have not problems.
Share Image Works:
let imageThump = NSURL(string: "https://example.com/image.png")
let imageData = NSData(contentsOfURL: imageThump!)
let objectsToShare = [comment!, imageData!]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
Share Video is not working. Why?
let videoShare = NSURL(string: "https://example.com/video.mp4")
let videoData = NSData(contentsOfURL: videoShare!)
let objectsToShare = [comment!, videoData!]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
THIS IS HOW YOU DO IT ...
guard let urlData = NSData(contentsOfURL: NSURL(string:"https://example.com/video.mov)")!) else {
return
}
print(urlData)
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let docDirectory = paths[0]
let filePath = "\(docDirectory)/tmpVideo.mov"
urlData?.writeToFile(filePath, atomically: true)
// File Saved
let videoLink = NSURL(fileURLWithPath: filePath)
let objectsToShare = [videoLink] //comment!, imageData!, myWebsite!]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
activityVC.setValue("Video", forKey: "subject")
// New Excluded Activities Code
if #available(iOS 9.0, *) {
activityVC.excludedActivityTypes = [UIActivityTypeAirDrop, UIActivityTypeAddToReadingList, UIActivityTypeAssignToContact, UIActivityTypeCopyToPasteboard, UIActivityTypeMail, UIActivityTypeMessage, UIActivityTypeOpenInIBooks, UIActivityTypePostToTencentWeibo, UIActivityTypePostToVimeo, UIActivityTypePostToWeibo, UIActivityTypePrint]
} else {
// Fallback on earlier versions
activityVC.excludedActivityTypes = [UIActivityTypeAirDrop, UIActivityTypeAddToReadingList, UIActivityTypeAssignToContact, UIActivityTypeCopyToPasteboard, UIActivityTypeMail, UIActivityTypeMessage, UIActivityTypePostToTencentWeibo, UIActivityTypePostToVimeo, UIActivityTypePostToWeibo, UIActivityTypePrint ]
}
self.presentViewController(activityVC, animated: true, completion: nil)
Remember that a video is usually a big file, so it'll take some time for it to be downloaded and saved. Here I used part of the first answer but adding some GCD. You'll get better results.
This is how I did it to avoid the App to stay frozen until the video is downloaded and saved:
let url = NSURL(string: "https://example.com/video.mov")
//Show activity indicator
DispatchQueue.global(qos: .background).async {
guard let urlData = NSData(contentsOf: url) else { return }
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let filePath="\(documentsPath)/tempFile.mov"
DispatchQueue.main.async {
urlData.write(toFile: filePath, atomically: true)
//Hide activity indicator
let activityVC = UIActivityViewController(activityItems: [NSURL(fileURLWithPath: filePath)], applicationActivities: nil)
activityVC.excludedActivityTypes = [.addToReadingList, .assignToContact]
self.present(activityVC, animated: true, completion: nil)
}
}
It is not working because this class does not allow NSData to be passed to the Facebook activity type.
"UIActivityTypePostToFacebook
The object posts the provided content to the user’s wall on Facebook.
When using this service, you can provide NSString, NSAttributedString, UIImage, ALAsset, and NSURL objects as data for the activity items. You may also specify NSURL objects whose contents use the assets-library scheme."
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIActivity_Class/index.html#//apple_ref/doc/constant_group/Built_in_Activity_Types
I have been trying to get this to work as well. Haven't figured out how to get it to work; however, this is why your code is not working.
Related
I'm trying to download video preview base on remote video URL. In my project, the server cannot return snapshot image of the videos, that's why I have to do it manually.
In table view, I have code like this in cellForItemAt to get video preview
DataManager.sharedInstance.getCachedImage(url: movie.url!, handler: { (image) in
cell.ivCover.image = image
})
and my getCachedImage function in DataManager:
func getCachedImage(url: String, handler: #escaping(_ result:UIImage) -> Void){
DispatchQueue.global().async {
let userDefaults = UserDefaults.standard
if let imageData = userDefaults.object(forKey: url){
if let finalImg = UIImage(data: imageData as! Data){
DispatchQueue.main.async {
handler(finalImg)
}
print("USING CACHED IMG")
}else{
DispatchQueue.main.async {
handler(#imageLiteral(resourceName: "cover"))
}
print("Cannot parse cached data to image. Use default")
}
}else{
let asset = AVURLAsset(url: URL(string: url)!)
let generate = AVAssetImageGenerator(asset: asset)
generate.appliesPreferredTrackTransform = true
var thumbTime = asset.duration
thumbTime.value = 1
var imgRef:CGImage?
do{
print("Downloading thum from url: \(url)")
imgRef = try generate.copyCGImage(at: thumbTime, actualTime: nil)
}catch let error{
print("Error download thum: \(error.localizedDescription)")
}
var finalImg:UIImage
if let _ = imgRef{
finalImg = UIImage(cgImage: imgRef!)
userDefaults.set(UIImagePNGRepresentation(finalImg), forKey: url)
}else{
finalImg = #imageLiteral(resourceName: "cover")
print("Download thumnail failed. Use default")
}
DispatchQueue.main.async {
handler(finalImg)
}
}
}
}
The problem is that, sometimes I scroll the Collection view, UI is freezed, sometimes it's not. Please note that this video is on REMOTE SERVER, NOT local video.
I've spent days to figure out the issue but still not able to find out what went wrong. Please help!
Or is there any existing library I can use?
import UIKit
import Photos
class ImportViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func myButton(_ sender: Any) {
let videoURL = "https://youerdomin.com/file.mp4"
print("1")
DispatchQueue.global(qos: .background).async {
print("2")
if let url = URL(string: videoURL), let urlData = NSData(contentsOf: url) {
print("3")
let documentsPath =
NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0];
let filePath="\(documentsPath)/tempFile.mp4"
DispatchQueue.main.async {
print("4")
urlData.write(toFile: filePath, atomically: true)
PHPhotoLibrary.shared().performChanges({
print("5")
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: URL(fileURLWithPath: filePath))
}) { completed, error in
print("6")
if completed {
print("Video is saved!")
}else{
print("7: ",filePath)
}
}
}
}else{
print("8")
}
}
}
}
I have a function in swift UIview that makes 2 mp4`s loop. The first mp4 is playing fine but the second one is not, it only seems to play once, how could i fix this?
let videoURL: NSURL = NSBundle.mainBundle().URLForResource(instrumentaimp4[skaicius], withExtension: "mp4")!
let sakeleURL: NSURL = NSBundle.mainBundle().URLForResource("sakele_blikas", withExtension: "mp4")!
player = AVPlayer(URL: videoURL)
player?.actionAtItemEnd = .None
player?.muted = true
sakele = AVPlayer(URL: sakeleURL)
sakele?.actionAtItemEnd = .None
sakele?.muted = true
let playerLayer = AVPlayerLayer(player: player)
playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
playerLayer.zPosition = 1
let playerLayer2 = AVPlayerLayer(player: sakele)
playerLayer2.videoGravity = AVLayerVideoGravityResizeAspectFill
playerLayer2.zPosition = 1
view.layer.addSublayer(playerLayer2)
view.layer.addSublayer(playerLayer)
player?.play()
sakele?.play()
//loop video
NSNotificationCenter.defaultCenter().addObserver(self,
selector: "loopVideo:",
name: AVPlayerItemDidPlayToEndTimeNotification,
object:nil)
func loopVideo(notification: NSNotification) {
if let finishedPlayer = notification.object as! AVPlayerItem!{
if finishedPlayer == self.sakele {
self.sakele?.seekToTime(kCMTimeZero)
self.sakele?.play()
NSLog("1")
}else{
self.player?.seekToTime(kCMTimeZero)
self.player?.play()
NSLog("2")}
}}
NSLog ("2") never happens. Where is my mistake? any help apreciated
Your sakele is an AVPlayer. But your notification.object is claimed to be (and is cast to) an AVPlayerItem, which is then assigned to finishedPlayer. This makes no sense. When you come to compare if finishedPlayer == self.sakele, it can never succeed, because they are not the same kind of object, let alone the same object.
Here`s my code:
let fileName = "someFileName"
func saveDataToFile(urlStr:String){
let url = NSURL(string: urlStr)
var data:NSData!
let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
let directory = paths[0]
let filePath = directory.stringByAppendingPathComponent(self.fileName)
print(filePath)//prints /Users/.../Library/Developer/CoreSimulator/Devices/1013B940-6FAB-406B-96FD-1774C670A91E/data/Containers/Data/Application/2F7139D6-C137-48BF-96F6-7579821B17B7/Documents/fileName
let fileManager = NSFileManager.defaultManager()
data = NSData(contentsOfURL: url!)
print(data) // prints a lot of data
if data != nil{
fileManager.createFileAtPath(filePath, contents: data, attributes: nil)
}
}
Now I want to read this data:
func readDataFromFile(){
let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
let directory = paths[0]
let filePath = directory.stringByAppendingPathComponent(self.fileName)
print(filePath) // prints the same path
let fileManager = NSFileManager.defaultManager()
if fileManager.fileExistsAtPath(filePath){
data = fileManager.contentsAtPath(filePath)
}else{
print("*****BAD*****") // always prints this which means that file wasn`t created
}
}
What`s wrong with the first func? What is the right way to save file to DocumentDirectory?
OK, in this case the answer was following:
First need to create directory (aka folder) and only after that create file inside that directory.
Added to code this:
let fullDirPath = directory.stringByAppendingPathComponent(folderName)
let filePath = fullDirPath.stringByAppendingPathComponent(fileName)
do{
try fileManager.createDirectoryAtPath(fullDirPath, withIntermediateDirectories: false, attributes: nil)
}catch let error as NSError{
print(error.localizedDescription)
}
And as I said after this you create your file:
fileManager.createFileAtPath(filePath, contents: data, attributes: nil)
Thanks to Eric.D
Hope someone will find this useful.
I am trying to read/display an image from Firebase. I am first encoding the image and then posting this encoded String to Firebase. This runs fine. When I try and decode the encoded string from Firebase and convert it to an image, I am getting a nil value exception.
This is how I am saving the image to Firebase
var base64String: NSString!
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
self.dismissViewControllerAnimated(true, completion: nil)
imageToPost.image = image
var uploadImage = image as! UIImage
var imageData = UIImagePNGRepresentation(uploadImage)!
self.base64String = imageData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
let ref = Firebase(url: "https://XXX.firebaseio.com")
var quoteString = ["string": self.base64String]
var usersRef = ref.childByAppendingPath("goalImages")
var users = ["image": quoteString]
usersRef.setValue(users)
displayAlert("Image Posted", message: "Your image has been successfully posted!")
}
This is how I am trying to read the image from Firebase
// ViewController.swift
import UIKit
import Firebase
class ViewController: UIViewController {
#IBOutlet weak var image: UIImageView!
var base64String: NSString!
#IBAction func buttonClicked(sender: AnyObject) {
sender.setTitle("\(sender.tag)", forState: UIControlState.Normal)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let ref = Firebase(url: "https://XXX.firebaseio.com/goalImages/image/string")
ref.observeEventType(.Value, withBlock: { snapshot in
self.base64String = snapshot.value as! NSString
let decodedData = NSData(base64EncodedString: self.base64String as String, options: NSDataBase64DecodingOptions())
//Next line is giving the error
var decodedImage = UIImage(data: decodedData!)
self.image.image = decodedImage
}, withCancelBlock: { error in
print(error.description)
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The error says: "fatal error: unexpectedly found nil while unwrapping an Optional value"; decodedData is nil. Could someone explain what is going wrong.
Instead of
let decodedData = NSData(base64EncodedString: self.base64String as String,
options: NSDataBase64DecodingOptions())
try adding IgnoreUnknownCharacters
NSDataBase64DecodingOptions.IgnoreUnknownCharacters
Use Example: Encode a jpg, store and read from firebase
encode and write our favorite starship
if let image = NSImage(named:"Enterprise.jpeg") {
let imageData = image.TIFFRepresentation
let base64String = imageData!.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
let imageRef = myRootRef.childByAppendingPath("image_path")
imageRef.setValue(base64String)
read and decode
imageRef.observeEventType(.Value, withBlock: { snapshot in
let base64EncodedString = snapshot.value
let imageData = NSData(base64EncodedString: base64EncodedString as! String,
options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters)
let decodedImage = NSImage(data:imageData!)
self.myImageView.image = decodedImage
}, withCancelBlock: { error in
print(error.description)
})
EDIT 2019_05_17
Update to Swift 5 and Firebase 6
func writeImage() {
if let image = NSImage(named:"Enterprise.jpg") {
let imageData = image.tiffRepresentation
if let base64String = imageData?.base64EncodedString() {
let imageRef = self.ref.child("image_path")
imageRef.setValue(base64String)
}
}
}
func readImage() {
let imageRef = self.ref.child("image_path")
imageRef.observeSingleEvent(of: .value, with: { snapshot in
let base64EncodedString = snapshot.value as! String
let imageData = Data(base64Encoded: base64EncodedString, options: Data.Base64DecodingOptions.ignoreUnknownCharacters)!
let decodedImage = NSImage(data: imageData)
self.myImageView.image = decodedImage
})
}
Firebase Engineer here:
I highly recommend using the new Firebase Storage API for uploading images to Firebase. It's simple to use, low cost, and backed by Google Cloud Storage for huge scale.
You can upload from NSData or an NSURL pointing to a local file (I'll show NSData, but the principle is the same):
// Data in memory
let data: NSData = ...
// 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
if (error != nil) {
// Uh-oh, an error occurred!
} else {
// Metadata contains file metadata such as size, content-type, and download URL.
let downloadURL = metadata!.downloadURL
// This can be stored in the Firebase Realtime Database
// It can also be used by image loading libraries like SDWebImage
}
}
You can even pause and resume uploads, and you can easily monitor uploads for progress:
// Upload data
let uploadTask = storageRef.putData(...)
// Add a progress observer to an upload task
uploadTask.observeStatus(.Progress) { snapshot in
// Upload reported progress
if let progress = snapshot.progress {
let percentComplete = 100.0 * Double(progress.completedUnitCount) / Double(progress.totalUnitCount)
}
}
All ,
I need to create a sample audio player and recorder, its not erroring but its not playing and perhaps not recording.. any ideas ?
#IBAction func Play(AnyObject)
{
println ("Play")
let path = NSBundle.mainBundle().pathForResource("171010", ofType:"m4a")
let fileURL = NSURL(fileURLWithPath: path)
player = AVAudioPlayer(contentsOfURL: outputFileURL, error: nil)
player.prepareToPlay()
player.delegate = self
player.play()
}
#IBAction func Record(AnyObject)
{
filename = "171010.m4a"
let paths: NSArray = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
var pathComponents = [paths.lastObject as String, filename as String]
outputFileURL = NSURL.fileURLWithPathComponents(pathComponents)
settings.setValue(kAudioFormatMPEG4AAC, forKey: AVFormatIDKey)
settings.setValue(44100.0, forKey: AVSampleRateKey)
settings.setValue(2, forKey: AVNumberOfChannelsKey)
recorder = AVAudioRecorder(URL: outputFileURL, settings: settings, error: &error)
recorder.delegate = self;
recorder.prepareToRecord()
recorder.record()
}
#IBAction func Stop(AnyObject)
{
recorder.stop()
println("Stop")
}
and the globals at the top are :
class MainMenu: UIViewController , AVAudioRecorderDelegate , AVAudioPlayerDelegate {
var filename: String!
var outputFileURL: NSURL!
var recorder: AVAudioRecorder!
var error: NSError?
var settings: NSMutableDictionary = NSMutableDictionary()
var player : AVAudioPlayer! = nil
Any ideas ? I don't know if its recording and creating the file. As I am getting URL is nill on the playing function. This is wired up to a storyboard with buttons. Any help within this simple stuff in swift would be brilliant.
I have changed it to this and this should work ? Can anyone advise ?
#IBAction func Play(AnyObject)
{
println ("Play")
var audioPlayer = AVAudioPlayer()
filename = "171010.m4a"
let paths: NSArray = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
var pathComponents = [paths.lastObject as String, filename as String]
outputFileURL = NSURL.fileURLWithPathComponents(pathComponents)
audioPlayer = AVAudioPlayer(contentsOfURL: outputFileURL, error: nil)
audioPlayer.prepareToPlay()
audioPlayer.delegate = self
audioPlayer.play()
}
#IBAction func Record(AnyObject)
{
filename = "171010.m4a"
let paths: NSArray = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
var pathComponents = [paths.lastObject as String, filename as String]
outputFileURL = NSURL.fileURLWithPathComponents(pathComponents)
settings.setValue(kAudioFormatMPEG4AAC, forKey: AVFormatIDKey)
settings.setValue(44100.0, forKey: AVSampleRateKey)
settings.setValue(2, forKey: AVNumberOfChannelsKey)
recorder = AVAudioRecorder(URL: outputFileURL, settings: settings, error: &error)
recorder.delegate = self;
recorder.prepareToRecord()
recorder.record()
}
The player needs to be an ivar so it isn't popped off the stack before playing.
Here is a github project with a functioning recorder if you need more info.
Also, the convention is to start function names with a lowercase character.