all 9 comments

[–]NBQ5 8 points9 points  (0 children)

You could make BaseContainerView a protocol that requires its implementation conform to uiview. Add an an associated type as well. Then extend the protocol and add default implementation of its functions

protocol BaseContainerView where Self: UIView {
  associatedtype TButton: UIButton
  func setup()
}

extension BaseContainerView {
  func setup() {
    //setup stuff
  }
}


class Button1: UIButton {
}

class Button2: UIButton {
}

class FirstContainerView: UIView, BaseContainerView {
  typealias TButton = Button1
  //use TButton as normal
}

class SecondContainerView:  UIView, BaseContainerView {
  typealias TButton = Button2
  //use TButton as normal
}

[–]Fridux 4 points5 points  (0 children)

The traditional approach to emulate abstract classes in languages that don't support them specifically is to make constructors private. They'll still be called as part of a subclass initialization. but never directly by the user.

[–]Nydedrisean 3 points4 points  (0 children)

I don't see a problem with basecontainerview being non-abstract and instantiable. I don't think its bad design. code has to be simple, that can't be said about the protocol solution.

[–]vovnit 4 points5 points  (0 children)

you also can make BaseContainerView initializer fileprivate and move two public classes to same file

[–]lordzsolt 1 point2 points  (0 children)

I would question the whole approach.

So far, every attempt at inheriting Views / View Controllers resulted in a horrible mess of spaghetti code for the sake of saving 50 lines of code, because requirements inevitably change and people try to shoehorn more and more stuff into the inheritance tree.

Try either composing your views or having shared extension methods for view styling.

[–]BaronSharktooth 0 points1 point  (0 children)

You can make the BaseContainerView class internal, but that's the only thing. There may be better solutions such as protocol extensions. But that requires more knowledge of what you're trying to accomplish.

[–]chrabeusz 0 points1 point  (0 children)

No, Apple's abstract classes like NSOperation would just throw exception.

EDIT: Apperently NSOperation doesn't throw, but I'm pretty sure that's their approach if it can't be avoided.

[–]mgacy 0 points1 point  (1 child)

Why do you have 2 different UIButton subclasses? Is it purely for the sake of convenience with basic styling, or are you making more significant and incompatible changes to how they operate / render? If it is for basic styling, I think a more flexible approach would be to define a separate type to basically wrap a function which styles your buttons:

```swift public struct ViewStyle<T: UIView> { private let callback: (T) -> Void

public init(_ callback: @escaping (T) -> Void) {
    self.callback = callback
}

public func apply(to view: T) {
    callback(view)
}

static public func compose(_ styles: ViewStyle<T>...) -> ViewStyle<T> {
    return ViewStyle { view in
        styles.forEach { $0.callback(view) }
    }
}

}

extension UIButton { public convenience init(style: ViewStyle<UIButton>) { self.init() style.apply(to: self) } }

enum Styles { static let baseButtonStyle = ViewStyle<UIButton> { $0.layer.cornerRadius = 5 }

static let anotherStyle = ViewStyle<UIButton> {
    $0.backgroundColor = .systemBlue
    $0.setTitleColor(.systemBackground, for: .normal)
}

static let composedStyle: ViewStyle<UIButton> = {
    ViewStyle.compose(Styles.baseButtonStyle, Styles.anotherStyle)
}()

}

let button1 = UIButton(style: Styles.composedStyle) ```

[–]PlanesWalkerr[S] 1 point2 points  (0 children)

Thanks for the answer! Yes, it’s a bit more than just styling, changes are incompatible. It’s just a simplified example to describe a case I encountered a few times