Replace only the first instance of a substring in an NSString - iphone

So if you have an NSString that goes:
#"My blue car is bigger than my blue shoes or my blue bicycle";
I would like a method that replaces only the first instance of blue with green, to produce:
#"My green car is bigger than my blue shoes or my blue bicycle";
How does one do this?

Assuming the following inputs:
NSString *myString = #"My blue car is bigger then my blue shoes or my blue bicycle";
NSString *original = #"blue";
NSString *replacement = #"green";
The algorithm is quite simple:
NSRange rOriginal = [myString rangeOfString:original];
if (NSNotFound != rOriginal.location) {
myString = [myString stringByReplacingCharactersInRange:rOriginal withString:replacement];
}

SWIFT 3 and 4 UPDATE:
extension String
{
func stringByReplacingFirstOccurrenceOfString(
target: String, withString replaceString: String) -> String
{
if let range = self.range(of: target) {
return self.replacingCharacters(in: range, with: replaceString)
}
return self
}
}

A more Swift'y version of the implementations posted here. This one matches the syntax replacingOccurrences(of:with:) in Swift 5.1
extension String {
func replacingFirstOccurrence(of target: String, with replacement: String) -> String {
guard let range = self.range(of: target) else { return self }
return self.replacingCharacters(in: range, with: replacement)
}
}
An example of usage:
let string = "I like bacon, please give me some more bacon!"
let newString = string.replacingFirstOccurrence(of: "bacon", with: "meat")

Swift 4 version:
func replacingFirstOccurrence(of string: String, with replacement: String) -> String {
guard let range = self.range(of: string) else { return self }
return replacingCharacters(in: range, with: replacement)
}

In Swift is useful extends String in this way:
public extension String {
func stringByReplacingFirstOccurrenceOfString(target: String, withString replaceString: String) -> String {
if let range = self.rangeOfString(target) {
return self.stringByReplacingCharactersInRange(range, withString: replaceString)
}
return self
}
}
In this way you can call wherever:
let s = "The red red cat".stringByReplacingFirstOccurrenceOfString("red", withString: "fat")
print(s) // "The fat red cat"

NSString *initialString = #"My blue car is bigger then my blue shoes or my blue bicycle";
NSRange range = [initialString rangeOfString:#"blue"];
NSString *replacedString = [initialString stringByReplacingCharactersInRange:range withString:#"green"];
NSLog(#"replacedString: %#", replacedString);

First locate the substring, and then make the replacement. Example :
NSString *aString = #"foo bar foo";
NSRange firstFooRange = [aString rangeOfString:#"foo"];
NSString *anotherString = [aString stringByReplacingOccurrencesOfString:#"foo"
withString:#"bar"
options:0
range:firstFooRange];
NSString documentation.

-(NSString*) replaceFirstOccuarnceFromString: (NSString*)input withOriginal:(NSString*) original AndReplacment:(NSString*)replacement
{
NSRange rOriginal = [input rangeOfString: original];
if (NSNotFound != rOriginal.location) {
input = [input
stringByReplacingCharactersInRange: rOriginal
withString: replacement];
}
return input;
}

Related

How to figure out the range of a substring in one string and then use it in another

Basicly I want something like this,
NSString* foobar(NSString *input) {
// say input is "1"
NSString *string = #"0123456789";
NSString *anotherString = #"零一二三四五六七八九";
NSRange range = [string rangeOfString:input];
// return "一" here
return [anotherString substringWithRange:range];
}
I tried the same stuff in Swift,
func foobar(input: String) -> String {
// say input is "1"
let string = "0123456789"
let range = string.range(of: input, options: .anchored)
let result = anotherString[range!]
// return "012" here
return String(result)
}
why?
And how can I achieve this?
String (or generally, collection) indices must only be used with the collection that they were created with. In order to find the same positions in another string, the indices must be converted to (integer) offsets and back to indices of the target string:
func foobar(input: String) -> String? {
let s1 = "0123456789"
let s2 = "😀一二三四五六七八九";
guard let range = s1.range(of: input) else {
return nil
}
let pos = s1.distance(from: s1.startIndex, to: range.lowerBound)
let len = s1.distance(from: range.lowerBound, to: range.upperBound)
guard
let lo = s2.index(s2.startIndex, offsetBy: pos, limitedBy: s2.endIndex),
let hi = s2.index(lo, offsetBy: len, limitedBy: s2.endIndex)
else {
return nil
}
return String(s2[lo..<hi])
}
print(foobar(input: "1") as Any) // Optional("一")
print(foobar(input: "123") as Any) // Optional("一二三")
print(foobar(input: "124") as Any) // nil
Your Objective-C code works as long as all characters in the string consume a single UTF-16 code unit (because that is what NSRange counts). It will not work correctly emojis, flags, and other characters which are represented as UTF-16 surrogate pairs, e.g. with
NSString *anotherString = #"😀一二三四五六七八九";
Another approach is converting strings to array of characters
func find(_ str: Character) {
let firstArr = Array("0123456789")
let secondArr = Array("零一二三四五六七八九")
guard let index = firstArr.firstIndex(of: str) else {
print("Not found")
return
}
print(firstArr[index]) // 2
print(secondArr[index]) // 二
}
find("2")

How do you limit UILabel characters in swift?

I'm trying to set a limit in my label to 10 characters, I'm a complete newbie to programming in general so its my 3rd month so far... anyway thanks in advance :-)
You can also create a String extension and reuse it with any text string.. You can do it in the following way:
create the extension:
extension String {
func maxLength(length: Int) -> String {
var str = self
let nsString = str as NSString
if nsString.length >= length {
str = nsString.substring(with:
NSRange(
location: 0,
length: nsString.length > length ? length : nsString.length)
)
}
return str
}
}
you can use the extension in the following way:
label.text = "This is a Very Long Label".maxLength(length: 10)
the previous code was tested in Swift 5.0
If you want to limit the UILabel to just 10 characters then
you just have to assign it with a text with length of 10.
You can use NSString and NSRange to extract the text you need.
let str = "This is a Very Long Label"
let nsString = str as NSString
if nsString.length >= 10
{
label.text = nsString.substringWithRange(NSRange(location: 0, length: nsString.length > 10 ? 10 : nsString.length))
}
Check out this answer: https://stackoverflow.com/a/39677331/2548473
My version is (Swift 5+):
extension String {
func shorted(to symbols: Int) -> String {
guard self.count > symbols else {
return self
}
return self.prefix(symbols) + " ..."
}
}
SWIFT 3
let str = GL_GetNews[indexPath.row]["n_body"].stringValue
let nsString = str as NSString
if nsString.length > 0
{
cell.newsDescription.text = nsString.substring(with: NSRange(location: 0, length: nsString.length > 200 ? 200 : nsString.length))
}

Convert NSRange in Objective-C to Swift

How do I convert this to Swift:
NSString *searchString = [NSString stringWithFormat:#"%#", #"Apple_HFS "];
NSRange range = [tempString rangeOfString:searchString];
NSUInteger *idx = range.location + range.length;
Thanks
If you use String, you can just reference endIndex:
let searchString: String = "Apple_HFS "
if let range: Range<String.Index> = tempString.rangeOfString(searchString) {
let index = range.endIndex
let stringAfter = tempString.substringFromIndex(index)
// do something with `stringAfter`
} else {
// not found
}
I included the types so you could see what's going on, but generally I'd just write:
let searchString = "Apple_HFS "
if let range = tempString.rangeOfString(searchString) {
let stringAfter = tempString.substringFromIndex(range.endIndex)
// do something with `stringAfter`
} else {
// not found
}
Is that what you're looking for ?
var str : NSString = "A string that include the searched string Apple_HFS inside"
let searchString : NSString = "Apple_HFS "
let range : NSRange = str.rangeOfString(searchString) // (42, 10)
let idx : Int = range.location + range.length // 52
Demonstration : http://swiftstub.com/831969865/
Simple solution without using ANY objc methods or types:
Suppose you have:
let nsrange = NSRange(location: 3, length: 5)
let string = "My string"
And now you need to convert NSRange to Range:
let range = Range(start: advance(string.startIndex, nsrange.location), end: advance(string.startIndex, nsrange.location + nsrange.length))

About AttributeString - making multiple occurrences bold

I am trying to make multiple occurrences in a attributed string bold using something like the following
[attrStr setFont:[UIFont ...] range:[attrStr.string rangeOfString:#"hello world"]];
As you know, 'rangeOfString' always return the FIRST occurrence of the match... I am still quite new to iOS, just wondering what's the best way to set all occurrences to bold... Is there any function provided in NSString or something?
Thanks in advance!
You should first try to get all ranges in the string and then set the attribute for every range. There are some great code examples right here on stackoverflow: https://stackoverflow.com/a/4653266/381870
Edit:
Here's an example for you
- (NSArray *)rangesOfString:(NSString *)searchString inString:(NSString *)str {
NSMutableArray *results = [NSMutableArray array];
NSRange searchRange = NSMakeRange(0, [str length]);
NSRange range;
while ((range = [str rangeOfString:searchString options:0 range:searchRange]).location != NSNotFound) {
[results addObject:[NSValue valueWithRange:range]];
searchRange = NSMakeRange(NSMaxRange(range), [str length] - NSMaxRange(range));
}
return results;
}
Usage:
NSArray *results = [self rangesOfString:#"foo" inString:#"foo bar foo"];
NSLog(#"%#", results);
gives you
(
"NSRange: {0, 3}",
"NSRange: {8, 3}"
)
Converting Tim's answer to Swift 4:
extension NSString {
open func ranges(of searchString: String) -> [NSRange] {
var ranges = [NSRange]()
var searchRange = NSRange(location: 0, length: self.length)
var range: NSRange = self.range(of: searchString)
while range.location != NSNotFound {
ranges.append(range)
searchRange = NSRange(location: NSMaxRange(range), length: self.length - NSMaxRange(range))
range = self.range(of: searchString, options: [], range: searchRange)
}
return ranges
}
}
Also did it to Swift's String primitive:
extension String {
func ranges(of substring: String, options: CompareOptions = [], locale: Locale? = nil) -> [Range<Index>] {
var ranges: [Range<Index>] = []
while let range = self.range(of: substring, options: options, range: (ranges.last?.upperBound ?? self.startIndex)..<self.endIndex, locale: locale) {
ranges.append(range)
}
return ranges
}
}
You will like have to use NSScanner to scan through the text and replace it.You can find an example for NSScanner here.

How to validate a website name in UItextfield in objective c? [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
}
}