How to extract and remove scheme name from NSURL? - ios

What is proper way to extract and remove scheme name and :// from a NSURL?
For example:
note://Hello -> #"Hello"
calc://3+4/5 -> #"3+4/5"
so
NSString *scheme = #"note://";
NSString *path = #"Hello";
for later use in:
[[NSNotificationCenter defaultCenter] postNotificationName:scheme object:path];

You can look at it like this (mostly untested code, but you get the idea):
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
{
NSLog(#"url: %#", url);
NSLog(#"scheme: %#", [url scheme]);
NSLog(#"query: %#", [url query]);
NSLog(#"host: %#", [url host]);
NSLog(#"path: %#", [url path]);
NSDictionary * dict = [self parseQueryString:[url query]];
NSLog(#"query dict: %#", dict);
}
So you can do this:
NSString * strNoURLScheme =
[strMyURLWithScheme stringByReplacingOccurrencesOfString:[url scheme] withString:#""];
NSLog(#"URL without scheme: %#", strNoURLScheme);
parseQueryString
- (NSDictionary *)parseQueryString:(NSString *)query
{
NSMutableDictionary *dict = [[[NSMutableDictionary alloc] initWithCapacity:6] autorelease];
NSArray *pairs = [query componentsSeparatedByString:#"&"];
for (NSString *pair in pairs) {
NSArray *elements = [pair componentsSeparatedByString:#"="];
NSString *key = [[elements objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *val = [[elements objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[dict setObject:val forKey:key];
}
return dict;
}

My inclination is to roll your own string searching for this:
NSRange dividerRange = [urlString rangeOfString:#"://"];
NSUInteger divide = NSMaxRange(dividerRange);
NSString *scheme = [urlString substringToIndex:divide];
NSString *path = [urlString substringFromIndex:divide];
This does what you've asked for in quite a literal fashion, dividing the URL in half around its scheme. For more advanced handling, you'll have to provide more details.

just use the NSURL's API named: resourceSpecifier :)
and remove the two slashes from the beginning of the string:
NSURL *url = [NSURL URLWithString:#"https://some.url.com/path];
url.resourceSpecifier will result in: //some.url.com/path

Remember, don't fight the frameworks especially when it comes to NSURL. This SO answer has a good breakdown of its abilities. https://stackoverflow.com/a/3693009/142358
NSURL's scheme and path properties are exactly what you want which (assuming the rest of your URL looks like a path) leaves you with this:
NSString *schemeWithDelimiter = [NSString stringWithFormat:#"%#://",[myURL scheme]];
[[NSNotificationCenter defaultCenter] postNotificationName: schemeWithDelimiter object:[myURL path];
No string searching needed!

Swift5, URL extension:
var resourceSpecifier: String {
get {
let nrl : NSURL = self as NSURL
return nrl.resourceSpecifier ?? self.absoluteString
}
}
var simpleSpecifier: String {
get {
let str = self.resourceSpecifier
return str[2..<str.count]
}
}

Related

How do I get the file metadata when located in the /Documents folder?

I am building a music player app and my .mp3 files come from the /Documents directiory in iOS 11. Thing is that I found out after a research how to read those files from the folder.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSError *error;
NSArray *directoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:documentsPath error:&error];
NSLog(#"%#", directoryContents);
The above code gets the the file name which I will print out later in a UITableViewController.
My issue is that now I dont know how to read the .mp3's metadata from the /Documents folder such as artist, or album artwork.
If you have a path to a .mp3 file, try doing this:
- (void)getMetaDataForSong:(NSString *)pathToMP3File {
// pathToMP3File should looks something like this:
// /var/mobile/Applications/741647B1-1341-4203-8CFA-9D0C555D670A/Library/Caches/All Summer Long.m4a
NSURL *mp3FileURL = [NSURL fileURLWithPath: pathToMP3File];
NSLog(#"%#", [mp3FileURL absoluteString]);
asset = [[AVURLAsset alloc] initWithURL: mp3FileURL options:nil];
NSLog(#"%#", asset);
for (NSString *format in [asset availableMetadataFormats]) {
for (AVMetadataItem *item in [asset metadataForFormat:format]) {
if ([[item commonKey] isEqualToString:#"title"]) {
musicItem.strSongTitle = (NSString *)[item value];
}
if ([[item commonKey] isEqualToString:#"artist"]) {
musicItem.strArtistName = (NSString *)[item value];
}
if ([[item commonKey] isEqualToString:#"albumName"]) {
musicItem.strAlbumName = (NSString *)[item value];
}
if ([[item commonKey] isEqualToString:#"artwork"]) {
UIImage *img = nil;
if ([item.keySpace isEqualToString:AVMetadataKeySpaceiTunes]) {
img = [UIImage imageWithData:[item.value copyWithZone:nil]];
}
else { // if ([item.keySpace isEqualToString:AVMetadataKeySpaceID3]) {
NSData *data = [(NSDictionary *)[item value] objectForKey:#"data"];
img = [UIImage imageWithData:data] ;
}
musicItem.imgArtwork = img;
}
}
}
}
This code comes straight from this related question.

XCDYouTubeVideoPlayerViewController videoURL coming as nil

In this method:
- (void) connectionDidFinishLoading:(NSURLConnection *)connection
{
NSError *error = nil;
NSURL *videoURL = [self videoURLWithData:self.connectionData error:&error];
if (videoURL)
self.moviePlayer.contentURL = videoURL;
else if (self.elFields.count > 0)
[self startVideoInfoRequest];
else
[self finishWithError:error];
}
videoURL is returned as nil and hence, it is going to the error block. Youtube video id that I am using is "5Uls9v1nnss". What seems to be the issue?
the videoURLWithData method which is used to retrieve the videoURL is this :
- (NSURL *) videoURLWithData:(NSData *)data error:(NSError * __autoreleasing *)error
{
NSString *videoQuery = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSStringEncoding queryEncoding = NSUTF8StringEncoding;
NSDictionary *video = DictionaryWithQueryString(videoQuery, queryEncoding);
NSMutableArray *streamQueries = [[video[#"url_encoded_fmt_stream_map"] componentsSeparatedByString:#","] mutableCopy];
[streamQueries addObjectsFromArray:[video[#"adaptive_fmts"] componentsSeparatedByString:#","]];
NSMutableDictionary *streamURLs = [NSMutableDictionary new];
for (NSString *streamQuery in streamQueries)
{
NSDictionary *stream = DictionaryWithQueryString(streamQuery, queryEncoding);
NSString *type = stream[#"type"];
NSString *urlString = stream[#"url"];
if (urlString && [AVURLAsset isPlayableExtendedMIMEType:type])
{
NSURL *streamURL = [NSURL URLWithString:urlString];
NSString *signature = stream[#"sig"];
if (signature)
streamURL = [NSURL URLWithString:[NSString stringWithFormat:#"%#&signature=%#", urlString, signature]];
if ([[DictionaryWithQueryString(streamURL.query, queryEncoding) allKeys] containsObject:#"signature"])
streamURLs[#([stream[#"itag"] integerValue])] = streamURL;
}
}
for (NSNumber *videoQuality in self.preferredVideoQualities)
{
NSURL *streamURL = streamURLs[videoQuality];
if (streamURL)
{
NSString *title = video[#"title"];
NSString *thumbnailSmall = video[#"thumbnail_url"];
NSString *thumbnailMedium = video[#"iurlsd"];
NSString *thumbnailLarge = video[#"iurlmaxres"];
NSMutableDictionary *userInfo = [NSMutableDictionary new];
if (title)
userInfo[XCDMetadataKeyTitle] = title;
if (thumbnailSmall)
userInfo[XCDMetadataKeySmallThumbnailURL] = [NSURL URLWithString:thumbnailSmall];
if (thumbnailMedium)
userInfo[XCDMetadataKeyMediumThumbnailURL] = [NSURL URLWithString:thumbnailMedium];
if (thumbnailLarge)
userInfo[XCDMetadataKeyLargeThumbnailURL] = [NSURL URLWithString:thumbnailLarge];
[[NSNotificationCenter defaultCenter] postNotificationName:XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification object:self userInfo:userInfo];
return streamURL;
}
}
if (error)
{
NSMutableDictionary *userInfo = [#{ NSURLErrorKey: self.connection.originalRequest.URL } mutableCopy];
NSString *reason = video[#"reason"];
if (reason)
{
reason = [reason stringByReplacingOccurrencesOfString:#"<br\\s*/?>" withString:#" " options:NSRegularExpressionSearch range:NSMakeRange(0, reason.length)];
NSRange range;
while ((range = [reason rangeOfString:#"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
reason = [reason stringByReplacingCharactersInRange:range withString:#""];
userInfo[NSLocalizedDescriptionKey] = reason;
}
NSInteger code = [video[#"errorcode"] integerValue];
*error = [NSError errorWithDomain:XCDYouTubeVideoErrorDomain code:code userInfo:userInfo];
}
return nil;
}
You should use the latest version (2.0.2 as of writing) of XCDYouTubeKit, the successor of XCDYouTubeVideoPlayerViewController. The video 5Uls9v1nnss should play fine.

Text File to NSArray

I have a text file that I download at launch from my website. It saves it to the Documents directory in the app. I want to read and process that text file and turn it into an NSArray.
I tried this:
- (NSArray *)articleReason {
NSString *filename3 = #"GameList.txt";
NSArray *pathArray3 = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString *documentsDirectory3 = [pathArray3 objectAtIndex:0];
NSString *yourSoundPath3 = [documentsDirectory3 stringByAppendingPathComponent:filename3];
NSURL *url = [NSURL fileURLWithPath:yourSoundPath3 isDirectory:NO];
NSString *urlData = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
NSArray *parsed = [urlData componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
NSIndexSet *indexes = [parsed indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
NSRange range = [(NSString *)obj rangeOfString:#"Reason:"];
if (range.location != NSNotFound)
{
return YES;
}
return NO;
}];
NSArray *disallowed = [parsed objectsAtIndexes:indexes];
NSString * myString = [disallowed componentsJoinedByString:#" "];
disallowed = [myString componentsSeparatedByString:#"Reason: "];
return disallowed;
}
This does not work. The thing is, if I download the text file while making the NSArray, it ends up working. Here's that code:
- (NSArray *)articleReason {
NSString *stringURL = kGameURL;
NSURL *url = [NSURL URLWithString:stringURL];
NSString *urlData = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
NSArray *parsed = [urlData componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
NSIndexSet *indexes = [parsed indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
NSRange range = [(NSString *)obj rangeOfString:#"Reason:"];
if (range.location != NSNotFound)
{
return YES;
}
return NO;
}];
NSArray *disallowed = [parsed objectsAtIndexes:indexes];
NSString * myString = [disallowed componentsJoinedByString:#" "];
disallowed = [myString componentsSeparatedByString:#"Reason: "];
return disallowed;
}
Any idea why this is not working? I verified in the Documents directory that the text file is downloading correctly. I do the downloading of the file in didFinishLaunching in the App Delegate. Here's that code:
NSString *stringURL2 = kGameURL;
NSURL *url2 = [NSURL URLWithString:stringURL2];
NSString *urlData2 = [NSString stringWithContentsOfURL:url2 encoding:NSUTF8StringEncoding error:nil];
NSFileManager *fileManager2 = [NSFileManager defaultManager];
NSString *docsDirectory2 = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *path2 = [docsDirectory2 stringByAppendingPathComponent:#"GameList.txt"];
[urlData2 writeToFile:path2 atomically:YES ];
Any help would be greatly appreciated.
Figured it out.
[urlData2 writeToFile:path2 atomically:YES ];
Writeto file needed it include the encoding.

How to determine the file extension of a file from a URL (Objective-C)

I have a URL of type: http://example.com/files/
And I need to determine what file is there (extension and name of file). Depending on extensions, I'll do something.
How can I determine it?
Try
NSString *ext = [[url lastPathComponent] pathExtension];
See https://developer.apple.com/documentation/foundation/nsstring/1407801-pathextension
You can use this code directly just passing your NSUrl and get File Extension in return.
- (NSString *)findExtensionOfFileInUrl:(NSURL *)url{
NSString *urlString = [url absoluteString];
NSArray *componentsArray = [urlString componentsSeparatedByString:#"."];
NSString *fileExtension = [componentsArray lastObject];
return fileExtension;
}
Try This,
NSURL *urll = [NSURL URLWithString:#"http://example.com/files/firstFile.com"];
NSString *filenameWithExtension = [urll lastPathComponent];
NSRange *range = [filenameWithExtension rangeOfString:#"."];
USing the substringWithRange just substring of those ....
NSString *fileName = [string substringWithRange:NSMakeRange(0, range.location)];
NSString *extension = [string substringWithRange:NSMakeRange(10, string.length - range.location-1)];
NSLog(#"%# %#", fileName, extension);

What is the best practic using url string with NSString stringWithFormat

I use NSString stringWithFormat method for create an URL string. But now I have problem with a "quick" editing this string.
For example I have an script on the server that process some request with parameters.
I have an URL string like this:
http://www.domain.com/api/?param1=%#&param2=%#&param3=%#&param4=%#&param5=%#&
but when I have more than 5, 6 parameters it is really hard to modify this string.
Anybody knows best method how to create URL string (I mean when we modify it).
This is a sample of how to add parameters in a safe way. Long but reliable.
NSString* const kBaseURL = #"http://maps.google.com/maps/api/geocode/xml";
NSMutableDictionary *parameterDic = [NSMutableDictionary dictionary];
[parameterDic setObject:#"plaza de la puerta del sol 1, madrid, spain" forKey:#"address"];
[parameterDic setObject:#"false" forKey:#"sensor"];
NSMutableArray *parameters = [NSMutableArray array];
for (__strong NSString *name in parameterDic) {
NSString *value = [parameterDic objectForKey:name];
name = encodeToPercentEscapeString(name);
value = encodeToPercentEscapeString(value);
NSString *queryComponent = [NSString stringWithFormat:#"%#=%#", name, value];
[parameters addObject:queryComponent];
}
NSString *query = [parameters componentsJoinedByString:#"&"];
NSString *urlString = [NSString stringWithFormat:#"%#?%#", kBaseURL, query];
NSURL *url = [NSURL URLWithString:urlString];
NSLog(#"%#",url);
The code above calls this C function because stringByAddingPercentEscapesUsingEncoding won't convert some special characters in the name or value of the parameters. As pointed by Jesse Rusak see Proper URL (Percent) Encoding in iOS for a discussion.
// remove CFBridgingRelease and __bridge if your code is not ARC
NSString* encodeToPercentEscapeString(NSString *string) {
return (NSString *)
CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL,
(__bridge CFStringRef) string,
NULL,
(CFStringRef) #"!*'();:#&=+$,/?%#[]",
kCFStringEncodingUTF8));
}
This prints
http://maps.google.com/maps/api/geocode/xml?sensor=false&address=plaza%20de%20la%20puerta%20del%20sol%201,%20madrid,%20spain
Bonus track: how to deconstruct and rebuild a string:
NSString *stringUrl = #"http://www.google.com:80/a/b/c;params?m=n&o=p#fragment";
NSURL *url = [NSURL URLWithString:stringUrl];
NSLog(#"%#",stringUrl);
NSLog(#" scheme: %#",[url scheme]);
NSLog(#" host: %#",[url host]);
NSLog(#" port: %#",[url port]);
NSLog(#" path: %#",[url path]);
NSLog(#" relativePath: %#",[url relativePath]);
NSLog(#"parameterString: %#",[url parameterString]);
NSLog(#" query: %#",[url query]);
NSLog(#" fragment: %#",[url fragment]);
NSMutableString *s = [NSMutableString string];
[s appendFormat:#"%#://%#",[url scheme],[url host]];
if ([url port]!=nil){
[s appendFormat:#":%#",[url port]];
}
[s appendFormat:#"%#",[url path]];
if ([url parameterString]!=nil){
[s appendFormat:#";%#",[url parameterString]];
}
if ([url query]!=nil){
[s appendFormat:#"?%#",[url query]];
}
if ([url fragment]!=nil){
[s appendFormat:#"#%#",[url fragment]];
}
NSLog(#"%#",s);
This prints
http://www.google.com:80/a/b/c;params?m=n&o=p#fragment
scheme: http
host: www.google.com
port: 80
path: /a/b/c
relativePath: /a/b/c
parameterString: params
query: m=n&o=p
fragment: fragment
I wrote this specially for you, quite simple:
+ (NSString*) URlStringForBaseURL:(NSString*)baseURL withParams:(NSDictionary*)paramsdictonary{
NSString* url = [baseURL stringByAppendingString:#"?"];
NSUInteger index = 0;
for (NSString* key in [paramsdictonary allKeys]) {
index++;
if (index == [paramsdictonary count])
url = [url stringByAppendingFormat:#"%#=%#",key,[paramsdictonary valueForKey:key]];
else
url = [url stringByAppendingFormat:#"%#=%#&",key,[paramsdictonary valueForKey:key]];
}
return url;
}
And you can use it (of course, the order of the URL params is dos not matter):
NSMutableDictionary* params = [NSMutableDictionary dictionary];
[params setValue:#"value1" forKey:#"param1"];
[params setValue:#"value2" forKey:#"param2"];
[params setValue:#"value3" forKey:#"param3"];
NSString* urlStr = [HTMLTextFormat URlStringForBaseURL:#"http://www.domain.com/api/" withParams:params];
NSLog(#"url_: %#",urlStr);

Resources