Using NSNumber numberWithBool in Swift - ios

I submitted my Version 2 of my app for review and it got rejected due to a high backup to iCloud - wasn't aware of this as I only have to pics in my app but anyway. Now I try to convert that code
NSError *error = nil;
NSURL *databaseUrl = [NSURL fileURLWithPath:databasePath];
BOOL success = [databaseUrl setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:&error];
if(!success){
NSLog(#"Error excluding %# from backup %#", [databaseUrl lastPathComponent], error);
}
to swift and I couldn't get it work...
This is what I have so far...
func excludeFromBackup() {
var error:NSError?
var fileToExclude = NSURL.fileURLWithPath("path")
var success:Bool = fileToExclude?.setResourceValue(NSNumber.numberWithBool(true), forKey: NSURLIsExcludedFromBackupKey, error: &error)
if success {
println("worked")
} else {
println("didn't work")
}
}
As you see, I fail with the numberWithBool Value.
Can anyone help me? Did anyone convert it before?
Thanks in advance...

Some Swift types (Int, Bool, String, ...) are automatically
bridged to the corresponding Objective-C type, so you can simply write:
let success = fileToExclude.setResourceValue(true, forKey: NSURLIsExcludedFromBackupKey, error: &error)
(More details in Working with Cocoa Data Types.)

I got it sorted... Here is the solution.
func excludeFromBackup(path:String) {
var error:NSError?
var fileToExclude = NSURL.fileURLWithPath(path)!
var success:Bool = fileToExclude.setResourceValue(NSNumber(bool: true), forKey: NSURLIsExcludedFromBackupKey, error: &error)
if success {
println("worked")
} else {
println("didn't work")
}
}
That works just fine now.
Thanks anyway...

Related

Apple Health initHeartBeatSeries , how to get HKHeartbeatSeriesSample?

Trying to get HeartbeatSeries working but not sure how to get HkHeartbeatSeriesSample. Here's my code
I have this query which is gonna return the data from HeartbeatSeries but I'm not sure how to get the HKHeartbeatSeriesSample
built the query from here
https://developer.apple.com/documentation/healthkit/hkheartbeatseriesquery/3113764-initwithheartbeatseries?language=objc
-(void)fetchHeartSeries:(HKHeartbeatSeriesSample *)sample
timeSinceStart: (NSTimeInterval *)timeSinceStart
completion:(void (^)(NSArray *, NSError *))completionHandler API_AVAILABLE(ios(13.0)){
HKHeartbeatSeriesSample *sampleSeries = sample;
NSTimeInterval *timeSince = timeSinceStart;
if (#available(iOS 13.0, *)) {
HKHeartbeatSeriesQuery *query = [
[HKHeartbeatSeriesQuery alloc]
initWithHeartbeatSeries:(HKHeartbeatSeriesSample *)sampleSeries
dataHandler:^(HKHeartbeatSeriesQuery *query,
NSTimeInterval timeSince,
BOOL precededByGap,
BOOL done,
NSError * error){
if (error) {
// Perform proper error handling here
NSLog(#"*** An error occurred while getting the heart beat series: %# ***", error.localizedDescription);
completionHandler(nil, error);
}
if(done){
NSArray *data = query.accessibilityElements;
NSLog(#"Successfully retrieved heart beat data");
completionHandler(data, nil);
}
}];
[self.healthStore executeQuery:query];
} else {
// Fallback on earlier versions
}
}
This is how I did it. Note, you need to add HKSeriesType.heartbeat() as one of the read types in the requestAuthorization function to have permissions to get the Beat-to-Beat Measurements.
A simple, proof-of-concept to grab the HKHeartbeatSeriesSamples from the last 2 hours and use the first one to get the beat-to-beat measurements and print out the timestamp differences from the start.
I apologize for using Swift here. Let me know and I can provide an Objective-C version.
let last2hours = HKQuery.predicateForSamples(withStart: Date().addingTimeInterval(-60 * 60 * 24), end: Date(), options: [])
let hbSeriesSampleType = HKSeriesType.heartbeat()
let heartbeatSeriesSampleQuery = HKSampleQuery(sampleType: hbSeriesSampleType, predicate: last2hours, limit: 20, sortDescriptors: nil) { (sampleQuery, samples, error) in
if let heartbeatSeriesSample = samples?.first as? HKHeartbeatSeriesSample {
let query = HKHeartbeatSeriesQuery(heartbeatSeries: heartbeatSeriesSample) { (query, timeSinceSeriesStart, precededByGap, done, error) in
print(timeSinceSeriesStart)
}
self.healthStore.execute(query)
}
}
healthStore.execute(heartbeatSeriesSampleQuery)
For this HKHeartbeatSeriesSample:
count=23 F7D641F8-07AD-4543-84C8-126EA7B98B0F "Eric’s Apple Watch" (7.3.3), "Watch6,2" (7.3.3) "Apple Watch" (2021-04-13 17:18:59 -0500 - 2021-04-13 17:19:59 -0500)
Code above prints out to console:
0.78125
1.5390625
2.296875
3.08203125
3.87109375
4.61328125
5.37109375
6.10546875
6.86328125
7.7109375
9.3359375
10.94921875
11.76953125
12.5625
20.05078125
20.84765625
21.625
22.45703125
32.62109375
33.36328125
34.08203125
34.8046875
35.53515625

Error Handling When Saving a CKRecord

I am looking for an example of proper error handling when saving a CKRecord. According to the Apple docs I should "Use the information in the error object to determine whether a problem has a workaround."
I understand that the error object has a userInfo dictionary, but how do I figure out what the keys are for the dictionary and how to handle the errors?
The following example illustrates how I'm currently saving a CKRecord:
CKRecord *record = [[CKRecord alloc] initWithRecordType:#"MyRecordType"];
[record setValue:[NSNumber numberWithInt:99] forKey:#"myInt"];
[db saveRecord:record completionHandler:^(CKRecord *savedPlace, NSError *error) {
// handle errors here
if (savedPlace) {
NSLog(#"save successful");
}else{
NSLog(#"save unsuccessful");
}
if (error) {
NSLog(#"Error saving %#", error.localizedDescription);
}
}];
How can I improve this code to provide work arounds for potential saving issues?
In my library EVCloudKitDao I have a separate method that will return a error type based on the error code. Depending on that type you can decide what to do. Here is that method:
public enum HandleCloudKitErrorAs {
case Success,
Retry(afterSeconds:Double),
RecoverableError,
Fail
}
public static func handleCloudKitErrorAs(error:NSError?, retryAttempt:Double = 1) -> HandleCloudKitErrorAs {
if error == nil {
return .Success
}
let errorCode:CKErrorCode = CKErrorCode(rawValue: error!.code)!
switch errorCode {
case .NetworkUnavailable, .NetworkFailure, .ServiceUnavailable, .RequestRateLimited, .ZoneBusy, .ResultsTruncated:
// Use an exponential retry delay which maxes out at half an hour.
var seconds = Double(pow(2, Double(retryAttempt)))
if seconds > 1800 {
seconds = 1800
}
// Or if there is a retry delay specified in the error, then use that.
if let userInfo = error?.userInfo {
if let retry = userInfo[CKErrorRetryAfterKey] as? NSNumber {
seconds = Double(retry)
}
}
NSLog("Debug: Should retry in \(seconds) seconds. \(error)")
return .Retry(afterSeconds: seconds)
case .UnknownItem, .InvalidArguments, .IncompatibleVersion, .BadContainer, .MissingEntitlement, .PermissionFailure, .BadDatabase, .AssetFileNotFound, .OperationCancelled, .NotAuthenticated, .AssetFileModified, .BatchRequestFailed, .ZoneNotFound, .UserDeletedZone, .InternalError, .ServerRejectedRequest, .ConstraintViolation:
NSLog("Error: \(error)")
return .Fail;
case .QuotaExceeded, .LimitExceeded:
NSLog("Warning: \(error)")
return .Fail;
case .ChangeTokenExpired, .ServerRecordChanged:
NSLog("Info: \(error)")
return .RecoverableError
default:
NSLog("Error: \(error)") //New error introduced in iOS...?
return .Fail;
}
}
Inside the callback of a CloudKit method you can then use this function like this:
func loadContacts(retryCount:Double = 1) {
// Look who of our contact is also using this app.
EVCloudKitDao.publicDB.allContactsUserInfo({ users in
EVLog("AllContactUserInfo count = \(users.count)");
Async.main{
self.contacts = users
self.tableView.reloadData()
}
}, errorHandler: { error in
switch EVCloudKitDao.handleCloudKitErrorAs(error, retryAttempt: retryCount) {
case .Retry(let timeToWait):
Async.background(after: timeToWait) {
self.loadContacts(retryCount + 1)
}
case .Fail:
Helper.showError("Something went wrong: \(error.localizedDescription)")
default: // For here there is no need to handle the .Success, .Fail and .RecoverableError
break
}
})
}
In my the case above I use a separate error callback handler. You can also call it directly form within a CloudKit method callback. Just first check if there is an error.
Here is an implementation in which I handle the common error of CKErrorNetworkFailure by retrying to save after the recommended retry after time interval which is stored in the userInfo dictionary.
-(void)saveRecord:(CKRecord*)record toDatabase:(CKDatabase*)database{
[database saveRecord:record completionHandler:^(CKRecord *record, NSError *error) {
if (error==nil) {
NSLog(#"The save was successful");
//Do something
}else{
NSLog(#"Error saving with localizedDescription: %#", error.localizedDescription);
NSLog(#"CKErrorCode = %lu", [error code]);
if ([error code]==CKErrorNetworkFailure) {
double retryAfterValue = [[error.userInfo valueForKey:CKErrorRetryAfterKey] doubleValue];
NSLog(#"Error code network unavailable retrying after %f", retryAfterValue);
NSTimer *timer = [NSTimer timerWithTimeInterval:retryAfterValue target:self selector:#selector(testOutCloudKit) userInfo:nil repeats:NO];
[timer fire];
}
}
}];
}
In Swift 3, I handle my CloudKit errors this way:
privateDB.perform(myQuery, inZoneWith: nil) {records, error in
if error != nil {
print (error?.localizedDescription)
if error?._code == CKError.notAuthenticated.rawValue {
// Solve problems here...
}
Note that the CKError.notAuthenticated is selected from the list presented by code completion after writing CKError.
Hope this helps!

Facebook SDK iOS with Swift - Requesting for Friend List - Explanation of the Method

Would anyone be able to explain this method that we should invoke to recieve the friend list?
var fbRequestFriends: FBRequest = FBRequest.requestForMyFriends()
fbRequestFriends.startWithCompletionHandler{
(connection:FBRequestConnection!,result:AnyObject?, error:NSError!) -> Void in
}
Specifically this line
(connection:FBRequestConnection!,result:AnyObject?, error:NSError!) -> Void in
It seems to me like we are calling a function "startWithCompletionHandler", after that I am lost to be honest. I can't understand what happens next. Can anyone please explain this?
Edit:
I understand this is the way to implement it. I'm Actually looking for an intuitive explanation like in this answer:
Method Syntax in Objective C
you can use below code to get the friend list
// Get List Of Friends
var friendsRequest : FBRequest = FBRequest.requestForMyFriends()
friendsRequest.startWithCompletionHandler
{
(connection:FBRequestConnection!, result:AnyObject!, error:NSError!) -> Void in
var resultdict = result as NSDictionary
println("Result Dict: \(resultdict)")
var data : NSArray = resultdict.objectForKey("data") as NSArray
for i in 0 ..< data.count
{
let valueDict : NSDictionary = data[i] as NSDictionary
let id = valueDict.objectForKey("id") as String
println("the id value is \(id)")
}
var friends = resultdict.objectForKey("data") as NSArray
println("Found \(friends.count) friends")
}
Well you can see the result of your request call in the closure that you are passing in . Try printing the result object to console like below .
var fbRequestFriends: FBRequest = FBRequest.requestForMyFriends()
fbRequestFriends.startWithCompletionHandler{
(connection:FBRequestConnection!,result:AnyObject?, error:NSError!) -> Void in
if error == nil && result != nil {
println("Request Friends result : \(result!)")
} else {
println("Error \(error)")
}
}
I have not worked with Swift sdk for Facebook yet but I think result object should be an array of facebook user objects ( friends ) .

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.

How to validate an url on the iPhone

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
}
}

Resources