Where do I register a ValueTransformer in Swift? - 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.

Related

Swift Use Custom Variables of AppDelegate in Framework

I have an application which contains some framework.
AppDelegate declares some variables, so I want to use these custom variables in framework. but unfortunately not getting it.
I use this stack overflow link according to this answer I am able to use app delegate methods like didFinishLaunchingWithOptions and other methods but not able to get the custom variables and methods from app delegate.
public class SampleManager {
public static let shared = SampleManager()
public weak var delegate: UIApplicationDelegate?//UIApplicationDelegate?
func doWhateverYouWant() {
}
}
I declared the SampleManager and assign its delegate in the didFinishLaunchingWithOptions like this
SampleManager.shared.delegate = self
now this delegate allow me to access default methods and variables like window
but not allowing me custom methods and variables.
Kindly guide me how to access custom variables and custom methods declared in app delegate.
That's because UIApplicationDelegate doesn't contain your custom ones, just extend it
protocol Custom: UIApplicationDelegate {
func yourFunc()
}
class AppDelegate: UIResponder, Custom {
func yourFunc() {}
...
}
class SampleManager {
static let shared = SampleManager()
weak var delegate: Custom?
func doWhateverYouWant() {
delegate?.yourFunc()
}
}

Cannot cast value of type MyClass to MyDelegate

I have a class that needs to set a component to self. That component requires the class to implement a protocol MyDelegate. Eventually, it fails (SIGNAL SIGABRT).
// I need the class to be a NSObject for unrelated requirements
class MyClass: NSObject {
// I force the compilation, but it then breaks apart at runtime anyway
private let myComponent =
Component(requiresAnObjectofTypeMyDelegate: self as! MyDelegate)
}
// in the same file
extension MyClass: MyDelegate {
func myUsefulDelegateCall() {
}
}
Why?
The problem is the usage of self inside the stored property myComponent.
Typically, it is not allowed to hand out self before the initializer has finished intializing the whole object. Therefore, your problem has nothing to do with protocols or extensions. More simple:
import Foundation
class Component {
init(requiresAnObjectofTypeMyClass:MyClass) {
}
}
// I need the class to be a NSObject for unrelated requirements
class MyClass : NSObject {
// I force the compilation, but it then breaks apart at runtime anyway
private let myComponent =
Component(requiresAnObjectofTypeMyClass : self as! MyClass)
}
let m = MyClass()
also crashes.
If you leave out the NSObject subclassing, you get a compiler error:
use of unresolved identifier 'self'
Component(requiresAnObjectofTypeMyClass : self as! MyClass)
This shows the problem: You must not use self here.
I think it's just an Xcode bug; Xcode seems to ignore the syntax error when subclassing NSObject. The cast as! MyClass is also a hint that we are looking for a strange workaround that finally get's Xcode to it's knees and causes the runtime crash.
To work-around, you could create a lazy property, which will be evaluated after the initialization process and therefore will allow self to be handed into the Component initializer:
private(set) lazy var myComponent = Component(requiresAnObjectofTypeMyClass:self)
Here, you also do not need the cast. Unfortunately, lazy let is not allowed in swift (and nobody knows why), so private(set) is close to it's semantic.
It's easy to transfer this code to your protocol example.
A type cast is not needed. As MyClass adopts MyDelegate it is also MyDelegate.
And initialize the property lazily to be able to use self on the top level at all.
private lazy var myComponent = Component(requiresAnObjectofTypeMyDelegate: self)

Weak and delegate fail-warnings when trying to update tableview through delegate method

I been struggling to update my tableview through another class I made.
I then found this stackoverflow solution:
How to access and refresh a UITableView from another class in Swift
But when I follow it step by step and implement all the codes, I get the following errors:
My line:
weak var delegate: UpdateDelegate?
Gets the warning
'weak' may only be applied to class and class-bound protocol types, not 'UpdateDelegate'
And my line:
self.delegate.didUpdate(self)
Gets warning:
Instance member 'delegate' cannot be used on type 'APIgetter'
Could this be because the code is old and I'm using swift 4? else I cannot see why this should be failing. I hope you can help me :)
Update:
My Protocol:
protocol UpdateDelegate: AnyObject {
func didUpdate(sender: APIgetter)
}
Snippet from my ViewController containing the tableview:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UpdateDelegate {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
APIgetter.addDataFromSQL()
let updates = APIgetter()
updates.delegate = self
}
//update func
func didUpdate(sender: APIgetter) {
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
My APIgetter class in APIgetter.swift:
class APIgetter {
weak var delegate: UpdateDelegate?
class func addDataFromSQL (){
//Code to fetch data from API
//Code that comes after DispatchQueue.global & DispatchQueue.main and my result being executed
//result
self.delegate.didUpdate(self)
just update your protocol definition.
protocol UpdateDelegate: class {
// protocol body
}
or
protocol UpdateDelegate: AnyObject {
// protocol body
}
This is needed (as of Swift 4 I think) because classes are reference types and you can only use a weak reference on reference types. Not value types like structs.
UPDATE: You cannot access a property/instance member from a static function the way that you currently are. Remove the class keyword from the function and it should work.
If you want/need to use a single instance of this class throughout your application you can use a static property to make it a Singleton
class APIgetter {
static let shared: APIgetter = APIgetter()
}
Then you would be able to access it like this:
APIgetter.shared.addDataFromSQL()
You could also update the delegate in the same way before calling your function.
APIgetter.shared.delegate = self
I think in this case though I would use a Singleton without the delegate. Just use a completion handler in your function. Setting and changing the delegate on a shared instance could have some side effects if not managed carefully.

How it can execute classes in swift without instance?

I am new to swift and there is something that i couldn't understand and it is about classes execute.
I thought that class can't execute them self , you need to define an instance variable to work with the class's methods and properties but i noticed in xcode files that the classes don't have in instance variable why is that? and how the class get executed itself without an instance ?
Thanks advance
import UIKit
class ViewController: UIViewController {
var theView: UIView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.redColor()
}
}
/// Why i donl't need this line of code to worke with the class above
/// instead the class above execute itself without this instance
var theViewControllerInstance = ViewController()
Normally you would create an instance of a class to execute a function, but if you wish to; you can make a class function:
class MyClass {
class func myClassMethod() {
print("This is printed from a class function")
}
func myInstanceMethod() {
print("This is printed from a normal function")
}
}
You use a class function in the following way:
MyClass.myClassMethod()
And a standard function like this:
let myInstance = MyClass()
myInstance.myInstanceMethod()
You can create a ViewController instance and do all the work by your self, but a more commonly way is to bind this class in interface builder (storyboard or xib), when app start and load UI resource, the instance is created for you.

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.