How can I create a static variable in a Swift ViewController? - swift

I'm having a lot of difficulty moving from Objective-C to Swift and writing my first Swift app. I'm having more difficulty finding anything online in a concise manner.
How can I create a static var in a ViewController class.
For example
class ViewController: UIViewController {
static var SOME_VIEW_WIDTH_AND_HEIGHT = 70.0
...
func someFunc() {
self.someView = SomeView.init(frame: CGRectMake(0,0,SOME_VIEW_WIDTH_AND_HEIGHT,SOME_VIEW_WIDTH_AND_HEIGHT)
etc.
However, I cannot do this. How can this be achieved?

Related

Should I use a bridge class for setting my delegates for my ViewController?

I can simply use delegates in my views or viewControllers, no issue there, I though that would be nice if I use a bridge class for my entire app for more control on delegates, like this:
protocol MyDelegate {
func work1()
func work2(value: Int)
func work3(value: String)
}
class MyClass {
lazy var work1Delegate: MyDelegate? = nil
lazy var work2Delegate: MyDelegate? = nil
lazy var work3Delegate: MyDelegate? = nil
}
let sharedMyClass: MyClass = MyClass()
And were I need to take action I simply use this on viewDidLoad and also conforming to MyDelegate as well:
sharedMyClass.work1Delegate = self
sharedMyClass.work2Delegate = self
sharedMyClass.work3Delegate = self
I found 2 downside in my method, first I have to type 3 line of codes like this for example for my ViewController, in the real app it would be like 10 function and 10 lines like this:
sharedMyClass.work1Delegate = self
sharedMyClass.work2Delegate = self
sharedMyClass.work3Delegate = self
And second if I just need func work2(value: Int) for another view or viewController I have to have those other 2 function as well because of conforming to my protocol.
I wanted show what I am doing and asking for solving this 2 issues, having a bridge class is a must for me, but I would like to solve those 2 minor issues.

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.

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.

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.

Singleton in swift is not accessible from another class

I am new to Swift and am trying to access a 'problemSolved' array that is appended during gameplay in my main GameController class, from another class. For some reason the array is not visible in a UIViewController class where I want to show all the problems solved in a table. I have read many of the Singleton examples on the site to see if this will do it, but that doesn't seem to. Any help or advice here much appreciated!
class GameController: TileDragDelegateProtocol, CenterViewControllerDelegate {
static let sharedInstance = GameController()
var problemsSolved = Array<String>()
func onProblemSolved() {
problemsSolved.append(problem)
println("problemsSolved contains \(problemsSolved)")
}
}
During gameplay I can see in the console that the array is being appended ok in GameController. But when I try to access it in a ViewController class the contents are showing as empty [].
class SidePanelViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
tableView.reloadData()
println("the array viewed from here is empty \(GameController.sharedInstance.problemsSolved)")
}
At the moment I only can imagine that you don't call
GameController.sharedInstance.onProblemSolved()
when you want to append a String to problemsSolved.
You should also consider making your functions and variables in GameController static.
If this doesn't solve your problem I would need more information about how and when you add something to problemsSolved.