Swift - How to pair variables from two Independent classes? - ios

Imagine we have two classes A and B. A is a nonspeciffic class and B is a subclass of a ViewController
How to make B instance variable equal to A instance variable?
A class cannot hold an instance of B, and mutually B cannot hold an instance of A.
The exact thing I'd like to achieve is that B class variable is updated to the value of A class variable.
Delegation is not possible because none of the classes holds the other's instance.
How should I approach such problem? KVO? I read that it rather shouldn't be used due to its flaws.

If neither class can hold a reference to the other one, they both need to hold a reference to an object of a third class, which holds the actual value. Properties inside A and B need to reference the corresponding property in C:
class C {
var prop : String = "hello"
}
class A {
var prop : String {
get { return c.prop }
set(v:String) { c.prop = v }
}
let C:c
init(c:C) {
self.c = c
}
}
class B : NSViewController {
var prop : String {
get { return c.prop }
set(v:String) { c.prop = v }
}
let C:c
init(c:C) {
self.c = c
}
}

KVO or NSNotification are your options. Consider using a lib such as KVOController
Try to read it

Related

Cannot convert value of generic type to expected argument of associated type

Say I have a protocol:
protocol Foo:Hashable, Comparable {}
And a struct that has this guy as a generic:
struct UsingFoo<T:Foo> {}
So far so good. Say I want to use Foo on a second protocol:
protocol Bar {
associatedtype FooType:Foo
func doSomething(with:UsingFoo<FooType>)
}
And use Bar on a class:
class UsingBar<F:Foo>:Bar {
typealias FooType = F
func doSomething(with: UsingFoo<F>) {}
}
Now say I want to bring these guys to a party:
class FooBarParty<F:Foo, B:Bar>: NSObject {
var b:B
init(b:B) {
self.b = b
// interestingly, this line below won't compile
// self.b = UsingBar<F>.init()
}
func thisWillCompile () {
UsingBar<F>.init().doSomething(with: UsingFoo<F>.init())
}
func thisWontCompile() {
b.doSomething(with: UsingFoo<F>.init())
}
func thisAlsoWont (anotherB:B) {
anotherB.doSomething(with: UsingFoo<F>.init())
}
}
The compiler says:
Cannot convert value of type 'UsingFoo<F>' to expected argument type 'UsingFoo<_>'
The question is: How can I ever use a property of type Bar? As always, any comment very appreciated
EDIT: Thanks to the accepted answer I figured out I should have specified FooType. It would look like this:
class FooBarParty<F:Foo, B:Bar> where B.FooType == F { ... }
So the question here basically is:
Why can't I use an instance of B to call doSomething(UsingFoo<F>()), but I can with an instance of UsingBar<F>
The problem lies in your associated type - FooType.
The doSomething method says that it only accepts arguments of type UsingFoo<FooType>. We know that in UsingBar<F>, FooType is F. So UsingBar<F>().doSomething requires a UsingFoo<F>. And in thisWillCompile, you give it a UsingFoo<F>! It works!
Now you get another random instance of B and call doSomething. What argument does it need? UsingFoo<FooType>, you might say. Well, what is FooType here? We don't know! It can be F, or String if we add this extension right here:
extension String: Foo { }
FooType can be anything that implements Foo. It doesn't need to be F. But you're passing it a UsingFoo<F>. That's why it does not work!
As you said, this line does not work either:
self.b = UsingBar<F>.init()
You need to be aware that B is not Bar. It can be any type that implements Bar, not necessarily UsingBar<F>. What you're doing is essentially:
class A {}
class B: A {}
class C: A {}
let obj: B = C()
That's why that does not compile.

Unable to understand behaviour of inheritance in swift?

I have created a parent class which has one property. Now i'm inheriting this class into my child class. It is obvious that member of parent class will become member of child class. So, when i change the value in child class, the same variable value is also getting changed in parent class.
I have used below mentioned code.
class SomeClass {
var avar:String = "Hello"
var bvar:String?
func someFunc() {
}
}
class Bclass:SomeClass {
func myFunc() {
self.avar = "Bye"
super.avar
}
}
let c = Bclass()
c.myFunc() // "Bye"
let d = SomeClass()
d.someFunc() // "Hello"
Here output is Bye but it should be Hello as i'm not changing parent class member value. When i access with instance of SomeClass then it shows output as Hello
Few questions-
Does it make copy of parent class variable in child class or refrence?
super means i want to access variable of parent class then why value changed ?
EDIT: As per Frankie answer
In below scenario why it still prints "Hello".
class SomeClass {
var avar:String = "Hello"
var bvar:String?
func someFunc() {
self.avar
}
}
class Bclass:SomeClass {
func myFunc() {
self.avar = "Bye"
super.avar
self.avar
}
}
class Cclass:SomeClass {
func myFunc() {
super.avar
self.avar
}
}
let c = Bclass()
c.myFunc()
let d = SomeClass()
d.someFunc()
let e = Cclass()
e.myFunc()
When you create an instance with let c = Bclass() there is one and only one instance created. There is no 'super instance' that is additionally created, so the notion of copying or reference doesn't make sense.
The subclass merely inherits all the properties and functions of the super class, or more plainly, it inherits only what defines the super class. Therefore self.avar and super.avar are the exact same thing.
Example:
class SomeClass {
var avar: String = "Hello"
}
class Bclass: SomeClass {
//imagine there is a (var avar: String = "Hello") here because it was defined in the super class
func myFunc() {
print(super.avar) //prints 'Hello'
self.avar = "Bye"
print(super.avar) //prints 'bye'
}
}
EDIT
From the OP's edit:
let c = Bclass()
c.myFunc() //assigns self.avar = "Bye", therefore prints "Bye"
let d = SomeClass()
d.someFunc() //does not make any assignment in someFunc, therefore prints the assigned value of "Hello"
let e = Cclass()
e.myFunc() //does not make any assignment in myFunc, therefore 'e' looks to its
//superclass for the value since it was never overridden and prints "Hello"
//It does NOT look to 'c' for the value because 'c' is a completely separate instance
A subclass takes all of the properties, function and methods of its superclass. The superclass is some kind of starting place / foundation for a subclass.
For instance, when you create a normal ViewController, you define it as:
class mySubclass: UIViewController{
// UIViewController's variables, properties, methods, functions...
// your code
}
You can imagine that all of the code from the superclass now lies inside the subclass as well. A good example of usage might be:
class Person{
var name: String = ""
var age: Int = 0
var job: String? = ""
}
class John: Person {
self.name = "John" // equivalent of super.name
self.age = 26
self.job = "Programmer"
}
You can say that the second class is some kind of customisation for the superclass, copying and changing its properties.

How to achieve object merging through generics in swift?

Imagine I have a class Number:
class Number {
var val: Double?
}
and have two instances of that class, A and B.
Now imagine I want to merge Binto Athrough a statement like
merge(B, into: A)
Now of course I could write the function like this:
func merge(from: Number, into: Number){
into.val = from.val
}
But that isn't reusable at all. Is there a way I could write a generic merge class?
UPDATE: Although some of the answers offer good and viable solutions, none of them are "generic" enough (generic here is meant in a non-technical way).So looking at the answers, I got some inspiration, and here is the solution I am now considering: make Number a NSObject subclass and declare all the properties that can be merged as dynamic. For example:
class Number: NSObject {
//Put the required init and initWithCoder: here
dynamic var val: Double?
}
Then declaring a protocol that mergeable classes must respect
protocol Mergeable: class {
var mergeablePropertyKeys:[String] {get}
}
And then declaring a global function that performs a merge:
func merge<U: Mergeable, Mergeable where U.Type == V.Type>(from: U, into:V){
for property in U.mergeablePropertyKeys {
V.setValue(U.valueForKey(property), property)
}
}
And I know that this will not work because the arguments to merge are not necessarily NSObjects.
How can I make sure that the arguments to merge are both NSObjects?
Can avoid having to specify the names of all my mergeable values by simply obtaining a list of my object's dynamic values?
It sounds like what you want is a generic function that uses reflection to merge properties. Reflection is limited in Swift, but it is doable using the MirrorType. I have used this method before to build a generic json parser in swift - you could do something similar but instead of parsing a json dictionary to properties map your two object's properties.
An example of using reflection to do this in swift:
func merge<T>(itemToMerge:T) {
let mirrorSelf = Mirror(reflecting: self)
let mirrorItemToMerge = Mirror(reflecting: itemToMerge)
for mirrorSelfItem in mirrorSelf.children {
// Loop through items in mirrorItemToMerge.
for mirrorImageItem in mirrorItemToMerge.children {
// If you have a parameter who's name is a match, map the value
// OR You could add any custom mapping logic you need for your specific use case
if mirrorSelfItem.label == mirrorImageItem.label {
// To set values, use self.setValue(valueToSet, forKey: propertyName)
self.setValue(mirrorImageItem.value as? AnyObject, forKey: mirrorImageItem.label!)
}
}
}
}
This assumes the object defining the merge method is a subclass of NSObject (so it can take advantage of NSKeyValueCoding). You could also make this a static method that could merge any 2 objects of any NSObject type:
static func merge<T1: NSObject, T2: NSObject>(itemChanging:T1, itemToMerge:T2) {
let mirrorSelf = Mirror(reflecting: itemChanging)
let mirrorItemToMerge = Mirror(reflecting: itemToMerge)
for mirrorSelfItem in mirrorSelf.children {
// Loop through items in mirrorItemToMerge.
for mirrorImageItem in mirrorItemToMerge.children {
// If you have a parameter who's name is a match, map the value
// OR You could add any custom mapping logic you need for your specific use case
if mirrorSelfItem.label == mirrorImageItem.label {
// To set values, use self.setValue(valueToSet, forKey: propertyName)
self.setValue(mirrorImageItem.value as? AnyObject, forKey: mirrorImageItem.label!)
}
}
}
}
Im not sure what you are expecting but there is generic solution:
class Number<T> {
var val: T?
}
protocol Merge {
func merge(from: Self, into: Self)
}
extension Number: Merge {
func merge(from: Number, into: Number) {
into.val = from.val
}
}
Protocol
Lets define a HasValue protocol (available only for classes) like this
protocol HasValue: class {
typealias T
var val: T? { get set }
}
Merge
Now we can define a generic function
func merge<U: HasValue, V:HasValue where U.T == V.T>(from: U, into:V) {
into.val = from.val
}
The constraints in the function signature do guarantee that
Both params do conform to HasValue (therefore are classes)
val types for both params are equals
Scenario 1: params have the same type
class Number: HasValue {
var val: Double?
}
let one = Number()
one.val = 1
let two = Number()
two.val = 2
merge(one, into: two)
print(two.val) // Optional(1.0)
Scenario 2: params have different types but their values have the same type
I did not constrain the 2 params of Merge to having the same type, I am only checking that the val properties of the 2 params must have the same type.
So we could also merge different instances of different classes having val of the same type like
class Phone: HasValue {
var val: Int?
}
class Computer: HasValue {
var val: Int?
}
let iPhone = Phone()
iPhone.val = 10
let iMac = Computer()
iMac.val = 9
merge(iPhone, into: iMac)
print(iMac.val) // Optional(10)
Scenario 3: params have generic types
class Box<S>: HasValue {
var val: S?
}
let boxOfString = Box<String>()
boxOfString.val = "hello world"
let boxOfInt = Box<Int>()
boxOfInt.val = 12
merge(boxOfString, into: boxOfInt) // << compile error
let boxOfWords = Box<String>()
boxOfWords.val = "What a wonderful world"
merge(boxOfString, into: boxOfWords)
print(boxOfWords.val) // Optional("hello world")

How to covert NSMutableOrderedSet to generic array?

I have this for loop, p is a NSManagedObject, fathers is a to-many relationship, so I need to cast NSMutableOrderedSet to [Family] but it does not work, why?
for f in p.fathers as [Family] {
}
You can obtain an array representation of the set via the array property - then you can downcast it to the proper type and assign to a variable:
let families = p.fathers.array as [Family]
but of course you can also use it directly in the loop:
for f in p.fathers.array as [Family] {
....
}
Update
A forced downcast is now required using the ! operator, so the code above should be:
let families = p.fathers.array as! [Family]
The simple solution by Antonio should be used in this case. I'd just like to discuss this a bit more. If we try to enumerate an instance of 'NSMutableOrderedSet' in a 'for' loop, the compiler will complain:
error: type 'NSMutableOrderedSet' does not conform to protocol
'SequenceType'
When we write
for element in sequence {
// do something with element
}
the compiler rewrites it into something like this:
var generator = sequence.generate()
while let element = generator.next() {
// do something with element
}
'NS(Mutable)OrderedSet' doesn't have 'generate()' method i.e. it doesn't conform to the 'SequenceType' protocol. We can change that. First we need a generator:
public struct OrderedSetGenerator : GeneratorType {
private let orderedSet: NSMutableOrderedSet
public init(orderedSet: NSOrderedSet) {
self.orderedSet = NSMutableOrderedSet(orderedSet: orderedSet)
}
mutating public func next() -> AnyObject? {
if orderedSet.count > 0 {
let first: AnyObject = orderedSet.objectAtIndex(0)
orderedSet.removeObjectAtIndex(0)
return first
} else {
return nil
}
}
}
Now we can use that generator to make 'NSOrderedSet' conform to 'SequenceType':
extension NSOrderedSet : SequenceType {
public func generate() -> OrderedSetGenerator {
return OrderedSetGenerator(orderedSet: self)
}
}
'NS(Mutable)OrderedSet' can now be used in a 'for' loop:
let sequence = NSMutableOrderedSet(array: [2, 4, 6])
for element in sequence {
println(element) // 2 4 6
}
We could further implement 'CollectionType' and 'MutableCollectionType' (the latter for 'NSMutableOrderedSet' only) to make 'NS(Mutable)OrderedSet' behave like Swift's standard library collections.
Not sure if the above code follows the best practises as I'm still trying to wrap my head around details of all these protocols.

Swift: subscript for array with custom type

I have a class, MyClass, and in another class I have a global variable array:
var myArray:[MyClass]
In MyClass I don't have any array.
Now, if I want to do this:
for index in 0..10 {
self.myArray[index] = //a new object
}
I get an error that MyClass doesn't have a subscript. Any thoughts on why? Remember that MyClass doesn't have any arrays in it.
Some more code to show you what I mean:
class TheirClass {
var myArray: [MyClass] = [] // or use an initializer
func test() -> Void {
for index in 0..10 {
self.myArray[index] = MyClass()
}
}
}
class MyClass {
var prop1 = ""
var prop2 = 0
}
If you are calling a var from another class you would have to address it like I do below:
class TheirClass {
var myArray: [MyClass] = [] // or use an initializer
}
class MyClass {
var objectArray = TheirClass().myArray
}
Not sure if this is what you planned to do or not. Let me know and I can alter the code to fit your purpose.
You have .. in the code which doesn't exist in Swift. You probably meant ... but there is ..< as well.
However, you have an array of size zero in your initializer, which means that you can't add values to it. You'll either have to create a new array of the right size first of all, or append to it using myArray += [MyClass()].
Also note that the -> Void is unnecessary - if you don't have a return type, it will implicitly be a void type.

Resources