How to validate an url on the iPhone - ios

In an iPhone app I am developing, there is a setting in which you can enter a URL, because of form & function this URL needs to be validated online as well as offline.
So far I haven't been able to find any method to validate the url, so the question is;
How do I validate an URL input on the iPhone (Objective-C) online as well as offline?

Why not instead simply rely on Foundation.framework?
That does the job and does not require RegexKit :
NSURL *candidateURL = [NSURL URLWithString:candidate];
// WARNING > "test" is an URL according to RFCs, being just a path
// so you still should check scheme and all other NSURL attributes you need
if (candidateURL && candidateURL.scheme && candidateURL.host) {
// candidate is a well-formed url with:
// - a scheme (like http://)
// - a host (like stackoverflow.com)
}
According to Apple documentation :
URLWithString: Creates and returns an NSURL object initialized with a
provided string.
+ (id)URLWithString:(NSString *)URLString
Parameters
URLString : The string with which to initialize the NSURL object. Must conform to RFC 2396. This method parses URLString according to RFCs 1738 and 1808.
Return Value
An NSURL object initialized with URLString. If the string was malformed, returns nil.

Thanks to this post, you can avoid using RegexKit.
Here is my solution (works for iphone development with iOS > 3.0) :
- (BOOL) validateUrl: (NSString *) candidate {
NSString *urlRegEx =
#"(http|https)://((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+";
NSPredicate *urlTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", urlRegEx];
return [urlTest evaluateWithObject:candidate];
}
If you want to check in Swift my solution given below:
func isValidUrl(url: String) -> Bool {
let urlRegEx = "^(https?://)?(www\\.)?([-a-z0-9]{1,63}\\.)*?[a-z0-9][-a-z0-9]{0,61}[a-z0-9]\\.[a-z]{2,6}(/[-\\w#\\+\\.~#\\?&/=%]*)?$"
let urlTest = NSPredicate(format:"SELF MATCHES %#", urlRegEx)
let result = urlTest.evaluate(with: url)
return result
}

Instead of writing your own regular expressions, rely on Apple's. I have been using a category on NSString that uses NSDataDetector to test for the presence of a link within a string. If the range of the link found by NSDataDetector equals the length of the entire string, then it is a valid URL.
- (BOOL)isValidURL {
NSUInteger length = [self length];
// Empty strings should return NO
if (length > 0) {
NSError *error = nil;
NSDataDetector *dataDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:&error];
if (dataDetector && !error) {
NSRange range = NSMakeRange(0, length);
NSRange notFoundRange = (NSRange){NSNotFound, 0};
NSRange linkRange = [dataDetector rangeOfFirstMatchInString:self options:0 range:range];
if (!NSEqualRanges(notFoundRange, linkRange) && NSEqualRanges(range, linkRange)) {
return YES;
}
}
else {
NSLog(#"Could not create link data detector: %# %#", [error localizedDescription], [error userInfo]);
}
}
return NO;
}

My solution with Swift:
func validateUrl (stringURL : NSString) -> Bool {
var urlRegEx = "((https|http)://)((\\w|-)+)(([.]|[/])((\\w|-)+))+"
let predicate = NSPredicate(format:"SELF MATCHES %#", argumentArray:[urlRegEx])
var urlTest = NSPredicate.predicateWithSubstitutionVariables(predicate)
return predicate.evaluateWithObject(stringURL)
}
For Test:
var boolean1 = validateUrl("http.s://www.gmail.com")
var boolean2 = validateUrl("https:.//gmailcom")
var boolean3 = validateUrl("https://gmail.me.")
var boolean4 = validateUrl("https://www.gmail.me.com.com.com.com")
var boolean6 = validateUrl("http:/./ww-w.wowone.com")
var boolean7 = validateUrl("http://.www.wowone")
var boolean8 = validateUrl("http://www.wow-one.com")
var boolean9 = validateUrl("http://www.wow_one.com")
var boolean10 = validateUrl("http://.")
var boolean11 = validateUrl("http://")
var boolean12 = validateUrl("http://k")
Results:
false
false
false
true
false
false
true
true
false
false
false

use this-
NSString *urlRegEx = #"http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w- ./?%&=]*)?";

I solved the problem using RegexKit, and build a quick regex to validate a URL;
NSString *regexString = #"(http|https)://((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+";
NSString *subjectString = brandLink.text;
NSString *matchedString = [subjectString stringByMatching:regexString];
Then I check if the matchedString is equal to the subjectString and if that is the case the url is valid :)
Correct me if my regex is wrong ;)

I've found the easiest way to do this is like so:
- (BOOL)validateUrl: (NSURL *)candidate
{
NSURLRequest *req = [NSURLRequest requestWithURL:candidate];
return [NSURLConnection canHandleRequest:req];
}

Oddly enough, I didn't really find a solution here that was very simple, yet still did an okay job for handling http / https links.
Keep in mind, THIS IS NOT a perfect solution, but it worked for the cases below. In summary, the regex tests whether the URL starts with http:// or https://, then checks for at least 1 character, then checks for a dot, and then again checks for at least 1 character. No spaces allowed.
+ (BOOL)validateLink:(NSString *)link
{
NSString *regex = #"(?i)(http|https)(:\\/\\/)([^ .]+)(\\.)([^ \n]+)";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", regex];
return [predicate evaluateWithObject:link];
}
Tested VALID against these URLs:
#"HTTP://FOO.COM",
#"HTTPS://FOO.COM",
#"http://foo.com/blah_blah",
#"http://foo.com/blah_blah/",
#"http://foo.com/blah_blah_(wikipedia)",
#"http://foo.com/blah_blah_(wikipedia)_(again)",
#"http://www.example.com/wpstyle/?p=364",
#"https://www.example.com/foo/?bar=baz&inga=42&quux",
#"http://✪df.ws/123",
#"http://userid:password#example.com:8080",
#"http://userid:password#example.com:8080/",
#"http://userid#example.com",
#"http://userid#example.com/",
#"http://userid#example.com:8080",
#"http://userid#example.com:8080/",
#"http://userid:password#example.com",
#"http://userid:password#example.com/",
#"http://142.42.1.1/",
#"http://142.42.1.1:8080/",
#"http://➡.ws/䨹",
#"http://⌘.ws",
#"http://⌘.ws/",
#"http://foo.com/blah_(wikipedia)#cite-",
#"http://foo.com/blah_(wikipedia)_blah#cite-",
#"http://foo.com/unicode_(✪)_in_parens",
#"http://foo.com/(something)?after=parens",
#"http://☺.damowmow.com/",
#"http://code.google.com/events/#&product=browser",
#"http://j.mp",
#"http://foo.bar/?q=Test%20URL-encoded%20stuff",
#"http://مثال.إختبار",
#"http://例子.测试",
#"http://उदाहरण.परीक्षा",
#"http://-.~_!$&'()*+,;=:%40:80%2f::::::#example.com",
#"http://1337.net",
#"http://a.b-c.de",
#"http://223.255.255.254"
Tested INVALID against these URLs:
#"",
#"foo",
#"ftp://foo.com",
#"ftp://foo.com",
#"http://..",
#"http://..",
#"http://../",
#"//",
#"///",
#"http://##/",
#"http://.www.foo.bar./",
#"rdar://1234",
#"http://foo.bar?q=Spaces should be encoded",
#"http:// shouldfail.com",
#":// should fail"
Source of URLs:
https://mathiasbynens.be/demo/url-regex

You can use this if you do not want http or https or www
NSString *urlRegEx = #"^(http(s)?://)?((www)?\.)?[\w]+\.[\w]+";
example
- (void) testUrl:(NSString *)urlString{
NSLog(#"%#: %#", ([self isValidUrl:urlString] ? #"VALID" : #"INVALID"), urlString);
}
- (void)doTestUrls{
[self testUrl:#"google"];
[self testUrl:#"google.de"];
[self testUrl:#"www.google.de"];
[self testUrl:#"http://www.google.de"];
[self testUrl:#"http://google.de"];
}
Output:
INVALID: google
VALID: google.de
VALID: www.google.de
VALID: http://www.google.de
VALID: http://google.de

Lefakir's solution has one issue.
His regex can't match with "http://instagram.com/p/4Mz3dTJ-ra/".
Url component has combined numerical and literal character. His regex fail such urls.
Here is my improvement.
"(http|https)://((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*)+)+(/)?(\\?.*)?"

Below code will let you find the valid URLs
NSPredicate *websitePredicate = [NSPredicate predicateWithFormat:#"SELF MATCHES %#",#"^(((((h|H)(t|T){2}(p|P)s?)|((f|F)(t|T)(p|P)))://(w{3}.)?)|(w{3}.))[A-Za-z0-9]+(.[A-Za-z0-9-:;\?#_]+)+"];
if ([websitePredicate evaluateWithObject:##MY_STRING##])
{
printf"Valid"
}
for such URLS
http://123.com
https://123.com
http://www.123.com
https://www.123.com
ftp://123.com
ftp://www.123.com
www.something.com

The approved answer is incorrect.
I have an URL with an "-" in it, and the validation fails.

Tweeked Vaibhav's answer to support G+ links:
NSString *urlRegEx = #"http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w-\\+ ./?%&=]*)?";

Some URL's without / at the end are not detected as the correct one in the solutions above. So this might be helpful.
extension String {
func isValidURL() -> Bool{
let length:Int = self.characters.count
var err:NSError?
var dataDetector:NSDataDetector? = NSDataDetector()
do{
dataDetector = try NSDataDetector(types: NSTextCheckingType.Link.rawValue)
}catch{
err = error as NSError
}
if dataDetector != nil{
let range = NSMakeRange(0, length)
let notFoundRange = NSRange(location: NSNotFound, length: 0)
let linkRange = dataDetector?.rangeOfFirstMatchInString(self, options: NSMatchingOptions.init(rawValue: 0), range: range)
if !NSEqualRanges(notFoundRange, linkRange!) && NSEqualRanges(range, linkRange!){
return true
}
}else{
print("Could not create link data detector: \(err?.localizedDescription): \(err?.userInfo)")
}
return false
}
}

URL Validation in Swift
Details
Xcode 8.2.1, Swift 3
Code
enum URLSchemes: String
import Foundation
enum URLSchemes: String {
case http = "http://", https = "https://", ftp = "ftp://", unknown = "unknown://"
static func detectScheme(urlString: String) -> URLSchemes {
if URLSchemes.isSchemeCorrect(urlString: urlString, scheme: .http) {
return .http
}
if URLSchemes.isSchemeCorrect(urlString: urlString, scheme: .https) {
return .https
}
if URLSchemes.isSchemeCorrect(urlString: urlString, scheme: .ftp) {
return .ftp
}
return .unknown
}
static func getAllSchemes(separetedBy separator: String) -> String {
return "\(URLSchemes.http.rawValue)\(separator)\(URLSchemes.https.rawValue)\(separator)\(URLSchemes.ftp.rawValue)"
}
private static func isSchemeCorrect(urlString: String, scheme: URLSchemes) -> Bool {
if urlString.replacingOccurrences(of: scheme.rawValue, with: "") == urlString {
return false
}
return true
}
}
extension String
import Foundation
extension String {
var isUrl: Bool {
// for http://regexr.com checking
// (?:(?:https?|ftp):\/\/)(?:xn--)?(?:\S+(?::\S*)?#)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[#-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?
let schemes = URLSchemes.getAllSchemes(separetedBy: "|").replacingOccurrences(of: "://", with: "")
let regex = "(?:(?:\(schemes)):\\/\\/)(?:xn--)?(?:\\S+(?::\\S*)?#)?(?:(?!10(?:\\.\\d{1,3}){3})(?!127(?:\\.\\d{1,3}){3})(?!169\\.254(?:\\.\\d{1,3}){2})(?!192\\.168(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[#-z\\u00a1-\\uffff]{2,})))(?::\\d{2,5})?(?:\\/[^\\s]*)?"
let regularExpression = try! NSRegularExpression(pattern: regex, options: [])
let range = NSRange(location: 0, length: self.characters.count)
let matches = regularExpression.matches(in: self, options: [], range: range)
for match in matches {
if range.location == match.range.location && range.length == match.range.length {
return true
}
}
return false
}
var toURL: URL? {
let urlChecker: (String)->(URL?) = { url_string in
if url_string.isUrl, let url = URL(string: url_string) {
return url
}
return nil
}
if !contains(".") {
return nil
}
if let url = urlChecker(self) {
return url
}
let scheme = URLSchemes.detectScheme(urlString: self)
if scheme == .unknown {
let newEncodedString = URLSchemes.http.rawValue + self
if let url = urlChecker(newEncodedString) {
return url
}
}
return nil
}
}
Usage
func tests() {
chekUrl(urlString:"http://example.com")
chekUrl(urlString:"https://example.com")
chekUrl(urlString:"http://example.com/dir/file.php?var=moo")
chekUrl(urlString:"http://xn--h1aehhjhg.xn--d1acj3b")
chekUrl(urlString:"http://www.example.com/wpstyle/?p=364")
chekUrl(urlString:"http://-.~_!$&'()*+,;=:%40:80%2f::::::#example.com")
chekUrl(urlString:"http://example.com")
chekUrl(urlString:"http://xn--d1acpjx3f.xn--p1ai")
chekUrl(urlString:"http://xn--74h.damowmow.com/")
chekUrl(urlString:"ftp://example.com:129/myfiles")
chekUrl(urlString:"ftp://user:pass#site.com:21/file/dir")
chekUrl(urlString:"ftp://ftp.example.com:2828/asdah%20asdah.gif")
chekUrl(urlString:"http://142.42.1.1:8080/")
chekUrl(urlString:"http://142.42.1.1/")
chekUrl(urlString:"http://userid:password#example.com:8080")
chekUrl(urlString:"http://userid#example.com")
chekUrl(urlString:"http://userid#example.com:8080")
chekUrl(urlString:"http://foo.com/blah_(wikipedia)#cite-1")
chekUrl(urlString:"http://foo.com/(something)?after=parens")
print("\n----------------------------------------------\n")
chekUrl(urlString:".")
chekUrl(urlString:" ")
chekUrl(urlString:"")
chekUrl(urlString:"-/:;()₽&#.,?!'{}[];'<>+_)(*#^%$")
chekUrl(urlString:"localhost")
chekUrl(urlString:"yandex.")
chekUrl(urlString:"коряга")
chekUrl(urlString:"http:///a")
chekUrl(urlString:"ftps://foo.bar/")
chekUrl(urlString:"rdar://1234")
chekUrl(urlString:"h://test")
chekUrl(urlString:":// should fail")
chekUrl(urlString:"http://-error-.invalid/")
chekUrl(urlString:"http://.www.example.com/")
}
func chekUrl(urlString: String) {
var result = ""
if urlString.isUrl {
result += "url: "
} else {
result += "not url: "
}
result += "\"\(urlString)\""
print(result)
}
Result

Objective C
- (BOOL)validateUrlString:(NSString*)urlString
{
if (!urlString)
{
return NO;
}
NSDataDetector *linkDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:nil];
NSRange urlStringRange = NSMakeRange(0, [urlString length]);
NSMatchingOptions matchingOptions = 0;
if (1 != [linkDetector numberOfMatchesInString:urlString options:matchingOptions range:urlStringRange])
{
return NO;
}
NSTextCheckingResult *checkingResult = [linkDetector firstMatchInString:urlString options:matchingOptions range:urlStringRange];
return checkingResult.resultType == NSTextCheckingTypeLink && NSEqualRanges(checkingResult.range, urlStringRange);
}
Hope this helps!

did you mean to check if what the user entered is a URL? It can be as simple as a regular expression, for example checking if the string contain www. (this is the way that yahoo messenger checks if the user status is a link or not)
Hope that help

Selfishly, I would suggest using a KSURLFormatter instance to both validate input, and convert it to something NSURL can handle.

I have created inherited class of UITextField which can handle all kind of validation using regex string. In this you just need to give them all the regex string in sequence and their message that you want to show when validation get failed. You can check my blog for more info, it will really help you
http://dhawaldawar.wordpress.com/2014/06/11/uitextfield-validation-ios/

Extending #Anthony's answer to swift, I wrote a category on String which returns an optional NSURL. The return value is nil if the String can not be validated to be a URL.
import Foundation
// A private global detector variable which can be reused.
private let detector = try! NSDataDetector(types: NSTextCheckingType.Link.rawValue)
extension String {
func URL() -> NSURL? {
let textRange = NSMakeRange(0, self.characters.count)
guard let URLResult = detector.firstMatchInString(self, options: [], range: textRange) else {
return nil
}
// This checks that the whole string is the detected URL. In case
// you don't have such a requirement, you can remove this code
// and return the URL from URLResult.
guard NSEqualRanges(URLResult.range, textRange) else {
return nil
}
return NSURL(string: self)
}
}

func checkValidUrl(_ strUrl: String) -> Bool {
let urlRegEx: String = "(http|https)://((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+"
let urlTest = NSPredicate(format: "SELF MATCHES %#", urlRegEx)
return urlTest.evaluate(with: strUrl)
}

My solution in Swift 5:
extension String {
func isValidUrl() -> Bool {
do {
let detector = try NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
// check if the string has link inside
return detector.numberOfMatches(in: self, options: [], range: .init( location: 0, length: utf16.count)) > 0
} catch {
print("Error during NSDatadetector initialization \(error)" )
}
return false
}
}

Related

How to write SFHFKeychainUtils in swift

how to write this code in swift 3.0
-(void)keychainitems
{
NSString *appidStr;
NSError *saveerror;
NSString *savedstring=[SFHFKeychainUtils getPasswordForUsername:#"vikramarka" andServiceName:#"com.vikram.vikramarka" error:&saveerror ];
NSString *tempuniqstr;
if (savedstring==nil || savedstring.length<1)
{
NSUUID *uuid = [[NSUUID alloc] init];
tempuniqstr=[uuid UUIDString];
[SFHFKeychainUtils storeUsername:#"vikramarka" andPassword:tempuniqstr forServiceName:#"com.vikram.vikramarka" updateExisting:YES error:&saveerror ];
appidStr = tempuniqstr;
else
{
appidStr = savedstring;
}
func keychainitems() {
var appidStr: String
var saveerror: Error?
let savedstring: String? = try? SFHFKeychainUtils.getPasswordForUsername("vikramarka", andServiceName: "com.vikram.vikramarka")
var tempuniqstr: String
if savedstring == nil || (savedstring?.characters.count ?? 0) < 1 {
let uuid = UUID()
tempuniqstr = uuid.uuidString
try? SFHFKeychainUtils.storeUsername("vikramarka", andPassword: tempuniqstr, forServiceName: "com.vikram.vikramarka", updateExisting: true)
appidStr = tempuniqstr
}
else {
appidStr = savedstring
}
}
Use this utility to convert future objective C code to swift 3

How can i validate an nsstring is valid url? [duplicate]

In an iPhone app I am developing, there is a setting in which you can enter a URL, because of form & function this URL needs to be validated online as well as offline.
So far I haven't been able to find any method to validate the url, so the question is;
How do I validate an URL input on the iPhone (Objective-C) online as well as offline?
Why not instead simply rely on Foundation.framework?
That does the job and does not require RegexKit :
NSURL *candidateURL = [NSURL URLWithString:candidate];
// WARNING > "test" is an URL according to RFCs, being just a path
// so you still should check scheme and all other NSURL attributes you need
if (candidateURL && candidateURL.scheme && candidateURL.host) {
// candidate is a well-formed url with:
// - a scheme (like http://)
// - a host (like stackoverflow.com)
}
According to Apple documentation :
URLWithString: Creates and returns an NSURL object initialized with a
provided string.
+ (id)URLWithString:(NSString *)URLString
Parameters
URLString : The string with which to initialize the NSURL object. Must conform to RFC 2396. This method parses URLString according to RFCs 1738 and 1808.
Return Value
An NSURL object initialized with URLString. If the string was malformed, returns nil.
Thanks to this post, you can avoid using RegexKit.
Here is my solution (works for iphone development with iOS > 3.0) :
- (BOOL) validateUrl: (NSString *) candidate {
NSString *urlRegEx =
#"(http|https)://((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+";
NSPredicate *urlTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", urlRegEx];
return [urlTest evaluateWithObject:candidate];
}
If you want to check in Swift my solution given below:
func isValidUrl(url: String) -> Bool {
let urlRegEx = "^(https?://)?(www\\.)?([-a-z0-9]{1,63}\\.)*?[a-z0-9][-a-z0-9]{0,61}[a-z0-9]\\.[a-z]{2,6}(/[-\\w#\\+\\.~#\\?&/=%]*)?$"
let urlTest = NSPredicate(format:"SELF MATCHES %#", urlRegEx)
let result = urlTest.evaluate(with: url)
return result
}
Instead of writing your own regular expressions, rely on Apple's. I have been using a category on NSString that uses NSDataDetector to test for the presence of a link within a string. If the range of the link found by NSDataDetector equals the length of the entire string, then it is a valid URL.
- (BOOL)isValidURL {
NSUInteger length = [self length];
// Empty strings should return NO
if (length > 0) {
NSError *error = nil;
NSDataDetector *dataDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:&error];
if (dataDetector && !error) {
NSRange range = NSMakeRange(0, length);
NSRange notFoundRange = (NSRange){NSNotFound, 0};
NSRange linkRange = [dataDetector rangeOfFirstMatchInString:self options:0 range:range];
if (!NSEqualRanges(notFoundRange, linkRange) && NSEqualRanges(range, linkRange)) {
return YES;
}
}
else {
NSLog(#"Could not create link data detector: %# %#", [error localizedDescription], [error userInfo]);
}
}
return NO;
}
My solution with Swift:
func validateUrl (stringURL : NSString) -> Bool {
var urlRegEx = "((https|http)://)((\\w|-)+)(([.]|[/])((\\w|-)+))+"
let predicate = NSPredicate(format:"SELF MATCHES %#", argumentArray:[urlRegEx])
var urlTest = NSPredicate.predicateWithSubstitutionVariables(predicate)
return predicate.evaluateWithObject(stringURL)
}
For Test:
var boolean1 = validateUrl("http.s://www.gmail.com")
var boolean2 = validateUrl("https:.//gmailcom")
var boolean3 = validateUrl("https://gmail.me.")
var boolean4 = validateUrl("https://www.gmail.me.com.com.com.com")
var boolean6 = validateUrl("http:/./ww-w.wowone.com")
var boolean7 = validateUrl("http://.www.wowone")
var boolean8 = validateUrl("http://www.wow-one.com")
var boolean9 = validateUrl("http://www.wow_one.com")
var boolean10 = validateUrl("http://.")
var boolean11 = validateUrl("http://")
var boolean12 = validateUrl("http://k")
Results:
false
false
false
true
false
false
true
true
false
false
false
use this-
NSString *urlRegEx = #"http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w- ./?%&=]*)?";
I solved the problem using RegexKit, and build a quick regex to validate a URL;
NSString *regexString = #"(http|https)://((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+";
NSString *subjectString = brandLink.text;
NSString *matchedString = [subjectString stringByMatching:regexString];
Then I check if the matchedString is equal to the subjectString and if that is the case the url is valid :)
Correct me if my regex is wrong ;)
I've found the easiest way to do this is like so:
- (BOOL)validateUrl: (NSURL *)candidate
{
NSURLRequest *req = [NSURLRequest requestWithURL:candidate];
return [NSURLConnection canHandleRequest:req];
}
Oddly enough, I didn't really find a solution here that was very simple, yet still did an okay job for handling http / https links.
Keep in mind, THIS IS NOT a perfect solution, but it worked for the cases below. In summary, the regex tests whether the URL starts with http:// or https://, then checks for at least 1 character, then checks for a dot, and then again checks for at least 1 character. No spaces allowed.
+ (BOOL)validateLink:(NSString *)link
{
NSString *regex = #"(?i)(http|https)(:\\/\\/)([^ .]+)(\\.)([^ \n]+)";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", regex];
return [predicate evaluateWithObject:link];
}
Tested VALID against these URLs:
#"HTTP://FOO.COM",
#"HTTPS://FOO.COM",
#"http://foo.com/blah_blah",
#"http://foo.com/blah_blah/",
#"http://foo.com/blah_blah_(wikipedia)",
#"http://foo.com/blah_blah_(wikipedia)_(again)",
#"http://www.example.com/wpstyle/?p=364",
#"https://www.example.com/foo/?bar=baz&inga=42&quux",
#"http://✪df.ws/123",
#"http://userid:password#example.com:8080",
#"http://userid:password#example.com:8080/",
#"http://userid#example.com",
#"http://userid#example.com/",
#"http://userid#example.com:8080",
#"http://userid#example.com:8080/",
#"http://userid:password#example.com",
#"http://userid:password#example.com/",
#"http://142.42.1.1/",
#"http://142.42.1.1:8080/",
#"http://➡.ws/䨹",
#"http://⌘.ws",
#"http://⌘.ws/",
#"http://foo.com/blah_(wikipedia)#cite-",
#"http://foo.com/blah_(wikipedia)_blah#cite-",
#"http://foo.com/unicode_(✪)_in_parens",
#"http://foo.com/(something)?after=parens",
#"http://☺.damowmow.com/",
#"http://code.google.com/events/#&product=browser",
#"http://j.mp",
#"http://foo.bar/?q=Test%20URL-encoded%20stuff",
#"http://مثال.إختبار",
#"http://例子.测试",
#"http://उदाहरण.परीक्षा",
#"http://-.~_!$&'()*+,;=:%40:80%2f::::::#example.com",
#"http://1337.net",
#"http://a.b-c.de",
#"http://223.255.255.254"
Tested INVALID against these URLs:
#"",
#"foo",
#"ftp://foo.com",
#"ftp://foo.com",
#"http://..",
#"http://..",
#"http://../",
#"//",
#"///",
#"http://##/",
#"http://.www.foo.bar./",
#"rdar://1234",
#"http://foo.bar?q=Spaces should be encoded",
#"http:// shouldfail.com",
#":// should fail"
Source of URLs:
https://mathiasbynens.be/demo/url-regex
You can use this if you do not want http or https or www
NSString *urlRegEx = #"^(http(s)?://)?((www)?\.)?[\w]+\.[\w]+";
example
- (void) testUrl:(NSString *)urlString{
NSLog(#"%#: %#", ([self isValidUrl:urlString] ? #"VALID" : #"INVALID"), urlString);
}
- (void)doTestUrls{
[self testUrl:#"google"];
[self testUrl:#"google.de"];
[self testUrl:#"www.google.de"];
[self testUrl:#"http://www.google.de"];
[self testUrl:#"http://google.de"];
}
Output:
INVALID: google
VALID: google.de
VALID: www.google.de
VALID: http://www.google.de
VALID: http://google.de
Lefakir's solution has one issue.
His regex can't match with "http://instagram.com/p/4Mz3dTJ-ra/".
Url component has combined numerical and literal character. His regex fail such urls.
Here is my improvement.
"(http|https)://((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*)+)+(/)?(\\?.*)?"
Below code will let you find the valid URLs
NSPredicate *websitePredicate = [NSPredicate predicateWithFormat:#"SELF MATCHES %#",#"^(((((h|H)(t|T){2}(p|P)s?)|((f|F)(t|T)(p|P)))://(w{3}.)?)|(w{3}.))[A-Za-z0-9]+(.[A-Za-z0-9-:;\?#_]+)+"];
if ([websitePredicate evaluateWithObject:##MY_STRING##])
{
printf"Valid"
}
for such URLS
http://123.com
https://123.com
http://www.123.com
https://www.123.com
ftp://123.com
ftp://www.123.com
www.something.com
The approved answer is incorrect.
I have an URL with an "-" in it, and the validation fails.
Tweeked Vaibhav's answer to support G+ links:
NSString *urlRegEx = #"http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w-\\+ ./?%&=]*)?";
Some URL's without / at the end are not detected as the correct one in the solutions above. So this might be helpful.
extension String {
func isValidURL() -> Bool{
let length:Int = self.characters.count
var err:NSError?
var dataDetector:NSDataDetector? = NSDataDetector()
do{
dataDetector = try NSDataDetector(types: NSTextCheckingType.Link.rawValue)
}catch{
err = error as NSError
}
if dataDetector != nil{
let range = NSMakeRange(0, length)
let notFoundRange = NSRange(location: NSNotFound, length: 0)
let linkRange = dataDetector?.rangeOfFirstMatchInString(self, options: NSMatchingOptions.init(rawValue: 0), range: range)
if !NSEqualRanges(notFoundRange, linkRange!) && NSEqualRanges(range, linkRange!){
return true
}
}else{
print("Could not create link data detector: \(err?.localizedDescription): \(err?.userInfo)")
}
return false
}
}
URL Validation in Swift
Details
Xcode 8.2.1, Swift 3
Code
enum URLSchemes: String
import Foundation
enum URLSchemes: String {
case http = "http://", https = "https://", ftp = "ftp://", unknown = "unknown://"
static func detectScheme(urlString: String) -> URLSchemes {
if URLSchemes.isSchemeCorrect(urlString: urlString, scheme: .http) {
return .http
}
if URLSchemes.isSchemeCorrect(urlString: urlString, scheme: .https) {
return .https
}
if URLSchemes.isSchemeCorrect(urlString: urlString, scheme: .ftp) {
return .ftp
}
return .unknown
}
static func getAllSchemes(separetedBy separator: String) -> String {
return "\(URLSchemes.http.rawValue)\(separator)\(URLSchemes.https.rawValue)\(separator)\(URLSchemes.ftp.rawValue)"
}
private static func isSchemeCorrect(urlString: String, scheme: URLSchemes) -> Bool {
if urlString.replacingOccurrences(of: scheme.rawValue, with: "") == urlString {
return false
}
return true
}
}
extension String
import Foundation
extension String {
var isUrl: Bool {
// for http://regexr.com checking
// (?:(?:https?|ftp):\/\/)(?:xn--)?(?:\S+(?::\S*)?#)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[#-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?
let schemes = URLSchemes.getAllSchemes(separetedBy: "|").replacingOccurrences(of: "://", with: "")
let regex = "(?:(?:\(schemes)):\\/\\/)(?:xn--)?(?:\\S+(?::\\S*)?#)?(?:(?!10(?:\\.\\d{1,3}){3})(?!127(?:\\.\\d{1,3}){3})(?!169\\.254(?:\\.\\d{1,3}){2})(?!192\\.168(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[#-z\\u00a1-\\uffff]{2,})))(?::\\d{2,5})?(?:\\/[^\\s]*)?"
let regularExpression = try! NSRegularExpression(pattern: regex, options: [])
let range = NSRange(location: 0, length: self.characters.count)
let matches = regularExpression.matches(in: self, options: [], range: range)
for match in matches {
if range.location == match.range.location && range.length == match.range.length {
return true
}
}
return false
}
var toURL: URL? {
let urlChecker: (String)->(URL?) = { url_string in
if url_string.isUrl, let url = URL(string: url_string) {
return url
}
return nil
}
if !contains(".") {
return nil
}
if let url = urlChecker(self) {
return url
}
let scheme = URLSchemes.detectScheme(urlString: self)
if scheme == .unknown {
let newEncodedString = URLSchemes.http.rawValue + self
if let url = urlChecker(newEncodedString) {
return url
}
}
return nil
}
}
Usage
func tests() {
chekUrl(urlString:"http://example.com")
chekUrl(urlString:"https://example.com")
chekUrl(urlString:"http://example.com/dir/file.php?var=moo")
chekUrl(urlString:"http://xn--h1aehhjhg.xn--d1acj3b")
chekUrl(urlString:"http://www.example.com/wpstyle/?p=364")
chekUrl(urlString:"http://-.~_!$&'()*+,;=:%40:80%2f::::::#example.com")
chekUrl(urlString:"http://example.com")
chekUrl(urlString:"http://xn--d1acpjx3f.xn--p1ai")
chekUrl(urlString:"http://xn--74h.damowmow.com/")
chekUrl(urlString:"ftp://example.com:129/myfiles")
chekUrl(urlString:"ftp://user:pass#site.com:21/file/dir")
chekUrl(urlString:"ftp://ftp.example.com:2828/asdah%20asdah.gif")
chekUrl(urlString:"http://142.42.1.1:8080/")
chekUrl(urlString:"http://142.42.1.1/")
chekUrl(urlString:"http://userid:password#example.com:8080")
chekUrl(urlString:"http://userid#example.com")
chekUrl(urlString:"http://userid#example.com:8080")
chekUrl(urlString:"http://foo.com/blah_(wikipedia)#cite-1")
chekUrl(urlString:"http://foo.com/(something)?after=parens")
print("\n----------------------------------------------\n")
chekUrl(urlString:".")
chekUrl(urlString:" ")
chekUrl(urlString:"")
chekUrl(urlString:"-/:;()₽&#.,?!'{}[];'<>+_)(*#^%$")
chekUrl(urlString:"localhost")
chekUrl(urlString:"yandex.")
chekUrl(urlString:"коряга")
chekUrl(urlString:"http:///a")
chekUrl(urlString:"ftps://foo.bar/")
chekUrl(urlString:"rdar://1234")
chekUrl(urlString:"h://test")
chekUrl(urlString:":// should fail")
chekUrl(urlString:"http://-error-.invalid/")
chekUrl(urlString:"http://.www.example.com/")
}
func chekUrl(urlString: String) {
var result = ""
if urlString.isUrl {
result += "url: "
} else {
result += "not url: "
}
result += "\"\(urlString)\""
print(result)
}
Result
Objective C
- (BOOL)validateUrlString:(NSString*)urlString
{
if (!urlString)
{
return NO;
}
NSDataDetector *linkDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:nil];
NSRange urlStringRange = NSMakeRange(0, [urlString length]);
NSMatchingOptions matchingOptions = 0;
if (1 != [linkDetector numberOfMatchesInString:urlString options:matchingOptions range:urlStringRange])
{
return NO;
}
NSTextCheckingResult *checkingResult = [linkDetector firstMatchInString:urlString options:matchingOptions range:urlStringRange];
return checkingResult.resultType == NSTextCheckingTypeLink && NSEqualRanges(checkingResult.range, urlStringRange);
}
Hope this helps!
did you mean to check if what the user entered is a URL? It can be as simple as a regular expression, for example checking if the string contain www. (this is the way that yahoo messenger checks if the user status is a link or not)
Hope that help
Selfishly, I would suggest using a KSURLFormatter instance to both validate input, and convert it to something NSURL can handle.
I have created inherited class of UITextField which can handle all kind of validation using regex string. In this you just need to give them all the regex string in sequence and their message that you want to show when validation get failed. You can check my blog for more info, it will really help you
http://dhawaldawar.wordpress.com/2014/06/11/uitextfield-validation-ios/
Extending #Anthony's answer to swift, I wrote a category on String which returns an optional NSURL. The return value is nil if the String can not be validated to be a URL.
import Foundation
// A private global detector variable which can be reused.
private let detector = try! NSDataDetector(types: NSTextCheckingType.Link.rawValue)
extension String {
func URL() -> NSURL? {
let textRange = NSMakeRange(0, self.characters.count)
guard let URLResult = detector.firstMatchInString(self, options: [], range: textRange) else {
return nil
}
// This checks that the whole string is the detected URL. In case
// you don't have such a requirement, you can remove this code
// and return the URL from URLResult.
guard NSEqualRanges(URLResult.range, textRange) else {
return nil
}
return NSURL(string: self)
}
}
func checkValidUrl(_ strUrl: String) -> Bool {
let urlRegEx: String = "(http|https)://((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+"
let urlTest = NSPredicate(format: "SELF MATCHES %#", urlRegEx)
return urlTest.evaluate(with: strUrl)
}
My solution in Swift 5:
extension String {
func isValidUrl() -> Bool {
do {
let detector = try NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
// check if the string has link inside
return detector.numberOfMatches(in: self, options: [], range: .init( location: 0, length: utf16.count)) > 0
} catch {
print("Error during NSDatadetector initialization \(error)" )
}
return false
}
}

How to get the public IP address of the device

I found this sample code to get all local IP addresses, but I don't find an easy solution to get the public IP.
A legacy class from Apple allowed to do that
... but it's legacy ...
It's as simple as this:
NSString *publicIP = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"https://icanhazip.com/"] encoding:NSUTF8StringEncoding error:nil];
publicIP = [publicIP stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]; // IP comes with a newline for some reason
I have used ALSystemUtilities in the past. You basically have to make a call externally to find this out.
+ (NSString *)externalIPAddress {
// Check if we have an internet connection then try to get the External IP Address
if (![self connectedViaWiFi] && ![self connectedVia3G]) {
// Not connected to anything, return nil
return nil;
}
// Get the external IP Address based on dynsns.org
NSError *error = nil;
NSString *theIpHtml = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://www.dyndns.org/cgi-bin/check_ip.cgi"]
encoding:NSUTF8StringEncoding
error:&error];
if (!error) {
NSUInteger an_Integer;
NSArray *ipItemsArray;
NSString *externalIP;
NSScanner *theScanner;
NSString *text = nil;
theScanner = [NSScanner scannerWithString:theIpHtml];
while ([theScanner isAtEnd] == NO) {
// find start of tag
[theScanner scanUpToString:#"<" intoString:NULL] ;
// find end of tag
[theScanner scanUpToString:#">" intoString:&text] ;
// replace the found tag with a space
//(you can filter multi-spaces out later if you wish)
theIpHtml = [theIpHtml stringByReplacingOccurrencesOfString:
[ NSString stringWithFormat:#"%#>", text]
withString:#" "] ;
ipItemsArray = [theIpHtml componentsSeparatedByString:#" "];
an_Integer = [ipItemsArray indexOfObject:#"Address:"];
externalIP =[ipItemsArray objectAtIndex:++an_Integer];
}
// Check that you get something back
if (externalIP == nil || externalIP.length <= 0) {
// Error, no address found
return nil;
}
// Return External IP
return externalIP;
} else {
// Error, no address found
return nil;
}
}
Source from ALSystemUtilities
Thanks #Tarek for his answer
Here, code in Swift 4 version
func getPublicIPAddress() -> String {
var publicIP = ""
do {
try publicIP = String(contentsOf: URL(string: "https://www.bluewindsolution.com/tools/getpublicip.php")!, encoding: String.Encoding.utf8)
publicIP = publicIP.trimmingCharacters(in: CharacterSet.whitespaces)
}
catch {
print("Error: \(error)")
}
return publicIP
}
NOTE1: To get public IP address, we must have external site to return public IP. The website I use is business company website, so, it will be their until the business gone.
NOTE2: You can made some site by yourself, however, Apple require HTTPS site to be able to use this function.
For those of us using Swift, here's my translation of Andrei's answer with the addition of NSURLSession to run it in the background. To check the network, I use Reachability.swift. Also, remember to add dyndns.org to NSExceptionDomains for NSAppTransportSecurity in your info.plist.
var ipAddress:String?
func getIPAddress() {
if reachability!.isReachable() == false {
return
}
guard let ipServiceURL = NSURL(string: "http://www.dyndns.org/cgi-bin/check_ip.cgi") else {
return
}
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(ipServiceURL, completionHandler: {(data, response, error) -> Void in
if error != nil {
print(error)
return
}
let ipHTML = NSString(data: data!, encoding: NSUTF8StringEncoding) as? String
self.ipAddress = self.scanForIPAddress(ipHTML)
})
task.resume()
}
func scanForIPAddress(var ipHTML:String?) -> String? {
if ipHTML == nil {
return nil
}
var externalIPAddress:String?
var index:Int?
var ipItems:[String]?
var text:NSString?
let scanner = NSScanner(string: ipHTML!)
while scanner.atEnd == false {
scanner.scanUpToString("<", intoString: nil)
scanner.scanUpToString(">", intoString: &text)
ipHTML = ipHTML!.stringByReplacingOccurrencesOfString(String(text!) + ">", withString: " ")
ipItems = ipHTML!.componentsSeparatedByString(" ")
index = ipItems!.indexOf("Address:")
externalIPAddress = ipItems![++index!]
}
if let ip = externalIPAddress {
print("External IP Address: \(ip)")
}
return externalIPAddress
}
I use ipify and with no complaints.
NSURL *url = [NSURL URLWithString:#"https://api.ipify.org/"];
NSString *ipAddress = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
NSLog(#"My public IP address is: %#", ipAddress);
In case you want to retrieve IP asynchronously, there is a way to do that using ipify.org and Alamofire in 1 line of code:
Alamofire.request("https://api.ipify.org").responseString { (response) in
print(response.result.value ?? "Unable to get IP")
}
You can call a public IP address lookup services to get this? I have set up http://ipof.in as a service that returns the device IP address as JSON / XML or plain text. You can find them here
For JSON with GeoIP data
http://ipof.in/json
https://ipof.in/json
For XML response
http://ipof.in/xml
https://ipof.in/xml
For plain text IP address
http://ipof.in/txt
https://ipof.in/txt
You have to query an external server to find out the public IP. Either set up your own server (1 line of php code) or use one of the many available ones which return the IP as plain text or json upon http query. Like for example http://myipdoc.com/ip.php .
I find both Andrei and Tarek's answers helpful.
Both are relying a web URL to query the "public IP" of the iOS/OS X device.
However, there is an issue with this approach in some part of the world where URL such as "http://www.dyndns.org/cgi-bin/check_ip.cgi" is censored as in andrei's answer:
NSString *theIpHtml = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://www.dyndns.org/cgi-bin/check_ip.cgi"]
encoding:NSUTF8StringEncoding
error:&error];
In this case, we will need to use an "uncensored" URL within the region such as http://1212.ip138.com/ic.asp
Note that the web URL could use a different HTML and encoding than what Andrei's answer could parse - in the URL above, some very gentle changes can fix it by using kCFStringEncodingGB_18030_2000 for http://1212.ip138.com/ic.asp
NSURL* externalIPCheckURL = [NSURL URLWithString: #"http://1212.ip138.com/ic.asp"];
encoding:NSUTF8StringEncoding error:nil];
NSStringEncoding encoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSString *theIpHtml = [NSString stringWithContentsOfURL: externalIPCheckURL
encoding: encoding
error: &error];

Load file in Today extension when device is locked

In my today extension with my device unlocked, this line of code works as expected, returning the data from the image path:
let imageData = NSData(contentsOfFile: path)
However when my device is locked with a passcode, it returns nil. Is there any way to access images in the file system when the device is locked? I can access UserDefaults just fine, but not files in the directory for my shared group. Here is how I am creating the path, calling imagePath, which is correctly populated with the path I expect in both cases:
func rootFilePath() -> String? {
let manager = NSFileManager()
let containerURL = manager.containerURLForSecurityApplicationGroupIdentifier(GROUP_ID)
if let unwrappedURL = containerURL {
return unwrappedURL.path
}
else {
return nil
}
}
func imagePath() -> String? {
let rootPath = rootFilePath()
if let uPath = rootPath {
return "\(uPath)/\(imageId).png"
}
else {
return nil
}
}
I just figured it out! You need to set the file permissions accordingly:
NSFileManager *fm = [[NSFileManager alloc] init];
NSDictionary *attribs = #{NSFileProtectionKey : NSFileProtectionNone};
NSError *unprotectError = nil;
BOOL unprotectSuccess = [fm setAttributes:attribs
ofItemAtPath:[containerURL path]
error:&unprotectError];
if (!unprotectSuccess) {
NSLog(#"Unable to remove protection from file! %#", unprotectError);
}
In many cases you wouldn't normally want to do this, but because the information is intended to be viewed from the lock screen, I'm OK with removing file protection.

NSString hasPrefix: function with case sensitivity

Simple and may asked many time but little trick with this. We know, NSString doesn't work with case sensitivity for hasPrefix: method.
NSString *string = #"Xyzabcdedfghij";
NSString *substring = #"xyz";
if ([string hasPrefix:substring])
NSLog(#"string has prefix "); // won't get here.
Question is: Is there any built-in method for resolve this issue? I mean, hasPrefix: with case sensitive?
I could use below answer at least case. But want to know if there is any method which better than this..?
Known answer:(lease case)
if ([[test substringWithRange:NSMakeRange(0,3)] caseInsensitiveCompare:#"xyz"] == NSOrderedSame) {
// ....
}
From Apple themselves:
NSString *searchString = #"age";
NSString *beginsTest = #"Agencies";
NSRange prefixRange = [beginsTest rangeOfString:searchString
options:(NSAnchoredSearch | NSCaseInsensitiveSearch)];
// prefixRange = {0, 3}
NSString *endsTest = #"BRICOLAGE";
NSRange suffixRange = [endsTest rangeOfString:searchString
options:(NSAnchoredSearch | NSCaseInsensitiveSearch | NSBackwardsSearch)];
// suffixRange = {6, 3}
This could be wrapped into an easy-to-use method:
- (BOOL) string:(NSString *)string
hasPrefix:(NSString *)prefix
caseInsensitive:(BOOL)caseInsensitive {
if (!caseInsensitive)
return [string hasPrefix:prefix];
const NSStringCompareOptions options = NSAnchoredSearch|NSCaseInsensitiveSearch;
NSRange prefixRange = [string rangeOfString:prefix
options:options];
return prefixRange.location == 0 && prefixRange.length > 0;
}
You can always use lowercaseString on both strings and thus forcing the same case. So for example
[[string lowercaseString] hasPrefix:[substring lowercaseString]];
Nasty way to do is to lower case the both string and than use hasPrefix
e.g.
[[mainString lowercaseString] hasPrefix:[stringToFind lowercaseString]];
Swift 5:
extension String {
public func hasPrefixIgnoringCase(_ prefix: String) -> Bool {
range(of: prefix, options: [.anchored, .caseInsensitive]) != nil
}
}
Usage:
"Hello".hasPrefixIgnoringCase("hEl") // return true
you can do this by
if([[string lowercaseString] hasPrefix:[substring lowercaseString]])
{
NSLog(#"found");
}
A Swift 4.2 version of accepted answer:
extension String {
public func hasPrefix<Prefix>(_ prefix: Prefix, caseSensitive: Bool) -> Bool where Prefix : StringProtocol {
if caseSensitive { return self.hasPrefix(prefix) }
let prefixRange = self.range(of: prefix, options: [.anchored, .caseInsensitive])
return prefixRange != nil
}
}
Alternatively, using .lowercased()
extension String {
public func hasPrefix<Prefix>(_ prefix: Prefix, caseSensitive: Bool) -> Bool where Prefix : StringProtocol {
if caseSensitive { return self.hasPrefix(prefix) }
return self.lowercased().hasPrefix(prefix.lowercased())
}
}
The extension is to use as following
let string = "Hello World"
let caseSensitiveSearch = string.hasPrefix("hello", caseSensitive: true) // return false
let caseNotSensitiveSearch = string.hasPrefix("hello", caseSensitive: false) // return true
For those of you who like functional style one-liners (like I do):
extension String {
func hasPrefix<Prefix>(_ prefix: Prefix, caseSensitive: Bool) -> Bool where Prefix: StringProtocol {
return caseSensitive ? hasPrefix(prefix) :
self.range(of: prefix, options: [.anchored, .caseInsensitive]) != nil
}
}
Swift 5
extension StringProtocol {
public func hasPrefix<T: StringProtocol>(caseInsensitive prefix: T) -> Bool {
lowercased().starts(with: prefix.lowercased())
}
}
Use:
"Hello World".hasPrefix(caseInsensitive: "hello") //true

Resources