I am trying to call a method which is in another class and use a variable of that class which is getting updated in the method, after the method call. But I am getting a nil value for the variable as the statement is getting called before the method executes completely.
for example: I have two classes A and B. B has a method update and a variable updated.
class A {
let obj_b = B()
func call() {
obj_b.update()
let updated = obj_b.updated
}
}
class B {
var updated: Int?
func updated() {
updated = 1
}
}
what I am trying is much more complex than the example and the method takes about 5secs to execute completely. Is there any way other than returning the variable that I need.
class A {
let obj_b = B()
func call() {
let updated = obj_b.updatedVariable
print("\(updated!)")
}
}
class B {
var updatedVariable: Int?{
return 1
}
}
While calling A
let obj_b = A()
obj_b.call()
Result Prints 1 , this is what you want the variable. You can use computed variable in such case.
Related
I have come across the array forEach function that is a higher order function and it takes only one parameter, i.e., a closure. Now this closure internally loops through all the elements of the array one by one but does not return anything. The implementation of the closure is left to the choice of the user.
I have a custom class MyClass that has a private variable inside it num and a public function setNum(num: Int) to set the value of that private variable from outside. I am just trying to create a similar function factorial inside my custom class that takes only one parameter, i.e., a closure. However, I have to manually call the closure inside factorial, pass the value of num as a parameter to the closure.
Is there a way that the closure can act on num without having passed it as a parameter? Basically I am just trying to replicate the array forEach function. Syntax of array forEach is:
array.forEach(body: (Int) -> Void) -> Void)
Implementation:
arr1.forEach { print($0) }
My code is as below:
import Foundation
public class MyClass {
private var factorialNumber: Double = 0
internal static var instance: MyClass?
public func setFactorialNumber(number value: Double) {
factorialNumber = value
}
public func factorial(body closure: (String?) -> Void) -> Void {
var outputString: String?
var result: Double = 1
if factorialNumber <= 0 {
outputString = nil
} else {
outputString = ""
while(factorialNumber >= 1) {
if factorialNumber == 1 {
outputString = outputString! + "\(factorialNumber) = \(result)"
break
} else {
outputString = outputString! + "\(factorialNumber) x "
}
result = result * factorialNumber
factorialNumber -= 1
}
}
// Finally closure call
closure(outputString)
}
private init() {}
public static func getInstance() -> MyClass {
if self.instance == nil {
self.instance = MyClass()
}
return self.instance!
}
}
And here is how I have to call my function to calculate the factorial:
var obj1 = MyClass.getInstance()
obj1.setFactorialNumber(number: 5)
obj1.factorial{ (result) in
print(result ?? "Factorial Result is Nil")
}
Please note that I have to pass a parameter result inside my closure to get the result of factorial.
Is there a way that the closure can act on num without having passed it as a parameter? Basically I am just trying to replicate the array forEach function ... [And, in your comment:] All I am trying to do is learn how to create higher order functions like array.forEach.
It's hard to understand what you think you're after, but taking you at your word, let's write forEach. Here we go:
extension Sequence {
func myForEach(f: (Element) -> ()) {
for e in self {
f(e)
}
}
}
Let's test it:
[1,2,3].myForEach { print($0) } // prints 1, then 2, then 3
We've done it! We wrote a higher-order function that acts exactly like forEach. So this must be how forEach actually works, more or less.
You can see from the example that it makes no sense to ask not to have to pass a parameter into the function that our forEach takes as a parameter. That is exactly what we must be able to do in order for that function to have an element to operate on.
Usecase
I have a superclass (FirebaseObject) with subclasses for most data items in my Firebase (ex: RecipeItem, User). I made a function in the superclass that automatically updates the data that is in the subclass, now I am trying to make a function with closures that get called when the object is updated.
Code
class FirebaseObject {
private var closures: [((FirebaseObject) -> Void)] = []
public func didChange(completion: #escaping (((FirebaseObject) -> Void))) {
// Save closures for future updates to object
closures.append(completion)
// Activate closure with the current object
completion(self)
}
//...
}
This calls the closure with the initial object and saves it for later updates. In my Firebase observer I can now activate all the closures after the data is updated by calling:
self.closures.forEach { $0(self) }
To add these closures that listen for object changes I need to do:
let recipeObject = RecipeItem(data)
recipeObject.didChange { newFirebaseObject in
// Need to set Type even though recipeObject was already RecipeItem
// this will never fail
if let newRecipeObject = newFirebaseObject as? RecipeItem {
// Do something with newRecipeObject
}
}
Question
Is there a way to have the completion handler return the type of the subclass so I don't have to do as? Subclass even though it won't ever fail? I tried to do this with generic type but I can't figure it out and I am not sure if this is the correct solution.
I would like to keep most code in the FirebaseObject class so I don't need to add a lot of code when creating a new subclass.
Edit
Based on this article I tried to add the type when creating a subclass:
class RecipeItem: FirebaseObject<RecipeItem> {
//...
}
class FirebaseObject<ItemType> {
private var handlers: [((ItemType) -> Void)] = []
public func didChange(completion: #escaping (((ItemType) -> Void))) {
//...
This compiles but it crashes as soon as RecipeItem is initialised. I also tried
class RecipeItem: FirebaseObject<RecipeItem.Type> {
//...
}
But this gives an interesting compiler error when I try to access RecipeItem data in didChange closure:
Instance member 'title' cannot be used on type 'RecipeItem'
Ok, so I've been working on this for a day and I have found a way to do it using the method in this answer for the didChange and initObserver functions and taking inspiration from this way of saving data in extensions.
First off, all the functions that need to use the type of the subclass are moved to a protocol.
protocol FirebaseObjectType {}
extension FirebaseObjectType where Self: FirebaseObject {
private func initObserver(at ref: DatabaseReference) {
//...
}
mutating func didChange(completion: #escaping (((Self) -> Void))) {
if observer == nil {
// init Firebase observer here so there will be no Firebase
// observer running when you don't check for changes of the
// object, and so the Firebase call uses the type of whatever
// FirebaseObject this function is called on eg:
// RecipeItem.didChange returns RecipeItem
// and NOT:
// RecipeItem.didChange returns FirebaseObject
initObserver(at: ref)
}
if closureWrapper == nil {
// init closureWrapper here instead of in init() so it uses
// the class this function is called on instead of FirebaseObject
closureWrapper = ClosureWrapper<Self>()
}
// Save closure for future updates to object
closures.append(completion)
// Activate closure with current object
completion(self)
}
}
To save the closures I now use a wrapper class so I can do type checking on that. In FirebaseObject:
class ClosureWrapper<T> {
var array: [((T) -> Void)]
init() {
array = []
}
}
fileprivate var closureWrapper: AnyObject?
Now I can get the closures with the right type in FirebaseObjectType protocol:
private var closures: [((Self) -> Void)] {
get {
let closureWrapper = self.closureWrapper as? ClosureWrapper<Self>
return closureWrapper?.array ?? []
}
set {
if let closureWrapper = closureWrapper as? ClosureWrapper<Self> {
closureWrapper.array = newValue
}
}
}
I can now use didChange on a FirebaseObject subclass without checking its type every time.
var recipe = RecipeItem(data)
recipe.didChange { newRecipe in
// Do something with newRecipe
}
The code below compiles just fine if I make the tester() method an instance method. How can I make it work while keeping it a class method?
protocol Numbers {
}
extension Numbers {
func amountFromText(text: String) -> Int {
return 0
}
}
class CommonDB: Numbers {
class func tester() {
let text = ""
let amount = amountFromText(text)
}
}
The way you have your function defined in your protocol means that it will be an instance function, that is, you need an instance of the class or structure in order to call that function. When you are in a class function, you don't have an instance of that class.
If you want to call amountFromText() from a class function, then declare it to be static. That way, it won't need an instance of the class or structure to be called:
extension Numbers {
static func amountFromText(text: String) -> Int {
return 0
}
}
I have the following class:
class ReportView: NSView {
var categoriesPerPage = [[Int]]()
var numPages: Int = { return categoriesPerPage.count }
}
Compilation fails with the message:
Instance member 'categoriesPerPage' cannot be used on type
'ReportView'
What does this mean?
Sometimes Xcode when overrides methods adds class func instead of just func. Then in static method you can't see instance properties. It is very easy to overlook it. That was my case.
You just have syntax error when saying = {return self.someValue}. The = isn't needed.
Use :
var numPages: Int {
get{
return categoriesPerPage.count
}
}
if you want get only you can write
var numPages: Int {
return categoriesPerPage.count
}
with the first way you can also add observers as set willSet & didSet
var numPages: Int {
get{
return categoriesPerPage.count
}
set(v){
self.categoriesPerPage = v
}
}
allowing to use = operator as a setter
myObject.numPages = 5
For anyone else who stumbles on this make sure you're not attempting to modify the class rather than the instance! (unless you've declared the variable as static)
eg.
MyClass.variable = 'Foo' // WRONG! - Instance member 'variable' cannot be used on type 'MyClass'
instanceOfMyClass.variable = 'Foo' // Right!
It is saying you have an instance variable (the var is only visible/accessible when you have an instance of that class) and you are trying to use it in the context of a static scope (class method).
You can make your instance variable a class variable by adding static/class attribute.
You instantiate an instance of your class and call the instance method on that variable.
Another example is, you have class like :
#obc class Album: NSObject {
let name:String
let singer:Singer
let artwork:URL
let playingSong:Song
// ...
class func getCurrentlyPlayingSongLyric(duration: Int = 0) -> String {
// ...
return playingSong.lyric
}
}
you will also get the same type of error like :
instance member x cannot be used on type x.
It's because you assign your method with "class" keyword (which makes your method a type method) and using like :
Album.getCurrentlyPlayingSongLyric(duration: 5)
but who set the playingSong variable before? Ok. You shouldn't use class keyword for that case :
// ...
func getCurrentlyPlayingSongLyric(duration: Int = 0) -> String {
// ...
return playingSong.lyric
}
// ...
Now you're free to go.
Your initial problem was:
class ReportView: NSView {
var categoriesPerPage = [[Int]]()
var numPages: Int = { return categoriesPerPage.count }
}
Instance member 'categoriesPerPage' cannot be used on type 'ReportView'
previous posts correctly point out, if you want a computed property, the = sign is errant.
Additional possibility for error:
If your intent was to "Setting a Default Property Value with a Closure or Function", you need only slightly change it as well. (Note: this example was obviously not intended to do that)
class ReportView: NSView {
var categoriesPerPage = [[Int]]()
var numPages: Int = { return categoriesPerPage.count }()
}
Instead of removing the =, we add () to denote a default initialization closure. (This can be useful when initializing UI code, to keep it all in one place.)
However, the exact same error occurs:
Instance member 'categoriesPerPage' cannot be used on type 'ReportView'
The problem is trying to initialize one property with the value of another. One solution is to make the initializer lazy. It will not be executed until the value is accessed.
class ReportView: NSView {
var categoriesPerPage = [[Int]]()
lazy var numPages: Int = { return categoriesPerPage.count }()
}
now the compiler is happy!
I kept getting the same error inspite of making the variable static.
Solution: Clean Build, Clean Derived Data, Restart Xcode. Or shortcut
Cmd + Shift+Alt+K
UserNotificationCenterWrapper.delegate = self
public static var delegate: UNUserNotificationCenterDelegate? {
get {
return UNUserNotificationCenter.current().delegate
}
set {
UNUserNotificationCenter.current().delegate = newValue
}
}
Just in case someone really needs a closure like that, it can be done in the following way:
var categoriesPerPage = [[Int]]()
var numPagesClosure: ()->Int {
return {
return self.categoriesPerPage.count
}
}
typealias CBType = () -> Void
class A {
let b = B()
func test() {
let token = b.register { CBType in
self.b.waitFor([token]) // ERROR: Variable used within its own initial value
}
b.dispatch()
}
}
class B {
private var _callbacks = [String:CBType]()
func register(callback: CBType) -> String {
let id = "1234"
_callbacks[id] = callback
return id
}
func dispatch() {
for (_, cb) in self._callbacks {
cb()
}
}
func waitFor(tokens: [String]) {
}
}
A().test()
When I modify the test function to use a instance variable, things are working again but that syntax feels a bit heavy.
class A {
let b = B()
var token: String?
func test() {
token = b.register { CBType in
self.b.waitFor([self.token!])
}
b.dispatch()
}
}
Why can't I use a local variable in the closure since it will be way past initialization when the closure is finally called?
The constant token doesn't have a value at the time it is captured by the closure.
You can use a mutable variable instead, and the closure will capture the variable rather the its value.
func test() {
var token = ""
token = b.register {
self.b.waitFor([token])
}
b.dispatch()
}
Alternatively, you can pass the token as a parameter into the closure:
typealias CBType = (String) -> Void
class A {
let b = B()
func test() {
let token = b.register { theToken in
self.b.waitFor([theToken])
}
b.dispatch()
}
}
class B {
private var _callbacks = [String:CBType]()
func register(callback: CBType) -> String {
let id = "1234"
_callbacks[id] = callback
return id
}
func dispatch() {
for (id, cb) in self._callbacks {
cb(id)
}
}
func waitFor(tokens: [String]) {
println("Wait for \(tokens)")
}
}
A().test()
In your first example, token doesn't have a value when you make the call self.b.waitFor([token]).
In your second example, everything appears to work because by declaring token like so: var token: String? it is given an initial value (nil).
The issue isn't whether you are using an instance variable -vs- a local variable (or that it's being used within a closure), the issue is that (in the first example) you are trying to use the token within the expression that provides its initial value.
Equivalent to this would be declaring an Int like so: let myValue: Int = myValue + 1 - its initial value is supposed to be "what" + 1?