I have this function that uses guard to make sure the parameters are correct:
func foo(bundle: NSBundle?, _ url: NSURL?)->Bool{
guard let _ = url, let _ = bundle else{
print("crap")
return false
}
return true
}
I noticed that the else clause will run if ANY of the parameters is nil. It behaves as an OR clause.
However, what I need is to check that BOTH are nil (an AND).
How can I do this?
If you don't actually care about your bound values (which I assume you don't, given the _ names), you can do this:
func foo(bundle: NSBundle?, _ url: NSURL?) -> Bool {
guard url != nil || bundle != nil else {
print("crap")
return false
}
return true
}
If you do actually want to bind the values, then you cannot accomplish this with a single guard statement. If it fails to unwrap a value from either of the optionals then it will execute the else block, as you've noted.
If you were still set on using a guard statement, you would have to put in a guard as I have above followed by various if let statements to unwrap things as appropriate (or just omit the guard and have an else statement in the code below that was the false case):
func foo(bundle: NSBundle?, _ url: NSURL?) -> Bool {
guard url != nil || bundle != nil else {
print("crap")
return false
}
if url == nil, let someBundle = bundle {
// Handle the bundle only case
}
else if bundle == nil, let someUrl = url {
// Handle the url only case
}
else if let someUrl = url, let someBundle = bundle {
// Handle the case for both
}
return true
}
This is not really a job for a guard statement. What you want is:
if(url == nil && bundle == nil)
{
print("crap")
return false
}
return true
Related
I have a macOS Share Extension invoked when users tap the Share button in Safari. I'm trying to get the page's URL from the -[NSExtensionItem attachments] attribute, but it comes as an NSSecureCoding object, and I'm unable to read the URL from it.
In the loadView method, I'm filtering and loading the attachments of type public.url:
override func loadView() {
super.loadView()
guard let inputItem = extensionContext?.inputItems.first as? NSExtensionItem else {
print("Didn't received input item from action.")
return
}
var itemProvider: NSItemProvider?
itemProvider = inputItem.attachments?.filter({ $0.registeredTypeIdentifiers.contains("public.url") }).first ?? inputItem.attachments?.filter({ $0.registeredTypeIdentifiers.contains("public.plain-text") }).first
guard let itemProvider = itemProvider else {
print("Didn't received attachments from input item.")
return
}
if itemProvider.canLoadObject(ofClass: URL.self) {
itemProvider.loadItem(forTypeIdentifier: "public.url", completionHandler: onLoadVideoURL)
} else if itemProvider.canLoadObject(ofClass: String.self) {
itemProvider.loadItem(forTypeIdentifier: "public.plain-text", completionHandler: onLoadVideoURL)
} else {
print("This action only supports URL and String.")
}
}
The itemProvider.loadItem method runs for the type identifier public.url, calling the completion handler bellow:
#objc private func onLoadVideoURL(dict: NSSecureCoding?, error: Error?) {
print("URL: \(dict.debugDescription)")
// ...
}
But the content that it prints to the console is:
URL: Optional(<68747470 733a2f2f 73746163 6b6f7665 72666c6f 772e636f 6d2f7175 65737469 6f6e732f 35323231 39373030 2f686f77 2d746f2d 63617374 2d6e7373 65637572 65636f64 696e672d 746f2d6d 6b6d6170 6974656d 2d696e2d 61637469 6f6e2d65 7874656e 73696f6e>)
The same code works as expected on iOS, printing the shared URL to the console.
Do I have to somehow convert this NSSecureCoding to URL or another object? Or should I do this in a completely different way on macOS? The goal is to access the page's URL from the Share Extension activated when the user selects it in the Share Menu.
The documentation is not very good on this. You can extract the URL (or whatever type) without writing a decoder by using loadObject instead of loadItem.
For example:
attachment.loadObject(ofClass: URL.self, completionHandler: { url, error in
print(url)
})
Turns out that the NSSecureCoding content was the URL, but in hexadecimal. Here's how I'm converting it to String:
let urlHex = dict.debugDescription
.replacingOccurrences(of: "Optional(", with: "")
.replacingOccurrences(of: ")", with: "")
.replacingOccurrences(of: "<", with: "")
.replacingOccurrences(of: ">", with: "")
.replacingOccurrences(of: " ", with: "")
guard let urlHexData = Data(fromHexEncodedString: urlHex) else { return }
guard let url = String(data: urlHexData, encoding: .utf8) else { return }
extension Data {
// From http://stackoverflow.com/a/40278391
init?(fromHexEncodedString string: String) {
func decodeNibble(u: UInt16) -> UInt8? {
switch(u) {
case 0x30 ... 0x39:
return UInt8(u - 0x30)
case 0x41 ... 0x46:
return UInt8(u - 0x41 + 10)
case 0x61 ... 0x66:
return UInt8(u - 0x61 + 10)
default:
return nil
}
}
self.init(capacity: string.utf16.count/2)
var even = true
var byte: UInt8 = 0
for c in string.utf16 {
guard let val = decodeNibble(u: c) else { return nil }
if even {
byte = val << 4
} else {
byte += val
self.append(byte)
}
even = !even
}
guard even else { return nil }
}
}
I wrote a function that I guess it should return a boolean , I'm using completion handler and switch and if statements within
this is my code :
class func login(username : String , password : String, _ completion: #escaping (Bool) -> ()) {
let url = "http://127.0.0.1:3000/login/"+username+"/"+password
Alamofire.request(url).responseJSON{response in
switch response.result
{
case .failure:
print(response)
completion(false)
case .success:
if let dict = response.value as? NSDictionary {
let dict = response.value as? NSDictionary
let user = dict!["users"] as? NSArray
if user!.count > 0 {
print(user!.count)
completion(true)
}
else {
print(user!.count)
completion(true)
}
}
}
}
}
And I want to use it like this :
#IBAction func LoginBtn(_ sender: Any) {
API.login(username: textUsername.text!, password: textPassword.text!) {
success in
if success{
// if the function return true print(hello)
//else print (type again)
print("Welcome")
}
else{
print("NO")
}
}
}
First of all don't use NSArray and NSDictionary in Swift, use native types.
Second of all never use .count > 0 to check for empty array or empty string. There is an optimized property isEmpty
Your code is very cumbersome and there are a few redundant parts in it.
The logic is to return true if the users array is valid and not empty otherwise return false.
So the success branch can be reduced to
case .success:
if let dict = response.value as? [String:Any], let users = dict!["users"] as? [[String:Any]], !users.isEmpty {
completion(true)
} else {
completion(false)
}
I had this code in my Swift App
func parseJSON() {
let urlString = "www.websitethatlinkstoJSONfile.com"
if NSURL(string: urlString) == true {
let url = NSURL(string: urlString)
let data = try? NSData(contentsOfURL: url!, options: []) as NSData
let json = NSData(data: data!)
// more code
However, even though the link actually worked and was true, the if statement was never met and it kept skipping it and moving to else. So I changed the code to
if NSURL(string: urlString) != false
and it worked perfectly. I'm not sure why though?
As already explained in the other answers, comparing the optional
NSURL? against true or false is not what you want, and you should
use optional binding instead.
But why does it compile at all? And how can the result be interpreted?
In NSURL(string: urlString) == true, the left-hand side has the type
NSURL?, and NSURL is a subclass of NSObject.
There is a == operator taking two optional operands:
public func ==<T : Equatable>(lhs: T?, rhs: T?) -> Bool
The compiler uses the implicit conversion of Bool to NSNumber
to make that compile. So your code is equivalent to
if NSURL(string: urlString) == NSNumber(bool: true)
and that will always be false, and
if NSURL(string: urlString) != NSNumber(bool: false)
will always be true, simply because the left-hand side is not a
number.
Here is a demonstration of the effect:
func foo(x: NSObject?) {
print(x == true, x == false)
}
foo(NSNumber(bool: true)) // true, false
foo(NSNumber(bool: false)) // false, true
foo(NSObject()) // false, false !!!
The last case is what you observed: Both x == true and x == false
return false.
For classes not inheriting from NSObject it would not compile:
class A { }
let a: A? = A()
if a == true { } // cannot convert value of type 'A?' to expected argument type 'Bool'
Remark: This is another argument for not comparing boolean values
against true or false, i.e.
if a == true && b == false { ... }
is better written as
if a && !b { ... }
Applied to your case, you would get a compiler error indicating
the problem:
let urlString = "http://www.websitethatlinkstoJSONfile.com"
if NSURL(string: urlString) { }
// error: optional type '_' cannot be used as a boolean; test for '!= nil' instead
You don't really want to check for a boolean value when creating a NSURL, but rather make sure the NSURL you create is non-nil. Try wrapping it it in an if let statement like so to make sure whatever URL's you create are non-nil before executing further code.
let urlString = "www.websitethatlinkstoJSONfile.com"
if let url = NSURL(string: urlString) {
if let data = try? NSData(contentsOfURL: url, options: []) {
let json = NSData(data: data)
} else {
//Handle case where data is nil
}
} else {
//Handle case where url is nil
}
Using if let statements in this way makes sure that the NSURL and NSData objects you are creating are non-nil and valid objects and then you can add an else statement to them to handle cases where your url or data objects are nil. This will save you from unwanted crashes due to force unwrapping with the ! operator.
Yes there is a different take a look at some of the documentation. In this init method it is fallible.
public convenience init?(string URLString: String)
the question mark indicates it is fallible.
So it will return a NSURL object or nil.
convenience init?(parameter: AnyObject) {
if parameter == nil {
return nil
}
self.init()
}
So in a specific example like your example. You can test it in playground
let urlString = "100"
if NSURL(string: urlString) == true {
print("true")
//Never prints in any circumstance//
}
if NSURL(string: urlString) != false {
print("true")
//always prints//
}
if NSURL(string: urlString) != nil {
print("true")
//object was created//
}
The == operator checks whether both side value is equal or not.For example:
var data:Int = 6
if data == 5 {
//if this block is executed that means `data` is exactly equal to 5
//do something
}
else {
//In this situation, this block of code will be executed.
//if this block is executed that means data is anything other than 5.
//do something
}
The != operator checks that two values are not equal to each other.For example:
var data:Int = 6
if data != 5 {
//In this situation, this block of code will be executed.
//If this block is executed that means `data` is anything other than 5.
//do something
}
else {
//if this block is executed that means data is exactly equal to 5.
//do something
}
In your case code if NSURL(string: urlString) == true checks that if NSURL(string: urlString) return true then it should excute if block otherwise else block.
NSURL(string: urlString) is convenience initializer which creates NSURL object and returns. In case if it fails to do so then it returns nil
In any case, it does not return either true or false.So when you compare this with true it always fails and goes to else block.
And when you check that it is not equal to false (!= false) becomes true because NSURL(string: urlString) returning NSURL object and that is not equal to false.
So if you want to check that whether NSURL object is created or not you can check whether the return value is nil or not.
if NSURL(string: urlString) != nil {
//Object is created successfully.Now you can do whatever you want this object.
}
else {
//It failed to create an object.
}
I have a static function which just reads a security scoped bookmark from NSUserdefaults. Like this:
static func desktopURL()->NSURL {
let desktopData = NSUserDefaults.standardUserDefaults().dataForKey("desktopSecurityBookmark")
if (desktopData != nil){
let desktop = try! NSURL(byResolvingBookmarkData: desktopData!, options: NSURLBookmarkResolutionOptions.WithSecurityScope, relativeToURL: nil, bookmarkDataIsStale: nil)
desktop.startAccessingSecurityScopedResource()
return desktop
} else {
// what the heck to return when URL was not set in the first place??
let desktopNil = NSURL() // :D ??? I need to return at leas something or not?
return desktopNil
}
}
I mean I need to return at least something or not?
The best way to deal with these situations is to make your return type optional, then you can return nil if your desktopData is nil. It is usually easier to handle the logic for a nil response in the function calling this function
Eg
static func desktopURL() -> NSURL? {
if let desktopData = NSUserDefaults.standardUserDefaults().dataForKey("desktopSecurityBookmark")
{
let desktop = try! NSURL(byResolvingBookmarkData: desktopData!, options: NSURLBookmarkResolutionOptions.WithSecurityScope, relativeToURL: nil, bookmarkDataIsStale: nil)
desktop.startAccessingSecurityScopedResource()
return desktop
} else {
return nil
}
}
I am new in Swift. My question is I am not sure how to unwrapping the optional value. When I print the object.objectForKey("profile_picture"), I can see Optional(<PFFile: 0x7fb3fd8344d0>).
let userQuery = PFUser.query()
//first_name is unique in Parse. So, I expect there is only 1 object I can find.
userQuery?.whereKey("first_name", equalTo: currentUser)
userQuery?.findObjectsInBackgroundWithBlock({ (objects: [PFObject]?, error: NSError?) -> Void in
if error != nil {
}
for object in objects! {
if object.objectForKey("profile_picture") != nil {
print(object.objectForKey("profile_picture"))
self.userProfilePicture.image = UIImage(data: object.objectForKey("profile_pricture")! as! NSData)
}
}
})
You'd use if let to perform "optional binding", only performing the block if the result in question is not nil (and binding the variable profilePicture to the unwrapped value in the process).
It would be something like:
userQuery?.findObjectsInBackgroundWithBlock { objects, error in
guard error == nil && objects != nil else {
print(error)
return
}
for object in objects! {
if let profilePicture = object.objectForKey("profile_picture") as? PFFile {
print(profilePicture)
do {
let data = try profilePicture.getData()
self.userProfilePicture.image = UIImage(data: data)
} catch let imageDataError {
print(imageDataError)
}
}
}
}
Or, if you want to get data asynchronously, perhaps:
userQuery?.findObjectsInBackgroundWithBlock { objects, error in
guard error == nil && objects != nil else {
print(error)
return
}
for object in objects! {
if let profilePicture = object.objectForKey("profile_picture") as? PFFile {
profilePicture.getDataInBackgroundWithBlock { data, error in
guard data != nil && error == nil else {
print(error)
return
}
self.userProfilePicture.image = UIImage(data: data!)
}
}
}
}
It would be something along those lines, using if let to unwrap that optional. And you then have to get the NSData associated with that the PFFile object (from the getData method or getDataInBackgroundWithBlock, presumably).
See Optional Binding discussion in The Swift Programming Language.