Add class property to protocol in Swift - ios

I want to have a protocol that have a variable. And class that conform to that protocol should use it like "normal" variable. What i want is something like:
protocol MyProtocol {
var foo: Int
}
class A {}
extension A: MyProtocol {
var foo: Int!
}
Code above not compile, i only want to show point i want to achieve.
I ended up with this, but i suppose there must be better way:
enum NextController {
case AuthSelection
case Main
}
protocol SmsEntryPresenterProtocol {
var nextController: NextController { get set }
}
class SmsEntryPresenter {
var _nextController: NextController!
weak var view: SmsEntryViewProtocol?
}
extension SmsEntryPresenter: SmsEntryPresenterProtocol {
var nextController: NextController {
get {
return _nextController
}
set {
_nextController = newValue
}
}
}

You can fix this as below,
class SmsEntryPresenter {
var nextController: NextController = .Main
weak var view: SmsEntryViewProtocol?
}
extension SmsEntryPresenter: SmsEntryPresenterProtocol {}

Related

Extending structs with protocols in Swift

I have the following piece of code. The protocols and extensions I've added don't seem to be doing quite what I expect. What am I missing?
struct foo {
var getFoo: String
var getBar: bar
struct bar {
var getBar: String
}
}
protocol FooProtocol {
var getFoo: String { get }
var getBar: BarProtocol { get }
}
extension foo: FooProtocol {} // Type 'foo' does not conform to protocol 'FooProtocol'
protocol BarProtocol {
var getBar: String { get }
}
extension foo.bar: BarProtocol {}
The problem in this code is the line extension foo: FooProtocol {}, where we're trying to say that, in effect foo.getBar.getBar should be a valid property. However, Xcode throws up a compilation error saying "Type 'foo' does not conform to protocol 'FooProtocol'.
I can fix this by changing the following:
protocol FooProtocol {
var getFoo: String { get }
var getBar: foo.bar { get } // instead of var getBar: BarProtocol { get }
}
However, it seems the line extension foo.bar: BarProtocol {} should mean FooProtocol doesn't care if we give it foo.bar or BarProtocol. What am I missing here?
#Drobs answer was correct. Here's a bonus question.
BONUS
Now suppose I need to implement another struct that conforms to FooProtocol. Neither of these approaches work. What's the fix?
struct FooImplementation: FooProtocol { // Type 'FooImplementation' does not conform to protocol 'FooProtocol'
var getFoo: String
var getBar: BarProtocol
}
Do I need to use some sort of typealias to achieve this?
You want to use an associatedtype in the protocol. In that case it essentially says give me a type that implements the protocol BarProtocol.
protocol FooProtocol {
associatedtype BarType: BarProtocol
var getFoo: String { get }
var getBar: BarType { get }
}
protocol BarProtocol {
var getBar: String { get }
}
struct Foo: FooProtocol {
var getFoo: String
var getBar: Bar
struct Bar: BarProtocol {
var getBar: String
}
}

Implement protocol variable conforming to single protocol as variable conforming to several protocols

I'm trying to do a simple thing, but the code gives an error: Type 'MyClass' does not conform to protocol 'MyProtocol3'.
protocol MyProtocol1 {
}
protocol MyProtocol2 {
}
protocol MyProtocol3 {
var output: MyProtocol2 { get }
}
class MyClass: MyProtocol3 {
var output: MyProtocol2 & MyProtocol1
init(output: MyProtocol1 & MyProtocol2) {
self.output = output
}
}
Conforming MyProtocol2 to MyProtocol1 also doesn't do the trick. Is it possible to use variable conforming to multiple protocol as another protocol var?
You say, "Conforming MyProtocol2 to MyProtocol1 also doesn't do the trick." Unless you have some trick in mind that you haven't mentioned, it looks to me like the answer.
The following works in a playground:
protocol MyProtocol1 {
func test1()
}
protocol MyProtocol2: MyProtocol1 {
func test2()
}
class OutputClass: MyProtocol2 {
func test1() {
print("test1")
}
func test2() {
print("test2")
}
}
protocol MyProtocol3 {
var output: MyProtocol2 { get }
}
class MyClass: MyProtocol3 {
var output: MyProtocol2
init(output: MyProtocol2) {
self.output = output
}
}
let o = OutputClass()
o.test1()
o.test2()
let m = MyClass(output: o)

What am I doing wrong on passing data through protocol

I'm trying to pass data between viewControllers, but something seems wrong.
The first viewController I want to set the "Bool" to the protocol function to be able to recover in the other screen. What am I doing wrong, I always used protocols but at this time I got in trouble.
That's how I'm doing that:
//
// ComboBoxNode.swift
//
import Foundation
import SWXMLHash
protocol ComboBoxNodeDelegate {
func getCustomOption(data:Bool)
}
class ComboBoxNode: FormControlNode, IFormControlDataSource {
var listType: String?
var dataSource: String?
var dataSourceValue: String?
var dataSourceText: String?
var hasCustomOption:Bool?
var customOptionText: String?
var ctrlDataSourceType: String?
var parameters = [ParameterNode]()
var staticList: FormControlStaticListNode?
var delegate:ComboBoxNodeDelegate?
override init(indexer: XMLIndexer) {
super.init(indexer: indexer)
guard let element = indexer.element else {
preconditionFailure("Error")
}
let isCustomOption = element.bool(by: .hasCustomOption) ?? hasCustomOption
if isCustomOption == true {
self.delegate?.getCustomOption(data: hasCustomOption!)
}
self.readFormControlDataSource(indexer: indexer)
}
override func accept<T, E: IViewVisitor>(visitor: E) -> T where E.T == T {
return visitor.visit(node: self)
}
}
That's how I'm trying to recover on next screen:
// FormPickerViewDelegate.swift
import Foundation
import ViewLib
import RxSwift
class FormPickerViewDelegate: NSObject {
var items = Variable([(value: AnyHashable, text: String)]()) {
didSet {
PickerNodeDelegate = self
self.setDefaultValues()
}
}
private var controlViewModel: FormControlViewModel
private var customText:Bool?
private var PickerNodeDelegate:ComboBoxNodeDelegate?
init(controlViewModel: FormControlViewModel) {
self.controlViewModel = controlViewModel
}
func getItemByValue(_ value: Any) -> (AnyHashable, String)? {
if value is AnyHashable {
let found = items.value.filter {$0.value == value as! AnyHashable}
if found.count >= 1 {
return found[0]
}
}
return nil
}
}
extension FormPickerViewDelegate:ComboBoxNodeDelegate {
func getCustomOption(data: Bool) {
customText = data
}
}
Instead of setting PickerNodeDelegate = self in didSet {} closure
var items = Variable([(value: AnyHashable, text: String)]()) {
didSet {
PickerNodeDelegate = self
self.setDefaultValues()
}
}
Assign it in your init() function instead
init(controlViewModel: FormControlViewModel) {
self.controlViewModel = controlViewModel
PickerNodeDelegate = self
}
Note, your should declare your delegate to be weak also, since it's a delegate, your protocol should conform to be a class type in order to be weakified.
protocol ComboBoxNodeDelegate: class
...
weak var delegate: ComboBoxNodeDelegate?
Here is an example, hope it helps!
protocol ComboBoxNodeDelegate {
func getCustomOption(data:Bool) -> String
}
class ViewOne:ComboBoxNodeDelegate {
var foo:Bool = false
var bar:String = "it works!"
/** Return: String */
func getCustomOption(data:Bool) -> String { //conform here to protocol
// do whatever you wanna do here ...example
self.foo = data // you can set
return bar // even return what you want
}
//initialize
func initalizeViewTwo() {
let v2 = ViewTwo()
v2.delegate = self //since `self` conforms to the ComboBoxNodeDelegate protcol you are allowed to set
}
}
class ViewTwo {
var delegate:ComboBoxNodeDelegate?
func getCustomOption_forV1() {
let view2_foo = delegate.getCustomOption(data:true)
print(view2_foo) // should print "it works!"
}
}
All parameters passed around in Swift are constants -- so you cannot change them.
If you want to change them in a function, you must declare your protocol to pass by reference with inout:
protocol ComboBoxNodeDelegate {
func getCustomOption(data: inout Bool)
}
Note: you cannot pass a constant (let) to this function. It must be a variable -- which I see you are doing!

When using MVVM, how do I extend the ViewModel in child ViewControllers?

I have a hierarchy of this sort:
class OneViewModel {
}
class OneViewController {
var viewModel = OneViewModel()
}
class TwoViewModel : OneViewModel {
}
class TwoViewController : OneViewController {
var viewModel = TwoViewModel() // ???
}
I know that overriding property types is not allowed in Swift, but it doesn't make sense for the child ViewController to carry two ViewModels, how would one go about solving this? Thanks!
this should work
class OneViewModel {
}
class OneViewController {
var viewModel = OneViewModel()
}
class TwoViewModel : OneViewModel {
}
class TwoViewController : OneViewController {
override init() {
super.init()
self.viewModel = TwoViewModel()
}
}
you can set your viewModel instance to be TwoViewModel class anywhere.
I use this:
class OneViewController {
var viewModel: OneViewModel {
return storedViewModel
}
lazy var storedViewModel: OneViewModel = {
type(of: self).viewModelProvider()
}()
class func viewModelProvider() -> OneViewModel {
return OneViewModel()
}
}
class TwoViewController {
override var viewModel: TwoViewModel {
return storedViewModel as! TwoViewModel
}
override class func viewModelProvider() -> OneViewModel {
return TwoViewModel()
}
}
It's somewhat verbose to setup, but is easy and clear to use afterwards.

How to make different classes conform to the same protocol with different functionality?

I apologize beforehand if title is unclear, but I like to know how this is possible with some code snippets first:
ClassOne:
#objc protocol FetchProtocol
{
var someVar: Bool
{
get set
}
var someUIView: FetchProtocol
{
get set
}
func someFuncOne()
}
class ClassOne: UIViewController, FetchProtocol
{
...
#IBOutlet var someUIView: FetchProtocol!
....
}
ClassTwo:
#objc protocol FetchProtocol
{
var someVar: Bool
{
get set
}
var someTableView: FetchProtocol
{
get set
}
var someUIView: FetchProtocol
{
get set
}
func someFuncOne()
func someFuncTwo()
}
class ClassTwo: UIViewController, FetchProtocol
{
...
#IBOutlet var someTableView: FetchProtocol!
#IBOutlet var someUIView: FetchProtocol!
....
}
Both ClassOne and ClassTwo conform to the same FetchProtocol and both classes use the same someVar as well as someFuncOne, but ClassTwo also uses someTableView and someFuncTwo unique to only ClassTwo.
How can I use the same protocol between both classes, but the other class has "additional" different skeletal implementation?
For example, something like the following:
if let vc = currentVC as? FetchProtocol
{
if vc.someVar == true
{
// Check if vc is of class type ClassOne, call someFuncOne
// Otherwise if vc is of class type ClassTwo, call someFuncOne and someFuncTwo
}
}
Is something like the above possible using protocols and if so, how to properly implement it, or is there another alternative?
Your code doesn't compile and I think you're overcomplicating things and use protocols for the sake of using them.. There is no need to use any protocols at all if all you want to do is this:
if let vc = currentVC as? FetchProtocol
{
if vc.someVar == true
{
// Check if vc is of class type ClassOne, call someFuncOne
// Otherwise if vc is of class type ClassTwo, call someFuncOne and someFuncTwo
}
}
Why not delete all protocols and just do:
if currentVC.someVar {
if let class1 = currentVC as? ClassOne {
class1.someFuncOne()
} else if let class2 = currentVC as? ClassTwo {
class2.someFuncOne()
class2.someFuncTwo()
}
}
You don't really need protocols here because whether protocols exist or not, you still have to check whether currentVC is ClassOne or ClassTwo.
Protocols act like "black boxes". Consider this method:
func foo(fetchObj: FetchProtocol) {
fetchObj.someFuncOne()
}
foo doesn't care about what fetchObj really is. It just says "I don't care what you are, just do someFuncOne!"
What you're trying to do here is completely the opposite: "I do care what you are. If you're ClassOne, do this. If you're ClassTwo, do that."
Your problem is quite abstract, and pretty hard to follow, but this does what you're asking for. That being said, I suspect there's no need for you to be using protocols here at all.
protocol P1 {
var someVar1: String { get }
func func1();
}
protocol P2: P1 {
var someVar2: String { get }
func func2();
}
class C1: P1 {
var someVar1 = "String 1 in C1"
func func1() {
print(someVar1)
}
}
class C2: P2 {
var someVar1 = "String 1 in C2"
var someVar2 = "String 2 in C2"
func func1() {
print(someVar1)
}
func func2() {
print(someVar2)
}
}
func foo(with object: P1) {
object.func1()
if let object = object as? P2 {
object.func2()
}
}
print("Test case 1:")
foo(with: C1())
print("Test case 1:\n")
foo(with: C2())

Resources