Swift Use Custom Variables of AppDelegate in Framework - swift

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

Related

How to create separate data mapping file with function names in Swift

I have a macOS app that I'm creating in Swift and I have integrated an external HID device that has a number of controls on it.
The HID part is done where I am receiving all of the hid commands from the device and I am trying to create a mapping file where I can maintain the HID key mappings in a separate swift file.
All I want in that file is the data and what I want to do is this;
raw hid data is received from HID device (In ViewController)
Lookup the function name assigned to this hid data (In separate file)
Run the function that is mapped to that key. (Function located in the main ViewController)
So far I have the external swift file setup with all of the mapping and that all works fine but my issue is when I try to call the looked up function in the ViewController, it says the function can't be found in the scope.
Initially I thought I would use a delegate but the external file isn't a viewcontroller, just a separate swift file so I don't know if I can do that?.
I've tried searching but everything I've found is calling a function from another ViewController which I'm not. It's very possible I'm not using the best approach and my goal is to just keep all of the mapping in a separate file as there is a lot and it woudl be easier to maintain.
Any suggestions are appreciated.
This is one way to achieve this. It can get tedious. You can totally skip writing out a separate protocol for the delegate, but this is cleaner design.
protocol HIDMessageDelegate: AnyObject {
// example messages
func message1()
func message2()
func message3()
}
class HIDMessageParser {
static weak var delegate: HIDMessageDelegate?
static func parseHIDMessage() {
var condition = 0
// this is where your switch statement will go and you'll parse things and call the relevant delegate method
switch (condition) {
default:
delegate?.message1()
}
}
}
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
HIDMessageParser.delegate = self
}
}
extension MyViewController: HIDMessageDelegate {
func message1() {
}
func message2() {
}
func message3() {
}
}
You can simply create a UIViewController as the external file and add it as a property to the main ViewController.
In the external file add this.
#IBOutlet var uiViewController: UIViewController!
In the ViewController add this.
var externalFileViewController: UIViewController!
override func viewDidLoad() {
super.viewDidLoad()
externalFileViewController = externalFileViewController?.loadView()
// If we have an object then load it
if let viewController = externalFileViewController {
viewController.view.frame = view.frame
viewController.view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
view.addSubview(viewController.view)
uiViewController = viewController
}
}
Now in the viewController look up the functions to be called from the external file and call them using the function name.
The functions are defined in the external file using #IBAction.
Let me know if you have any questions.

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.

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.

Trying to access property of a class which subclasses a cocoa class gives run-time error

I made a customized button type(class) that inherits from NSButton and has some additional methods as well, but when I try to access the methods that I declared myself, I get a run-time error. Here's my code:
import Cocoa
class MCButton: NSButton {
func testFunc()->Bool {
return true
}
}
class ViewController: NSViewController {
#IBOutlet weak var button: MCButton!
override func viewDidLoad() {
super.viewDidLoad()
if button.testFunc() { //Thread 1: EXC_BAD_ACCESS(code=2, address=0x608000264600)
button.title = "Hi!"
}
}
}
Note that I don't have any problems when I only use the methods declared in the superclass(NSButton). What's the problem? What should I do to fix it?
You have to correctly set the button's class in the InterfaceBuilder. It probably has the predefined value set, which is NSButton. You have to set it to MCButton instead.
Only then you actually get a reference to a correct instance of one of those MCButtons.

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.