I'm using SDWebImage library and I have this code:
[cell.imgLogo setImageWithURL:[NSURL URLWithString:[item objectForKey:#"s_logo"]] placeholderImage:[UIImage imageNamed:#"default.png"]];
I have tweak the library SDWebImage a little bit to ignore empty string or a NSUrl with empty string in method downloadWithURL: delegate: options: userInfo::
if ([url isKindOfClass:NSString.class])
{
if ([(NSString *)url length] > 0) {
url = [NSURL URLWithString:(NSString *)url];
} else {
return;
}
}
else if (![url isKindOfClass:NSURL.class])
{
url = nil; // Prevent some common crashes due to common wrong values passed like NSNull.null for instance
}
else if ([url isKindOfClass:NSURL.class]) {
if ([[url absoluteString] length] > 0) {
//valid url
} else {
return;
}
}
So now it works with empty string and just to display its default image but the problem is when it comes to a string that is not an image url like:
http://beta.xxxxxxx.com/gangnamwe?to=boko
It displays nothing, it removes the placeholder image and displays nothing.
So how will I identify a valid image url? or is there any better work around for this?
Your help are much appreaciated.
you can check after getting NSData from NSURL . You can use GCD to download data
here is an example i created which save your image in photo library.
dispatch_async(dispatch_queue_create("com.getImage", NULL), ^(void) {
NSData *data=[NSData dataWithContentsOfURL:[NSURL URLWithString:#"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRKII9COB-hvMef4Zvb9XYVbXKDFZHJAHwwzzGyMiy_b-q65GD43Chd37jH"]];
UIImage *image=[UIImage imageWithData:data];
if (image==nil) {
//yourImageURL is not valid
image=[UIImage imageNamed:#"placeholder.png"];
}
else{
//yourImageURL is valid
dispatch_async(dispatch_get_main_queue(), ^{
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
//show your image
});
}
});
There is method in NSURL to check if the file exsists.
- (BOOL)checkResourceIsReachableAndReturnError:(NSError **)error
Example
NSURL *theURL = [NSURL URLWithString:string];
NSError *err;
if ([theURL checkResourceIsReachableAndReturnError:&err] == NO)
{
NSLog(#"resource not reachable");
}
Here's a category on NSURL for you :
// nsurl+documentTypes.h
#interface NSURL (documentTypes)
- (BOOL)isImageType;
#end
// nsurl+documentTypes.m
#implementation NSURL (documentTypes)
- (BOOL)isImageType
{
NSString * UTI = (__bridge NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension,(__bridge CFStringRef)[self pathExtension],NULL);
return UTTypeConformsTo((__bridge CFStringRef)UTI, kUTTypeImage);
}
#end
You can check if the url ends with some image name or not.You can get the parts of the url in following way:
NSURL* url = [NSURL URLWithString:#"http://digg.com/news/business/24hr"];
NSString* reducedUrl = [NSString stringWithFormat:
#"%#://%#/%#",
url.scheme,
url.host,
[url.pathComponents objectAtIndex:1]];
Now, take the last object of the pathComponents and check if it contains .png or .jpg etc.
In Swift:
import Foundation
public extension NSURL {
public var isImage: Bool {
return UTI.map{ UTTypeConformsTo($0, kUTTypeImage) } ?? false
}
public var UTI: String? {
var value: AnyObject?
let _ = try? getResourceValue(&value, forKey: NSURLTypeIdentifierKey)
return value as? String
}
}
e.g:
let url = NSURL(fileURLWithPath: "/Users/i/Desktop/image.png")
url.isImage //--> true
Related
I am using the YouTube API in UIWebView.
I have created a NSString with the HTML5 player that I load in the UIWebView. Everything works perfectly on iPhone 5 and iPad.
But, if I test the app using an iPhone 4, the player returns the buffering state all the time. Only if I explicitly press the play button, the player starts playing, without stopping again for buffering. It seems that although the video has been buffered, the player still gives me this state.
Is anyone aware of this problem? Any idea?
Thank you very much in advance!!
In LBYouTubePlayerViewController.m file
Replace Following method on yr old Method....
then test...
-(NSURL*)_extractYouTubeURLFromFile:(NSString *)html error:(NSError *__autoreleasing *)error {
NSString *JSONStart = nil;
// NSString *JSONStartFull = #"ls.setItem('PIGGYBACK_DATA', \")]}'";
NSString *JSONStartFull = #"bootstrap_data = \")]}'";
NSString *JSONStartShrunk = [JSONStartFull stringByReplacingOccurrencesOfString:#" " withString:#""];
if ([html rangeOfString:JSONStartFull].location != NSNotFound)
JSONStart = JSONStartFull;
else if ([html rangeOfString:JSONStartShrunk].location != NSNotFound)
JSONStart = JSONStartShrunk;
if (JSONStart != nil) {
NSScanner* scanner = [NSScanner scannerWithString:html];
[scanner scanUpToString:JSONStart intoString:nil];
[scanner scanString:JSONStart intoString:nil];
NSString *JSON = nil;
[scanner scanUpToString:#"}\";" intoString:&JSON];
JSON = [NSString stringWithFormat:#"%#}",JSON]; // Add closing bracket } to get vallid JSON again
// [scanner scanUpToString:#"\");" intoString:&JSON];
JSON = [self _unescapeString:JSON];
NSError* decodingError = nil;
NSDictionary* JSONCode = nil;
// First try to invoke NSJSONSerialization (Thanks Mattt Thompson)
id NSJSONSerializationClass = NSClassFromString(#"NSJSONSerialization");
SEL NSJSONSerializationSelector = NSSelectorFromString(#"dataWithJSONObject:options:error:");
if (NSJSONSerializationClass && [NSJSONSerializationClass respondsToSelector:NSJSONSerializationSelector]) {
JSONCode = [NSJSONSerialization JSONObjectWithData:[JSON dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:&decodingError];
}
else {
JSONCode = [JSON objectFromJSONStringWithParseOptions:JKParseOptionNone error:&decodingError];
}
if (decodingError) {
// Failed
*error = decodingError;
}
else {
// Success
NSDictionary *dict = [JSONCode objectForKey:#"content"];
NSDictionary *dictTemp = [dict objectForKey:#"video"];
NSArray* videos = [dictTemp objectForKey:#"fmt_stream_map"];
NSString* streamURL = nil;
if (videos.count) {
NSString* streamURLKey = #"url";
if (self.quality == LBYouTubePlayerQualityLarge) {
streamURL = [[videos objectAtIndex:0] objectForKey:streamURLKey];
}
else if (self.quality == LBYouTubePlayerQualityMedium) {
unsigned int index = MAX(0, videos.count-2);
streamURL = [[videos objectAtIndex:index] objectForKey:streamURLKey];
}
else {
streamURL = [[videos lastObject] objectForKey:streamURLKey];
}
}
if (streamURL) {
return [NSURL URLWithString:streamURL];
}
else {
*error = [NSError errorWithDomain:kLBYouTubePlayerControllerErrorDomain code:2 userInfo:[NSDictionary dictionaryWithObject:#"Couldn't find the stream URL." forKey:NSLocalizedDescriptionKey]];
}
}
}
else {
*error = [NSError errorWithDomain:kLBYouTubePlayerControllerErrorDomain code:3 userInfo:[NSDictionary dictionaryWithObject:#"The JSON data could not be found." forKey:NSLocalizedDescriptionKey]];
}
return nil;
}
I'm finding a number of conflicting data about playing sounds in iOS. What is a recommended way to play just a simple "ping" sound bite every time the user touches the screen?
I use this:
Header file:
#import <AudioToolbox/AudioServices.h>
#interface SoundEffect : NSObject
{
SystemSoundID soundID;
}
- (id)initWithSoundNamed:(NSString *)filename;
- (void)play;
#end
Source file:
#import "SoundEffect.h"
#implementation SoundEffect
- (id)initWithSoundNamed:(NSString *)filename
{
if ((self = [super init]))
{
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:filename withExtension:nil];
if (fileURL != nil)
{
SystemSoundID theSoundID;
OSStatus error = AudioServicesCreateSystemSoundID((__bridge CFURLRef)fileURL, &theSoundID);
if (error == kAudioServicesNoError)
soundID = theSoundID;
}
}
return self;
}
- (void)dealloc
{
AudioServicesDisposeSystemSoundID(soundID);
}
- (void)play
{
AudioServicesPlaySystemSound(soundID);
}
#end
You will need to create an instance of SoundEffect and direct call the method play on it.
This is the best way of playing a simple sound in iOS (no more than 30 seconds):
//Retrieve audio file
NSString *path = [[NSBundle mainBundle] pathForResource:#"soundeffect" ofType:#"m4a"];
NSURL *pathURL = [NSURL fileURLWithPath : path];
SystemSoundID audioEffect;
AudioServicesCreateSystemSoundID((__bridge CFURLRef) pathURL, &audioEffect);
AudioServicesPlaySystemSound(audioEffect);
// call the following function when the sound is no longer used
// (must be done AFTER the sound is done playing)
AudioServicesDisposeSystemSoundID(audioEffect);
(Small amendment to the correct answer to take care of the disposing of the audio)
NSString *path = [[NSBundle mainBundle] pathForResource:#"soundeffect" ofType:#"m4a"];
NSURL *pathURL = [NSURL fileURLWithPath : path];
SystemSoundID audioEffect;
AudioServicesCreateSystemSoundID((__bridge CFURLRef) pathURL, &audioEffect);
AudioServicesPlaySystemSound(audioEffect);
// Using GCD, we can use a block to dispose of the audio effect without using a NSTimer or something else to figure out when it'll be finished playing.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(30 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
AudioServicesDisposeSystemSoundID(audioEffect);
});
You can use AVFoundation or AudioToolbox.
Here're two examples which use the libraries separately.
SAMSoundEffect (archived repository)
BRYSoundEffectPlayer
Here's an updated answer for Swift (4):
import AudioToolbox
func playSound() {
var soundId: SystemSoundID = 0
guard let soundPath = Bundle.main.url(forResource: "Success7", withExtension: "wav") else {
print("Error finding file")
return
}
let error = AudioServicesCreateSystemSoundID(soundPath as CFURL, &soundId)
if error != kAudioServicesNoError {
print("Error loading sound")
return
}
AudioServicesPlaySystemSoundWithCompletion(soundId) {
AudioServicesDisposeSystemSoundID(soundId)
}
}
If you have a sound effect that you want to play multiple times in a view, then you can be a little more smart about loading and disposing of the audio:
class YourViewController: UIViewController {
fileprivate lazy var soundId: SystemSoundID? = {
guard let soundPath = Bundle.main.url(forResource: "Success7", withExtension: "wav") else {
return nil
}
var soundId: SystemSoundID = 0
let error = AudioServicesCreateSystemSoundID(soundPath as CFURL, &soundId)
if error != kAudioServicesNoError {
return nil
}
return soundId
}()
func playScannedSound() {
guard let soundId = self.soundId else {
return
}
AudioServicesPlaySystemSoundWithCompletion(soundId, nil)
}
deinit {
guard let soundId = self.soundId else {
return
}
AudioServicesDisposeSystemSoundID(soundId)
}
}
I would like to allow my application users to use their own fonts in the app, by copying them inside the Documents directory (through iTunes). However, I can't find a way to use custom fonts in this way, since the right way to do it depends on using the UIAppFonts key in the app's Info.plist.
Is there any way to override this during runtime?
Thanks.
I know this is an old question, but I was trying to do the same today and found a way using CoreText and CGFont.
First be sure you add the CoreText framework and
#import <CoreText/CoreText.h>
Then this should do it (in this example I am using a font I previously downloaded and saved to a fonts directory inside the Documents directory):
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * documentsDirectory = [paths objectAtIndex:0];
NSString * fontPath = [documentsDirectory stringByAppendingPathComponent:#"Fonts/Chalkduster.ttf"];
NSURL * url = [NSURL fileURLWithPath:fontPath];
CGDataProviderRef fontDataProvider = CGDataProviderCreateWithURL((__bridge CFURLRef)url);
CGFontRef newFont = CGFontCreateWithDataProvider(fontDataProvider);
NSString * newFontName = (__bridge NSString *)CGFontCopyPostScriptName(newFont);
CGDataProviderRelease(fontDataProvider);
CFErrorRef error;
CTFontManagerRegisterGraphicsFont(newFont, &error);
CGFontRelease(newFont);
UIFont * finalFont = [UIFont fontWithName:newFontName size:20.0f];
Hope it helps anyone stumbling across this question!
Try this one
#import "MBProgressHUD.h"
#import <CoreText/CoreText.h>
- (void)viewDidLoad
{
NSURL *fileNameURL=[NSURL URLWithString:#"http://www.ge.tt/api/1/files/6d7jEnk/0/"];
NSMutableURLRequest *filenameReq=[[NSMutableURLRequest alloc] initWithURL:fileNameURL];
NSData *responseData=[NSURLConnection sendSynchronousRequest:filenameReq returningResponse:nil error:nil];
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:nil];
NSString *fontFileName=[[[json valueForKey:#"filename"] componentsSeparatedByString:#"."] objectAtIndex:0];
NSLog(#"file name is %#",fontFileName);
NSURL *url=[NSURL URLWithString:#"http://www.ge.tt/api/1/files/6d7jEnk/0/blob?download"];
NSMutableURLRequest *request=[[NSMutableURLRequest alloc] initWithURL:url];
__block NSError *error;
__block NSURLResponse *response;
MBProgressHUD *hud=[MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText=#"Changing Font..";
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSString *rootPath=[NSHomeDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:#"Documents"]];
NSString *filePath=[rootPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.ttf",fontFileName]];
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
NSFileManager *fm=[NSFileManager defaultManager];
if (![fm fileExistsAtPath:filePath]) {
[urlData writeToFile:filePath atomically:YES];
}
NSString *rootPath=[NSHomeDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:#"Documents"]];
NSString *filePath=[rootPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.ttf",fontFileName]];
NSURL * fonturl = [NSURL fileURLWithPath:filePath];
CGDataProviderRef fontDataProvider = CGDataProviderCreateWithURL((__bridge CFURLRef)fonturl);
CGFontRef newFont = CGFontCreateWithDataProvider(fontDataProvider);
NSString * newFontName = (__bridge NSString *)CGFontCopyPostScriptName(newFont);
CGDataProviderRelease(fontDataProvider);
CFErrorRef fonterror;
CTFontManagerRegisterGraphicsFont(newFont, &fonterror);
CGFontRelease(newFont);
UIFont * finalFont = [UIFont fontWithName:newFontName size:20.0f];
[txt_UserName setFont:finalFont];
});
});
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
Sample Code Here
It will look like
There is a class created by the guys at Zynga which makes it possible to load any custom fonts: FontLabel.
You have to call [FontManager loadFont:] in your application startup (for example in your app delegate) for each font that you want to use in your app.
Therefore is non-trivial to iterate in the Documents folder looking for .ttf files (the library works only with ttf font).
A little notice: this class use a subclass of UILabel.
extension UIFont {
func registerNewFontFromAppBundle(withSize: CGFloat) {
guard let filePath = Bundle.main.url(forResource: "Art Brewery", withExtension: "ttf") else { return }
guard let dataProvider = CGDataProvider(url: filePath as CFURL), let cgFont = CGFont(dataProvider) else { return }
var error: Unmanaged<CFError>?
if !CTFontManagerRegisterGraphicsFont(cgFont, &error)
{
print("Error registering Font")
} else {
guard let uiFont = UIFont(name: cgFont.postScriptName! as String, size: withSize) else { return }
CurrentTheme.shared.currentFont = uiFont
}
}
func registerNewFontFromDownloadedFiles(withSize: CGFloat) {
guard let filePath = FileUtils().getFilePathAtDocumentFolder(fileName: "Art Brewery.ttf") else { return }
if FileUtils.fileExists(atPath: filePath) {
let url = URL(fileURLWithPath: filePath)
guard let dataProvider = CGDataProvider(url: url as CFURL), let cgFont = CGFont(dataProvider) else { return }
var error: Unmanaged<CFError>?
if !CTFontManagerRegisterGraphicsFont(cgFont, &error)
{
print("Error registering Font")
} else {
guard let uiFont = UIFont(name: cgFont.postScriptName! as String, size: withSize) else { return }
CurrentTheme.shared.currentFont = uiFont
CurrentTheme.shared.currentFontName = cgFont.postScriptName! as String
}
}
}
}
Usage :
UIFont.registerNewFontFromAppBundle(withSize: 30)
UIFont.registerNewFontFromDownloadedFiles(withSize: 30)
mylabel.font = CurrentTheme.shared.currentFont // saved the font in a Singleton
or
mylabel.font = UIFont(name: CurrentTheme.shared.currentFontName, size: 30) // Saved the Font name to reuse
Using the new asset library framework available in iOS 4 i see that I can get the url for a given video using the UIImagePickerControllerReferenceURL. The url returned is in the following format:
assets-library://asset/asset.M4V?id=1000000004&ext=M4V
I am trying to upload this video to a website so as a quick proof of concept I am trying the following
NSData *data = [NSData dataWithContentsOfURL:videourl];
[data writeToFile:tmpfile atomically:NO];
Data is never initialized in this case. Has anyone managed to access the url directly via the new assets library? Thanks for your help.
I use the following category on ALAsset:
static const NSUInteger BufferSize = 1024*1024;
#implementation ALAsset (Export)
- (BOOL) exportDataToURL: (NSURL*) fileURL error: (NSError**) error
{
[[NSFileManager defaultManager] createFileAtPath:[fileURL path] contents:nil attributes:nil];
NSFileHandle *handle = [NSFileHandle fileHandleForWritingToURL:fileURL error:error];
if (!handle) {
return NO;
}
ALAssetRepresentation *rep = [self defaultRepresentation];
uint8_t *buffer = calloc(BufferSize, sizeof(*buffer));
NSUInteger offset = 0, bytesRead = 0;
do {
#try {
bytesRead = [rep getBytes:buffer fromOffset:offset length:BufferSize error:error];
[handle writeData:[NSData dataWithBytesNoCopy:buffer length:bytesRead freeWhenDone:NO]];
offset += bytesRead;
} #catch (NSException *exception) {
free(buffer);
return NO;
}
} while (bytesRead > 0);
free(buffer);
return YES;
}
#end
This is not the best way to do this. I am answering this question in case another SO user comes across the same issue.
Basically my need was to be able to spool the video file to a tmp file so I can upload it to a website using ASIHTTPFormDataRequest. There is probably a way of streaming from the asset url to the ASIHTTPFormDataRequest upload but I could not figure it out. Instead I wrote the following function to drop the file to a tmp file to add to ASIHTTPFormDataRequest.
+(NSString*) videoAssetURLToTempFile:(NSURL*)url
{
NSString * surl = [url absoluteString];
NSString * ext = [surl substringFromIndex:[surl rangeOfString:#"ext="].location + 4];
NSTimeInterval ti = [[NSDate date]timeIntervalSinceReferenceDate];
NSString * filename = [NSString stringWithFormat: #"%f.%#",ti,ext];
NSString * tmpfile = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
{
ALAssetRepresentation * rep = [myasset defaultRepresentation];
NSUInteger size = [rep size];
const int bufferSize = 8192;
NSLog(#"Writing to %#",tmpfile);
FILE* f = fopen([tmpfile cStringUsingEncoding:1], "wb+");
if (f == NULL) {
NSLog(#"Can not create tmp file.");
return;
}
Byte * buffer = (Byte*)malloc(bufferSize);
int read = 0, offset = 0, written = 0;
NSError* err;
if (size != 0) {
do {
read = [rep getBytes:buffer
fromOffset:offset
length:bufferSize
error:&err];
written = fwrite(buffer, sizeof(char), read, f);
offset += read;
} while (read != 0);
}
fclose(f);
};
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror)
{
NSLog(#"Can not get asset - %#",[myerror localizedDescription]);
};
if(url)
{
ALAssetsLibrary* assetslibrary = [[[ALAssetsLibrary alloc] init] autorelease];
[assetslibrary assetForURL:url
resultBlock:resultblock
failureBlock:failureblock];
}
return tmpfile;
}
Here is a clean swift solution to get videos as NSData.
It uses the Photos framework as ALAssetLibrary is deprecated as of iOS9:
IMPORTANT
The Assets Library framework is deprecated as of iOS 9.0. Instead, use the Photos framework instead, which in iOS 8.0 and later provides more features and better performance for working with a user’s photo library. For more information, see Photos Framework Reference.
import Photos
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
self.dismissViewControllerAnimated(true, completion: nil)
if let referenceURL = info[UIImagePickerControllerReferenceURL] as? NSURL {
let fetchResult = PHAsset.fetchAssetsWithALAssetURLs([referenceURL], options: nil)
if let phAsset = fetchResult.firstObject as? PHAsset {
PHImageManager.defaultManager().requestAVAssetForVideo(phAsset, options: PHVideoRequestOptions(), resultHandler: { (asset, audioMix, info) -> Void in
if let asset = asset as? AVURLAsset {
let videoData = NSData(contentsOfURL: asset.URL)
// optionally, write the video to the temp directory
let videoPath = NSTemporaryDirectory() + "tmpMovie.MOV"
let videoURL = NSURL(fileURLWithPath: videoPath)
let writeResult = videoData?.writeToURL(videoURL, atomically: true)
if let writeResult = writeResult where writeResult {
print("success")
}
else {
print("failure")
}
}
})
}
}
}
There you go...
AVAssetExportSession* m_session=nil;
-(void)export:(ALAsset*)asset withHandler:(void (^)(NSURL* url, NSError* error))handler
{
ALAssetRepresentation* representation=asset.defaultRepresentation;
m_session=[AVAssetExportSession exportSessionWithAsset:[AVURLAsset URLAssetWithURL:representation.url options:nil] presetName:AVAssetExportPresetPassthrough];
m_session.outputFileType=AVFileTypeQuickTimeMovie;
m_session.outputURL=[NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:#"%f.mov",[NSDate timeIntervalSinceReferenceDate]]]];
[m_session exportAsynchronouslyWithCompletionHandler:^
{
if (m_session.status!=AVAssetExportSessionStatusCompleted)
{
NSError* error=m_session.error;
m_session=nil;
handler(nil,error);
return;
}
NSURL* url=m_session.outputURL;
m_session=nil;
handler(url,nil);
}];
}
You can use a different preset key if you wish to re-encode the movie (AVAssetExportPresetMediumQuality for example)
Here is the Objective C solution of Alonzo answer, Using photos framework
-(NSURL*)createVideoCopyFromReferenceUrl:(NSURL*)inputUrlFromVideoPicker{
NSURL __block *videoURL;
PHFetchResult *phAssetFetchResult = [PHAsset fetchAssetsWithALAssetURLs:#[inputUrlFromVideoPicker ] options:nil];
PHAsset *phAsset = [phAssetFetchResult firstObject];
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[[PHImageManager defaultManager] requestAVAssetForVideo:phAsset options:nil resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) {
if ([asset isKindOfClass:[AVURLAsset class]]) {
NSURL *url = [(AVURLAsset *)asset URL];
NSLog(#"Final URL %#",url);
NSData *videoData = [NSData dataWithContentsOfURL:url];
// optionally, write the video to the temp directory
NSString *videoPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:#"%f.mp4",[NSDate timeIntervalSinceReferenceDate]]];
videoURL = [NSURL fileURLWithPath:videoPath];
BOOL writeResult = [videoData writeToURL:videoURL atomically:true];
if(writeResult) {
NSLog(#"video success");
}
else {
NSLog(#"video failure");
}
dispatch_group_leave(group);
// use URL to get file content
}
}];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
return videoURL;
}
this from Zoul's Answer
thanks
Similar Code in Xamarin C#
Xamarin C# Equivalent
IntPtr buffer = CFAllocator.Malloc.Allocate(representation.Size);
NSError error;
nuint buffered = representation.GetBytes(buffer, Convert.ToInt64(0.0),Convert.ToUInt32(representation.Size),out error);
NSData sourceData = NSData.FromBytesNoCopy(buffer,buffered,true);
NSFileManager fileManager = NSFileManager.DefaultManager;
NSFileAttributes attr = NSFileAttributes.FromDictionary(NSDictionary.FromFile(outputPath));
fileManager.CreateFile(outputPath, sourceData,attr);
I have a URL like myApp://action/1?parameter=2&secondparameter=3
With the property query I get following part of my URL
parameter=2&secondparameter=3
Is there any way easy to put this in a NSDictionary or an Array?
Thx a lot
You can use queryItems in URLComponents.
When you get this property’s value, the NSURLComponents class parses the query string and returns an array of NSURLQueryItem objects, each of which represents a single key-value pair, in the order in which they appear in the original query string.
Swift
let url = "http://example.com?param1=value1¶m2=param2"
let queryItems = URLComponents(string: url)?.queryItems
let param1 = queryItems?.filter({$0.name == "param1"}).first
print(param1?.value)
Alternatively, you can add an extension on URL to make things easier.
extension URL {
var queryParameters: QueryParameters { return QueryParameters(url: self) }
}
class QueryParameters {
let queryItems: [URLQueryItem]
init(url: URL?) {
queryItems = URLComponents(string: url?.absoluteString ?? "")?.queryItems ?? []
print(queryItems)
}
subscript(name: String) -> String? {
return queryItems.first(where: { $0.name == name })?.value
}
}
You can then access the parameter by its name.
let url = "http://example.com?param1=value1¶m2=param2"
print(url.queryParameters["param1"])
I had reason to write some extensions for this behavior that might come in handy. First the header:
#import <Foundation/Foundation.h>
#interface NSString (XQueryComponents)
- (NSString *)stringByDecodingURLFormat;
- (NSString *)stringByEncodingURLFormat;
- (NSMutableDictionary *)dictionaryFromQueryComponents;
#end
#interface NSURL (XQueryComponents)
- (NSMutableDictionary *)queryComponents;
#end
#interface NSDictionary (XQueryComponents)
- (NSString *)stringFromQueryComponents;
#end
These methods extend NSString, NSURL, and NSDictionary, to allow you to convert to and from query components strings and dictionary objects containing the results.
Now the related .m code:
#import "XQueryComponents.h"
#implementation NSString (XQueryComponents)
- (NSString *)stringByDecodingURLFormat
{
NSString *result = [self stringByReplacingOccurrencesOfString:#"+" withString:#" "];
result = [result stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
return result;
}
- (NSString *)stringByEncodingURLFormat
{
NSString *result = [self stringByReplacingOccurrencesOfString:#" " withString:#"+"];
result = [result stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
return result;
}
- (NSMutableDictionary *)dictionaryFromQueryComponents
{
NSMutableDictionary *queryComponents = [NSMutableDictionary dictionary];
for(NSString *keyValuePairString in [self componentsSeparatedByString:#"&"])
{
NSArray *keyValuePairArray = [keyValuePairString componentsSeparatedByString:#"="];
if ([keyValuePairArray count] < 2) continue; // Verify that there is at least one key, and at least one value. Ignore extra = signs
NSString *key = [[keyValuePairArray objectAtIndex:0] stringByDecodingURLFormat];
NSString *value = [[keyValuePairArray objectAtIndex:1] stringByDecodingURLFormat];
NSMutableArray *results = [queryComponents objectForKey:key]; // URL spec says that multiple values are allowed per key
if(!results) // First object
{
results = [NSMutableArray arrayWithCapacity:1];
[queryComponents setObject:results forKey:key];
}
[results addObject:value];
}
return queryComponents;
}
#end
#implementation NSURL (XQueryComponents)
- (NSMutableDictionary *)queryComponents
{
return [[self query] dictionaryFromQueryComponents];
}
#end
#implementation NSDictionary (XQueryComponents)
- (NSString *)stringFromQueryComponents
{
NSString *result = nil;
for(__strong NSString *key in [self allKeys])
{
key = [key stringByEncodingURLFormat];
NSArray *allValues = [self objectForKey:key];
if([allValues isKindOfClass:[NSArray class]])
for(__strong NSString *value in allValues)
{
value = [[value description] stringByEncodingURLFormat];
if(!result)
result = [NSString stringWithFormat:#"%#=%#",key,value];
else
result = [result stringByAppendingFormat:#"&%#=%#",key,value];
}
else {
NSString *value = [[allValues description] stringByEncodingURLFormat];
if(!result)
result = [NSString stringWithFormat:#"%#=%#",key,value];
else
result = [result stringByAppendingFormat:#"&%#=%#",key,value];
}
}
return result;
}
#end
Something like that:
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
for (NSString *param in [url componentsSeparatedByString:#"&"]) {
NSArray *elts = [param componentsSeparatedByString:#"="];
if([elts count] < 2) continue;
[params setObject:[elts lastObject] forKey:[elts firstObject]];
}
Note : This is sample code. All error cases are not managed.
Try this ;)!
NSString *query = #"parameter=2&secondparameter=3"; // replace this with [url query];
NSArray *components = [query componentsSeparatedByString:#"&"];
NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
for (NSString *component in components) {
NSArray *subcomponents = [component componentsSeparatedByString:#"="];
[parameters setObject:[[subcomponents objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]
forKey:[[subcomponents objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
}
All previous posts do not do the url encoding properly. I would suggest the following methods:
+(NSString*)concatenateQuery:(NSDictionary*)parameters {
if([parameters count]==0) return nil;
NSMutableString* query = [NSMutableString string];
for(NSString* parameter in [parameters allKeys])
[query appendFormat:#"&%#=%#",[parameter stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet],[[parameters objectForKey:parameter] stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet]];
return [[query substringFromIndex:1] copy];
}
+(NSDictionary*)splitQuery:(NSString*)query {
if([query length]==0) return nil;
NSMutableDictionary* parameters = [NSMutableDictionary dictionary];
for(NSString* parameter in [query componentsSeparatedByString:#"&"]) {
NSRange range = [parameter rangeOfString:#"="];
if(range.location!=NSNotFound)
[parameters setObject:[[parameter substringFromIndex:range.location+range.length] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] forKey:[[parameter substringToIndex:range.location] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
else [parameters setObject:[[NSString alloc] init] forKey:[parameter stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
}
return [parameters copy];
}
According to the already very clean answer of Onato I wrote an extension for NSURL in Swift where you can get a query param like this:
e.g. the URL contains the pair param=some_value
let queryItem = url.queryItemForKey("param")
let value = queryItem.value // would get String "someValue"
The extension looks like:
extension NSURL {
var allQueryItems: [NSURLQueryItem] {
get {
let components = NSURLComponents(URL: self, resolvingAgainstBaseURL: false)!
let allQueryItems = components.queryItems!
return allQueryItems as [NSURLQueryItem]
}
}
func queryItemForKey(key: String) -> NSURLQueryItem? {
let predicate = NSPredicate(format: "name=%#", key)!
return (allQueryItems as NSArray).filteredArrayUsingPredicate(predicate).first as? NSURLQueryItem
}
}
Here is the extension in swift:
extension NSURL{
func queryParams() -> [String:AnyObject] {
var info : [String:AnyObject] = [String:AnyObject]()
if let queryString = self.query{
for parameter in queryString.componentsSeparatedByString("&"){
let parts = parameter.componentsSeparatedByString("=")
if parts.count > 1{
let key = (parts[0] as String).stringByReplacingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
let value = (parts[1] as String).stringByReplacingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
if key != nil && value != nil{
info[key!] = value
}
}
}
}
return info
}
}
The preferred way to deal with URLs is now NSURLComponents. In particular the queryItems property which returns an NSArray of params.
If you want the params in a NSDictionary, here's a method:
+(NSDictionary<NSString *, NSString *>*)queryParamsFromURL:(NSURL*)url
{
NSURLComponents* urlComponents = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
NSMutableDictionary<NSString *, NSString *>* queryParams = [NSMutableDictionary<NSString *, NSString *> new];
for (NSURLQueryItem* queryItem in [urlComponents queryItems])
{
if (queryItem.value == nil)
{
continue;
}
[queryParams setObject:queryItem.value forKey:queryItem.name];
}
return queryParams;
}
Caveat: URLs can have repeated params, but the dictionary will only contain the last value of any duplicated param. If that is undesirable, use the queryItems array directly.
For those using Bolts Framework you can use:
NSDictionary *parameters = [BFURL URLWithURL:yourURL].inputQueryParameters;
Remember to import:
#import <Bolts/BFURL.h>
If you happen to have Facebook SDK in your project, you also have Bolts. Facebook is using this framework as a dependency.
Swift 2.1
Oneliner:
"p1=v1&p2=v2".componentsSeparatedByString("&").map {
$0.componentsSeparatedByString("=")
}.reduce([:]) {
(var dict: [String:String], p) in
dict[p[0]] = p[1]
return dict
}
// ["p1": "v1", "p2": "v2"]
Used as an extension on NSURL:
extension NSURL {
/**
* URL query string as dictionary. Empty dictionary if query string is nil.
*/
public var queryValues : [String:String] {
get {
if let q = self.query {
return q.componentsSeparatedByString("&").map {
$0.componentsSeparatedByString("=")
}.reduce([:]) {
(var dict: [String:String], p) in
dict[p[0]] = p[1]
return dict
}
} else {
return [:]
}
}
}
}
Example:
let url = NSURL(string: "http://example.com?p1=v1&p2=v2")!
let queryDict = url.queryValues
// ["p1": "v1", "p2": "v2"]
Please note, if using OS X 10.10 or iOS 8 (or later), it's probably better to use NSURLComponents and the queryItems property and create the dictionary from the NSURLQueryItems directly.
Here's a NSURLComponents based NSURL extension solution:
extension NSURL {
/// URL query string as a dictionary. Empty dictionary if query string is nil.
public var queryValues : [String:String] {
get {
guard let components = NSURLComponents(URL: self, resolvingAgainstBaseURL: false) else {
return [:]
}
guard let queryItems = components.queryItems else {
return [:]
}
var result:[String:String] = [:]
for q in queryItems {
result[q.name] = q.value
}
return result
}
}
}
A footnote to the NSURL extension is that it's actually possible in Swift to give the property the same name as the existing string property—query. I didn't know until I tried it, but the polymorphism in Swift lets you differ only on the return type. So if the extended NSURL property is public var query: [String:String] it works. I didn't use this in the example as I find it a little bit crazy, but it does work ...
I published a simple class doing the job under MIT:
https://github.com/anegmawad/URLQueryToCocoa
With it you can have arrays and objects in the query, which are collected and glued together
For Example
users[0][firstName]=Amin&users[0][lastName]=Negm&name=Devs&users[1][lastName]=Kienle&users[1][firstName]=Christian
will become:
#{
name : #"Devs",
users :
#[
#{
firstName = #"Amin",
lastName = #"Negm"
},
#{
firstName = #"Christian",
lastName = #"Kienle"
}
]
}
You can think of it as a URL query counterpart of NSJSONSerializer.
It looks that you are using it to process incoming data from another iOS application. If so, this is what I use for the same purpose.
Initial call (e.g. in external application):
UIApplication *application = [UIApplication sharedApplication];
NSURL *url = [NSURL URLWithString:#"myApp://action/1?parameter=2&secondparameter=3"];
if ([application canOpenURL:url]) {
[application openURL:url];
NSLog(#"myApp is installed");
} else {
NSLog(#"myApp is not installed");
}
Method to extract QueryString data from NSURL and save as NSDictionary:
-(NSDictionary *) getNSDictionaryFromQueryString:(NSURL *)url {
NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
NSRange needle = [url.absoluteString rangeOfString:#"?" options:NSCaseInsensitiveSearch];
NSString *data = nil;
if(needle.location != NSNotFound) {
NSUInteger start = needle.location + 1;
NSUInteger end = [url.absoluteString length] - start;
data = [url.absoluteString substringWithRange:NSMakeRange(start, end)];
}
for (NSString *param in [data componentsSeparatedByString:#"&"]) {
NSArray *keyvalue = [param componentsSeparatedByString:#"="];
if([keyvalue count] == 2){
[result setObject:[keyvalue objectAtIndex:1] forKey:[keyvalue objectAtIndex:0]];
}
}
return result;
}
Usage:
NSDictionary *result = [self getNSDictionaryFromQueryString:url];
This class is a nice solution for url parsing.
.h file
#interface URLParser : NSObject {
NSArray *variables;
}
#property (nonatomic, retain) NSArray *variables;
- (id)initWithURLString:(NSString *)url;
- (NSString *)valueForVariable:(NSString *)varName;
#end
.m file
#import "URLParser.h"
#implementation URLParser
#synthesize variables;
- (id) initWithURLString:(NSString *)url{
self = [super init];
if (self != nil) {
NSString *string = url;
NSScanner *scanner = [NSScanner scannerWithString:string];
[scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:#"&?"]];
NSString *tempString;
NSMutableArray *vars = [NSMutableArray new];
[scanner scanUpToString:#"?" intoString:nil]; //ignore the beginning of the string and skip to the vars
while ([scanner scanUpToString:#"&" intoString:&tempString]) {
[vars addObject:[tempString copy]];
}
self.variables = vars;
}
return self;
}
- (NSString *)valueForVariable:(NSString *)varName {
for (NSString *var in self.variables) {
if ([var length] > [varName length]+1 && [[var substringWithRange:NSMakeRange(0, [varName length]+1)] isEqualToString:[varName stringByAppendingString:#"="]]) {
NSString *varValue = [var substringFromIndex:[varName length]+1];
return varValue;
}
}
return nil;
}
#end
Hendrik wrote a nice example for extension in this question, however I had to re-write it to not use any objective-c library methods. Using NSArray in swift is not the correct approach.
This is the result, all swift and a bit more safe. The usage example will be less lines of code with Swift 1.2.
public extension NSURL {
/*
Set an array with all the query items
*/
var allQueryItems: [NSURLQueryItem] {
get {
let components = NSURLComponents(URL: self, resolvingAgainstBaseURL: false)!
if let allQueryItems = components.queryItems {
return allQueryItems as [NSURLQueryItem]
} else {
return []
}
}
}
/**
Get a query item form the URL query
:param: key The parameter to fetch from the URL query
:returns: `NSURLQueryItem` the query item
*/
public func queryItemForKey(key: String) -> NSURLQueryItem? {
let filteredArray = filter(allQueryItems) { $0.name == key }
if filteredArray.count > 0 {
return filteredArray.first
} else {
return nil
}
}
}
Usage:
let queryItem = url.queryItemForKey("myItem")
Or, more detailed usage:
if let url = NSURL(string: "http://www.domain.com/?myItem=something") {
if let queryItem = url.queryItemForKey("myItem") {
if let value = queryItem.value {
println("The value of 'myItem' is: \(value)")
}
}
}
try this:
-(NSDictionary *)getUrlParameters:(NSString *)url{
NSArray *justParamsArr = [url componentsSeparatedByString:#"?"];
url = [justParamsArr lastObject];
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
for (NSString *param in [url componentsSeparatedByString:#"&"]) {
NSArray *elts = [param componentsSeparatedByString:#"="];
if([elts count] < 2) continue;
[params setObject:[elts lastObject] forKey:[elts firstObject]];
}
return params;
}
Fairly compact approach:
func stringParamsToDict(query: String) -> [String: String] {
let params = query.components(separatedBy: "&").map {
$0.components(separatedBy: "=")
}.reduce(into: [String: String]()) { dict, pair in
if pair.count == 2 {
dict[pair[0]] = pair[1]
}
}
return params
}
Most robust solution if you are using a URL to pass data from the web app to the phone and you want to pass arrays, numbers, strings, ...
JSON encode your object in PHP
header("Location: myAppAction://".urlencode(json_encode($YOUROBJECT)));
And JSON decode the result in iOS
NSData *data = [[[request URL] host] dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *packed = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];