Completion blocks return nothing - swift

import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
isSuccess(true, success: { (name) -> String in
return "My name is \(name)"
})
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
func isSuccess(val:Bool, success: (name: String) -> String) {
if val {
success(name: "Jacky")
}
}
}
I expect it to return string "My name is Jacky",but it didn't .But if I change the isSuccess to :
func isSuccess(val:Bool, success: (name: String) -> String) {
if val {
print(success(name: "Jacky"))
}
}
}
Then it worked properly, why is that? Thanks in advance!

Your completion block returns a String.
When you invoke it by calling
success(name: "Jacky")
the completion block returns the String My name is Jacky. But you do nothing with that string. You just returned it and never used it.
In your second example, you actually used it - you took the string from the completion block, and printed it.
For example, instead the print, you could also write
let stringFromCompletionBlock = success(name: "Jacky")
That way you can see that it indeed returned a value.
And another thing is that the completion block should be call as the last thing in the function - this way you "notify" that the function has finished it's purpose, so it's not reasonable to use the value returned from a completion block inside the same function which called that completion block

First of all the closure in the function isSuccess should be declared this way. The closure should not return a String, it should just accept a String as param.
func isSuccess(val:Bool, success: (name: String) -> ()) {
if val {
success(name: "Jacky")
}
}
Next you could use that value to update the UI like follow
class ViewController: UIViewController {
weak var label:UILabel!
override func viewDidLoad() {
isSuccess(true) { (name) -> () in
dispatch_async(dispatch_get_main_queue()) {
self.label.text = "My name is \(name)"
}
}
super.viewDidLoad()
}
func isSuccess(val:Bool, success: (name: String) -> ()) {
if val {
success(name: "Jacky")
}
}
}

Related

Closure var not capture string

This code is not working, how do I repair it?
I try to handle the string and return it, but no result. printing does not output anything to the console
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let x = "hello"
let viewm = viewmodel()
viewm.handler(x)
viewm.handler = { item in
print(item)
}
viewm.execute { (tt) in
print(tt)
}
}
class viewmodel {
var handler:(String) -> Void = {
(data: String) in
}
func execute(complete: #escaping (String) -> Void) {
self.handler = complete
}
}
It is not clear what you are trying to achieve here, but if your issue is that print(item) is not printing anything, that is happening because this code by itself is not going to print anything:
viewm.handler = { item in
print(item)
}
handler property is a closure that receives a String parameter and returns Void. On your viewDidLoad method, you are assigning a closure that complies with those characteristics and, in addition to that, is going to print the passed parameter.
Given that closures are self-contained blocks of functionality, you need to call it in order to execute its block of functionality; but you need to call it after it has been defined; otherwise, it is not going to print anything.
If you write your code in this order, "hello" is going to be printed.
viewm.handler = { item in
print(item)
}
viewm.handler(x)

Swift closure defindet as class property cannot access other class properies

When directly assigning / implementing a Swift closure it is no problem to access class properties. But when I try to define the closure as class propertie as well, access to other class properties is not possible. Why is this?
Here is an example:
While the closure directly assigned to editorVC.completionBlock can access the class property tableView without any problem, the same code within leads to an error when the closure is defined as class property editorCompletionBlock:
class MyViewController: UIViewController {
#IBOutlet var tableView: UITableView!
func showEditor(withData: String) {
let editorVC = EditorViewController()
// Directly assign closure - Works without any problem
editorVC.completionBlock = { (result) in
self.tableView.reloadData()
doSomething(withResult: result)
// ...
}
present(editorVC, animated: true, completion: nil)
}
// Define closure as class property ==> Error
let editorCompletionBlock: EditorCompletionBlock = { (resut) in
// ERROR: Value of type '(MyViewController) -> () -> MyViewController' has no member 'tableView'
self.tableView.reloadData()
doSomething(withResult: result)
// ...
}
}
typealias EditorCompletionBlock = (String) -> Void
class EditorViewController: UIViewController {
var completionBlock: EditorCompletionBlock?
func closeEditor(withResult result: String) {
completionBlock?(result)
}
}
Reason:
You can't access self until the initialization process of a type is completed.
In your code, editorCompletionBlock is a stored property and you're trying to access self.tableView inside it. This is the reason it is giving compile time error.
Solution:
Instead, make editorCompletionBlock as a lazy property to get that working.
lazy var editorCompletionBlock: EditorCompletionBlock = { (result) in
self.tableView.reloadData()
doSomething(withResult: result)
}
Solution 1:
place your editorCompletionBlock in viewDidLoad(:_) and it should work like charm:
class MyViewController: UIViewController {
#IBOutlet var tableView: UITableView!
func showEditor(withData: String) {
let editorVC = EditorViewController()
editorVC.completionBlock = { (result) in
self.tableView.reloadData()
}
present(editorVC, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
let editorCompletionBlock: EditorCompletionBlock = { (resut) in
self.tableView.reloadData() ///should work :)
}
}
}
typealias EditorCompletionBlock = (String) -> Void
class EditorViewController: UIViewController {
var completionBlock: EditorCompletionBlock?
func closeEditor(withResult result: String) {
completionBlock?(result)
}
}
Solution 2:
Or you can just declare your closure as lazy:
lazy var editorCompletionBlock: EditorCompletionBlock = { (result)
in
self.tableView.reloadData()
doSomething(withResult: result)
}

Method does not override any method from its superclass (Swift 4, Xcode 9)

I've checked few topics related with the same issue but I can't find any solution to this problem inside my own code...
The error occurs when I try to override the function tune() in my Piano: Instrument subclass.
I've copy/past the superclass original function to ensure that the syntax is strictly the same.
Also, it seems to me that the subclass is well done, because the super.init method seems to work as intended (no compiler error).
Please, can someone point out to me where is my error ?
The code :
class Music {
let notes: [String]
init(notes: [String]) {
self.notes = notes
}
func prepared() -> String {
return notes.joined(separator: " ")
}
}
class Instrument {
let model: String
init(model: String) {
self.model = model
func tune() -> String {
fatalError("Implement this method for \(model)")
}
func play(_ music: Music) -> String {
return music.prepared()
}
func perform(_ music: Music) {
print(tune())
print(play(music))
}
}
}
class Piano: Instrument {
let hasPedals: Bool
init(hasPedals: Bool, model: String) {
self.hasPedals = hasPedals
super.init(model: model)
}
override func tune() -> String {
fatalError("Implement this method for \(model)")
}
}
class Guitar: Instrument {
let hasAmplifyer: Bool
init(hasAmplifyer: Bool, model: String) {
self.hasAmplifyer = hasAmplifyer
super.init(model: model)
}
}
Thank you very much !
You have accidentally defined your tune, play, and perform functions inside of your init function. Move them to the top level:
class Instrument {
let model: String
init(model: String) {
self.model = model
}
func tune() -> String {
fatalError("Implement this method for \(model)")
}
func play(_ music: Music) -> String {
return music.prepared()
}
func perform(_ music: Music) {
print(tune())
print(play(music))
}
}
Swift sees no tune function to override because it expects it to be at the top level in Instrument.

Check if the user pass the value or it used the default optional parameter in Swift function

This is not a common case, I want to know if the user passed the value or it used the default value in Swift. For example
func test(firstThing: Int? = nil) {
if firstThing.isNil {
if // firstThing used the default value {
print("default")
} else {
print("nil ingresed")
}
}
}
It should have the following behaviour:
test() // "default"
test(firstThing: nil) // "nil ingresed"
For this you must write two function with same name. But in one function u set perimeter and in another without perimeter, for example :-
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.myFunc()
self.myFunc("demo")
// Do any additional setup after loading the view, typically from a nib.
}
func myFunc() {
print("empty")
}
func myFunc(_ str : String){
print(str)
}
}

Custom protocols

Im getting this error "Class declaration cannot close over value 'viewcontainer' defined in outer scope"
I created a procotol called NetworkResponse which have two methods on sucessResponse and onErrorResponse.
Then I have a class called Callback that extends from NetworkResponse and forced to implement that methods.
Here is my function :
public func login (callback : Callback, viewController : UIViewController) {
let callbackInstance: NetworkResponse = {
class callback : Callback {
override func onSucessResponse(response : NSDictionary){
viewController.dismiss(animated: true, completion: nil)
}
override func onErrorResponse(message : String, code : Int){
print("error")
}
}
return callback()
}()
postPath(callback: callbackInstance as? Callback)
}
I want to dismiss the controller from the anonymous class.
Any recomendation ?
No need to define a protocol and Callback class. Closure is just what you need.
import UIKit
public class TestInnerClass: UIViewController {
public func login(successCallback: ((response: NSDictionary) -> Void), errorCallback: ((message: String, code: Int) -> Void)) {
let success = false
let response = NSDictionary()
//
// Make your login request here, and change the `success` value depends on your response
// let response = ...
//
// If you are making a async request to login, then put the following codes inside your request callback closure.
//
if success {
successCallback(response: response)
} else {
errorCallback(message: "error occurred", code: -1)
}
}
override public func viewDidLoad() {
super.viewDidLoad()
login({
(response) in
// Get Called when success
self.dismissViewControllerAnimated(true, completion: nil)
}, errorCallback: ({
// Get called when failed
(message, code) in
print(message)
}))
}
}
I have written some sample codes for your case in my Github repo, and this example is using Alamofire to make network request, Just for your reference.
PS: Since I still using Xcode 7.3.1, so you may need to do some changes to the above code to adopt the swift 3 syntax requirement.