Trouble delaying function with variables passed to it in swift 4 - swift

I'm very new to swift and am having trouble with calling this function again on the 6th line after a delay, Xcode is telling me that
Argument of '#selector' does not refer to an '#objc' method, property, or initializer
I know what the problem is and have tried searching around but haven't been able to make anything work.
#objc func attemptToIdentify(_ user: String) {
if manager.status == .connected {
socket.emit("identify", user) //This functions correctly
} else {
print(manager.status, "... will attempt subscription in 1 second")
self.perform(#selector(attemptToIdentify(user)), with: nil, afterDelay: 1)
}
}

The problem is that attemptToIdentify(user) is a call. This is not the place for a call; you need a selector, i.e. the name of a function. So just put attemptToIdentify. If you need to pass something, you can pass it in the with parameter.
Even better, don't use perform:afterDelay: at all. If the idea is to add a delay to a call, just use asyncAfter (or my delay encapsulation of it).

Related

How can you mark a method that it is fatal, like `fatalError`?

We have a special logging function that represents a particular type of crash, and as such, internally it calls fatalError. However, unlike fatalError, when we use it in a guard statement, it still complains we haven't exited the scope of the guard.
I checked the source code and fatalError returns -> Never so I tried that, but that didn't work either. I also didn't see any attributes applied to it that may be telling the compiler 'this is the end of the road'.
So is it possible to have your own fatalError-like method?
func myFatalError(memberName:String = #function) -> Never {
let message = "Failed in \(memberName)"
NSLog(message)
fatalError(message)
}
As pointed out by others, the issue was something on my machine specifically. The following (returning 'Never') works as expected.
func myFatalError() -> Never {
print("Doing something fatal")
}

What does this syntax mean in Swift?

I am new to Swift and am confused with this type of syntax. I know when you add () to the end of something you initialize it. I am still confused what this means though! I am adding code below.
Please help clarify what parenthesis at the end of this means! Thank you!
Also what does it mean to have all that code after the equal sign in this case? ( I know how to create a variable and add a String,Int or something like that to it).
I am just confused a bit wth this code.
Thanks for being understanding to a beginner!
var viewController: ViewController = {
return self.instantiateViewControllerWithIdentifier("Play") as ViewController
}()
EDIT 1 -
var statusBarStyle: UIStatusBarStyle = .Default {
didSet{
setNeedsStatusBarAppearanceUpdate()
}
}
{} declares a closure, which is anonymous function. Everything between { and } is a function body. Since closure defined in provided code does not have arguments it can be executed as regular function by (). { .. }() is just defining and immediately executing of anonymous function.
In a body of a function there is a call of instantiateViewControllerWithIdentifier("Play") which returns AnyObject. As viewController variable (var) expected to ba a type of ViewController, we cast AnyObject result of instantiateViewControllerWithIdentifier as ViewController
As for statusBarStyle, UIStatusBarStyle is an enum. .Default is one of enum's cases. It can be written alternatively as var statusBarStyle = UIStatusBarStyle.Default. The code that goes in the { ... } is a way to declare getter and setter behavior. In this particular case there is only one behavior didSet defined, which means as soon as value of UIStatusBarStyle updated (which is possible, as it is var), call function setNeedsStatusBarAppearanceUpdate. There are other getters & setters keywords that you may read about in Swift Book — https://itunes.apple.com/us/book/swift-programming-language/id881256329 such as get, set, willSet.
As Nikita said its instantly calling the anonymous function you declared. This is really useful as it allows you to add logic when initialising a var or let.
Since the function takes no arguments, it makes it harder to see at first that it actually is a function. An example with an argument makes this concept a lot clearer.
let oneMore: Int = { (num: Int) in num + 1 }(5) //oneMore = 6
We are creating a function that takes one Int argument num and implicitly returns an Int (the compiler knows this because of the type annotation on oneMore. By following the closure with (5) we are calling the anonymous function with the value of 5.
Hopefully this example makes it more clear what happening. Note for an anonymous function in the context we would never need to provide argument since it will only ever be called once with the arguments following it, so we can just include the argument within the function body.
let oneMore: Int = { 5 + 1 }()
In the second example the braces are allowing you to include property observers to the variable. an example of a property observer is didSet which is called each time after you assign a value to the variable. more info can be found in apples docs here.

Method parameters in nested closure

I am trying to understand how parameters passed to a method are available to nested closures. I'm nervous that something I wrote won't always have the original parameters available.
(these are drastically simplified examples)
I have a method that I wrote that specifies a closure as a parameter:
func saveNameAndAgeToServer(serverParams: [String:String], completionHandler: (age: NSNumber) -> ()) {
// Connect to a server
// POST a name and dob from serverParams
// Receives a current age in completion:
completionHandler(age: 22)
}
Now somewhere else I create another method, that also specifies a closure, takes two parameters and calls the first function:
func AwesomeFunc(name: String, dob: NSDate, completionHandler: (isOverTwentyOne: Bool) -> ()) {
let formattedDob = NSDateFormatter().stringFromDate(dob)
saveNameAndAgeToServer([name:formattedDob]) { (age) -> () in
if (age as Int) >= 21 {
print("\(name) can have a beer.")
completionHandler(isOverTwentyOne: true)
} else {
print("\(name) is too young to drink, he can have a water.")
completionHandler(isOverTwentyOne: false)
}
}
}
Am I able to guarantee that the parameters (name and dob) passed into this latter function will always be available?
What I'm trying to ask is will the memory that the saveNameAndAgeToServer closure runs within always have the parameters of AwesomeFunc available to it? I'm pretty sure the function is all being held while whatever it calls is completed but would love a 2nd opinion.
You are correct, captured variables will last for the life of the closure. Here's an exert on capturing variables from apple's swift documentation:
A closure can capture constants and variables from the surrounding
context in which it is defined. The closure can then refer to and
modify the values of those constants and variables from within its
body, even if the original scope that defined the constants and
variables no longer exists.
In Swift, the simplest form of a closure that can capture values is a
nested function, written within the body of another function. A nested
function can capture any of its outer function’s arguments and can
also capture any constants and variables defined within the outer
function.
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html

Swift Selector with default argument

I have Write simple Code here
self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Cancel, target: self, action: Selector("cancelClick"))
Actual Function
func cancelClick(isAlert:String = "yes"){
self.dismissViewControllerAnimated(true, completion: { () -> Void in
if isAlert == "yes" {
Functions.displayAlert("called")
}
})
}
self.cancelClick() - Worked but if i didn't pass the argument
self.cancelClick(isAlert:"no") - Crashed
So what should be my selector if i have to pass argument in default perameter
tried with both Selector("cancelClick") and Selector("cancelClick:") but no luck.
The thing is that the parameter is not up to you. It is always the button (the "sender"), and that is the only thing it can be.
In other words, if you want this function to have a parameter, then by all means you will need to set your selector string as "cancelClick:" - the colon means that it takes a parameter. But that parameter must be the button:
func cancelClick(bbi:UIBarButtonItem?) {
However, you will notice that I have cleverly made this UIBarButtonItem parameter an Optional. Why do you think I did that? Because now you can also call it directly and pass nil:
self.cancelClick(nil)
Thus, cancelClick: now has a way to know whether the call comes from the tapping of a button or by a direct call - if bbi is not nil, the button was tapped; if bbi is nil, we were called directly from code. Sneaky, eh?
Another sneaky approach is to make the parameter an AnyObject:
func cancelClick(sender:AnyObject) {
The beauty of this is that you can call it with any kind of class instance. cancelClick can check the type of the sender. If it is a UIBarButtonItem (sender is UIBarButtonItem), then we were called by tapping the button. Otherwise, if called in code, you can pass in a string or anything else that this function might be prepared to deal with.
Don't use Selector("cancelClick:"), try instead just "cancelClick:" with the colon so you can pass an argument.
And as Matt said, the argument you must pass is the sender (the button) itself.

Selector string from Swift function

Given a function reference, is there a way in Swift to get the name of that function as a string suitable for passing to NSSelectorFromString?
I'd like to wrap NSNotificationCenter's addObserver with a version that takes a function reference instead of a selector string:
addObserver(self, function: someCallback, name: "some notification", object: nil)
But addObserver takes a String selector argument.
You're reinventing an unnecessary wheel. NSNotificationCenter already has an observation method that takes a function (what Objective-C calls a "block"):
addObserverForName:object:queue:usingBlock:
So just use that.
I'd still like to find an answer to my original question, but based on #matt's suggestion, I'm currently using this extension:
extension NSNotificationCenter {
class func addObserver(function: (NSNotification!) -> (), name: String? = nil, object: AnyObject? = nil, queue: NSOperationQueue? = nil) {
defaultCenter().addObserverForName(name, object: object, queue: nil, usingBlock: function)
}
}
Since it implicitly uses defaultCenter() and provides defaults for object and queue, which I'd almost always pass nil for, it allows for a more succinct call:
NSNotificationCenter.addObserver(doSomething, name: "some notification")
I like that it links against the actual function (doSomething), rather than a string representation. It's not a general purpose extension, but I think it covers 90% of the cases where I register for notifications.
Edit: I'm leaving this here for interest, but it's way too complicated (got wrapped up in how the question was asked, rather than the goal). Beyond the existing block-based approach, there's also this handy extension to it.
I wouldn't do it this way. It's too limiting because it would exclude function literals (anonymous functions).
Instead, I would play this game:
Create an dictionary property mapping [String: Void -> ()] (string to function)
When you register a new function, make up a unique, random key and store the function you're passed in your dictionary with that key.
Register with the selector observer_dispatcher_<key> (or whatever prefix you like).
Implement resolveInstanceMethod: to dynamically create any observer_dispatcher_ method you're requested. They can all point to the same IMP, something like:
(assuming this is good Swift; I haven't tried it):
void _observer_dispatcher(self: AnyObject, _cmd: Selector) {
let name = // strip the leading stuff off of _cmd
if let f = self.dispatchTable[name] {
f()
}
}
(This is still pretty sloppy; I'll have to think about the correct correct impl more, but this is the direction I'd go in.)