Let's say I have an NSURL? Whether or not it already has an empty query string, how do I add one or more parameters to the query of the NSURL? I.e., does anyone know of an implementation of this function?
- (NSURL *)URLByAppendingQueryString:(NSString *)queryString
So that it satisfies this NSURL+AdditionsSpec.h file:
#import "NSURL+Additions.h"
#import "Kiwi.h"
SPEC_BEGIN(NSURL_AdditionsSpec)
describe(#"NSURL+Additions", ^{
__block NSURL *aURL;
beforeEach(^{
aURL = [[NSURL alloc] initWithString:#"http://www.example.com"];
aURLWithQuery = [[NSURL alloc] initWithString:#"http://www.example.com?key=value"];
});
afterEach(^{
[aURL release];
[aURLWithQuery release];
});
describe(#"-URLByAppendingQueryString:", ^{
it(#"adds to plain URL", ^{
[[[[aURL URLByAppendingQueryString:#"key=value&key2=value2"] query] should]
equal:#"key=value&key2=value2"];
});
it(#"appends to the existing query sting", ^{
[[[[aURLWithQuery URLByAppendingQueryString:#"key2=value2&key3=value3"] query] should]
equal:#"key=value&key2=value2&key3=value3"];
});
});
});
SPEC_END
Since iOS 7 you can use NSURLComponents that is very simple to use. Take a look on these examples:
Example 1
NSString *urlString = #"https://mail.google.com/mail/u/0/?shva=1#inbox";
NSURLComponents *components = [[NSURLComponents alloc] initWithString:urlString];
NSLog(#"%# - %# - %# - %#", components.scheme, components.host, components.query, components.fragment);
Example 2
NSString *urlString = #"https://mail.google.com/mail/u/0/?shva=1#inbox";
NSURLComponents *components = [[NSURLComponents alloc] initWithString:urlString];
if (components) {
//good URL
} else {
//bad URL
}
Example 3
NSURLComponents *components = [NSURLComponents new];
[components setScheme:#"https"];
[components setHost:#"mail.google.com"];
[components setQuery:#"shva=1"];
[components setFragment:#"inbox"];
[components setPath:#"/mail/u/0/"];
[self.webview loadRequest:[[NSURLRequest alloc] initWithURL:[components URL]]];
But you can do many other things with NSURLComponents take a look on NSURLComponents class reference on Apple documentation or on this link: http://nshipster.com/nsurl/
Here's an implementation that passes your specs:
#implementation NSURL (Additions)
- (NSURL *)URLByAppendingQueryString:(NSString *)queryString {
if (![queryString length]) {
return self;
}
NSString *URLString = [[NSString alloc] initWithFormat:#"%#%#%#", [self absoluteString],
[self query] ? #"&" : #"?", queryString];
NSURL *theURL = [NSURL URLWithString:URLString];
[URLString release];
return theURL;
}
#end
And here is an implementation for NSString:
#implementation NSString (Additions)
- (NSURL *)URLByAppendingQueryString:(NSString *)queryString {
if (![queryString length]) {
return [NSURL URLWithString:self];
}
NSString *URLString = [[NSString alloc] initWithFormat:#"%#%#%#", self,
[self rangeOfString:#"?"].length > 0 ? #"&" : #"?", queryString];
NSURL *theURL = [NSURL URLWithString:URLString];
[URLString release];
return theURL;
}
// Or:
- (NSString *)URLStringByAppendingQueryString:(NSString *)queryString {
if (![queryString length]) {
return self;
}
return [NSString stringWithFormat:#"%#%#%#", self,
[self rangeOfString:#"?"].length > 0 ? #"&" : #"?", queryString];
}
#end
The iOS8+ modern way
adding (or replacing 'ref' value if exists) ref=impm to url which is on min60.com
if ([[url host] hasSuffix:#"min60.com"]) {
NSURLComponents *components = [[NSURLComponents alloc] initWithURL:url resolvingAgainstBaseURL:NO];
NSURLQueryItem * newQueryItem = [[NSURLQueryItem alloc] initWithName:#"ref" value:#"impm"];
NSMutableArray * newQueryItems = [NSMutableArray arrayWithCapacity:[components.queryItems count] + 1];
for (NSURLQueryItem * qi in components.queryItems) {
if (![qi.name isEqual:newQueryItem.name]) {
[newQueryItems addObject:qi];
}
}
[newQueryItems addObject:newQueryItem];
[components setQueryItems:newQueryItems];
url = [components URL];
}
Just a friendly post for those who don't want to write boilerplate code while building NSURL with NSURLComponents.
Since iOS8 we have NSURLQueryItem that helps building URL request freaking fast.
I wrote a little handy category to ease the work, that you can grab here: URLQueryBuilder
Here is example of how easy it is to work with it:
NSString *baseURL = #"https://google.com/search";
NSDictionary *items = #{
#"q" : #"arsenkin.com",
#"hl" : #"en_US",
#"lr" : #"lang_en"
};
NSURL *URL = [NSURL ars_queryWithString:baseURL queryElements:items];
// https://google.com/search?q=arsenkin.com&hl=en_US&lr=lang_en
I have an extension to NSURLComponents that add query item, in swift:
extension NSURLComponents {
func appendQueryItem(name name: String, value: String) {
var queryItems: [NSURLQueryItem] = self.queryItems ?? [NSURLQueryItem]()
queryItems.append(NSURLQueryItem(name: name, value: value))
self.queryItems = queryItems
}
}
To use,
let components = NSURLComponents(string: urlString)!
components.appendQueryItem(name: "key", value: "value")
If you're using RestKit it provides additions to NSString. One of which is:
- (NSString *)stringByAppendingQueryParameters:(NSDictionary *)queryParameters
So you could do:
NSDictionary *shopParams = [NSDictionary dictionaryWithKeysAndObjects:
#"limit",#"20",
#"location",#"latitude,longitude",
nil];
NSString *pathWithQuery = [#"/api/v1/shops.json" stringByAppendingQueryParameters:shopParams]
As others have mentioned, you can use NSURLComponents to construct URLs.
#implementation NSURL (Additions)
- (NSURL *)URLByAppendingQueryParameters:(NSDictionary *)queryParameters
{
NSURLComponents *components = [[NSURLComponents alloc] initWithURL:self resolvingAgainstBaseURL:NO];
NSMutableArray *queryItems = [NSMutableArray array:components.queryItems];
for (NSString *key in [queryParameters allKeys]) {
NSURLQueryItem *queryItem = [[NSURLQueryItem alloc] initWithName:key value:queryParameters[key]];
[queryItems addObject:queryItem];
}
components.queryItems = queryItems;
return [components URL];
}
#end
NSURL is not mutable so you cannot implement this functionality directly based on NSURL. Instead you will have to obtain the string representation of the URL, append your parameters to that and then create a new NSURL.
This does not sound like a good solution. Unless there is a good reason, it is better to work with strings until the last moment and only create an NSURL when you have your fully formed request.
Related
I have an NSURL:
serverCall?x=a&y=b&z=c
What is the quickest and most efficient way to get the value of y?
Thanks
UPDATE:
Since 2010 when this was written, it seems Apple has released a set of tools for that purpose. Please see the answers below for those.
Old-School Solution:
Well I know you said "the quickest way" but after I started doing a test with NSScanner I just couldn't stop. And while it is not the shortest way, it is sure handy if you are planning to use that feature a lot. I created a URLParser class that gets these vars using an NSScanner. The use is a simple as:
URLParser *parser = [[[URLParser alloc] initWithURLString:#"http://blahblahblah.com/serverCall?x=a&y=b&z=c&flash=yes"] autorelease];
NSString *y = [parser valueForVariable:#"y"];
NSLog(#"%#", y); //b
NSString *a = [parser valueForVariable:#"a"];
NSLog(#"%#", a); //(null)
NSString *flash = [parser valueForVariable:#"flash"];
NSLog(#"%#", flash); //yes
And the class that does this is the following (*source files at the bottom of the post):
URLParser.h
#interface URLParser : NSObject {
NSArray *variables;
}
#property (nonatomic, retain) NSArray *variables;
- (id)initWithURLString:(NSString *)url;
- (NSString *)valueForVariable:(NSString *)varName;
#end
URLParser.m
#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;
[vars release];
}
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;
}
- (void) dealloc{
self.variables = nil;
[super dealloc];
}
#end
*if you don't like copying and pasting you can just download the source files - I made a quick blog post about this here.
So many custom url parsers here, remember NSURLComponents is your friend!
Here is an example where I pull out a url encoded parameter for "page"
Swift
let myURL = "www.something.com?page=2"
var pageNumber : Int?
if let queryItems = NSURLComponents(string: myURL)?.queryItems {
for item in queryItems {
if item.name == "page" {
if let itemValue = item.value {
pageNumber = Int(itemValue)
}
}
}
}
print("Found page number: \(pageNumber)")
Objective-C
NSString *myURL = #"www.something.com?page=2";
NSURLComponents *components = [NSURLComponents componentsWithString:myURL];
NSNumber *page = nil;
for(NSURLQueryItem *item in components.queryItems)
{
if([item.name isEqualToString:#"page"])
page = [NSNumber numberWithInteger:item.value.integerValue];
}
"Why reinvent the wheel!" - Someone Smart
I'm pretty sure you have to parse it yourself. However, it's not too bad:
NSString * q = [myURL query];
NSArray * pairs = [q componentsSeparatedByString:#"&"];
NSMutableDictionary * kvPairs = [NSMutableDictionary dictionary];
for (NSString * pair in pairs) {
NSArray * bits = [pair componentsSeparatedByString:#"="];
NSString * key = [[bits objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString * value = [[bits objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[kvPairs setObject:value forKey:key];
}
NSLog(#"y = %#", [kvPairs objectForKey:#"y"]);
In Swift you can use NSURLComponents to parse the query string of an NSURL into an [AnyObject].
You can then create a dictionary from it (or access the items directly) to get at the key/value pairs. As an example this is what I am using to parse a NSURL variable url:
let urlComponents = NSURLComponents(URL: url, resolvingAgainstBaseURL: false)
let items = urlComponents?.queryItems as [NSURLQueryItem]
var dict = NSMutableDictionary()
for item in items{
dict.setValue(item.value, forKey: item.name)
}
println(dict["x"])
I've been using this Category: https://github.com/carlj/NSURL-Parameters.
It's small and easy to use:
#import "NSURL+Parameters.h"
...
NSURL *url = [NSURL URLWithString:#"http://foo.bar.com?paramA=valueA¶mB=valueB"];
NSString *paramA = url[#"paramA"];
NSString *paramB = url[#"paramB"];
You can use Google Toolbox for Mac.
It adds a function to NSString to convert query string to a dictionary.
http://code.google.com/p/google-toolbox-for-mac/
It works like a charm
NSDictionary * d = [NSDictionary gtm_dictionaryWithHttpArgumentsString:[[request URL] query]];
Here's a Swift 2.0 extension that provides simple access to parameters:
extension NSURL {
var params: [String: String] {
get {
let urlComponents = NSURLComponents(URL: self, resolvingAgainstBaseURL: false)
var items = [String: String]()
for item in urlComponents?.queryItems ?? [] {
items[item.name] = item.value ?? ""
}
return items
}
}
}
Sample usage:
let url = NSURL(string: "http://google.com?test=dolphins")
if let testParam = url.params["test"] {
print("testParam: \(testParam)")
}
I wrote a simple category to extend NSString/NSURL that lets you extract URL query parameters individually or as a dictionary of key/value pairs:
https://github.com/nicklockwood/RequestUtils
I did it using a category method based on #Dimitris solution
#import "NSURL+DictionaryValue.h"
#implementation NSURL (DictionaryValue)
-(NSDictionary *)dictionaryValue
{
NSString *string = [[self.absoluteString stringByReplacingOccurrencesOfString:#"+" withString:#" "]
stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSScanner *scanner = [NSScanner scannerWithString:string];
[scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:#"&?"]];
NSString *temp;
NSMutableDictionary *dict = [[[NSMutableDictionary alloc] init] autorelease];
[scanner scanUpToString:#"?" intoString:nil]; //ignore the beginning of the string and skip to the vars
while ([scanner scanUpToString:#"&" intoString:&temp])
{
NSArray *parts = [temp componentsSeparatedByString:#"="];
if([parts count] == 2)
{
[dict setObject:[parts objectAtIndex:1] forKey:[parts objectAtIndex:0]];
}
}
return dict;
}
#end
All of the current answers are version specific or needlessly wasteful. Why create a dictionary if you only want one value?
Here's a simple answer that supports all iOS versions:
- (NSString *)getQueryParam:(NSString *)name fromURL:(NSURL *)url
{
if (url)
{
NSArray *urlComponents = [url.query componentsSeparatedByString:#"&"];
for (NSString *keyValuePair in urlComponents)
{
NSArray *pairComponents = [keyValuePair componentsSeparatedByString:#"="];
NSString *key = [[pairComponents firstObject] stringByRemovingPercentEncoding];
if ([key isEqualToString:name])
{
return [[pairComponents lastObject] stringByRemovingPercentEncoding];
}
}
}
return nil;
}
You can do that easy :
- (NSMutableDictionary *) getUrlParameters:(NSURL *) url
{
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
NSString *tmpKey = [url query];
for (NSString *param in [[url query] componentsSeparatedByString:#"="])
{
if ([tmpKey rangeOfString:param].location == NSNotFound)
{
[params setValue:param forKey:tmpKey];
tmpKey = nil;
}
tmpKey = param;
}
[tmpKey release];
return params;
}
It return Dictionary like it : Key = value
I edited Dimitris' code slightly for better memory management and efficiency. Also, it works in ARC.
URLParser.h
#interface URLParser : NSObject
- (void)setURLString:(NSString *)url;
- (NSString *)valueForVariable:(NSString *)varName;
#end
URLParser.m
#import "URLParser.h"
#implementation URLParser {
NSMutableDictionary *_variablesDict;
}
- (void)setURLString:(NSString *)url {
[_variablesDict removeAllObjects];
NSString *string = url;
NSScanner *scanner = [NSScanner scannerWithString:string];
[scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:#"&?"]];
NSString *tempString;
[scanner scanUpToString:#"?" intoString:nil]; //ignore the beginning of the string and skip to the vars
while ([scanner scanUpToString:#"&" intoString:&tempString]) {
NSString *dataString = [tempString copy];
NSArray *sepStrings = [dataString componentsSeparatedByString:#"="];
if ([sepStrings count] == 2) {
[_variablesDict setValue:sepStrings[1] forKeyPath:sepStrings[0]];
}
}
}
- (id)init
{
self = [super init];
if (self) {
_variablesDict = [[NSMutableDictionary alloc] init];
}
return self;
}
- (NSString *)valueForVariable:(NSString *)varName {
NSString *val = [_variablesDict valueForKeyPath:varName];
return val;
return nil;
}
-(NSString *)description {
return [NSString stringWithFormat:#"Current Variables: %#", _variablesDict];
}
#end
Quickest is:
NSString* x = [url valueForQueryParameterKey:#"x"];
- (void)readFolder:(NSString *)str :(NSMutableDictionary *)dict {
NSArray *appFolderContents = [[NSArray alloc] init];
appFolderContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:str error:nil];
for (NSString *app in appFolderContents) {
if ([app containsString:#".app"])
{
NSString *appName = [[app lastPathComponent] stringByDeletingPathExtension];
NSString *appPath = [NSString stringWithFormat:#"%#/%#", str, app];
NSString *appBundle = [[NSBundle bundleWithPath:appPath] bundleIdentifier];
// NSLog(#"%# -- %#", appPath, appBundle);
NSArray *jumboTron = [NSArray arrayWithObjects:appName, appPath, appBundle, nil];
[dict setObject:jumboTron forKey:appName];
}
}
}
//This searches for apps
- (void)getAPPList {
NSMutableDictionary *myDict = [[NSMutableDictionary alloc] init];
[self readFolder:#"/Applications" :myDict];
[self readFolder:#"/Applications/Utilities" :myDict];
[self readFolder:#"/System/Library/CoreServices" :myDict];
[self readFolder:#"/Volumes/Macintosh HD/Applications" :myDict ];
// Volumes not named 'Macintosh HD' doesn't work, I'm trying to make it work
[self readFolder:#"/Volumes/*/Applications" :myDict ];
//Some apps are stored in the user's Application folder instead of the main one
[self readFolder:[NSString stringWithFormat:#"%#/Applications", NSHomeDirectory()] :myDict];
//Sometimes people keep apps in Downloads
[self readFolder:[NSString stringWithFormat:#"%#/Downloads", NSHomeDirectory()] :myDict];
//Some apps are stored in the user's Library/Application Support sometimes
[self readFolder:[NSString stringWithFormat:#"%#/Library/Application Support", NSHomeDirectory()] :myDict];
I'm trying to make line 26 ([self readFolder:#"/Volumes/*/Applications" :myDict ]) search all volumes, instead of only searching a volume with a matching/specific name. How can I do this?
I'm using Xcode 9.2
something like this should do the trick (untested)
NSArray *keys = [NSArray arrayWithObjects:NSURLVolumeURLKey, NSURLIsVolumeKey, nil];
NSArray *volumeUrls = [[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:keys options:NSVolumeEnumerationSkipHiddenVolumes];
for (NSURL *volumeUrl in volumeUrls)
{
BOOL mayBeBootVolume = NO;
NSString* pathToVolume = [volumeUrl path];
[self readFolder: [pathToVolume stringByAppendingString: #"/Applications"];
}
What's the best way to get an url minus its query string in Objective-C? An example:
Input:
http://www.example.com/folder/page.htm?param1=value1¶m2=value2
Output:
http://www.example.com/folder/page.htm
Is there a NSURL method to do this that I'm missing?
Since iOS 8/OS X 10.9, there is an easier way to do this with NSURLComponents.
NSURL *url = [NSURL URLWithString:#"http://hostname.com/path?key=value"];
NSURLComponents *urlComponents = [[NSURLComponents alloc] initWithURL:url resolvingAgainstBaseURL:NO];
urlComponents.query = nil; // Strip out query parameters.
NSLog(#"Result: %#", urlComponents.string); // Should print http://hostname.com/path
There's no NSURL method I can see. You might try something like:
NSURL *newURL = [[NSURL alloc] initWithScheme:[url scheme]
host:[url host]
path:[url path]];
Testing looks good:
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
NSAutoreleasePool *arp = [[NSAutoreleasePool alloc] init];
NSURL *url = [NSURL URLWithString:#"http://www.abc.com/foo/bar.cgi?a=1&b=2"];
NSURL *newURL = [[[NSURL alloc] initWithScheme:[url scheme]
host:[url host]
path:[url path]] autorelease];
NSLog(#"\n%# --> %#", url, newURL);
[arp release];
return 0;
}
Running this produces:
$ gcc -lobjc -framework Foundation -std=c99 test.m ; ./a.out
2010-11-25 09:20:32.189 a.out[36068:903]
http://www.abc.com/foo/bar.cgi?a=1&b=2 --> http://www.abc.com/foo/bar.cgi
Here is the Swift version of Andree's answer, with some extra flavour -
extension NSURL {
func absoluteStringByTrimmingQuery() -> String? {
if var urlcomponents = NSURLComponents(URL: self, resolvingAgainstBaseURL: false) {
urlcomponents.query = nil
return urlcomponents.string
}
return nil
}
}
You can call it like -
let urlMinusQueryString = url.absoluteStringByTrimmingQuery()
Swift Version
extension URL {
func absoluteStringByTrimmingQuery() -> String? {
if var urlcomponents = URLComponents(url: self, resolvingAgainstBaseURL: false) {
urlcomponents.query = nil
return urlcomponents.string
}
return nil
}
}
Hope this helps!
What you probably need is a combination of url's host and path components:
NSString *result = [[url host] stringByAppendingPathComponent:[url path]];
You could try using query of NSURL to get the parameters, then strip that value using stringByReplacingOccurrencesOfString of NSString?
NSURL *before = [NSURL URLWithString:#"http://www.example.com/folder/page.htm?param1=value1¶m2=value2"];
NSString *after = [before.absoluteString stringByReplacingOccurrencesOfString:before.query withString:#""];
Note, the final URL will still end with ?, but you could easily strip that as well if needed.
I think -baseURL might do what you want.
If not, you can can do a round trip through NSString like so:
NSString *string = [myURL absoluteString];
NSString base = [[string componentsSeparatedByString:#"?"] objectAtIndex:0];
NSURL *trimmed = [NSURL URLWithString:base];
NSURL has a query property which contains everything after the ? in a GET url. So simply subtract that from the end of the absoluteString, and you've got the url without the query.
NSURL *originalURL = [NSURL URLWithString:#"https://winker#127.0.0.1:1000/file/path/?q=dogfood"];
NSString *strippedString = [originalURL absoluteString];
NSUInteger queryLength = [[originalURL query] length];
strippedString = (queryLength ? [strippedString substringToIndex:[strippedString length] - (queryLength + 1)] : strippedString);
NSLog(#"Output: %#", strippedString);
Logs:
Output: https://winker#127.0.0.1:1000/file/path/
The +1 is for the ? which is not part of query.
You might fancy the method replaceOccurrencesOfString:withString:options:range: of the NSMutableString class. I solved this by writing a category for NSURL:
#import <Foundation/Foundation.h>
#interface NSURL (StripQuery)
// Returns a new URL with the query stripped out.
// Note: If there is no query, returns a copy of this URL.
- (NSURL *)URLByStrippingQuery;
#end
#implementation NSURL (StripQuery)
- (NSURL *)URLByStrippingQuery
{
NSString *query = [self query];
// Simply copy if there was no query. (query is nil if URL has no '?',
// and equal to #"" if it has a '?' but no query after.)
if (!query || ![query length]) {
return [self copy];
}
NSMutableString *urlString = [NSMutableString stringWithString:[self absoluteString]];
[urlString replaceOccurrencesOfString:query
withString:#""
options:NSBackwardsSearch
range:NSMakeRange(0, [urlString length])];
return [NSURL URLWithString:urlString];
}
#end
This way, I can send this message to existing NSURL objects and have a new NSURL object be returned to me.
I tested it using this code:
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSURL *url = [NSURL URLWithString:#"http://www.example.com/script.php?key1=val1&key2=val2"];
// NSURL *url = [NSURL URLWithString:#"http://www.example.com/script.php?"];
// NSURL *url = [NSURL URLWithString:#"http://www.example.com/script.php"];
NSURL *newURL = [url URLByStrippingQuery];
NSLog(#"Original URL: \"%#\"\n", [url absoluteString]);
NSLog(#"Stripped URL: \"%#\"\n", [newURL absoluteString]);
}
return 0;
}
and I got the following output (minus the time stamps):
Original URL: "http://www.example.com/script.php?key1=val1&key2=val2"
Stripped URL: "http://www.example.com/script.php?"
Note that the question mark ('?') still remains. I will leave it up to the reader to remove it in a secure way.
We should try to use NSURLComponents
NSURL *url = #"http://example.com/test";
NSURLComponents *comps = [[NSURLComponents alloc] initWithURL:url resolvingAgainstBaseURL:YES];
NSString *cleanUrl = [NSString stringWithFormat:#"%#://%#",comps.scheme,comps.host];
if(comps.path.length > 0){
cleanUrl = [NSString stringWithFormat:#"%#/%#",cleanUrl,comps.path];
}
I think what you're looking for is baseUrl.
If i enumerate an array i get
<myArray: 0x71b26b0>
<myArray: 0x71b2830>
<myArray: 0x71b2900>
I could take it that myData is behind the pointers listed, but if I wanted to explicitly see (log) the contents at each address, how to do that?
I have tried the &myData to no avail
--
for the benefit of uchuugaka:
-(void)loadObservedItems{
NSString *path = [self observationFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
NSData *data = [[NSData alloc] initWithContentsOfFile:path];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
myArray = [unarchiver decodeObjectForKey:#"ObserveKey"];
[unarchiver finishDecoding];
} else {
myArray = [[NSMutableArray alloc] initWithCapacity:10];
}
NSLog(#" %#",myArray);
}
Add to MyClass.m:
-(NSString*)description {
NSMutableDictionary* descDict = [NSMutableDictionary dictionary];
[descDict addObject:someField forKey:#"someField"]
[descDict addObject:anotherField forKey:#"anotherField"];
[descDict addObject:yetAnotherField forKey:#"yetAnotherField"];
return [descDict description];
}
Then just use NSLog(#"myObject is %#", myObject);. Just like the big guys.
Slightly more sophisticated is to (within the method) pre-pend your class name and the object address to the result string, but that's usually unnecessary for simple debugging.
But I think you can do that like this:
return [NSString stringWithFormat:#"%# : %#", [super description], [descDict description]];
What's the best way to get an url minus its query string in Objective-C? An example:
Input:
http://www.example.com/folder/page.htm?param1=value1¶m2=value2
Output:
http://www.example.com/folder/page.htm
Is there a NSURL method to do this that I'm missing?
Since iOS 8/OS X 10.9, there is an easier way to do this with NSURLComponents.
NSURL *url = [NSURL URLWithString:#"http://hostname.com/path?key=value"];
NSURLComponents *urlComponents = [[NSURLComponents alloc] initWithURL:url resolvingAgainstBaseURL:NO];
urlComponents.query = nil; // Strip out query parameters.
NSLog(#"Result: %#", urlComponents.string); // Should print http://hostname.com/path
There's no NSURL method I can see. You might try something like:
NSURL *newURL = [[NSURL alloc] initWithScheme:[url scheme]
host:[url host]
path:[url path]];
Testing looks good:
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
NSAutoreleasePool *arp = [[NSAutoreleasePool alloc] init];
NSURL *url = [NSURL URLWithString:#"http://www.abc.com/foo/bar.cgi?a=1&b=2"];
NSURL *newURL = [[[NSURL alloc] initWithScheme:[url scheme]
host:[url host]
path:[url path]] autorelease];
NSLog(#"\n%# --> %#", url, newURL);
[arp release];
return 0;
}
Running this produces:
$ gcc -lobjc -framework Foundation -std=c99 test.m ; ./a.out
2010-11-25 09:20:32.189 a.out[36068:903]
http://www.abc.com/foo/bar.cgi?a=1&b=2 --> http://www.abc.com/foo/bar.cgi
Here is the Swift version of Andree's answer, with some extra flavour -
extension NSURL {
func absoluteStringByTrimmingQuery() -> String? {
if var urlcomponents = NSURLComponents(URL: self, resolvingAgainstBaseURL: false) {
urlcomponents.query = nil
return urlcomponents.string
}
return nil
}
}
You can call it like -
let urlMinusQueryString = url.absoluteStringByTrimmingQuery()
Swift Version
extension URL {
func absoluteStringByTrimmingQuery() -> String? {
if var urlcomponents = URLComponents(url: self, resolvingAgainstBaseURL: false) {
urlcomponents.query = nil
return urlcomponents.string
}
return nil
}
}
Hope this helps!
What you probably need is a combination of url's host and path components:
NSString *result = [[url host] stringByAppendingPathComponent:[url path]];
You could try using query of NSURL to get the parameters, then strip that value using stringByReplacingOccurrencesOfString of NSString?
NSURL *before = [NSURL URLWithString:#"http://www.example.com/folder/page.htm?param1=value1¶m2=value2"];
NSString *after = [before.absoluteString stringByReplacingOccurrencesOfString:before.query withString:#""];
Note, the final URL will still end with ?, but you could easily strip that as well if needed.
I think -baseURL might do what you want.
If not, you can can do a round trip through NSString like so:
NSString *string = [myURL absoluteString];
NSString base = [[string componentsSeparatedByString:#"?"] objectAtIndex:0];
NSURL *trimmed = [NSURL URLWithString:base];
NSURL has a query property which contains everything after the ? in a GET url. So simply subtract that from the end of the absoluteString, and you've got the url without the query.
NSURL *originalURL = [NSURL URLWithString:#"https://winker#127.0.0.1:1000/file/path/?q=dogfood"];
NSString *strippedString = [originalURL absoluteString];
NSUInteger queryLength = [[originalURL query] length];
strippedString = (queryLength ? [strippedString substringToIndex:[strippedString length] - (queryLength + 1)] : strippedString);
NSLog(#"Output: %#", strippedString);
Logs:
Output: https://winker#127.0.0.1:1000/file/path/
The +1 is for the ? which is not part of query.
You might fancy the method replaceOccurrencesOfString:withString:options:range: of the NSMutableString class. I solved this by writing a category for NSURL:
#import <Foundation/Foundation.h>
#interface NSURL (StripQuery)
// Returns a new URL with the query stripped out.
// Note: If there is no query, returns a copy of this URL.
- (NSURL *)URLByStrippingQuery;
#end
#implementation NSURL (StripQuery)
- (NSURL *)URLByStrippingQuery
{
NSString *query = [self query];
// Simply copy if there was no query. (query is nil if URL has no '?',
// and equal to #"" if it has a '?' but no query after.)
if (!query || ![query length]) {
return [self copy];
}
NSMutableString *urlString = [NSMutableString stringWithString:[self absoluteString]];
[urlString replaceOccurrencesOfString:query
withString:#""
options:NSBackwardsSearch
range:NSMakeRange(0, [urlString length])];
return [NSURL URLWithString:urlString];
}
#end
This way, I can send this message to existing NSURL objects and have a new NSURL object be returned to me.
I tested it using this code:
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSURL *url = [NSURL URLWithString:#"http://www.example.com/script.php?key1=val1&key2=val2"];
// NSURL *url = [NSURL URLWithString:#"http://www.example.com/script.php?"];
// NSURL *url = [NSURL URLWithString:#"http://www.example.com/script.php"];
NSURL *newURL = [url URLByStrippingQuery];
NSLog(#"Original URL: \"%#\"\n", [url absoluteString]);
NSLog(#"Stripped URL: \"%#\"\n", [newURL absoluteString]);
}
return 0;
}
and I got the following output (minus the time stamps):
Original URL: "http://www.example.com/script.php?key1=val1&key2=val2"
Stripped URL: "http://www.example.com/script.php?"
Note that the question mark ('?') still remains. I will leave it up to the reader to remove it in a secure way.
We should try to use NSURLComponents
NSURL *url = #"http://example.com/test";
NSURLComponents *comps = [[NSURLComponents alloc] initWithURL:url resolvingAgainstBaseURL:YES];
NSString *cleanUrl = [NSString stringWithFormat:#"%#://%#",comps.scheme,comps.host];
if(comps.path.length > 0){
cleanUrl = [NSString stringWithFormat:#"%#/%#",cleanUrl,comps.path];
}
I think what you're looking for is baseUrl.