How to get Values from a NSString - ios

I have an NSString. It is a URL I am getting when using Universal Links. I want to get id value from it. Is there any direct methods in SDK or do we need to use componententsSeparated String values?
Below is the NSString/URL:
https://some.com/cc/test.html?id=3039#value=test
I want to get two things: "test" from test.html and "id" value.

Use NSURLComponents created from an NSURL or NSString of your URL.
From there you can use path to get the /cc/test.html part. Then use lastPathComponent to get test.html and finally use stringByDeletingPathExtension to get test.
To get the "id" value start with the components' queryItems value. Iterate that array finding the NSURLQueryItem with the name of "id" and then get its value.

You could create NSURLComponents from URL string get parameters by calling queryItems method. It will return array of NSURLQueryItem
NSURLComponents *components = [NSURLComponents componentsWithString:#"https://some.com/cc/test.html?id=3039&value=test"];
NSArray *array = [components queryItems];
for(NSURLQueryItem *item in array){
NSLog(#"Name: %#, Value: %#", item.name, item.value);
}
NSLog(#"Items: %#", array);

We can make extension
extension URL {
func getParamValue(paramaterName: String) -> String? {
guard let url = URLComponents(string:self.absoluteString ) else { return nil }
return url.queryItems?.first(where: { $0.name == paramaterName})?.value
}
}
Now you can call like below
let someURL = URL(string: "https://some.com/cc/test.html?id=3039&value=test")!
someURL.getParamValue("id") // 3039
someURL.getParamValue("value") // test

Related

Youtube URL validation ios

I am using this method to validate youtube url but it's not working.
-(BOOL) validateUrl: (NSString *) candidate
{
NSString *urlRegEx = #"(http://youtu.be/[a-zA-Z0-9_-]+)";
urlTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", urlRegEx];
return [urlTest evaluateWithObject:candidate];
}
-(BOOL) validateUrl1: (NSString *) candidate1
{
NSString *urlRegEx1 = #"(https://(www|m){0,1}.youtube.com/(watch|playlist)[?]v=[a-zA-Z0-9_-]+)";
urlTest1 = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", urlRegEx1];
return [urlTest1 evaluateWithObject:candidate1];
}
Even if I edit the url and make it y.be instead of youtu.be, still
these methods are returning YES
. Kindly tell me what's wrong in my code.
If any one has a better RegEx please share that with me.
I would also like to know how to write a RegEx.
If you want to check if String is the link to the youtube video (not the link for channel, or for embedding video):
func isYoutubeLink(checkString checkString: String) -> Bool {
let youtubeRegex = "(http(s)?:\\/\\/)?(www\\.|m\\.)?youtu(be\\.com|\\.be)(\\/watch\\?([&=a-z]{0,})(v=[\\d\\w]{1,}).+|\\/[\\d\\w]{1,})"
let youtubeCheckResult = NSPredicate(format: "SELF MATCHES %#", youtubeRegex)
return youtubeCheckResult.evaluateWithObject(checkString)
}
NSString *urlString = [NSString stringWithFormat:#"URL_STRING"];
NSString *urlRegEx = #"(?:(?:\.be\/|embed\/|v\/|\\?v=|\&v=|\/videos\/)|(?:[\\w+]+#\\w\/\\w(?:\/[\\w]+)?\/\\w\/))([\\w-_]+)";
NSPredicate *urlPredic = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", urlRegEx];
BOOL isValidURL = [urlPredic evaluateWithObject:urlString];
if(isValidURL)
{
// your URL is valid;
}
else
{
// show alert message for invalid URL;
}
now please check let me know i'm waiting your replay it's work or not.
If it is regex that you are looking for, then perhaps you could try this:
(((?:https?:\/\/)?(?:www\.|m\.)?(?:youtube\.[a-z]{2,}|youtu\.be)(?:\/(?:playlist|watch\?|channel\/|user\/)[\w=]+)+)
It will match:
http://, https:// or none of these
www. or m. or none of these
youtube.<anyTLD> or youtu.be
/
playlist, watch?, channel or user
a string of characters from a-zA-Z0-9 and = more than 1 time
This should effectively match most urls on Youtube apart from the homepage, which if requested I could add in with a bit of tweaking.
You may have to add another escape, I haven't got much experience with objective-c
(((?:https?:\\/\\/)?(?:www\\.|m\.)?(?:youtube\\.[a-z]{2,}|youtu\\.be)(?:\\/(?:playlist|watch\\?|channel\\/|user\\/)[\\w=]+)+)
Here is an example of it working in Javascript, Python and PCRE:
https://regex101.com/r/sG1xP7/1
I hope this helps you
For Swift 5:
func isValidYouTubeLink(givenString: String) -> Bool {
let youtubeRegex = "(http(s)?:\\/\\/)?(www\\.|m\\.)?youtu(be\\.com|\\.be)(\\/watch\\?([&=a-z]{0,})(v=[\\d\\w]{1,}).+|\\/[\\d\\w]{1,})"
let youtubeCheckResult = NSPredicate(format: "SELF MATCHES %#", youtubeRegex)
return youtubeCheckResult.evaluate(with: givenString)
}

A clean and robust way to parse URL strings in Objective C

I have a requirement to take a string that represents a URL that can be in many formats and standardise it so it conforms with the URL spec.
If the URL string does not have a scheme, or it has a scheme that is not 'http' or 'https', it should use a default scheme.
I wanted to use NSURLComponents but if a scheme is not provided it parses the host as a path
NSURLComponents *components = [NSURLComponents componentsWithString:#"www.google.com.au"];
components.scheme = #"http";
NSLog(#"1: %#", components.path);
NSLog(#"2: %#", components.host);
NSLog(#"3: %#", components.string);
testtest[2619:869020] 1: www.google.com.au
testtest[2619:869020] 2: ((null))
testtest[2619:869020] 3: http:www.google.com.au <-- Invalid
Therefore I ended up with this category on NSString
#define DEFAULT_SCHEME #"http"
#implementation NSString (standardiseUrlFormat)
- (NSString*)standardiseUrlFormat {
NSURLComponents *components = [NSURLComponents componentsWithString:self];
BOOL hasScheme = components.scheme != nil;
// If no scheme or an invalid scheme is provided, default to http
if (!hasScheme) {
// We have to use string concatenation here because NSURLComponents will
// put the hostname as the path if there is no scheme
return [NSString stringWithFormat:#"%#://%#", DEFAULT_SCHEME, self];
}
// Now we know that a scheme exists, check if it is a correct scheme
if (![components.scheme isEqualToString:#"http"] &&
![components.scheme isEqualToString:#"https"]) {
// Overwrite scheme if not supported
components.scheme = DEFAULT_SCHEME;
}
return [components string];
}
#end
With the following output
NSLog(#"1: %#", [#"http://www.google.com" standardiseUrlFormat]);
NSLog(#"2: %#", [#"www.google.com" standardiseUrlFormat]);
NSLog(#"3: %#", [#"https://www.google.com" standardiseUrlFormat]);
NSLog(#"4: %#", [#"https://www.google.com/some_path" standardiseUrlFormat]);
NSLog(#"5: %#", [#"www.google.com/some_path" standardiseUrlFormat]);
testtest[7411:944022] 1: http://www.google.com
testtest[7411:944022] 2: http://www.google.com
testtest[7411:944022] 3: https://www.google.com
testtest[7411:944022] 4: https://www.google.com/some_path
testtest[7411:944022] 5: http://www.google.com/some_path
Can anyone suggest a cleaner solution that doesn't use two methods (NSURLComponents and string concatenation) to construct the string?
Don't use string concatenation at all. Use NSURLComponents to form the desired NSURL; that's what it's for. For example, if you don't like what the scheme is, set the scheme to what you do want.
EDIT I guess I was thinking that having detected that this is a hostless URL you would rejigger it by hand, e.g.
let s = "www.apple.com/whatever" as NSString
let arr = s.pathComponents
let c = NSURLComponents()
c.scheme = "http"
c.host = arr[0]
c.path = "/" + (Array(arr.dropFirst()) as NSArray).componentsJoinedByString("/")
But perhaps this can't be done, and the problem really is that a URL without a scheme is more or less not a URL.

Swift filter array using NSPredicate

I have an application written in Swift that is pulling in the users contacts from their address book.
I want to filter out the contact that only contain a company name (so that you get your "assumed" real person contact and not businesses)
Here is how this is being accomplish in the Objective-C version of my app:
NSArray *allContacts = (__bridge_transfer NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id person, NSDictionary *bindings) {
NSString *firstName = CFBridgingRelease(ABRecordCopyValue((__bridge ABRecordRef)person, kABPersonFirstNameProperty));
NSString *lastName = CFBridgingRelease(ABRecordCopyValue((__bridge ABRecordRef)person, kABPersonLastNameProperty));
return (firstName || lastName);
}];
NSArray *peopleNotCompanies = [allContacts filteredArrayUsingPredicate:predicate];
This works perfectly, so here is my attempt to do this in Swift:
var contactList: NSArray = ABAddressBookCopyArrayOfAllPeople(addressBook).takeRetainedValue()
var predicate: NSPredicate = NSPredicate { (AnyObject person, NSDictionary bindings) -> Bool in
var firstName: String = ABRecordCopyValue(person as ABRecordRef, kABPersonFirstNameProperty).takeRetainedValue() as String
var lastName: String = ABRecordCopyValue(person as ABRecordRef, kABPersonLastNameProperty).takeRetainedValue() as String
return firstName || lastName
})
Now this has a couple problems. I am getting these errors on the return statement and the end of the predicate call:
How can I provide similar functionality found in my ObjC code in Swift? Or is there a better way in swift to check if a contact has ONLY a company name and then omit it from the final array?
Thanks!
If you have firstName and lastName be optional strings, you can compare them against nil and use them in a boolean expression.
Your second error is due to the extra paren after your closure. This code should work.
var predicate: NSPredicate = NSPredicate { (AnyObject person, NSDictionary bindings) -> Bool in
var firstName: String? = ABRecordCopyValue(person as ABRecordRef, kABPersonFirstNameProperty).takeRetainedValue() as? String
var lastName: String? = ABRecordCopyValue(person as ABRecordRef, kABPersonLastNameProperty).takeRetainedValue() as? String
return firstName != nil || lastName != nil
}
If you convert the NSArray into a Swift Array, you can use Swift's Array.filter method. Here's an example with simpler objects for clarity:
let arrObjc: NSArray = ["aaa", "bab", "bbb", "baa", "cbc"]
let arr: [AnyObject] = arrObjc //create a swift array with same data
// filter takes a block that returns a boolean. True: keep the item, False: drop it.
arr.filter{
if let s = $0 as? String { // first try to cast AnyObject to our expected type.
return contains(s, "a") // return true if we want this item, false otherwise.
} else {
return false // this object is of the wrong type, return false.
}
}
// returns: ["aaa", "bab", "baa"]
Your test at the last line in Objective-C will return if firstName is nil or if lastName is nil. In your swift code, you are just trying to compare two strings as if they were Bools. Instead, you want to return if firstName exists, so you should instead do this in both your objective-c and Swift code:
return firstName != nil

Generate a complete list of key-value coding paths for nested NSDictionary's?

I have an NSDictionary that contains keys and values, and some values will also be NSDictionarys... to an arbitrary (but reasonable) level.
I would like to get a list of all valid KVC paths, e.g. given:
{
"foo" = "bar",
"qux" = {
"taco" = "delicious",
"burrito" = "also delicious",
}
}
I would get:
[
"foo",
"qux",
"qux.taco",
"qux.burrito"
]
Is there a simple way to do this that already exists?
You could recurse through allKeys. A key is a key path, obviously, and then if the value is an NSDictionary you can recurse and append.
- (void) obtainKeyPaths:(id)val intoArray:(NSMutableArray*)arr withString:(NSString*)s {
if ([val isKindOfClass:[NSDictionary class]]) {
for (id aKey in [val allKeys]) {
NSString* path =
(!s ? aKey : [NSString stringWithFormat:#"%#.%#", s, aKey]);
[arr addObject: path];
[self obtainKeyPaths: [val objectForKey:aKey]
intoArray: arr
withString: path];
}
}
}
And here is how to call it:
NSMutableArray* arr = [NSMutableArray array];
[self obtainKeyPaths:d intoArray:arr withString:nil];
Afterwards, arr contains your list of key paths.
Here is a Swift version I wrote after taking note from Matt's answer.
extension NSDictionary {
func allKeyPaths() -> Set<String> {
//Container for keypaths
var keyPaths = Set<String>()
//Recursive function
func allKeyPaths(forDictionary dict: NSDictionary, previousKeyPath path: String?) {
//Loop through the dictionary keys
for key in dict.allKeys {
//Define the new keyPath
guard let key = key as? String else { continue }
let keyPath = path != nil ? "\(path!).\(key)" : key
//Recurse if the value for the key is another dictionary
if let nextDict = dict[key] as? NSDictionary {
allKeyPaths(forDictionary: nextDict, previousKeyPath: keyPath)
continue
}
//End the recursion and append the keyPath
keyPaths.insert(keyPath)
}
}
allKeyPaths(forDictionary: self, previousKeyPath: nil)
return keyPaths
}
}

Append to beginning of NSString method

I would like to append NSString A in front of NSString B. Is there a built in method to append to the beginning of a NSString instead of the end of the NSString?
I know that I can use stringWithFormat, but then what is the difference between using stringWithFormat and stringByAppendingString to add text to the end of a NSString?
If you can append to the end of a string, you can prepend to the beginning of the string.
Append
NSString* a = #"A";
NSString* b = #"B";
NSString* result = [a stringByAppendingString:b]; // Prints "AB"
Prepend
NSString* a = #"A";
NSString* b = #"B";
NSString* result = [b stringByAppendingString:a]; // Prints "BA"
Single line solution:
myString = [#"pretext" stringByAppendingString:myString];
You can use stringWithFormat too:
NSString *A = #"ThisIsStringA";
NSString *B = #"ThisIsStringB";
B = [NSString stringWithFormat:#"%#%#",A,B];
stringByAppendingString is an instance method of NSString,
stringWithFormat is a class method of the class NSString.
It's probably worth pointing out that there is no such thing as "appending one string onto another". NSString is immutable.
In every case, you are "creating a new string that consists of one string following another".
It doesn't matter that you put the newly created string back into the same variable.
You are never "adding text to the end of a string".
Here I’ve solution in Swift:
extension String {
// Add prefix only, if there is not such prefix into a string
mutating func addPrefixIfNeeded(_ prefixString: String?) {
guard let stringValue = prefixString, !self.hasPrefix(stringValue) else {
return
}
self = stringValue + self
}
// Add force full prefix, whether there is already such prefix into a string
mutating func addPrefix(_ prefixString: String?) {
guard let stringValue = prefixString else {
return
}
self = stringValue + self
}
}

Resources