Add weak reference to assigned Closure in Swift? - ios

I have the following closure:
class BISSettingController : XLFormViewController {
class func initializeForm() -> XLFormDescriptor {
var form : XLFormDescriptor
var section : XLFormSectionDescriptor
var row : XLFormRowDescriptor
form = XLFormDescriptor()
row = XLFormRowDescriptor(tag: "tag", rowType: XLFormRowDescriptorTypeButton, title: "Title")
row.action.formBlock = {[weak self](sender: XLFormRowDescriptor!) -> Void in
self?.deselectFormRow(sender)
...
}
}
}
I want to use self as weak reference inside the closure. But when I build the code I get the following error:
'weak' cannot be applied to non-class type 'BISSettingController.Type'
How can I solve this to make it work?

The problem is that this is a class method (class func). In a class method, self means the class. There is no need for memory management on a self representing the class; the class cannot "leak", because it persists for the life of the app anyway. Thus, you cannot describe a reference to a class as weak.

Based on the error message you're getting, it sounds like self is not an object, so you don't need the access list at all.
Can you post more information on the object that contains this code?
EDIT:
Looking at your updated question, it seems that the code you're using is in a class method, not an instance method. That code doesn't seem to make sense from a a class method however.

Related

Initialising properties in a subclass?

I have a subclass of a subclass as i require a variation on a varients behaviour based on a base class.
The issue im facing is that this new subclass requires some custom values that need to be initialised in an init method.
The situation is:
I have a value e.g. let time: Dynamic<String?> where I have to use let due to the binding method
and so i need to provide a value in an init in order to compile...
but as the class signature looks like this: class NewViewModel: DurationViewModel<Model>
the superclass is also class DurationViewModel<T: Model>: BaseViewModel<T>
I dont seem to be able to use convenience init or init with a call to super
Is there a correct way to achieve this so i can initialise these let constants in this subclass
I'm not sure I understand. Does this not work for you?
class Parent {
let someVar: String
init() {
someVar = "Some value"
}
}
class Child: Parent {
let someOtherVar: String
override init() {
someOtherVar = "Some other value"
super.init()
}
}

How to use foreign variables in classes

I have two .swift files so I want one of them to modify the text of an IBoutlet label that is the other class.
Class 1:
class class1: UIViewController {
#IBOutlet var label1: UILabel!
}
Class 2:
class class2: SKScene {
var cool_variable = 1
func coolFunc () {
class1.label1.text = "\(cool_variable)"
}
}
by doing this I'm getting the following error message:
Instance member "label1" cannot be used on type "class2"
Thanks in advance!
The distinction and relationship between classes and instances is absolutely crucial to object-oriented programming. Thus, consider your proposed code:
func coolFunc () {
class1.label1.text = "\(cool_variable)"
}
The problem here is that class1 is the name of a class. (That would be more obvious if you just, please, follow the elementary rule that class names start with a capital letter.) But label1 is an instance property. Thus what you need here is the name of an instance of the class1 class — presumably a reference to the actual existing instance that is already part of the view hierarchy.
You never create an instance of class1 in class2 to access the variables.
My guess is that you are using Storyboards. In this case you wouldn't want to create an instance of class1. Instead you would use delegation (This would also be a good idea if you are not using Storyboards).
Delegation can be a complicated topic, so I will try to keep this simple.
First, you start with a protocol. Usually you name it something like <CLASS-NAME>DataSource, so you would do something like:
protocol class2DataSource: class {}
The class keyword is required for delegation protocols.
Then you would add the methods to it that you want called in other classes when you call a method in the class the protocol delegates for, so, for example, willCoolFunc:
protocol class2DataSource: class {
func willCoolFunc(with variable: Int)
}
You have the parameter so you can access the variable cool_variable as you are trying to.
Next, you need to create a a variable in class2 that is of type class2DataSource:
weak var dataSource: class2DataSource?
Make sure the variable is weak and an optional.
Next, call the method, you would do it in coolFunc:
func coolFunc () {
dataSource?.willCoolFunc(with: cool_variable)
}
Now you, to access cool_variable when the function is called, you need to implement class2DataSource on class1. Create an extension of class1 that implements class2DataSource and add the function willCoolFunc:
extension class1: class2DataSource {
func willCoolFunc(with variable: Int) {
}
}
Now you can access the variable cool_variable in class1! The reason why is because when you call class2.coolFunc(), the willCoolFunc method is called with cool_variable passed in. Any class that implements the class2DataSource can access cool_variable with the willCoolFunc method.
To finish of the method, here is what the extension would look like:
extension class1: class2DataSource {
func willCoolFunc(with variable: Int) {
self.label1.text = "\(variable)"
}
}
We are almost done, but not quite. We still have to set the class1 as the data source for class2DataSource. To do this, I will reference Nikolay Mamaev from this post:
Go to the Interface Builder.
Type "Object" in the search text field of the Objects Library and drag an 'Object' to your view controller containing that connects to
class1 (i.e. do the same as you add any view or view controller to
storyboard scene, with the exception you add not a view or view
controller but an abstract object).
In the left-side 'Scenes' panel of your storyboard, highlight just added Object; in right-side panel go to the 'Identity Inspector' and
type class2DataSource instead of pre-defined NSObject. In left side
panel ('Scenes'), 'Object' will be renamed to 'Class2 Data Source'
automatically.
In the left-side 'Scenes' panel of your storyboard, control-drag from your UIView [*not the controller*] to the 'Class2 Data Source';
in pop-up appeared, select dataSource outlet.
There! You now set class1's label1's text to the value of cool_variable when you call class2.coolFunc()!
I do not know the exact problem you're trying to solve, but I'm just going to consider the part that you want to access the variable in class1 in class2. There are two basic ways to go about this, one is Inheritance and the other one is by creating an object. These are basic Object Oriented Programming concepts which you need to be familiar with.
Swift does not support multiple inheritance. So that rules out inheritance for solving you problem, since you are inheriting two classes SKScene and UIViewController.
Create an object in the class1 and call the function coolFunc
class class1: UIViewController {
#IBOutlet var label1: UILabel!
func modifyLabel(){
let someObject = class2()
someObject.coolFunc()
}
}
Of course this solution might not be the one you're looking for. You will have to explain more about the problem you're facing if you need a solution that will work for you.

Methods in Swift

I have 2 classes in Swift in different Swift files, and I'm trying to call a method from the other class, and pass an integer as an argument. I have the following:
Class 1:
Inschatting.wentBack(Punten)
Class 2:
class Inschatting : UIViewController {
var Punten:Int = 0;
#IBOutlet var inschattingAdvies: UILabel!
func wentBack(Punten:Int) {
self.inschattingAdvies.text = Punten
}
}
Given error: Cannot convert value of type "Int" to expected argument type 'Inschatting'
Bonus question: Class 2 also complaints about the fact I want to put down a String, but it's clearly an Int
When you want to call your wentBack()-Func like above you should declare it as a class function... otherwise you should create an instance of Inschatting.
the problem is that Inschatting should declare the function the following way:
static func wentBack(Punten:Int) {
self.inschattingAdvies.text = Punten
}
But the problem is that, that you have an instance value the wentBack function.
self.inschattingAdvies
Now what you need to do is to make a decision:
1. you should call the wentback function on a Inschatting instance.
In Class A:
let instance = Inschatting()
instance.wentBack(5)
2. You should remove the self.inschattingAdvies from wentBack.
Answer to your bonus question:
"Punten" does not seem to be an integer, or an enum value at all to me.
Since your Inschatting class is a UIViewController, I suspect you want to display the data from your 1st class in your UIViewController. If this is indeed true your mistake is that you are using a Class instead of an instance of the class.
If you indeed want to display the data from your 1st class in your 2nd class (The VC), you will need to create a class instance of your second class.
let inschattingVC = Inschatting()
this way you can use inschattingVC.wentBack(someRandInt) to call your function and set the text accordingly.
Secondly your Punten in Inschatting.wentBack(Punten) is also probably not a variable but again an instance (or you should rename it. Variables should start with lowercase letters!!!).
So if you are creating a class instance using Inschatting() you should be able to use .wentBack(12345) to set the text in your class instance.
First you say :
#IBOutlet var inschattingAdvies: UILabel!
so inschattingAdvies is a UILabel
Then you have a function :
func wentBack(Punten:Int) {
self.inschattingAdvies.text = Punten
}
where you have Punten which is an integer. And then you try to set the text property of your label to punten.
Your code should be :
func wentBack(Punten:Int) {
self.inschattingAdvies.text = String(Punten)
}
note the cast to a string.
Not sure if that's the error you were getting, but it's definitely also an error.

Send class type after "as!" as an argument

I need to "read" ViewController, which was sent as an argument to a function, as a VC of the specific class. So I need something like that (we get a class also from a function arguments):
let vc = vc_from_func_args as! type_from_func_args
I can pass a class to let's say isMemberOfClass() by doing that:
let klass: AnyClass = MyClass.self
vc.isMemberOfClass(klass)
But I can't do the same thing with "as" expression. It gives me an error:
klass is not a type
How can we pass class (type?) after "as" as a variable?
Given your comments, this is exactly what protocols are for. If you want a thing you can call pop on, then make that a requirement for your function. If it's easy to list all the things you need, then just put them in your protocol:
protocol Stackable {
var parent: UIViewController {get set}
var child: UIViewController {get set}
}
func push(vc: Stackable) {
// do the work
}
If you really need this to be a UIViewController that also happens to be Stackable, that's fine, too:
func pop<VC: UIViewController where VC: Stackable>(vc: VC) {
// do the work
}
Then just mark your view controllers as conforming to Stackable:
class SomeViewController: UIViewController, Stackable {
var parent: UIViewController
var child: UIViewController
...
}
If you find yourself doing a lot of as! or AnyClass, you're probably on the wrong track in Swift.
How about something like that...
if let checkedClass: MyFirstClass = vc_from_func_args as? MyFirstClass {
//It only hits here if it is MyFirstClass
}
if let checkedClass: MySecondClass = vc_from_func_args as? MySecondClass {
//It only hits here if it is MySecondClass
}
if let checkedClass: MyThirdClass = vc_from_func_args as? MyThirdClass {
//It only hits here if it is MyThirdClass
}
Also you are tying to instantiate a little bit wired :-)
Change this
let klass: AnyClass = MyClass.self
vc.isMemberOfClass(klass)
To something like this
vc.isMemberOfClass(MyClass)
You don't need to create an instance to check if another object is kind of a class :-) But just use my code from above... Its even better than this one
I discovered the same issue a little while ago, and ended up writing a downcast global function, and a protocol called Castable that includes the asType function:
protocol Castable: class {
func asType<T>(t: T.Type, defaultValue: T?, file: StaticString,
function: StaticString, line: UWord) -> T?
}
Basically, you can take any class object and write myObject.asType(newType) and it performs the cast. If the cast fails, it logs the failure to the console, reporting the types you were casting to and from, and the file name, method name, and line number where the method was called. I use it for debugging.
At any rate, the way that the asType and downcast functions are written, you can pass the type that you are casting to as a named variable, which is what your original question wanted to do.
The complete code for downcast, the Castable protocol, and the asType function are available at this link.

In swift, how to refer to topmost class when my class has subclass of same name

In Swift:
How can I assign a topmost myObject to the innerObj variable?
Does swift have some sort of namespace operator that lets me create an myObject from global namespace?
Consider the code below.
//my object that can init with a message=string
class MyObject {
init(message: String) {
println(message)
}
}
//here I define a global works fine
let global = myObject(message: "this works")
//other class
class ViewController: UIViewController {
//defines an inner class with same name
class MyObject {
func failsFunction(){
//cannot invoke initializer for type "ViewController.myObject" with an argument of type (String)
let innerObj = myObject("how can I refer to the topmost myObject here?")
}
}
}
My first answer would be "don't do that." It's technically legal because the two classes have unique scope, but it's confusing as all hell, and will come back to bite you 6 months from now when you are coming back to this code and don't remember that you have a global class and a child class of ViewController with the same name.
If you are going to ignore that advice, Lou provided your solution: Create a typeAlias at the top level and use that inside your ViewController class so that you can reference the global class inside ViewController.
Secondly, class names should start with an upper-case letter. So class myObject should be class MyObject. This is a documented convention of the language.
Thirdly, myObject is a dreadful name for a class. It doesn't give you any idea what the class is for. Even if this is a learning exercise, you should still follow good coding practices. It trains good habits, and test code has a way of finding itself in real projects, or posted as demo code somewhere, or whatever.
You need to alias it before you hide it with:
typealias GlobalMyObject = MyObject
One usual way is to bind your outer class into struct. This pattern is quite similar to creating a namespace. You could do it like this
struct MyNameSpace {
class myObject {
init(message: String) {
print(message)
}
}
}
//here I define a global works fine
let global = MyNameSpace.myObject(message: "this works")
//other class
class ViewController: UIViewController {
//defines a subclass with same name
class myObject {
func failsFunction(){
//cannot invoke initializer for type "ViewController.myObject" with an argument of type (String)
let innerObj = MyNameSpace.myObject(message: "how can I refer to the topmost myObject here?")
}
}
}
Then, you could use both the classes and the compiler determines the use cases differently for both.

Resources