Best approach for reusable swift code UIKit - swift

I cannot see a practical approach for reusable storyboard patterns in Xcode/Swift. I have a number of ViewControllers which have almost identical behavior and appearance. So the viewDidLoad() is always the same:
override func viewDidLoad() {
super.viewDidLoad()
labelKESumme.text = something
}
Because each ViewController has to be configured with its own class and Swift lacks multi inheritance I've tried to create an extension like this:
extension UIViewController {
func show() {
self.labelKESumme.text = something <== ... has no member
}
}
But that doesn't work due to 'has no member' errors. Also it's not possible to configure outlets between the label and the extension code. Need an approach for configure many view controllers with same content without code duplication.

You can use protocol extensions like below
// protocol
protocol Showable {
var labelKESumme: UILabel?
func show()
}
//protocol extension if you want same functionality everywhere
extension Showable {
func show() {
labelKESumme?.text = something
}
}
// just conform the the protocol
ABCViewController: Showable {
}
// just conform the the protocol
XYZViewController: Showable {
}
// In some code where you have viewController as UIViewController and you know
// that this object conforms to Showable protocol then you can do like this
guard let showable = viewController as? Showable {
return
}
showable.show()

Related

Swift extension only when conforming to Class AND protocol

I have two protocols with extensions LoginPresenting and LoginDismissing. I want LoginPresenting extension only to apply to UIViewControllers that also implement LoginDismissing. I am trying to do it like so but have so far been unable to work out the syntax.
protocol LoginDismissing : class {
func loginHasCompleted(withController: UIViewController)
}
extension LoginDismissing where Self:UIViewController {
func loginHasCompleted(withController controller:UIViewController) {
//...code ommited
}
}
protocol LoginPresenting : class {
func presentLogin()
}
The following is bad code, but I think it explains how I am attempting to make LoginPresenting only apply to UIViewControllers that also implement LoginDismissing.
extension LoginPresenting where Self:UIViewController, LoginDismissing //Syntax error here
func presentLogin() {
let lc = LoginViewController()
let nav = UINavigationController(rootViewController: lc)
nav.modalPresentationStyle = .fullScreen
lc.loginDismissingDelegate = self //type LoginDismissing
self.present(nav, animated: true, completion: nil)
}
}
You can resolve your syntax error with the use of & instead of a comma:
extension LoginPresenting where Self: UIViewController & LoginDismissing {
// ...
}
This makes the extension only apply to view controllers that also conform to LoginDismissing.
You should enforce as many requirements as possible on the protocol itself, not the extensions.
protocol LoginPresenting: LoginDismissing & UIViewController {
func presentLogin()
}
Also, class is old syntax, though not deprecated.
protocol LoginDismissing: AnyObject {

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
}
}

How to make self.dynamicType into a typealias

Basically I want to add a typealias to UIViewController or any other default swift classes. The reasoning behind this is that I want to abstract my code so that I can access some static functions by just using this instead of self.dynamicType
extension UIViewController {
typealias this = TheClassThatSubclassedThis
}
class DetailViewController: UIViewController {
func doStuff() {
this.doStaticStuff()
}
static func doStaticStuff() {
...
}
}
I know this is possible by creating a protocol, then just implement said protocol to the class I want to implement it to, like this
protocol CanAccessStaticSelf {
typealias this
}
class DetailVC: UIViewController, CanAccessStaticSelf {
typealias this = DetailVC
}
But is there a more efficient way to do this? Like for example, by just subclassing a certain class or by extending a superclass?
Like this for example
extension UIViewController {
public static var defaultNibName: String {
return self.description().componentsSeparatedByString(".").dropFirst().joinWithSeparator(".")
}
}
class DetailVC: UIViewController, CanAccessStaticSelf {
func doSomeStuffAgain() {
// no other code just subclass and I can access self.dynamicType as just `this`
print(this.defaultNibName)
}
}
Try this instead:
protocol CanAccessStaticSelf {
typealias this = Self
}
...but what you are trying to achieve looks somewhat confusing to me ;-(
Thanks to this proposal from Erica Sadun we all might be able to use the Self keyword for that in the near future.
For instance:
class MyClass {
static func staticMethod() { ... }
func instanceMethod() {
MyClass.staticMethod()
Self.staticMethod()
}
}
it is not possible to access through this.
but you can access through "UIViewController.defaultNibName".

Swift require protocol extension

If I have
protocol AppearingTextContainer {
func clipBounds() -> ()
}
extension AppearingTextContainer where Self: UIView {
func clipBounds() { self.clipsToBounds = true }
}
then a class that adopts my protocol CustomView: AppearingTextContainer is not forced by the compiler to implement clipBounds. If I remove the extension it won't compile. Is there any way to enforce CustomView to call clipBounds without having to delete the default clipBounds implementation?
Thanks
You've only provided a method that will become available to any UIView that chooses to conform to AppearingTextContainer.
So now ideally you would create a UIView subclass that conforms
class AppearingTextView: UIView, AppearingTextContainer {
// you can now call clipBounds() on this class
}
or
extension UIView: AppearingTextView { }
what I think you should consider is whether or not what you really want is a protocol extension vs. just an extension on UIView:
extension UIView {
func clipsBounds() -> Void { clipsToBounds = true }
}
if you want a protocol, I suggest going the route of:
protocol ClippingView {
var clipsToBounds: Bool { get set }
}
extension ClippingView {
func clipsBounds() -> Void { clipsToBounds = true }
}
extension UIView: ClippingView { }
let clippingViews: [ClippingView] = [......]
clippingViews.forEach { $0.clipsBounds() }
If your custom view class has its own clipBounds function, then you would have to cast your custom view to an AppearingTextContainer in order to get the AppearingTextContainer protocol extension implementation.
But keep in mind that Objective-C cannot see your protocol extension, so Cocoa will never call the protocol extension clipBounds for you; only a direct Swift call can call it.

Delegation on a class in Swift

I have a delegation/initialization problem I can't seem to solve. Basically I have a storyboard with a few View controllers. Inside the storyboard there is this "View controller" which consists of a UITableview that I have connected with a DeviceListViewController class so that it populates the information. In here I have declared the following protocol:
protocol DeviceListViewControllerDelegate: UIAlertViewDelegate {
var connectionMode:ConnectionMode { get }
func connectPeripheral(peripheral:CBPeripheral, mode:ConnectionMode)
func stopScan()
func startScan()
}
and inside the class itself I have a init method like this (which is probably wrong but I didn't know what else I could do at this point):
convenience init(aDelegate: DeviceListViewControllerDelegate) {
self.init()
self.delegate = aDelegate
}
Then there is this second class that is not attached to any view controller called BLEMainViewController. It should be a singleton handling all the bluetooth actions. This means I should be able to delegate some stuff between DevicelistViewController and BLEMainViewController.
In the BLEMainViewController I have inherited the DeviceListViewControllerDelegate:
class BLEMainViewController: NSObject, DeviceListViewControllerDelegate {
var deviceListViewController:DeviceListViewController!
var delegate: BLEMainViewControllerDelegate?
static let sharedInstance = BLEMainViewController()
}
override init() {
super.init()
// deviceListViewController.delegate = self
deviceListViewController = DeviceListViewController(aDelegate: self)
}
The problem is that BLEMainViewController is not attached to any View Controller (and it shouldn't IMO) but it needs to be initialized as a singleton in order to handle all the BLE actions. Can anyone point me in the right direction (with an example preferably) on how to work around this?
I think you simply used wrong code architecture.
The BLEManager is a shared-instance, you can call it from everywhere, set it properties, and call its methods.
Its can delegate your view-controller with any predefine events you will add to its protocol and provide proper implementation
Here is some code, hope it helps
protocol BLEManagerDelegate{
func bleManagerDidStartScan(manager : BLEManager)
}
class BLEManager: NSObject {
static let sharedInstance = BLEManager()
var delegate: BLEManagerDelegate?
var devices : [AnyObject] = []
func startScan(){
delegate?.bleManagerDidStartScan(self)
//do what ever
}
func stopScan(){
}
}