Modeling an abstract base class in Swift - swift

I have a Cocoa Touch class BaseViewController:
class BaseViewController: Named, UIViewController {
open var name = "Emma"
override func viewDidAppear() {
self.doSomethingImportant(self.name)
}
}
protocol Named {
var name: String
}
The intent behind BaseViewController is that all view controllers in my app will be derived from it, because it does something important in viewDidAppear() with name.
However, I'm trying to model this in a way where it'll be a compile-time error to subclass from BaseViewController without specifying name, and not have to supply a default name value in the base class. In other words:
Can I have BaseViewController require that its subclasses implement Named without it implementing Named itself?
If not, how else can I achieve this within the paradigm of Cocoa Touch plus Protocol-Oriented Programming?

Related

Retaining cancellable from objective-c class swift extension

I have a bit of a strange situation. I'm working on a project that contains a lot of legacy objective-c code. We are working on migrating to swift, but in the meantime are relying a bit on using swift class extensions to get the ball rolling. I'm looking for a way to store a cancellable on the extended objective-c class, but I can't access AnyCancellable from objective-c land. This function, declared within the class extension in swift, creates a few notification observers. They need to be retained in memory so long as the class itself is.
How would I go about adding a class property to objective-c that I can use to store the cancellable in? I tried creating a sort of proxy class like this:
#objc class CancellablesHolder: NSObject {
var cancellables: [AnyCancellable] = []
}
and declaring a property in the objc class header like so:
#property (nonatomic, strong) CancellablesHolder *cancellablesHolder;
But Xcode throws a failed to emit precompiled header. Is there a better way?
Would creating an NSMutableArray on the objective-c class and simply doing array.add(publisher.sink {...}) suffice, or is there a better way I'm not seeing?
I had a similar problem and solved it by writing a wrapper class that Swift exposes to Objective-C:
import Foundation
import Combine
#objc final class MyCancellable: NSObject {
let cancellable: AnyCancellable
init(cancellable: AnyCancellable)
{
self.cancellable = cancellable
}
#objc func cancel() {
self.cancellable.cancel()
}
}
It's a Obj-C friendly box that hides a Swift AnyCancellable inside of it. Then you can pass these around in Obj-C and even cancel them from Obj-C if you like.

swift VAR restriction of types [duplicate]

My app has a protocol for detail view controllers, stating they must have a viewModel property:
protocol DetailViewController: class {
var viewModel: ViewModel? {get set}
}
I also have a few different classes that implement the protocol:
class FormViewController: UITableViewController, DetailViewController {
// ...
}
class MapViewController: UIViewController, DetailViewController {
// ...
}
My master view controller needs a property that can be set to any UIViewController subclass that implements the DetailViewController protocol.
Unfortunately I can't find any documentation on how to do this. In Objective-C it would be trivial:
#property (strong, nonatomic) UIViewController<DetailViewController>;
It appears that there isn't any syntax available in Swift to do this. The closest I've come is to declare a generic in my class definition:
class MasterViewController<T where T:UIViewController, T:DetailViewController>: UITableViewController {
var detailViewController: T?
// ...
}
But then I get an error saying that "Class 'MasterViewController' does not implement its superclass's required members"
This seems like it should be as easy to do in Swift as it is in Objective-C, but I can't find anything anywhere that suggests how I might go about it.
I think you can get there by adding an (empty) extension to UIViewController and then specifying your detailViewController attribute using a composed protocol of the empty extension and your DetailViewController. Like this:
protocol UIViewControllerInject {}
extension UIViewController : UIViewControllerInject {}
Now all subclasses of UIViewController satisfy protocol UIViewControllerInject. Then with that, simply:
typealias DetailViewControllerComposed = protocol<DetailViewController, UIViewControllerInject>
class MasterViewController : UITableViewController {
var detailViewController : DetailViewControllerComposed?
// ...
}
But, this is not particularly 'natural'.
=== Edit, Addition ===
Actually, you could make it a bit better if you define your DetailViewController using my suggested UIViewControllerInject. Like such:
protocol UIViewControllerInject {}
extension UIViewController : UIViewControllerInject {}
protocol DetailViewController : UIViewControllerInject { /* ... */ }
and now you don't need to explicitly compose something (my DetailViewControllerComposed) and can use DetailViewController? as the type for detailViewController.
As of Swift 4, you can now do this.
Swift 4 implemented SE-0156 (Class and Subtype existentials).
The equivalent of this Objective-C syntax:
#property (strong, nonatomic) UIViewController<DetailViewController> * detailViewController;
Now looks like this in Swift 4:
var detailViewController: UIViewController & DetailViewController
Essentially you get to define one class that the variable conforms to, and N number of protocols it implements. See the linked document for more detailed information.
Another way would be to introduce intermediate base classes for the appropriate UIKit view controllers that implement the protocol:
class MyUIViewControler : UIViewController, DetailViewController ...
class MyUITableViewController : UITableViewController, DetailViewController ...
Then you inherit your view controllers from these view controllers, not the UIKit ones.
This is not natural either, but it doesn't force all your UIViewControllers to satisfy the UIViewControllerInject protocol as GoZoner suggested.

Where do I register a ValueTransformer in Swift?

I'm trying to use a ValueTransformer (né NSValueTransformer) in Swift that is being used by the first window that my application opens. Value transformers need to be registered with ValueTransformer.registerValueTransformer(_:forName:) before they can be queried by the user interface runtime.
The documentation for NSValueTransformer recommends registering value transformers in +[AppDelegate initialize]. However, Swift doesn't allow you to override +initialize. I tried to register from applicationWillFinishLaunching(_) and applicationDidFinishLaunching(_), but they both happen too late and my window doesn't get filled because the runtime can't find the value transformer.
Where should I register my value transformer?
In AppDelegate you can use a dummy property of type Void with a closure. The closure is even executed before init
private let transformer : Void = {
let myTransformer = MyValueTransformer()
ValueTransformer.setValueTransformer(myTransformer, forName:NSValueTransformerName("MyValueTransformer"))
}()
I found that I can count on the app delegate class to be initialized early and only once, so I stuck my ValueTransformer.registerValueTransformer call in it.
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
override init() {
ValueTransformer.setValueTransformer(MyValueTransformer(), forName: NSValueTransformerName("MyValueTransformer"))
}
}
You are right, you can register your value transformers in the AppDelegate. If you want something that closer resembles ObjectiveC's +initialize you can use lazy initialization of a class variable. E.g:
class AppDelegate: NSObject, NSApplicationDelegate {
static let doInitialize: Void = {
// register transformers here
}()
override init() {
super.init()
AppDelegate.doInitialize
}
}
This pattern should also work for classes other than the AppDelegate if you want to keep the transformers things closer to the classes that actually use them.

Extend a generic class in swift

I have some code which has to be available on all UIViewController of application. So I created a class UIViewControllerExtension: UIViewController, which will be extended by each class which I want to use as UIViewController. Which works as expected.
Now I have new screens where I have to use UITableViewController, so I can't extend same class UIViewControllerExtension, And to keep code centralized so I do not want to create another class UITableViewControllerExtension with same code, and want to have a common solution for both cases.
I tried various ways to extend generic class <T:UIViewController> so I can use it in both cases, but it didn't work (as it wouldn't compile). I did some research on internet but didn't find any solution to it. Does someone had same issue and have a solution?
I thought if there would be some solution like
class CommonViewController<T:UIViewController>: T{ //I know it doesn't compile
//...
}
Usage:
class MyHomeScreenViewController: CommonViewController<UIViewController>{
}
class MyItemListScreenViewController: CommonViewController<UITableController>{
}
I am open to any other solution if it solves my problem.
Edit: More details
1> I would like to extend viewDidLoad() method of UIViewController and UITableViewController in common way (no duplication of code as said before)
2> I would like to add some supporting methods to UIViewController (and UITableViewController), supporting methods like navigateBack, loginUser(name:String,password:String) etc..
A solution would be to extend UIViewController to add additional functionality to all UIViewControllers and override viewDidLoad in your own classes:
extension UIViewController {
func navigateBack() {
...
}
// an extension cannot override methods
// so this method gets called later in an overridden viewDidLoad
func viewDidLoadNavigate() {
...
}
}
// you own classes
class MyHomeScreenViewController: UIViewController {
// you have to make sure that all view controllers which can navigate override viewDidLoad
override viewDidLoad() {
// optional call to super
super.viewDidLoad()
// this is needed and called from the extension
viewDidLoadNavigate()
}
}
class MyItemListScreenViewController: UITableViewController {
override viewDidLoad() {
super.viewDidLoad()
viewDidLoadNavigate()
}
}
As you can see there is some code duplication but this is necessary since UITableViewController can also override viewDidLoad.
If generic inheritance is possible some day this code duplication can be reduced.

Define a variable which conforms to a protocol and inherits from a class in Swift [duplicate]

My app has a protocol for detail view controllers, stating they must have a viewModel property:
protocol DetailViewController: class {
var viewModel: ViewModel? {get set}
}
I also have a few different classes that implement the protocol:
class FormViewController: UITableViewController, DetailViewController {
// ...
}
class MapViewController: UIViewController, DetailViewController {
// ...
}
My master view controller needs a property that can be set to any UIViewController subclass that implements the DetailViewController protocol.
Unfortunately I can't find any documentation on how to do this. In Objective-C it would be trivial:
#property (strong, nonatomic) UIViewController<DetailViewController>;
It appears that there isn't any syntax available in Swift to do this. The closest I've come is to declare a generic in my class definition:
class MasterViewController<T where T:UIViewController, T:DetailViewController>: UITableViewController {
var detailViewController: T?
// ...
}
But then I get an error saying that "Class 'MasterViewController' does not implement its superclass's required members"
This seems like it should be as easy to do in Swift as it is in Objective-C, but I can't find anything anywhere that suggests how I might go about it.
I think you can get there by adding an (empty) extension to UIViewController and then specifying your detailViewController attribute using a composed protocol of the empty extension and your DetailViewController. Like this:
protocol UIViewControllerInject {}
extension UIViewController : UIViewControllerInject {}
Now all subclasses of UIViewController satisfy protocol UIViewControllerInject. Then with that, simply:
typealias DetailViewControllerComposed = protocol<DetailViewController, UIViewControllerInject>
class MasterViewController : UITableViewController {
var detailViewController : DetailViewControllerComposed?
// ...
}
But, this is not particularly 'natural'.
=== Edit, Addition ===
Actually, you could make it a bit better if you define your DetailViewController using my suggested UIViewControllerInject. Like such:
protocol UIViewControllerInject {}
extension UIViewController : UIViewControllerInject {}
protocol DetailViewController : UIViewControllerInject { /* ... */ }
and now you don't need to explicitly compose something (my DetailViewControllerComposed) and can use DetailViewController? as the type for detailViewController.
As of Swift 4, you can now do this.
Swift 4 implemented SE-0156 (Class and Subtype existentials).
The equivalent of this Objective-C syntax:
#property (strong, nonatomic) UIViewController<DetailViewController> * detailViewController;
Now looks like this in Swift 4:
var detailViewController: UIViewController & DetailViewController
Essentially you get to define one class that the variable conforms to, and N number of protocols it implements. See the linked document for more detailed information.
Another way would be to introduce intermediate base classes for the appropriate UIKit view controllers that implement the protocol:
class MyUIViewControler : UIViewController, DetailViewController ...
class MyUITableViewController : UITableViewController, DetailViewController ...
Then you inherit your view controllers from these view controllers, not the UIKit ones.
This is not natural either, but it doesn't force all your UIViewControllers to satisfy the UIViewControllerInject protocol as GoZoner suggested.