Share image/text through WhatsApp in an iOS app - iphone

Is it possible to share images, text or whatever you want through Whatsapp in a iOS app? I'm searching on google but I only found results talking about Android implementations.

Is now possible in this way:
Send Text - Obj-C
NSString * msg = #"YOUR MSG";
NSString * urlWhats = [NSString stringWithFormat:#"whatsapp://send?text=%#",msg];
NSURL * whatsappURL = [NSURL URLWithString:[urlWhats stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
if ([[UIApplication sharedApplication] canOpenURL: whatsappURL]) {
[[UIApplication sharedApplication] openURL: whatsappURL];
} else {
// Cannot open whatsapp
}
Send Text - Swift
let msg = "YOUR MSG"
let urlWhats = "whatsapp://send?text=\(msg)"
if let urlString = urlWhats.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet()) {
if let whatsappURL = NSURL(string: urlString) {
if UIApplication.sharedApplication().canOpenURL(whatsappURL) {
UIApplication.sharedApplication().openURL(whatsappURL)
} else {
// Cannot open whatsapp
}
}
}
Send Image - Obj-C
-- in .h file
<UIDocumentInteractionControllerDelegate>
#property (retain) UIDocumentInteractionController * documentInteractionController;
-- in .m file
if ([[UIApplication sharedApplication] canOpenURL: [NSURL URLWithString:#"whatsapp://app"]]){
UIImage * iconImage = [UIImage imageNamed:#"YOUR IMAGE"];
NSString * savePath = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/whatsAppTmp.wai"];
[UIImageJPEGRepresentation(iconImage, 1.0) writeToFile:savePath atomically:YES];
_documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:savePath]];
_documentInteractionController.UTI = #"net.whatsapp.image";
_documentInteractionController.delegate = self;
[_documentInteractionController presentOpenInMenuFromRect:CGRectMake(0, 0, 0, 0) inView:self.view animated: YES];
} else {
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:#"WhatsApp not installed." message:#"Your device has no WhatsApp installed." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
Send Image - Swift
let urlWhats = "whatsapp://app"
if let urlString = urlWhats.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet()) {
if let whatsappURL = NSURL(string: urlString) {
if UIApplication.sharedApplication().canOpenURL(whatsappURL) {
if let image = UIImage(named: "image") {
if let imageData = UIImageJPEGRepresentation(image, 1.0) {
let tempFile = NSURL(fileURLWithPath: NSHomeDirectory()).URLByAppendingPathComponent("Documents/whatsAppTmp.wai")
do {
try imageData.writeToURL(tempFile, options: .DataWritingAtomic)
self.documentInteractionController = UIDocumentInteractionController(URL: tempFile)
self.documentInteractionController.UTI = "net.whatsapp.image"
self.documentInteractionController.presentOpenInMenuFromRect(CGRectZero, inView: self.view, animated: true)
} catch {
print(error)
}
}
}
} else {
// Cannot open whatsapp
}
}
}
Because a new security feature of iOS 9, you need add this lines on
.plist file:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>whatsapp</string>
</array>
More information about url sheme: https://developer.apple.com/videos/play/wwdc2015-703/
I did not find a single solution for both.
More information on http://www.whatsapp.com/faq/en/iphone/23559013
I made a small project to help some.
https://github.com/salesawagner/SharingWhatsApp

It is now possible. Have not tried yet though.
The latest release notes for whatsapp indicate that you can through the share extension:
WhatsApp accepts the following types of content:
text (UTI: public.plain-text)
photos (UTI: public.image)
videos (UTI: public.movie)
audio notes and music files (UTI: public.audio)
PDF documents (UTI: com.adobe.pdf)
contact cards (UTI: public.vcard)
web URLs (UTI: public.url)

No this is not possible, whatsapp does not have any public API you can use.
Please note that this answer is correct for 2011 when there was no API for WhatsApp.
Now there is an api available for interacting with WhatsApp: http://www.whatsapp.com/faq/en/iphone/23559013
The Objective-C call to open one of these URLs is as follows:
NSURL *whatsappURL = [NSURL URLWithString:#"whatsapp://send?text=Hello%2C%20World!"];
if ([[UIApplication sharedApplication] canOpenURL: whatsappURL]) {
[[UIApplication sharedApplication] openURL: whatsappURL];
}

This is the correct code for share link to whats app users.
NSString * url = [NSString stringWithFormat:#"http://video...bla..bla.."];
url = (NSString*)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL,(CFStringRef) url, NULL,CFSTR("!*'();:#&=+$,/?%#[]"),kCFStringEncodingUTF8));
NSString * urlWhats = [NSString stringWithFormat:#"whatsapp://send?text=%#",url];
NSURL * whatsappURL = [NSURL URLWithString:urlWhats];
if ([[UIApplication sharedApplication] canOpenURL: whatsappURL]) {
[[UIApplication sharedApplication] openURL: whatsappURL];
} else {
// can not share with whats app
}

Simple code and Sample code ;-)
Note:- You can only share text or image, both sharing together in whatsApp is not working from whatsApp side
/*
//Share text
NSString *textToShare = #"Enter your text to be shared";
NSArray *objectsToShare = #[textToShare];
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:objectsToShare applicationActivities:nil];
[self presentViewController:activityVC animated:YES completion:nil];
*/
//Share Image
UIImage * image = [UIImage imageNamed:#"images"];
NSArray *objectsToShare = #[image];
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:objectsToShare applicationActivities:nil];
[self presentViewController:activityVC animated:YES completion:nil];

For Swift 4 - Works fine
delclare
var documentInteractionController:UIDocumentInteractionController!
func sharePicture() {
let urlWhats = "whatsapp://app"
if let urlString = urlWhats.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed) {
if let whatsappURL = NSURL(string: urlString) {
if UIApplication.shared.canOpenURL(whatsappURL as URL) {
let imgURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileName = "yourImageName.jpg"
let fileURL = imgURL.appendingPathComponent(fileName)
if let image = UIImage(contentsOfFile: fileURL.path) {
if let imageData = image.jpegData(compressionQuality: 0.75) {
let tempFile = NSURL(fileURLWithPath: NSHomeDirectory()).appendingPathComponent("Documents/yourImageName.jpg")
do {
try imageData.write(to: tempFile!, options: .atomicWrite)
self.documentInteractionController = UIDocumentInteractionController(url: tempFile!)
self.documentInteractionController.uti = "net.whatsapp.image"
self.documentInteractionController.presentOpenInMenu(from: CGRect.zero, in: self.view, animated: true)
} catch {
print(error)
}
}
}
} else {
// Cannot open whatsapp
}
}
}
}
Do not forget to edit the .plist with the following lines
<key>LSApplicationQueriesSchemes</key>
<array>
<string>whatsapp</string>
</array>
Enjoy!!!

Swift 3 version of Wagner Sales' answer:
let urlWhats = "whatsapp://app"
if let urlString = urlWhats.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) {
if let whatsappURL = URL(string: urlString) {
if UIApplication.shared.canOpenURL(whatsappURL) {
if let image = UIImage(named: "image") {
if let imageData = UIImageJPEGRepresentation(image, 1.0) {
let tempFile = NSURL(fileURLWithPath: NSHomeDirectory()).appendingPathComponent("Documents/whatsAppTmp.wai")
do {
try imageData.write(to: tempFile!, options: .atomic)
self.documentIC = UIDocumentInteractionController(url: tempFile!)
self.documentIC.uti = "net.whatsapp.image"
self.documentIC.presentOpenInMenu(from: CGRect.zero, in: self.view, animated: true)
}
catch {
print(error)
}
}
}
} else {
// Cannot open whatsapp
}
}
}

I added a Whatsapp Sharer to ShareKit.
Check out here:
https://github.com/heringb/ShareKit

Swift 4
let urlWhats = "whatsapp://app"
if let urlString = urlWhats.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlHostAllowed) {
if let whatsappURL = URL(string: urlString) {
if UIApplication.shared.canOpenURL(whatsappURL) {
if let imageData = UIImageJPEGRepresentation(image, 1.0) {
let tempFile = NSURL(fileURLWithPath: NSHomeDirectory()).appendingPathComponent("Documents/whatsAppTmp.wai")!
do {
try imageData.write(to: tempFile, options: .atomic)
self.documentController = UIDocumentInteractionController(url: tempFile)
self.documentController.uti = "net.whatsapp.image"
self.documentController.presentOpenInMenu(from: CGRect.zero, in: self.view, animated: true)
} catch {
print(error)
}
}
} else {
let ac = UIAlertController(title: "MessageAletTitleText".localized, message: "AppNotFoundToShare".localized, preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OKButtonText".localized, style: .default))
present(ac, animated: true)
print("Whatsapp isn't installed ")
// Cannot open whatsapp
}
}
}

WhatsApp provides two ways for your iPhone app to interact with WhatsApp:
Through a custom URL scheme
Through the iOS Document Interaction API
For more information Visit this link
Thanks.

Yes it's possible :
NSMutableArray *arr = [[NSMutableArray alloc]init];
NSURL *URL = [NSURL fileURLWithPath:path];
NSString *textToShare = [NSString stringWithFormat:#"%# \n",_model.title];
NSString *SchoolName= [[AppUtility sharedUtilityInstance]getAppConfigInfoByKey:#"SchoolName" SecondKeyorNil:Nil];
[arr addObject:textToShare];
[arr addObject:URL];
[arr addObject:_model.body];
[arr addObject:SchoolName];
TTOpenInAppActivity *openInAppActivity = [[TTOpenInAppActivity alloc] initWithView:_parentController.view andRect:((UIButton *)sender).frame];
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:arr applicationActivities:#[openInAppActivity]];
// Store reference to superview (UIActionSheet) to allow dismissal
openInAppActivity.superViewController = activityViewController;
// Show UIActivityViewController
[_parentController presentViewController:activityViewController animated:YES completion:NULL];

Swift 3 version for sending text:
func shareByWhatsapp(msg:String){
let urlWhats = "whatsapp://send?text=\(msg)"
if let urlString = urlWhats.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
if let whatsappURL = NSURL(string: urlString) {
if UIApplication.shared.canOpenURL(whatsappURL as URL) {
UIApplication.shared.openURL(whatsappURL as URL)
} else {
let alert = UIAlertController(title: NSLocalizedString("Whatsapp not found", comment: "Error message"),
message: NSLocalizedString("Could not found a installed app 'Whatsapp' to proceed with sharing.", comment: "Error description"),
preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("Ok", comment: "Alert button"), style: UIAlertActionStyle.default, handler:{ (UIAlertAction)in
}))
self.present(alert, animated: true, completion:nil)
// Cannot open whatsapp
}
}
}
}
Also, you need to add whatsapp to LSApplicationQueriesSchemes in your Info.plist

NSString *shareText = #"http:www.google.com";
NSArray *objectsToShare = #[shareText];
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:objectsToShare applicationActivities:nil];
if (isIphone)
{
[self presentViewController:activityVC animated:YES completion:nil];
}
else {
UIPopoverController *popup = [[UIPopoverController alloc] initWithContentViewController:activityVC];
[popup presentPopoverFromRect:CGRectMake(self.view.frame.size.width/2, self.view.frame.size.height/4, 0, 0)inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}

Related

Share image via WhatsApp

I have in my app button to share image via whatsapp and it does work. But there is some strange thing appears in the menu of UIDocumentInteractionController on some devices.
This is the code:
let urlWhats = "whatsapp://app"
if let urlString = urlWhats.addingPercentEncoding(withAllowedCharacters:CharacterSet.urlQueryAllowed) {
if let whatsappURL = URL(string: urlString) {
if UIApplication.shared.canOpenURL(whatsappURL as URL) {
if let imageData = UIImageJPEGRepresentation(self.ivFramedPicture.image!, 1.0) {
let tempFile = URL(fileURLWithPath: NSHomeDirectory()).appendingPathComponent("/Documents/whatsAppTmp.wai")
do {
try imageData.write(to: tempFile, options: .atomic)
self.documentInteractionController = UIDocumentInteractionController(url: tempFile)
self.documentInteractionController.delegate = self
self.documentInteractionController.uti = "net.whatsapp.image"
self.documentInteractionController.presentOpenInMenu(from: CGRect.zero, in: self.view, animated: true)
} catch {
print(error)
}
}
} else {
// Cannot open whatsapp
}
}
}
If I click on the 1 whatsapp icon it sends some file that doesn't open on iPhones (Android opens that file like image)
Does anyone can help to resolve that problem? I want only one icon with share image, that's it. Thanks
Simply use UIActivityController for sharing functionality instead of all that code.
Example:
if let image = self.ivFramedPicture.image
{
let activityViewController = UIActivityViewController(activityItems: [image], applicationActivities: nil)
self.present(activityViewController, animated: true, completion: nil)
}
Maybe try using the UIActivityViewController

Add Instagram to UIActivityViewController

I'm trying to share an image using standard UIActivityViewController, it's fine to share on Facebook, Twitter and Save Image using this code:
let firstActivityItem = "foo text"
let secondActivityItem : UIImage = image!
let activityViewController : UIActivityViewController = UIActivityViewController(
activityItems: [firstActivityItem, secondActivityItem], applicationActivities: nil)
activityViewController.excludedActivityTypes = [
UIActivityTypePostToWeibo,
UIActivityTypePrint,
UIActivityTypeAssignToContact,
UIActivityTypeAddToReadingList,
UIActivityTypePostToVimeo,
UIActivityTypePostToTencentWeibo
]
self.presentViewController(activityViewController, animated: true, completion: nil)
I need one more thing, Instagram:
If UIApplication.sharedApplication().canOpenURL(instagramURL!) {
// Success
var img = image!
var savePath: String = NSHomeDirectory().stringByAppendingPathComponent("Documents/Test.igo")
UIImageJPEGRepresentation(img, 1).writeToFile(savePath, atomically: true)
var imgURL = NSURL(string: NSString(format: "file://%#", savePath) as! String)
docController = UIDocumentInteractionController(URL: imgURL!) // 1
docController.UTI = "com.instagram.exclusivegram" // 2
docController.delegate = self
docController.annotation = ["InstagramCaption":"testsss"] // 3
docController.presentOpenInMenuFromRect(self.view.frame, inView: self.view, animated: true) // 4
} else {
// Error
}
Both these codes work fine separately, how can I add Instagram to the UIActivityViewController? Is it possible at all?
I think it would be very easier to add other social shares to the code you wrote for Instagram. The ".igo" extension is exclusive for Instagram so other apps will not support it. Just change this extension from ".igo" to ".ig" and other apps will read it:
var savePath: String = NSHomeDirectory().stringByAppendingPathComponent("Documents/Test.ig")
But Instagram also have an exclusive UTI to avoiding other apps to appear in the same Document Interaction View. So you will also need to change it from "exclusivegram" to "photo":
docController.UTI = "com.instagram.photo"
I have an app with a similar functionality and this is my original code:
#IBAction func shareOnIntagram(sender: UIButton) {
let finalImage: UIImage = UIImage.imageWithView(photoView)
let instagramURL = NSURL(string: "instagram://app")
if (UIApplication.sharedApplication().canOpenURL(instagramURL!)) {
let imageData = UIImageJPEGRepresentation(finalImage, 1)
let captionString = "caption"
let writePath = (NSTemporaryDirectory() as NSString).stringByAppendingPathComponent("instagram.ig")
if imageData?.writeToFile(writePath, atomically: true) == false {
return
} else {
let fileURL = NSURL(fileURLWithPath: writePath)
self.documentController = UIDocumentInteractionController(URL: fileURL)
self.documentController.delegate = self
self.documentController.UTI = "com.instagram.photo"
self.documentController.annotation = NSDictionary(object: captionString, forKey: "InstagramCaption")
self.documentController.presentOpenInMenuFromRect(self.view.frame, inView: self.view, animated: true)
}
} else {
print(" Instagram is not installed ")
}
}
To make this code work, don't forget to add UIDocumentInteractionControllerDelegate in the UIViewController class.
It seems it's not possible, because of .igo extension which is needed by Instagram.

How to use dispatch_async in swift

func get_data_from_server() {
let urlPath = NSString(format:"url")
var url: NSURL = NSURL(string: urlPath)!
let data : NSData = NSData(contentsOfURL: url)!
var error: NSErrorPointer = nil
var jsonResult: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: error) as? NSDictionary
if (jsonResult != nil) {
if let image_id_array : NSArray! = jsonResult?.valueForKey("Result")?.valueForKey("id") as? NSArray {
self.id_array = image_id_array
}
if let description_Array = jsonResult?.valueForKey("Result")?.valueForKey("description") as? NSArray {
self.description_array = description_Array
}
if let img_array = jsonResult?.valueForKey("Result")?.valueForKey("image") as? NSArray {
self.images_array = img_array
}
}
}
How to use dispatch_async in swift. I'd like to load an image from a URL in my application
Add the whole method of downloading images and add it to array in the following box:
dispatch_async(dispatch_get_main_queue()) {
// add you code here
}
You should write your code to load your image from a URL like this
cell.imageView.image = [UIImage imageNamed:#"default_icon"];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSData *data = [NSData dataWithContentsOfURL:imageURL];
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = image;
});
});
you should use main queue for change UI only not for Download image.
Try like this for async request...
let url = NSURL(string:"http://juzhotel.com/smartcreatives/catholic_app/app/index.php/webservices/getCatholicHighlight")
let request = NSMutableURLRequest(URL: url!)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue())
{
(response, data, error) in
if let HTTPResponse = response as? NSHTTPURLResponse {
let statusCode = HTTPResponse.statusCode
if statusCode == 200 {
// Yes, Do something.
let resstr = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Data Reriving %#",resstr)
}
}
}
Its working fine in my code

Unable to load image from asset URL

I have a class that stores information about the assets on the phone (images, videos).
My class has the ResourceURLString defined as such
#property NSURL *ResourceURL;
I am setting the property while looping trough the assets on the phone as such
Item.ResourceURLString = [[asset valueForProperty:ALAssetPropertyURLs] objectForKey:[[asset valueForProperty:ALAssetPropertyRepresentations] objectAtIndex:0]];
When the user clicks on an image I want to load the image.
The code that I have is this
NSData *imageUrl = [NSData dataWithContentsOfURL:[NSURL URLWithString:[CurrentItem.ResourceURL absoluteString]]];
Img = [UIImage imageWithData:imageUrl];
But the Image is always nil
I have verified that the ResourceURL property contains the URL
assets: library://asset/asset.JPG?id=82690321-91C1-4650-8348-F3FD93D14613&ext=JPG
You can't load images in this way.
You need to use ALAssetsLibrary class for this.
Add assetslibrary framework to your project and add header files.
Use the below code for loading image:
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
{
ALAssetRepresentation *rep = [myasset defaultRepresentation];
CGImageRef iref = [rep fullResolutionImage];
if (iref) {
UIImage *largeimage = [UIImage imageWithCGImage:iref];
yourImageView.image = largeImage;
}
};
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror)
{
NSLog(#"Can't get image - %#",[myerror localizedDescription]);
};
NSURL *asseturl = [NSURL URLWithString:yourURL];
ALAssetsLibrary* assetslibrary = [[[ALAssetsLibrary alloc] init] autorelease];
[assetslibrary assetForURL:asseturl
resultBlock:resultblock
failureBlock:failureblock];
Since iOS 8 you can use the Photos Framework here is how to do it in Swift 3
import Photos // use the Photos Framework
// declare your asset url
let assetUrl = URL(string: "assets-library://asset/asset.JPG?id=9F983DBA-EC35-42B8-8773-B597CF782EDD&ext=JPG")!
// retrieve the list of matching results for your asset url
let fetchResult = PHAsset.fetchAssets(withALAssetURLs: [assetUrl], options: nil)
if let photo = fetchResult.firstObject {
// retrieve the image for the first result
PHImageManager.default().requestImage(for: photo, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFill, options: nil) {
image, info in
let myImage = image //here is the image
}
}
Use PHImageManagerMaximumSize if you want to retrieve the original size of the picture. But if you want to retrieve a smaller or specific size you can replace PHImageManagerMaximumSize by CGSize(width:150, height:150)
As of iOS 9.0 ALAssetsLibraryis deprecated. Since iOS 8.0, this works with the PHPhotoLibrary. This is a small UIImage extension, Swift 2X.
This uses a fixed image size.
import Photos
extension UIImageView {
func imageFromAssetURL(assetURL: NSURL) {
let asset = PHAsset.fetchAssetsWithALAssetURLs([assetURL], options: nil)
guard let result = asset.firstObject where result is PHAsset else {
return
}
let imageManager = PHImageManager.defaultManager()
imageManager.requestImageForAsset(result as! PHAsset, targetSize: CGSize(width: 200, height: 200), contentMode: PHImageContentMode.AspectFill, options: nil) { (image, dict) -> Void in
if let image = image {
self.image = image
}
}
}
}
Getting the imageReferenceURL from the UIImagePickerController delegate:
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
imageURL = info[UIImagePickerControllerReferenceURL] as? NSURL
}
Setting the image
let imageView = UIImageView()
imageView.imageFromAssetURL(imageURL)
There might be effects I haven't encountered yet, a classic would be UITableViewCell or thread problems. I'll keep this updated, also appreciate your feedback.
For Swift 5
fetchAssets(withALAssetURLs) will be removed in a future release. Hence we using fetchAssets to get image from asset local identifier
extension UIImageView {
func imageFromLocalIdentifier(localIdentifier: String, targetSize: CGSize) {
let fetchOptions = PHFetchOptions()
// sort by date desending
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
// fetch photo with localIdentifier
let results = PHAsset.fetchAssets(withLocalIdentifiers: [localIdentifier], options: fetchOptions)
let manager = PHImageManager.default()
results.enumerateObjects { (thisAsset, _, _) in
manager.requestImage(for: thisAsset, targetSize: targetSize, contentMode: .aspectFit, options: nil, resultHandler: {(image, _) in
DispatchQueue.main.async {[weak self] in
self?.image = image
}
})
}
}
}
Update
let image = UIImage(data: NSData(contentsOf: imageURL as URL)! as Data)
ALAsset *asset = "asset array index"
[tileView.tileImageView setImage:[UIImage imageWithCGImage:[asset thumbnail]]];

How to trim the video using AVFoundation

Iam able to record the video by using AVFoundation or UIImagePickerController. But i am unable to trim the video from one particular second to another particular duration/time. Can any one help me.
Thanks,
Siva Krishna.
You can have the UIImagePickerController enable trimming
UIImagePickerController *videoRecorder = [[UIImagePickerController alloc]init];
NSArray *sourceTypes = [UIImagePickerController availableMediaTypesForSourceType:videoRecorder.sourceType];
NSLog(#"Available types for source as camera = %#", sourceTypes);
if (![sourceTypes containsObject:(NSString*)kUTTypeMovie] ) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil
message:#"Device Not Supported for video Recording." delegate:self
cancelButtonTitle:#"Yes"
otherButtonTitles:#"No",nil];
[alert show];
[alert release];
return;
}
videoRecorder.allowsEditing = YES;
Unfortunately after you get back from the imagePickerController, You are forced to convert the video manually.
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
if ([self.popoverLibraryBrowser isPopoverVisible])
{
[self.popoverLibraryBrowser dismissPopoverAnimated:YES];
}
NSString *type = [info objectForKey:UIImagePickerControllerMediaType];
if ([type isEqualToString:(NSString *)kUTTypeVideo] ||
[type isEqualToString:(NSString *)kUTTypeMovie]) { // movie != video
NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL];
NSNumber *start = [info objectForKey:#"_UIImagePickerControllerVideoEditingStart"];
NSNumber *end = [info objectForKey:#"_UIImagePickerControllerVideoEditingEnd"];
// if start and end are nil then clipping was not used.
// You should use the entire video.
int startMilliseconds = ([start doubleValue] * 1000);
int endMilliseconds = ([end doubleValue] * 1000);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSFileManager *manager = [NSFileManager defaultManager];
NSString *outputURL = [documentsDirectory stringByAppendingPathComponent:#"output"] ;
[manager createDirectoryAtPath:outputURL withIntermediateDirectories:YES attributes:nil error:nil];
outputURL = [outputURL stringByAppendingPathComponent:#"output.mp4"];
// Remove Existing File
[manager removeItemAtPath:outputURL error:nil];
//[self loadAssetFromFile:videoURL];
[self.recorder dismissModalViewControllerAnimated:YES];
AVURLAsset *videoAsset = [AVURLAsset URLAssetWithURL:videoURL options:nil];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:videoAsset presetName:AVAssetExportPresetHighestQuality];
exportSession.outputURL = [NSURL fileURLWithPath:outputURL];
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
CMTimeRange timeRange = CMTimeRangeMake(CMTimeMake(startMilliseconds, 1000), CMTimeMake(endMilliseconds - startMilliseconds, 1000));
exportSession.timeRange = timeRange;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
switch (exportSession.status) {
case AVAssetExportSessionStatusCompleted:
// Custom method to import the Exported Video
[self loadAssetFromFile:exportSession.outputURL];
break;
case AVAssetExportSessionStatusFailed:
//
NSLog(#"Failed:%#",exportSession.error);
break;
case AVAssetExportSessionStatusCancelled:
//
NSLog(#"Canceled:%#",exportSession.error);
break;
default:
break;
}
}];
//NSData *videoData = [NSData dataWithContentsOfURL:videoURL];
//NSString *videoStoragePath;//Set your video storage path to this variable
//[videoData writeToFile:videoStoragePath atomically:YES];
//You can store the path of the saved video file in sqlite/coredata here.
}
}
Swift version of above
import UIKit
import AVFoundation
import MobileCoreServices
func pickVideo(){
if UIImagePickerController.isSourceTypeAvailable(.Camera) {
let videoRecorder = UIImagePickerController()
videoRecorder.sourceType = .Camera
videoRecorder.mediaTypes = [kUTTypeMovie as String]
videoRecorder.allowsEditing = true
videoRecorder.delegate = self
presentViewController(videoRecorder, animated: true, completion: nil)
}
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
picker.dismissViewControllerAnimated(true, completion: nil)
let manager = NSFileManager.defaultManager()
guard let documentDirectory = try? manager.URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true) else {return}
guard let mediaType = info[UIImagePickerControllerMediaType] as? String else {return}
guard let url = info[UIImagePickerControllerMediaURL] as? NSURL else {return}
if mediaType == kUTTypeMovie as String || mediaType == kUTTypeVideo as String {
let asset = AVAsset(URL: url)
let length = Float(asset.duration.value) / Float(asset.duration.timescale)
print("video length: \(length) seconds")
let start = info["_UIImagePickerControllerVideoEditingStart"] as? Float
let end = info["_UIImagePickerControllerVideoEditingEnd"] as? Float
var outputURL = documentDirectory.URLByAppendingPathComponent("output")
do {
try manager.createDirectoryAtURL(outputURL, withIntermediateDirectories: true, attributes: nil)
outputURL = outputURL.URLByAppendingPathComponent("output.mp4")
}catch let error {
print(error)
}
//Remove existing file
_ = try? manager.removeItemAtURL(outputURL)
guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality) else {return}
exportSession.outputURL = outputURL
exportSession.outputFileType = AVFileTypeMPEG4
let startTime = CMTime(seconds: Double(start ?? 0), preferredTimescale: 1000)
let endTime = CMTime(seconds: Double(end ?? length), preferredTimescale: 1000)
let timeRange = CMTimeRange(start: startTime, end: endTime)
exportSession.timeRange = timeRange
exportSession.exportAsynchronouslyWithCompletionHandler{
switch exportSession.status {
case .Completed:
print("exported at \(outputURL)")
case .Failed:
print("failed \(exportSession.error)")
case .Cancelled:
print("cancelled \(exportSession.error)")
default: break
}
}
}
}
The best solution for swift 4, i have found there. I did fixes it for my needs, but it's really clear and convenience.
The code:
import AVFoundation
import Foundation
extension FileManager {
func removeFileIfNecessary(at url: URL) throws {
guard fileExists(atPath: url.path) else {
return
}
do {
try removeItem(at: url)
}
catch let error {
throw TrimError("Couldn't remove existing destination file: \(error)")
}
}
}
struct TrimError: Error {
let description: String
let underlyingError: Error?
init(_ description: String, underlyingError: Error? = nil) {
self.description = "TrimVideo: " + description
self.underlyingError = underlyingError
}
}
extension AVMutableComposition {
convenience init(asset: AVAsset) {
self.init()
for track in asset.tracks {
addMutableTrack(withMediaType: track.mediaType, preferredTrackID: track.trackID)
}
}
func trim(timeOffStart: Double) {
let duration = CMTime(seconds: timeOffStart, preferredTimescale: 1)
let timeRange = CMTimeRange(start: kCMTimeZero, duration: duration)
for track in tracks {
track.removeTimeRange(timeRange)
}
removeTimeRange(timeRange)
}
}
extension AVAsset {
func assetByTrimming(timeOffStart: Double) throws -> AVAsset {
let duration = CMTime(seconds: timeOffStart, preferredTimescale: 1)
let timeRange = CMTimeRange(start: kCMTimeZero, duration: duration)
let composition = AVMutableComposition()
do {
for track in tracks {
let compositionTrack = composition.addMutableTrack(withMediaType: track.mediaType, preferredTrackID: track.trackID)
try compositionTrack?.insertTimeRange(timeRange, of: track, at: kCMTimeZero)
}
} catch let error {
throw TrimError("error during composition", underlyingError: error)
}
return composition
}
func export(to destination: URL) throws {
guard let exportSession = AVAssetExportSession(asset: self, presetName: AVAssetExportPresetPassthrough) else {
throw TrimError("Could not create an export session")
}
exportSession.outputURL = destination
exportSession.outputFileType = AVFileType.m4v
exportSession.shouldOptimizeForNetworkUse = true
let group = DispatchGroup()
group.enter()
try FileManager.default.removeFileIfNecessary(at: destination)
exportSession.exportAsynchronously {
group.leave()
}
group.wait()
if let error = exportSession.error {
throw TrimError("error during export", underlyingError: error)
}
}
}
func time(_ operation: () throws -> ()) rethrows {
let start = Date()
try operation()
let end = Date().timeIntervalSince(start)
print(end)
let sourceURL = URL(fileURLWithPath: CommandLine.arguments[1])
let destinationURL = URL(fileURLWithPath: CommandLine.arguments[2])
do {
try time {
let asset = AVURLAsset(url: sourceURL)
let trimmedAsset = try asset.assetByTrimming(timeOffStart: 1.0)
try trimmedAsset.export(to: destinationURL)
}
} catch let error {
print("💩 \(error)")
}
}
you should add kUTTypeMovie in the setMediaTypes array and it will work.