I am trying to find a way to name function type parameters for my case. To be more clear, let me share a bit of code.
class ViewController: UIViewController {
private lazy var testView: TestView = {
let view = TestView()
view.action = action
return view
}()
func action() {
//stuff
}
}
class TestView: UIView {
var action: () -> () = {}
#objc func buttonAction(_ sender: UIButton) {
action()
}
}
Code above is working well injecting UIButton actions from View to Controller but it becomes worse when there are parameters to be passed through. When I try to change the action inside TestView to,
var action: (name: String, surname: String) -> () = { _, _ in }
it does not let me to name String parameters. Afterwards I tried to change it as using tuple,
var action: ((name: String, surname: String)) -> () = { _ in }
the code inside the TestView works. However I can not match the types view.action = action in ViewController.
I am trying to do it because of two reasons. One of which is to not have any kind of logical inside View and other one is easily identify and avoid confusion the parameters inside ViewController by its given name when there are 2+ parameters that is going to be sent to the Controller.
Lastly, I know I can use delegation. But it has its own problems in my opinion. I am already have been trying to decrease Delegates in the project since I started to struggle naming the protocols.
Thanks in advance.
The swift does not allow named parameters in function declaration, so you can do like the following:
class TestView: UIView {
// var action: () -> () = {}
var action: (_ name: String , _ surname: String ) -> () = { _, _ in }
#objc func buttonAction(_ sender: UIButton) {
action("name", "surname")
}
}
Related
So I'm setting up a simple VIPER architecture in Swift.
The Interactor gets some data from an API, and passes the data to the presenter that then passes formatted data to the view.
The presenter will process the data, and just count the number of objects that are downloaded. To do so I have stored a var in the presenter. The question is should I store data in the presenter?
Interactor:
class Interactor {
weak var presenter: Presenter?
func getData() {
ClosureDataManager.shared.fetchBreaches(withURLString: baseUrl + breachesExtensionURL, completion: { [weak self] result in
guard let self = self else { return }
switch result {
case .failure(let error):
print(error)
case .success(let breaches):
self.presenter?.dataDidFetch(breaches: breaches)
self.presenter?.dataNumberDidFetch(number: breaches.count)
}
})
}
}
Presenter:
class Presenter {
var wireframe: Wireframe?
var view: ViewController?
var interactor: Interactor?
var dataDownloaded = 0
func viewDidLoad() {
print ("presenter vdl")
}
func loadData() {
interactor?.getData()
}
func dataDidFetch(breaches: [BreachModel]) {
view?.dataReady()
}
func showDetail(with text: String, from view: UIViewController) {
wireframe?.pushToDetail(with: text, from: view)
}
func dataNumberDidFetch(number: Int) {
dataDownloaded += number
view?.showData(number: String(dataDownloaded) )
}
}
View (ViewController)
protocol dataViewProtocol {
func showData(number: String)
}
class ViewController: UIViewController, dataViewProtocol {
#IBOutlet weak var showDetailButton: UIButton!
#IBOutlet weak var dataLabel: UILabel!
// weak here means it won't work
var presenter: Presenter?
#IBAction func buttonPressAction(_ sender: UIButton) {
presenter?.loadData()
}
#IBAction func buttonShowDetailAction(_ sender: UIButton) {
presenter?.showDetail(with: "AAA", from: self)
}
func dataReady() {
showDetailButton.isEnabled = true
}
func showData(number: String) {
dataLabel.text = number
}
override func viewDidLoad() {
super.viewDidLoad()
Wireframe.createViewModule(view: self)
presenter?.viewDidLoad()
}
}
Router (Wireframe)
class Wireframe {
static func createViewModule (view: ViewController) {
let presenterInst = Presenter()
view.presenter = presenterInst
view.presenter?.wireframe = Wireframe()
view.presenter?.view = view
view.presenter?.interactor = Interactor()
view.presenter?.interactor?.presenter = presenterInst
}
}
So should the presenter be used to store the number of objects downloaded?
What have you tried I've implemented the var, as shown above. This is a minimum example of the problem.
What resources have you used I've looked on StackOverflow, and Googled the issue. I can't find an answer, but know I could store the data in the view but I think this is incorrect. I could store the number of data in the Interactor, but this also doesn't seem right. It all seems...to violate separation of concerns...
I won't do your homework / use a different architecture / You should use protocols / Why is there a single protocol in your implementation This isn't homework, it is for my own self - study. There may be other architectures that can be used to do this (and coding to protocols is good practice) but this is about storing a variable in the presenter. I want to know if I should store the variable in the presenter, using VIPER and using Swift. Comments about trivia around the question are seldom helpful if they are about variable names, or the like.
What is the question? I want to know if I can store the number of downloaded data items in the presenter.
I am working on two views that are subclassing subclass of UITableViewCell. In the base one (subclass of UITableViewCell) I am trying to setup gesture recognizer in a way that each of super class could change the behavior (eventually call didTapped method on it's delegate) of the tap.
I have written following code. I can use #selector(tap), however I think that using a variable instead of overriding a tap method in each super class is a much cleaner way. Is it even possible to use something like #selector(tapFunc)? If no what would be the cleanest and best from engineering point of view solution?
class BaseCell: UITableViewCell {
#objc var tapFunc: () -> () = { () in
print("Tapped")
}
#objc func tap() {
print("TEST")
}
func setupBasicViews(withContent: () -> ()) {
let tapGestureRecoginzer = UITapGestureRecognizer(target: self, action: #selector(tapFunc))
contentView.isUserInteractionEnabled = true
contentView.addGestureRecognizer(tapGestureRecoginzer)
}
}
And then two views that are building on top of this one:
class ViewA: BaseCell {
//don't want to do this
override func tap() {
//do stuff
}
func setup {
//setup everything else
}
class ViewB: BaseCell {
var delegate: ViewBProtocool?
func setup {
tapFunc = { () in
delegate?.didTapped(self)
}
//setup everything else
}
You're not too far off. Make the following changes:
class BaseCell: UITableViewCell {
var tapFunc: (() -> Void)? = nil
// Called by tap gesture
#objc func tap() {
tapFunc?()
}
func setupBasicViews(withContent: () -> ()) {
let tapGestureRecoginzer = UITapGestureRecognizer(target: self, action: #selector(tap))
contentView.isUserInteractionEnabled = true
contentView.addGestureRecognizer(tapGestureRecoginzer)
}
}
class ViewA: BaseCell {
func setup() {
//setup everything else
}
}
class ViewB: BaseCell {
var delegate: ViewBProtocol?
func setup() {
tapFunc = {
delegate?.didTapped(self)
}
//setup everything else
}
}
Now each subclass can optionally provide a closure for the tapFunc property.
I show above that tapFunc is optional with no default functionality in the base class. Feel free to change that to provide some default functionality if desired.
I created a custom control and I want to pass the action in an #IBInspectable property to achieve the same effect of setting up an #IBAction using UIButton. How should I go about doing this?
class MyControl: UIButton {
// Problem with this string approach is
//I have no way to know which instance to perform the selector on.
#IBInspectable var tapAction: String?
// set up tap gesture
...
func labelPressed(_ sender: UIGestureRecognizer) {
if let tapAction = tapAction {
// How should I convert the string in tapAction into a selector here?
//I also want to pass an argument to this selector.
}
}
}
I really don't know why do you want it, but... Here is my solution:
Create a MyActions class, with actions that MyControl can to call:
class MyActions: NSObject {
func foo() {
print("foo")
}
func bar() {
print("bar")
}
func baz() {
print("baz")
}
}
Replace your MyControl class to
class MyControl: UIButton {
#IBInspectable var actionClass: String?
#IBInspectable var tapAction: String?
private var actions: NSObject?
override func awakeFromNib() {
// initialize actions class
let bundleName = Bundle.main.bundleIdentifier!.components(separatedBy: ".").last!
let className = "\(bundleName).\(actionClass!)"
guard let targetClass = NSClassFromString(className) as? NSObject.Type else {
print("Class \(className) not found!")
return
}
self.actions = targetClass.init()
// create tap gesture
let tap = UITapGestureRecognizer(target: self, action: #selector(pressed(_:)))
self.addGestureRecognizer(tap)
}
func pressed(_ sender: UIGestureRecognizer) {
actions!.perform(Selector(tapAction!))
}
}
And, set attributes of your button:
You can change the Tap Action in run time, for example:
#IBAction func buttonChangeAction(_ sender: Any) {
buttonMyControl.tapAction = textField.text!
}
Maybe you can change my code to pass parameters, but... it's you want?
I'm a swift beginner, so be gentle...
I'm having trouble assigning a function as a parameter.
I have defined this struct:
struct dispatchItem {
let description: String
let f: ()->Void
init(description: String, f: #escaping ()->()) {
self.description = description
self.f = f
}
}
I make use of this within a class called MasterDispatchController like so:
class MasterDispatchController: UITableViewController {
let dispatchItems = [
dispatchItem(description: "Static Table", f: testStaticTable),
dispatchItem(description: "Editable Table", f: testEditableTable)
]
func testEditableTable() {
//some code
}
func testStaticTable() {
//some code
}
etc.
Then I have a table view in my code that dispatches out to whichever function was clicked on (there are more than just the two I showed in the code above, but that's unimportant) like so
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
dispatchItems[indexPath.row].f()
}
So... The compiler is not happy with this. It says when I am defining the dispatchItems let statement:
Cannot convert value of type '(MasterDispatchController) -> () -> ()' to expected argument type '() -> ()'
I figure... ok... I'm not sure I exactly understand this, but it seems like the compiler wants to know what class the callback functions are going to come from. I can see why it might need that. So I kind of blindly follow the pattern the compiler gives me, and change my struct to:
struct dispatchItem {
let description: String
let f: (MasterDispatchController)->()->Void
init(description: String, f: #escaping (MasterDispatchController)->()->()) {
self.description = description
self.f = f
}
}
Great the compiler is happy there, but now when I try to call the function with dispatchItems[indexPath.row].f() it says:
Missing parameter #1 in call
The function has no parameters, so I got confused...
I thought maybe it was asking me for the instance of the object in question which would make some sense... that would be "self" in my example, so I tried dispatchItems[indexPath.row].f(self) but then I got an error:
Expression resolves to an unused function
So I'm kind of stuck.
Sorry if this is a stupid question. Thanks for your help.
The problem is that you're trying to refer to the instance methods testStaticTable and testEditableTable in your instance property's initialiser before self is fully initialised. Therefore the compiler cannot partially apply these methods with self as the implicit parameter, but can instead only offer you the curried versions – of type (MasterDispatchController) -> () -> ().
One might be tempted then to mark the dispatchItems property as lazy, so that the property initialiser runs on the first access of the property, when self is fully initialised.
class MasterDispatchController : UITableViewController {
lazy private(set) var dispatchItems: [DispatchItem] = [
DispatchItem(description: "Static Table", f: self.testStaticTable),
DispatchItem(description: "Editable Table", f: self.testEditableTable)
]
// ...
}
(Note that I renamed your struct to conform to Swift naming conventions)
This now compiles, as you now can refer to the partially applied versions of the methods (i.e of type () -> Void), and can call them as:
dispatchItems[indexPath.row].f()
However, you now have a retain cycle, because you're storing closures on self which are strongly capturing self. This is because when used as a value, self.someInstanceMethod resolves to a partially applied closure that strongly captures self.
One solution to this, which you were already close to achieving, is to instead work with the curried versions of the methods – which don't strongly capture self, but instead have to be applied with a given instance to operate on.
struct DispatchItem<Target> {
let description: String
let f: (Target) -> () -> Void
init(description: String, f: #escaping (Target) -> () -> Void) {
self.description = description
self.f = f
}
}
class MasterDispatchController : UITableViewController {
let dispatchItems = [
DispatchItem(description: "Static Table", f: testStaticTable),
DispatchItem(description: "Editable Table", f: testEditableTable)
]
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
dispatchItems[indexPath.row].f(self)()
}
func testEditableTable() {}
func testStaticTable() {}
}
These functions now take a given instance of MasterDispatchController as a parameter, and give you back the correct instance method to call for that given instance. Therefore, you need to first apply them with self, by saying f(self) in order to get the instance method to call, and then call the resultant function with ().
Although it may be inconvenient constantly applying these functions with self (or you may not even have access to self). A more general solution would be to store self as a weak property on DispatchItem, along with the curried function – then you can apply it 'on-demand':
struct DispatchItem<Target : AnyObject> {
let description: String
private let _action: (Target) -> () -> Void
weak var target: Target?
init(description: String, target: Target, action: #escaping (Target) -> () -> Void) {
self.description = description
self._action = action
}
func action() {
// if we still have a reference to the target (it hasn't been deallocated),
// get the reference, and pass it into _action, giving us the instance
// method to call, which we then do with ().
if let target = target {
_action(target)()
}
}
}
class MasterDispatchController : UITableViewController {
// note that we've made the property lazy again so we can access 'self' when
// the property is first accessed, after it has been fully initialised.
lazy private(set) var dispatchItems: [DispatchItem<MasterDispatchController>] = [
DispatchItem(description: "Static Table", target: self, action: testStaticTable),
DispatchItem(description: "Editable Table", target: self, action: testEditableTable)
]
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
dispatchItems[indexPath.row].action()
}
func testEditableTable() {}
func testStaticTable() {}
}
This ensures that you have no retain cycles, as DispatchItem doesn't have a strong reference to self.
Of course, you may be able to use unowned references to self, such as shown in this Q&A. However, you should only do so if you can guarantee that your DispatchItem instances don't outlive self (you would want to make dispatchItems a private property for one).
The problem here is that Swift treats class methods and functions differently under the hood. Class methods get a hidden self parameter (similar to how it works in Python), which allows them to know on which class instance they were called. That's why even though you declared testEditableCode as () -> (), the actual function has type (MasterDispatchController) -> () -> (). It needs to know on which object instance it was called.
The correct way to do what you're trying to do would be to create a closure which calls the correct method, like such:
class MasterDispatchController: UITableViewController {
let dispatchItems = [
dispatchItem(description: "Static Table", f: {() in
self.testStaticTable()
}),
dispatchItem(description: "Editable Table", f: {() in
self.testEditableTable()
})
]
func testEditableTable() {
//some code
}
func testStaticTable() {
//some code
}
If you're familiar with JavaScript, the {() in ...code...} notation is the same as function() { ...code... } or the same as lambda: ...code... in Python.
Here is the way to achieve what you want.
Implement protocol with parameters you want:
protocol Testable {
func perfromAction()
var description: String { get set }
weak var viewController: YourViewController? { get set } //lets assume it is fine for testing
}
Accessing your UIViewController like this is not quite right, but for now it is ok. You can access labels, segues, etc.
Create Class for each test you want:
class TestA: Testable {
var description: String
weak var viewController: YourViewController?
func perfromAction() {
print(description) //do something
viewController?.testCallback(description: description) //accessing your UIViewController
}
init(viewController: YourViewController, description: String) {
self.viewController = viewController
self.description = description
}
}
class TestB: Testable {
var description: String
weak var viewController: YourViewController?
func perfromAction() {
print(description) //do something
viewController?.testCallback(description: description) //accessing your UIViewController
}
init(viewController: YourViewController, description: String) {
self.viewController = viewController
self.description = description
}
}
You can add some custom parameters for each Class, but those 3 from protocol are required.
Then your UIViewController would be like
class YourViewController: UIViewController {
var arrayOfTest: [Testable] = []
override func viewDidLoad() {
super.viewDidLoad()
arrayOfTest.append(TestA(viewController: self, description: "testA"))
arrayOfTest.append(TestB(viewController: self, description: "testB"))
arrayOfTest[0].perfromAction()
arrayOfTest[1].perfromAction()
}
func testCallback(description: String) {
print("I am called from \(description)")
}
}
I'm a swift beginner, so be gentle...
I'm having trouble assigning a function as a parameter.
I have defined this struct:
struct dispatchItem {
let description: String
let f: ()->Void
init(description: String, f: #escaping ()->()) {
self.description = description
self.f = f
}
}
I make use of this within a class called MasterDispatchController like so:
class MasterDispatchController: UITableViewController {
let dispatchItems = [
dispatchItem(description: "Static Table", f: testStaticTable),
dispatchItem(description: "Editable Table", f: testEditableTable)
]
func testEditableTable() {
//some code
}
func testStaticTable() {
//some code
}
etc.
Then I have a table view in my code that dispatches out to whichever function was clicked on (there are more than just the two I showed in the code above, but that's unimportant) like so
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
dispatchItems[indexPath.row].f()
}
So... The compiler is not happy with this. It says when I am defining the dispatchItems let statement:
Cannot convert value of type '(MasterDispatchController) -> () -> ()' to expected argument type '() -> ()'
I figure... ok... I'm not sure I exactly understand this, but it seems like the compiler wants to know what class the callback functions are going to come from. I can see why it might need that. So I kind of blindly follow the pattern the compiler gives me, and change my struct to:
struct dispatchItem {
let description: String
let f: (MasterDispatchController)->()->Void
init(description: String, f: #escaping (MasterDispatchController)->()->()) {
self.description = description
self.f = f
}
}
Great the compiler is happy there, but now when I try to call the function with dispatchItems[indexPath.row].f() it says:
Missing parameter #1 in call
The function has no parameters, so I got confused...
I thought maybe it was asking me for the instance of the object in question which would make some sense... that would be "self" in my example, so I tried dispatchItems[indexPath.row].f(self) but then I got an error:
Expression resolves to an unused function
So I'm kind of stuck.
Sorry if this is a stupid question. Thanks for your help.
The problem is that you're trying to refer to the instance methods testStaticTable and testEditableTable in your instance property's initialiser before self is fully initialised. Therefore the compiler cannot partially apply these methods with self as the implicit parameter, but can instead only offer you the curried versions – of type (MasterDispatchController) -> () -> ().
One might be tempted then to mark the dispatchItems property as lazy, so that the property initialiser runs on the first access of the property, when self is fully initialised.
class MasterDispatchController : UITableViewController {
lazy private(set) var dispatchItems: [DispatchItem] = [
DispatchItem(description: "Static Table", f: self.testStaticTable),
DispatchItem(description: "Editable Table", f: self.testEditableTable)
]
// ...
}
(Note that I renamed your struct to conform to Swift naming conventions)
This now compiles, as you now can refer to the partially applied versions of the methods (i.e of type () -> Void), and can call them as:
dispatchItems[indexPath.row].f()
However, you now have a retain cycle, because you're storing closures on self which are strongly capturing self. This is because when used as a value, self.someInstanceMethod resolves to a partially applied closure that strongly captures self.
One solution to this, which you were already close to achieving, is to instead work with the curried versions of the methods – which don't strongly capture self, but instead have to be applied with a given instance to operate on.
struct DispatchItem<Target> {
let description: String
let f: (Target) -> () -> Void
init(description: String, f: #escaping (Target) -> () -> Void) {
self.description = description
self.f = f
}
}
class MasterDispatchController : UITableViewController {
let dispatchItems = [
DispatchItem(description: "Static Table", f: testStaticTable),
DispatchItem(description: "Editable Table", f: testEditableTable)
]
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
dispatchItems[indexPath.row].f(self)()
}
func testEditableTable() {}
func testStaticTable() {}
}
These functions now take a given instance of MasterDispatchController as a parameter, and give you back the correct instance method to call for that given instance. Therefore, you need to first apply them with self, by saying f(self) in order to get the instance method to call, and then call the resultant function with ().
Although it may be inconvenient constantly applying these functions with self (or you may not even have access to self). A more general solution would be to store self as a weak property on DispatchItem, along with the curried function – then you can apply it 'on-demand':
struct DispatchItem<Target : AnyObject> {
let description: String
private let _action: (Target) -> () -> Void
weak var target: Target?
init(description: String, target: Target, action: #escaping (Target) -> () -> Void) {
self.description = description
self._action = action
}
func action() {
// if we still have a reference to the target (it hasn't been deallocated),
// get the reference, and pass it into _action, giving us the instance
// method to call, which we then do with ().
if let target = target {
_action(target)()
}
}
}
class MasterDispatchController : UITableViewController {
// note that we've made the property lazy again so we can access 'self' when
// the property is first accessed, after it has been fully initialised.
lazy private(set) var dispatchItems: [DispatchItem<MasterDispatchController>] = [
DispatchItem(description: "Static Table", target: self, action: testStaticTable),
DispatchItem(description: "Editable Table", target: self, action: testEditableTable)
]
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
dispatchItems[indexPath.row].action()
}
func testEditableTable() {}
func testStaticTable() {}
}
This ensures that you have no retain cycles, as DispatchItem doesn't have a strong reference to self.
Of course, you may be able to use unowned references to self, such as shown in this Q&A. However, you should only do so if you can guarantee that your DispatchItem instances don't outlive self (you would want to make dispatchItems a private property for one).
The problem here is that Swift treats class methods and functions differently under the hood. Class methods get a hidden self parameter (similar to how it works in Python), which allows them to know on which class instance they were called. That's why even though you declared testEditableCode as () -> (), the actual function has type (MasterDispatchController) -> () -> (). It needs to know on which object instance it was called.
The correct way to do what you're trying to do would be to create a closure which calls the correct method, like such:
class MasterDispatchController: UITableViewController {
let dispatchItems = [
dispatchItem(description: "Static Table", f: {() in
self.testStaticTable()
}),
dispatchItem(description: "Editable Table", f: {() in
self.testEditableTable()
})
]
func testEditableTable() {
//some code
}
func testStaticTable() {
//some code
}
If you're familiar with JavaScript, the {() in ...code...} notation is the same as function() { ...code... } or the same as lambda: ...code... in Python.
Here is the way to achieve what you want.
Implement protocol with parameters you want:
protocol Testable {
func perfromAction()
var description: String { get set }
weak var viewController: YourViewController? { get set } //lets assume it is fine for testing
}
Accessing your UIViewController like this is not quite right, but for now it is ok. You can access labels, segues, etc.
Create Class for each test you want:
class TestA: Testable {
var description: String
weak var viewController: YourViewController?
func perfromAction() {
print(description) //do something
viewController?.testCallback(description: description) //accessing your UIViewController
}
init(viewController: YourViewController, description: String) {
self.viewController = viewController
self.description = description
}
}
class TestB: Testable {
var description: String
weak var viewController: YourViewController?
func perfromAction() {
print(description) //do something
viewController?.testCallback(description: description) //accessing your UIViewController
}
init(viewController: YourViewController, description: String) {
self.viewController = viewController
self.description = description
}
}
You can add some custom parameters for each Class, but those 3 from protocol are required.
Then your UIViewController would be like
class YourViewController: UIViewController {
var arrayOfTest: [Testable] = []
override func viewDidLoad() {
super.viewDidLoad()
arrayOfTest.append(TestA(viewController: self, description: "testA"))
arrayOfTest.append(TestB(viewController: self, description: "testB"))
arrayOfTest[0].perfromAction()
arrayOfTest[1].perfromAction()
}
func testCallback(description: String) {
print("I am called from \(description)")
}
}