How to send information from one class A to another class B using rxswift ? In iOS . for example I want to send my class Account.
Maybe rxswift is not for delegates or sending information at all.
https://www.reddit.com/r/swift/comments/7pxt4h/rxswift_vs_delegation_which_is_better/
class Account
{
var myId = 0
var Name = "name"
init (myId inputMyId:Int , Name inputName: String)
{
myId = inputMyId
Name = inputName
}
}
class B
{
var myAccount : Account
func receive()
{
// here self.myAccount should be replaced with myA.myAccount
// where myA is object of class A
}
}
class A
{
var myAccount : Account
func send()
{
// here myB.myAccount should be replaced with self.myAccount
// where myB is object of class B
}
}
solution 1
create class C that will make observables equal from A and B
class SimpleC
{
func makeAB()
{
var theB : SimpleB = SimpleB()
var theA : SimpleA = SimpleA()
theA.valueAccount = theB.valueAccount
theB.receive()
theA.send()
}
}
class SimpleA
{
var myAccount : Account = Account(myId : 0, Name : "")
var valueAccount = PublishSubject<Account>()
var myB : SimpleB?
func send()
{
// here myB.myAccount should be replaced with self.myAccount
// where myB is object of class B
self.myAccount = Account(myId : 1, Name : "MicrosoftTest5")
self.valueAccount.onNext(self.myAccount)
self.myAccount = Account(myId : 2, Name : "bestWorkTest5")
self.valueAccount.onNext(self.myAccount)
}
}
class SimpleB
{
var myAccount : Account = Account(myId : 0, Name : "")
var valueAccount = PublishSubject<Account>()
let disposer = DisposeBag()
func receive()
{
// here self.myAccount should be replaced with myA.myAccount
// where myA is object of class A
valueAccount.subscribe(onNext: { (newValue) in
print("SimpleB subscribe")
self.myAccount = newValue
var theString = "Account{" + "\(self.myAccount.myId)"
theString = theString + "} {"
theString = theString + "\(self.myAccount.Name)" + "}"
print(theString)
}).addDisposableTo(disposer)
}
}
Answering this question in hope to cover two scinarios -
Case 1: passing data while object creation (I can create and wrap it in variables, and pass along), this is same as vanilla swift
This holds true in cases of pushing/ presenting as well
class Account
{
var myId = Variable(0)
var name = Variable("")
init (myId inputMyId:Int , Name inputName: String)
{
myId.value = inputMyId
name.value = inputName
}
func passDataAnotherAccount() {
let anotherAccount = AnotherAccount(id: myId, name: name)
anotherAccount.makeChangesToVars()
}
}
class AnotherAccount {
var classListenerOfMyId = Variable(0)
var classListenerOfMyName = Variable("name")
init(id: Variable<Int>, name: Variable<String>) {
self.classListenerOfMyId = id
self.classListenerOfMyName = name
}
func makeChangesToVars() {
self.classListenerOfMyId.value = self.classListenerOfMyId.value + 1
self.classListenerOfMyName.value = Date().description
}
}
case 2 (Alternative approach to DELEGATION and callbacks)
lets say you want to know about changes from class AnotherAccount in previous class, you can make use of OBSERVABLES in that case
change the method from class Account to this
func passDataAnotherAccount() {
let anotherAccount = AnotherAccount(id: myId, name: name)
anotherAccount.makeChangesToVars()
// this will observe changes from AnotherAccount class
// we just created above in my current class,
// i.e. Account class
anotherAccount.classListenerOfMyId.asObservable().subscribe(onNext:{[weak self] valueChange in
print(valueChange)
}).disposed(by: DisposeBag())
anotherAccount.classListenerOfMyName.asObservable().subscribe(onNext:{[weak self] valueChange in
print(valueChange)
}).disposed(by: DisposeBag())
}
for handling delegations/callbacks you need to observe the changes by subscribing to the underlying Variables/Subjects/Observable.
Points to Note
Please use single dispose bag for one class, unlike me, i have disposed using DisposeBag() itself.
Read More about replacing delegates in RxSwift with Observables (this will be helpful)
Related
I have a shared handler class in which I 'manage' objects.
In this shared class, there is a 'main object (mainObject)' and a 'single object (singleData)'.
If I now assign the singleData with the reference to mainObject.data[index] in viewA and then change mainObject.data[index] in viewB, then the singleData object also changes. How can I avoid this strong-reference here?
==> In short: I want to change the mainObject without changing the singleObject. <==
struct kOBJECT {
let name: String
let data: [Int]
}
class HandlerClass {
let shared = HandlerClass()
var mainObject = kOBJECT(name: "AnyName", data: [1,2,3,4,5])
var singleData: Int?
}
class viewA: UIViewController {
.....
func didSelectRow(at indexPath: IndexPath) {
HandlerClass.shared.singleData = HandlerClass.shared.mainObject.data[indexPath.row] // Create Reference
viewB.indexPath = indexPath
pushToViewController(viewB)
}
}
class viewB: UIViewController {
.....
public var indexPath: IndexPath!
func someFunction() {
HandlerClass.shared.mainObject.data[indexPath.row] = 10000 // <- Does this also change the `singleData` Reference? In my case it does......
}
}
I tried the following in 'didSelectRow'
let tempValue = HandlerClass.shared.mainObject.data[indexPath.row]
HandlerClass.shared.singleObject = tempValue
You misunderstand reference meaning. Reference works only of instances - that's a part of memory reserved for specific objects. Structs are also objects, but it works as Type (similar to Int, String, Double, Float, etc), so when you modify a struct as a result you will have a new object, but for class instance, you will still modify the same object as you copied only reference to that object.
Here example below:
class Apple {
init(name: String) {
self.name = name
}
var name: String
}
struct Pear {
var name: String
}
var apple = Apple(name: "Apple 1")
var pear = Pear(name: "Pear 1")
var apple2 = apple // here we copy only reference
apple2.name = "Apple 2"
print(apple.name) // 1st object
print(apple2.name) // the same object
var pear2 = pear // here we create new object
pear2.name = "Pear 2"
print(pear.name) // 1st object
print(pear2.name) // 2nd object
Also a result (run on Playground)
I tried to create a custom iterator which returns wrapper abcContainer over raw data class abc
// raw data class
class abc {
var name : String = "";
init( _ value : String) {
name = value;
}
}
// with container, only "name" is to be visible
class abcContainer {
private var _abc : abc;
init( _ obj : abc) {
_abc = obj;
}
// + extra methods here
func getName() -> String {
return _abc.name
}
}
The point would be that the dictionary would return instances of abcContainer instead of just the plain raw abc class.
I wanted to use the sequence protocol to make the conversion automatic, but I was not able to transform the [String:abc] into [String:abcContainer] automatically like this:
// the iterator is implemented just iterating the inner basic dict
// but wrapping the result value as abcContainer
class abcIterator : Sequence, IteratorProtocol {
private var __source : [String:abc]?;
var index = 0
var myIterator : DictionaryIterator<String, abc>;
init(_ ctxArray: [String:abc]) {
self.__source = ctxArray
index = 0;
myIterator = (__source?.makeIterator())!
}
func next() -> abcContainer? {
let nextItem = myIterator.next();
if(nextItem != nil) {
return abcContainer((nextItem?.value)!);
}
return nil;
}
}
// this was supposed to be the wrapper over the collection
class abcCollection : Sequence {
private var __source : [String:abc]?;
init(_ list: [String:abc]) {
self.__source = list
}
func makeIterator() -> abcIterator {
return abcIterator(self.__source!);
}
}
I'm probably missing something very basic here. When I try to use the collection like this:
var dict : [String:abc] = [String:abc]();
dict["abba"] = abc("John Smith");
for (key,value) in abcCollection(dict) {
print(key, value.getName());
}
I get error: Expression type "abcCollection" is ambiguous without more context
Does anyone have idea how to make it work? What is missing? I have a feeling that this answer has the information I need...
Swift 2 to 3 Migration for Swift Sequence Protocol
The problem in your original code is that abcCollection(dict)
returned a sequence of abcContainer objects, and those cannot
be assigned to a (key, value) tuple.
You can achieve your goal with
class abcCollection : Sequence {
private var __source : [String:abc]
init(_ list: [String:abc]) {
self.__source = list
}
public func makeIterator() -> AnyIterator<(AnyObject,abcContainer)> {
let mapped = self.__source.lazy.map {
($0.key as AnyObject, abcContainer($0.value))
}
return AnyIterator(mapped.makeIterator())
}
}
Making __source non-optional makes all the (optional) unwrappings
redundant, and lazy.map { ... } returns a lazily evaluated
sequence of key/value pairs which is then type-erased.
Ok, perhaps the answer was abcIterator was not necessary, you could have defined the iterator directly just like done in the linked answer like this:
class abcCollection : Sequence {
private var __source : [String:abc]?;
init(_ list: [String:abc]) {
self.__source = list
}
public func makeIterator() -> AnyIterator<(AnyObject,abcContainer)> {
var it = self.__source?.makeIterator();
return AnyIterator {
let n = it?.next();
if n == nil { return nil }
return (n?.key as AnyObject, abcContainer((n?.value)!))
}
}
}
After that, the custom collection returned wrapped objects correctly.
I have following code working in a playground. The didSet observer is working as expected.
struct itemStruct {
var name : String = "" {
didSet (newName) {
didSetNameTest(name)
}
}
}
func didSetNameTest (name : String) {
println("didSet itemStruct: \(name)")
}
var item = itemStruct()
item.name = "test"
If I move the code within a class I get an compiler error:
class itemClass {
struct classItemStruct{
var name : String = "" {
didSet(newName) {
didSetClassNameTest(name)
}
}
}
func didSetClassNameTest(name:String) {
println("didSet itemClass: \(name)")
}
var structItem = classItemStruct()
}
var cItem = itemClass()
cItem.structItem.name = "Test"
Error: Cannot invoke 'didSelectClassNameTest' with an argument list of type '(String)'.
All code could be copied in playground.
Since instance of an inner class is independent of any instance of the outer class as described in a link of #ABakerSmith comment a possible workaround for this would be by makeing didSetClassNameTest function private and static and then call it statically itemClass.didSetClassNameTest(name) on didSet method
Inner types are independent of their outer types. Unlike in java classItemStruct knows nothing about its outer type itemClass.
mainController = (ViewController *)self.presentingViewController;
mainController.pressureUnit = _pressureSettings.selectedSegmentIndex;
This is how I will do it in Objective C. How do I do this in swift?
otherClass().methodFromOtherClass() is the syntax for swift
If you have to pass a property with an external name it might be:
otherClass().methodFromOtherClass(propertyName: stringName) //example of passing a string
To call the method to do something, if it is returning a result of a method:
let returnValue = otherClass().methodFromOtherClass(propertyName: stringName)
Example of accessing a property or a function
class CarFactory {
var name = ""
var color = ""
var horsepower = 0
var automaticOption = false
func description() {
println("My \(name) is \(color) and has \(horsepower) horsepowers")
}
}
var myCar = CarFactory()
myFirstCar.name = "Mustang"
myCar.color = "Red"
myCar.horsepower = 200 myCar.automaticOption = true
myFirstCar.description() // $: "My Mustang is Red and has 200 horsepowers and is Automatic"
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks.
Here you just call the method but not a new instance of the class.
class SomeClass {
class func someTypeMethod() {
// type method implementation goes here
}
}
SomeClass.someTypeMethod()
I would like to create a typed map (Dictionary) class to meet the following requirements:
func testMap() {
var map = ActivitiesMap()
var activity = Activity()
activity.title = "Activity 1"
activity.uuid = "asdf1234"
map[activity.uuid] = activity
for (key, mapActivity) in map {
logger.debug("ACTIVITY MAP: \(key)=\(mapActivity)")
}
}
In short, I want this class to both be a dictionary such that it can be used in the for loop, however I want to ensure the keys are strings and the values are Activity objects.
I tried many variations of inheriting from Dictionary or typing the class, but so far it's resulted in multiple errors.
EDIT:
I don't think a simple generic dictionary will work, such as String:Activity. I want to have extra methods in the ActivityMap class, such as getAllActivitiesBetweenDates().
I need an actual class definition, not a generic dictionary expression.
You can make it looks like dictionary by implement subscript operator
And conform to Sequence protocol to support for-in loop
struct ActivitiesMap : Sequence {
var map = [String:Activity]()
subscript(key: String) -> Activity? {
get {
return map[key]
}
set(newValue) {
map[key] = newValue
}
}
func generate() -> GeneratorOf<(String, Activity)> {
var gen = map.generate()
return GeneratorOf() {
return gen.next()
}
}
// I can't find out type of map.generator() now, if you know it, you can do
//func generate() -> /*type of map.generator()*/ {
// return map.generate();
//}
}
This works for me. Not sure what is in your ActivitiesMap class, but just typed a Dictionary
class Activity{
var title:String = "";
var uuid: String = "";
}
func testMap() {
//var map = ActivitiesMap()
var map: Dictionary< String, Activity> = Dictionary< String, Activity>();
var activity = Activity()
activity.title = "Activity 1"
activity.uuid = "asdf1234"
map[activity.uuid] = activity
for (key, mapActivity) in map {
println("ACTIVITY MAP: \(key)=\(mapActivity)")
}
}
testMap();
This is my output:
ACTIVITY MAP: asdf1234=C11lldb_expr_08Activity (has 2 children)
class Activity {
var title=""
var id=""
init(id:String, title:String) { self.id=id; self.title = title }
}
var activities = [String:Activity]()
let a1 = Activity(id:"a1", title:"title1")
let a2 = Activity(id:"a2", title:"title2")
let a3 = Activity(id:"a3", title:"title3")
activities[a1.id] = a1
activities[a2.id] = a2
activities[a3.id] = a3
for (id,activity) in activities {
println("id: \(id) - \(activity.title)")
}
should print
id: a2 - title2
id: a3 - title3
id: a1 - title1
(key order not guaranteed to be the same)
You can use typealias keyword to define nice name of any type.
Here is how it can be used for your code:
class Activity { /* your code */ }
typealias ActivityMap = Dictionary<String, Activity>
var activityDict = ActivityMap()
And to support custom functions you can write an extension, example bellow:
extension Dictionary {
func getAllActivitiesBetweenDates(fromDate:NSDate, toDate:NSDate) -> Array<Activity>
// your code
return []
}
}
Usage:
let matchedActivities = activityDict.getAllActivitiesBetweenDates(/*date*/, /*date*/)