How it can execute classes in swift without instance? - swift

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.

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.

How to define a variable function and call it from an extension file in swift?

I created an extension swift file, and in that file I wrote extension UIViewController{ ... } in my project, rather than writing all lines in every view controller. Everything worked ok. Later on I created a public func(i.e myFunc), with the same name inside each of the 30 view controllers, and having different outputs. I am trying to call this common named function from the extension file, but I get an error "Value of type 'UIViewController' has no member 'myFunc'. On the extension file, how can I call this common named function which executes different outputs on each different view controller?
Extension file Code:
extension UIViewController {
func sampleFunc {
let viewController = self.navigationController?.visibleViewController {
........
viewController.myFunc() }. *//error: Value of type 'UIViewController?' has no member 'myFunc'*
}
}
Note: I don't want to use like this:
if let viewController = self as? ViewController1 {viewController.myFunc()}
if let viewController = self as? ViewController2 {viewController.myFunc()}
if let ... 3,
4....30
Or maybe is there a way to check whether the function exists, and if it exists then execute command without receiving that kind of errors ?
use of unresolved identifier
or
value of type 'UIViewController?' has no member 'myFunc'
The viewController variable in the extension is could be any VC, couldn't it? Not just the 30 you created. You can't guarantee that any random VC will have a method called myFunc, so you can't call it in the extension.
One way to resolve this problem is to create a protocol that your 30 VCs all implement:
protocol MyFuncable : UIViewController { // please come up with a better name
func myFunc ()
}
This is an example of how you implement the protocol:
class ViewController: UIViewController, MyFuncable {
func myFunc() {
// do whatever you want...
print("myFunc executed")
}
}
Now we guarantee that everything that implements myFuncable will have a method called myFunc, so now in your extension, you can check whether the VCs are MyFuncable:
extension UIViewController {
func sampleFunc() {
// here is where the checking happens:
if let viewController = self.navigationController?.visibleViewController as? MyFuncable {
viewController.myFunc()
}
}
}
Try this code below.
extension UIViewController {
func sampleFunc() {
if let viewController = self.navigationController?.visibleViewController {
print("Class name: \(NSStringFromClass(type(of: viewController)))")
let anyObject = viewController as AnyObject
if anyObject.responds(to: #selector(anyObject.myFunc)) {
anyObject.myFunc()
}
}
}
}

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.