Imagine a situation that we have two classes like these:
class MyClass {
func printInstanceName() {
print(what?);
}
}
class User {
var firstObject: MyClass!
var secondObject: MyClass!
}
I will instantiate an object of class User like this:
let user = User();
user.firstObject = MyClass()
user.secondObject = MyClass()
How should i implement the printInstanceName that the output of code below
user.firstObject.printInstanceName()
user.secondObject.printInstanceName()
be
firstObject
secondObject
The purpose of doing this is, i want to know from which instance of MyClass in User, printInstanceName is called!
You can get variable name only in this way
class MyClass {
func printInstanceNameIn(user: User) {
let mirror = Mirror(reflecting: user)
for (label, value) in mirror.children {
if let value = value as? MyClass, value === self {
print(label!)
}
}
}
}
class User {
var firstObject: MyClass!
var secondObject: MyClass!
}
let u = User()
u.firstObject = MyClass()
u.secondObject = MyClass()
u.firstObject.printInstanceNameIn(user: u)
Use String(describing: type(of: self)) to print the class name.
class MyClass {
func printInstanceName() {
print(String(describing: type(of: self)))
}
}
Don't use force-unwrapped variables unnecessarily. You can create an init(myObject:) instead.
class User {
var myObject: MyClass
init(myObject: MyClass) {
self.myObject = myObject
}
}
Create User instance like,
let user = User(myObject: MyClass())
user.myObject.printInstanceName()
You cant get property name from MyClass, only from User.
class MyClass { }
class User {
var myObject: MyClass!
func printInstanceName() {
let mirror = Mirror(reflecting: self)
for (label, value) in mirror.children {
if type(of: value) == Optional<MyClass>.self {
print(label!)
}
}
}
}
let u = User()
u.printInstanceName()
I know this is not what you're exactly looking for but it might helps:
for child in Mirror(reflecting: user).children {
print(child.label)
}
Related
I want access to SampleMoel's 'head' at ViewModel
How to fix this error?
this is MVVM(not Use combine, RxSwift ... only use Uikit)
SampleViewModel.swift
class SampleViewModel {
var model: SampleModel?
let changeData = Observer(model?.head) //Line error
init() {
self.model = SampleModel()
}
func changeLabel(_ tf: String) {
self.changeData.value = tf
}
}
SampleModel.swift
struct SampleModel {
var head = "initValue"
}
Modify your implementation as follows:
class SampleViewModel {
private let model: SampleModel
let changeData: Observer<String>
init(model: SampleModel) {
self.model = model
changeData = Observer(model.head)
}
func changeLabel(_ tf: String) {
self.changeData.value = tf
}
}
I have the following code:
class MySuperClass : UIViewController {
var model: ModelA!
}
class ModelA {
var aBool = true
}
class ModelB: ModelA {
var boolBelongsToB = true
}
class MySubclass: MySuperclass {
func testFunction() {
let theBool = (model as! ModelB).boolBelongsToB // Simplify this
}
}
var aSubclass = MySubclass()
var aModelB = ModelB()
aSubclass.model = aModelB
What I want to do is simplify having to use the code (model as! ModelB) everytime I want to access my model in MySubclass. How can I do this?
Why not just create a computed property in your subclass Y which returns the correct type. Like
var modelB: ModelB {get { return model as! ModelB }}
Instead of getting it then as "(model as! ModelB)" you can just get it as "modelB".
I have a class that is generic, like:
class Row<T> {
...
}
If I have an instance of Row where T is ExampleClass, I want to be able to do:
row.bind(to: \ExampleClass.category)
Then in bind I want to start observing ExampleClass.category for my ExampleClass instance that I have in the class.
I've implemented:
func bind<Value>(to targetKeyPath: ReferenceWritableKeyPath<T, Value>) {
if let model = model as? NSObject {
model.observe(targetKeyPath, options: [.new ,.old], changeHandler: { _, change in
Log.info?.msg("Now we have some stuff: \(change)")
})
}
}
This gives me the error: Generic parameter 'Value' could not be inferred.
How is that possible? The T is solved but why can't Value be inferred? It should come from the parameter targetKeyPath.
Full code for repoducable:
class Row<T> {
let model: T
init(model: T) {
self.model = model
}
func bind<Value>(to targetKeyPath: ReferenceWritableKeyPath<T, Value>) {
if let model = model as? NSObject {
model.observe(targetKeyPath, options: [], changeHandler: { _, _ in
})
}
}
}
How I'd like to use the example class above:
class Person: NSObject {
#objc var age: NSNumber?
}
class Book: NSObject {
#objc var title: String?
}
let row1 = Row(model: Person())
let row2 = Row(model: Book())
row1.bind(to: \Person.age)
row2.bind(to: \Book.title)
You're probably over-thinking this. The problem is that model is known only to be an NSObject. You can reproduce simply like this:
class ExampleClass:NSObject {
#objc dynamic var category = ""
}
let model = NSObject()
model.observe(\ExampleClass.category) { _,_ in }
Same error. If you change model to be an ExampleClass, the problem goes away.
I have an error when un want init my B object.
My error is : Use of 'self' in property access 'name' before super.init initializes self
class A {
let name = "myName";
}
class B:A {
let point: ObjectWithName;
init() {
self.point = ObjectWithName(name); // error here
super.init();
}
}
Thanks for your help !
The problem is that you are accessing name which is declared in the superclass. But the superclass has not been initialized yet (it will after super.init()).
So it's a logic problem.
Solution #1
You can declare point as lazy, this way it will be executed after the whole init process has been completed, unless you call it before.
struct ObjectWithName {
let name: String
}
class A {
let name = "myName";
}
class B: A {
lazy var point: ObjectWithName = { ObjectWithName(name:self.name) }()
}
Solution #2
Inside A you can define name as static
class A {
static let name = "myName";
}
class B:A {
let point: ObjectWithName;
override init() {
self.point = ObjectWithName(name: B.name)
super.init();
}
}
I've set some variables as an Object
import UIKit
class SpeedTestResult: NSObject {
var testTime: NSDate?
}
Then in the controller I set this object and pass it to a class to store it:
testResult.testTime = NSDate()
SpeedTestManager().addTestResult(testResult)
I need to store this object and then access the elements within in a view later, This is what I have:
import Foundation
class SpeedTestManager : NSObject {
var testResultArray = [NSObject]()
func addTestResult(testResult: NSObject) {
testResultArray.append(testResult)
print("Printing testResultArray: \(testResultArray)")
}
}
But when I try to print the the object I just get
Printing testResultArray: [<ProjectName.SpeedTestResult: 0x127b85e50>]
How do I access elements within the object and store this object and retrieve it for later use in a view?
class TestResult : NSObject, NSSecureCoding {
var testTime: NSDate?
override init() {
super.init()
}
#objc required init?(coder decoder: NSCoder) {
self.testTime = decoder.decodeObjectForKey("testTime") as? NSDate
}
#objc func encodeWithCoder(encoder: NSCoder) {
encoder.encodeObject(self.testTime, forKey: "testTime")
}
#objc static func supportsSecureCoding() -> Bool {
return true
}
override var description: String {
return String.init(format: "TestResult: %#", self.testTime ?? "null")
}
}
class SpeedTestManager : NSObject, NSSecureCoding {
var testResultArray = [NSObject]()
func addTestResult(testResult: NSObject) {
testResultArray.append(testResult)
print("Printing testResultArray: \(testResultArray)")
}
override init() {
super.init()
}
#objc func encodeWithCoder(encoder: NSCoder) {
encoder.encodeObject(self.testResultArray, forKey: "testResultArray")
}
#objc required init?(coder decoder: NSCoder) {
self.testResultArray = decoder.decodeObjectForKey("testResultArray") as! [NSObject]
}
#objc static func supportsSecureCoding() -> Bool {
return true
}
override var description: String {
return String.init(format: "SpeedManager: [%#]", self.testResultArray.map({"\($0)"}).joinWithSeparator(","))
}
}
class TestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let testResult = TestResult()
testResult.testTime = NSDate()
let speedManager = SpeedTestManager()
speedManager.addTestResult(testResult)
NSUserDefaults.standardUserDefaults().setObject(NSKeyedArchiver.archivedDataWithRootObject(speedManager), forKey: "speedManager")
NSUserDefaults.standardUserDefaults().synchronize()
if let archivedSpeedManager = NSUserDefaults.standardUserDefaults().objectForKey("speedManager") as? NSData {
let unarchivedSpeedManager = NSKeyedUnarchiver.unarchiveObjectWithData(archivedSpeedManager)
print("SpeedManager: \(unarchivedSpeedManager ?? "null")")
}
else {
print("Failed to unarchive speed manager")
}
}
}
Here is one way you can do it:
import Foundation
class SpeedTestResult: NSObject {
var testTime: NSDate?
}
class SpeedTestManager : NSObject {
var testResultArray = [NSObject]()
func addTestResult(testResult: NSObject) {
testResultArray.append(testResult)
for result in testResultArray {
// This will crash if result is not a SpeedTestResult.
// print((result as! SpeedTestResult).testTime)
// This is better:
if let timeResult = result as? SpeedTestResult
{
print(timeResult.testTime)
}
else
{
print("Not time type...")
}
}
}
}
var testResult = SpeedTestResult()
testResult.testTime = NSDate()
SpeedTestManager().addTestResult(testResult)
This addresses your specific question, but there are some other problems here:
If you are going to store only SpeedTestResult instances in
testResultArray, then why not make it of type
[SpeedTestResutl]()?
If you will store different types of tests in the array, then how do
you find out which type of test an NSObject element represents?
There are ways... In the above code we at least make sure we are not treating a wrong type of object as a SpeedTestResult.
When you do SpeedTestManager().addTestResult(testResult), you don't
keep a reference to the SpeedTestManager instance. The next time
you make the same call, you will be creating a different
SpeedTestManager instance.
This is not really a problem, but SpeedTestManager does not have to
be a sub-class of NSObject, unless you want to use it in
Objective-C.
You probably don't want to print the content of testResultArray in
the addTestResult() method. You could have other methods for
accessing the array.
To add your test results to the same test manager, you could do:
let myTestManager = SpeedTestManager()
myTestManager.addTestResult(testResult)
// create other test results and add them ...