Swift - Cast [AnyObject]! to [Protocol]! directly - swift

I am trying to overcome the limitation explained here about not being able to directly connect IB elements to outlets with protocol types.
The workaround mentioned is not ideal because what I need an Outlet Collection and connecting a new element means redoing this again and again and that defeats the purpose of this part of my project.
So I figured I would create an intermediate Controller whose sole purpose was to readily translate a an AnyObject outlet collection to the array typed with my Protocol.
I come to discover that the array casting you see below throws the error: "Type 'FormField' does not conform to protocol 'AnyObject'"
However, the simple per-item loop commented out actually works.
I would like to understand why the former fails and if there is a way to actually avoid the per-item loop.
class FormViewController: UIViewController, Form {
#IBOutlet var fieldCollection: [AnyObject]!
var formFields: [FormField]!
override func viewDidLoad() {
super.viewDidLoad()
self.formFields = self.fieldCollection as! [FormField]!
/*
self.formFields = [FormField]()
for field in self.fieldCollection {
self.formFields.append(field as! FormField)
}*/
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

You didn't show how the FormField protocol is defined but I'm guessing it doesn't have the AnyObject class as a constraint.
for example: protocol FormField:class, AnyObject {}
If it did, the type cast would work.
you could then declare it as:
class FormViewController: UIViewController, Form {
#IBOutlet var fieldCollection: [AnyObject]!
var formFields: [FormField] { return fieldCollection as! [FormField] }
and I believe you could event do this (in Swift 2.1):
#IBOutlet var fieldCollection: [FormField]!

Related

Implementing MVP using protocols

I'm trying to implement MVP using protocols,
I have View controller that holds a reference to a presenter protocol.
The presenter protocol contains a reference to the view
and has associatedtype that represent the ResultType.
The ResultType is different at each presenter.
for example:
class PresenterA: PresenterProtocol {
weak var view: ViewController!
typealias ResultType = String
var onDidPressCallback: ((ResultType) -> Void)?
}
It also can be
class PresenterB: PresenterProtocol {
weak var view: ViewController!
typealias ResultType = Apple
var onDidPressCallback: ((ResultType) -> Void)?
}
The problem start's when Im holding a reference to the presenter
from the ViewController.
class ViewController: UIViewController {
var presenter: PresenterProtocol!
}
Of course It is not possible and I get this error:
Protocol 'PresenterProtocol' can only be used as a generic constraint
because it has Self or associated type requirements
So I tried:
class ViewController<T: PresenterProtocol>: UIViewController {
var presenter: T!
}
But now the PresenterProtocol has this issue:
Reference to generic type 'ViewController' requires arguments in <...>
What am I doing wrong? And how can I solve it?
Plus, let's say I can not support new versions so I cannot use opaque type (some keyword).
In answer to your question, when you make the view controller generic, the error says that the “generic type 'ViewController' requires arguments in <...>”. So you can do precisely that:
class PresenterA: PresenterProtocol {
weak var view: ViewController<PresenterA>!
...
}
You will probably end up wanting to make a required initializer so you can instantiate these presenters, e.g.:
protocol PresenterProtocol {
associatedtype ResultType
var onDidPressCallback: ((ResultType) -> Void)? { get set }
init()
}
class PresenterA: PresenterProtocol {
weak var view: ViewController<PresenterA>!
var onDidPressCallback: ((String) -> Void)?
required init() { ... }
}
class PresenterB: PresenterProtocol {
weak var view: ViewController<PresenterB>!
var onDidPressCallback: ((Apple) -> Void)?
required init() { ... }
}
class ViewController<Presenter: PresenterProtocol>: UIViewController {
var presenter = Presenter()
override func viewDidLoad() {
super.viewDidLoad()
presenter.onDidPressCallback = { value in
// show the value in the UI
}
...
}
}
Hopefully, that answers the question, but there are a few issues here:
Presenters should be independent of UIKit. You do not want to have a reference to your view controller (or have any UIKit dependencies) in the presenter. Only the “view” (views, view controllers, etc.) should have UIKit dependencies. This separation of responsibilities is a core concept of MVP.
There are a number of options to allow the presenter to inform the view of events (such as delegate protocols, async-await, etc.). But you have a closure variable, and that is a perfectly adequate way to proceed. The presenter does not need a reference to the view controller, but rather should simply call the appropriate closure. The view controller, obviously, will just set those closure variables so it can respond to the presenter events.
But, as I said, there are a variety of different approaches to inform the “view” of events triggered by the presenter. But the presenter should not be reaching into the view controller, itself. No reference to the view controller is required or desired.
By making the view controller a generic, it can no longer be instantiated via standard UIKit mechanisms (storyboards, NIBs, etc.). Unless you really want to lose the benefits of IB outlets, actions, etc., and do everything programmatically, you probably do not want to make it generic. You introduce significant development/maintenance costs for the sake of generics.
Besides, a view controller for Apple and a view controller for String will probably have unique controls. Making it a generic might feel intuitively appealing at first, but in practice, it falls apart over time. The few times I went down this road, I ended up regretting it. As the UI evolves, you end up refining/specializing the UI for the individual types over time. The few times I tried permutations of this pattern, I found myself ripping it out later. It feels so intuitive, but it often becomes a hindrance later.
Fortunately, when you remove the reference to the view controller from the presenter and do not make the view controller generic, much of this noise disappears:
protocol PresenterProtocol {
associatedtype ResultType
var onDidPressCallback: ((ResultType) -> Void)? { get set }
}
class PresenterA: PresenterProtocol {
var onDidPressCallback: ((String) -> Void)?
// ...
}
class PresenterB: PresenterProtocol {
var onDidPressCallback: ((Apple) -> Void)?
// ...
}
class ViewControllerA: UIViewController {
var presenter = PresenterA()
override func viewDidLoad() {
super.viewDidLoad()
presenter.onDidPressCallback = { value in
// show the data in the UI
}
// ...
}
}
At this point, the protocol becomes more of a contract to ensure that all the presenters follow certain conventions for the shared functionality. But we no longer tie ourselves down, limiting our presenters to least-common-denominator functionality.

Why array's append method cannot be used in viewController?

I am beginner of swift. I tried to use array's append method in my code but it doesn't work. How should I implement the array correctly?
The error messages:
Swift Compiler Error Group
ViewController.swift:16:5: Expected declaration
ViewController.swift:11:7: In declaration of 'ViewController'
I tried to use array's append method in my code but it doesn't work.
import UIKit
class ViewController: UIViewController { //Error msg: In declaration of 'ViewController'
#IBOutlet weak var dice: UIImageView!
#IBOutlet weak var dice2: UIImageView!
var dices : [String] = []
dices.append("Hi") //Error: Expected declaration
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func rollPressed(_ sender: UIButton) {
dice.image = UIImage(named: "dice3")
}
}
I expect I can add "hi" into the array dices.
You should call the append inside a function after the vc is fully initated
class ViewController: UIViewController { //Error msg: In declaration of 'ViewController'
#IBOutlet weak var dice: UIImageView!
#IBOutlet weak var dice2: UIImageView!
var dices : [String] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
dices.append("Hi") // e.x here
}
#IBAction func rollPressed(_ sender: UIButton) {
dice.image = UIImage(named: "dice3")
}
}
Or replace
var dices : [String] = []
with
var dices = ["Hi"]
SH_Khan is right. I'll explain why though.
When defining a class, the first level of indentation is only for its methods and properties, aka func, var, and let. (You can also define other classes/structs/enums in there too)
Calling those functions or system functions like Array.append() or print("dog sweat") must happen inside of another function. The reason why is that your application's live logic is literally just functions all the way down. No function gets called unless it's inside of another function first. (The only exceptions are Swift's quick and dirty initializations like setting a default value to a var outside of an init() { } or another function.)
A dog doesn't wake up from its nap unless you make some noise. It won't do it on its own. (crappy metaphor, but yeah)
I hope that made any sense.

Using protocol's associated type in generic functions

I'm trying to write a simple MVP pattern to follow in my app, so I've written two porotocols to define View Controller and Presenters:
protocol PresenterType: class {
associatedtype ViewController: ViewControllerType
var viewController: ViewController? { get set }
func bind(viewController: ViewController?)
}
protocol ViewControllerType: class {
associatedtype Presenter: PresenterType
var presenter: Presenter { get }
init(presenter: Presenter)
}
After having those defined I started writing some RootViewController and RootViewPresenter. The latter looks like:
protocol RootViewControllerType: ViewControllerType {
}
final class RootPresenter<VC: RootViewControllerType>: PresenterType {
weak var viewController: VC?
func bind(viewController: VC?) {
self.viewController = viewController
}
}
Up to this point everything complies and is fine, but when I start implementing View Controller like this:
protocol RootPresenterType: PresenterType {
}
final class RootViewController<P: RootPresenterType>: UIViewController, ViewControllerType {
let presenter: P
init(presenter: Presenter) {
self.presenter = presenter
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
presenter.bind(viewController: self)
}
}
Immediately I get the following error message:
Cannot convert value of type 'RootViewController' to expected argument type '_?'
I know that protocols with associated types can introduce some limitations, but this example is pretty straightforward and I can't make it work. Is it possible to achieve something that I want, or do I have to look for some other, less Swifty pattern?
I don't think what you're trying to achieve is possible due to the circular dependency between the respective associated types of the PresenterType and ViewControllerType protocols.
Consider for a moment if the suspect code above did compile ... how would you go about instantiating either the RootPresenter or RootViewController classes? Because both depend on one another, you'll end up with errors like the following:
As you can see, the compiler can't fully resolve the generic parameters due to the associated types.
I think your best bet is to remove the associated type from one or both of the protocols. For example, removing the associated type from the PresenterType protocol and updating the RootPresenter class breaks the circular dependency and allows your code to compile normally.
protocol PresenterType: class {
var viewController: UIViewController? { get set }
func bind(viewController: UIViewController?)
}
final class RootPresenter: PresenterType {
weak var viewController: UIViewController?
func bind(viewController: UIViewController?) {
self.viewController = viewController
}
}

What's the proper way to subclass a delegate?

I'm trying to learn the delegation process for Swift in Xcode 8.
I can get it working just fine, but have a question about the subclass in my delegate. Normally, in Objective-C, the subclass for this would be NSObject. I'm able to get it working with NSObject and AnyObject. I read a article about not crossing Objective-C because of performance. Does this really matter? If it's not a view or any other type of controller, what's the subclass in Swift for an object?
Is AnyObject the same as NSObject?
ViewController
import UIKit
class ViewController: UIViewController, TestDelegate {
// init the delegate
let theDelegate = TheDelegate ()
#IBOutlet weak var label1: UILabel!
#IBAction func button1(_ sender: Any) {
// tell the delegate what to do
theDelegate.run(add: 1)
}
override func viewDidLoad() {
super.viewDidLoad()
theDelegate.delegate = self
}
// the protocol
func didTest(int: Int) {
label1.text = "\(int)"
print ("Got back from delegate \(int)")
}
}
Object TestProtocol.swift
import Foundation
protocol TestDelegate: class {
func didTest(int: Int)
}
class TheDelegate: AnyObject{
weak var delegate: TestDelegate?
func run(add: Int){
let test = add + 1
delegate?.didTest(int: test)
}
}
There is no reason to subclass AnyObject, since everything is an AnyObject just by existing (like object in Java).
NSObject is the old base class in Obj-C, so if you are wanting to have your protocol or class be seen in Obj-C code it must either be marked #objc or it must subclass NSObject.
So unless you are also using Obj-C in your program, then you don't need to subclass NSObject.
Look at
Swift 3: subclassing NSObject or not?
https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html

NSViewController delegate?

I'm new to using delegates in Swift, and I can't seem to figure out how to communicate with my View Controller from a different class. Specifically, I call the custom class's functions from my App Delegate, and then from within that custom class, I call a function within my View Controller. My basic setup, following this question, is:
AppDelegate.swift:
var customClass = customClass()
func applicationDidFinishLaunching(aNotification: NSNotification) {
customClass.customFunction()
}
CustomClass.swift:
weak var delegate: ViewControllerDelegate?
func customFunction() {
delegate?.delegateMethod(data)
}
ViewController.swift:
protocol ViewControllerDelegate: class {
func customFunction(data: AnyObject)
}
class ViewController: NSViewController, ViewControllerDelegate
func customFunction(data: AnyObject){
println("called")
}
}
However, delegate is always nil. I am assuming this is either because the ViewControllerDelegate protocol never gets initialized or because I never set the delegate of the actual NSViewController? I know I'm missing something obvious/straightfoward, however I have yet to see what that is.
Your question is hard to answers because you have completely misunderstood the point of a protocol.
A protocol is a type which is used to define functionality. A class that conforms to this protocol provides the specified functionality, by implementing the required methods.
You can not initialize a protocol.
So if your CustomClass looks like this:
class CustomClass {
weak var delegate: ViewControllerDelegate?
func customFunction() {
delegate?.delegateMethod(data)
}
}
Why do you expect that delegate has suddenly a value?
Of course you have to set delegate to something first. The delegate must set delegate. If you want a ViewController instance to be the delegate, it must assign itself to delegate.
This for instance will work.
protocol ViewControllerDelegate {
func delegateMethod(data: AnyObject) //I renamed this because in
//CustomClass you are trying to call `delegateMethod` on the delegate
}
class CustomClass {
weak var delegate: ViewControllerDelegate?
func customFunction() {
delegate?.delegateMethod(data)
}
}
class ViewController: NSViewController, ViewControllerDelegate
var customClass = CustomClass()
func viewDidLoad(){
customClass.delegate = self
customClass.customFunction()
}
func delegateMethod(data: AnyObject){
println("called")
}
}
Read more about delegation here.