I have a Paho MQTT connection, with a callback updating a object and i need to update a coposable..
I can see the in Logcat that i receive information, but the composable is not updated.
I am suspecting that the issue is that i am using a static object and it is not mutable. What is the practice on this scenario? I did not implement a ViewModel. It could be done with a timer, but i think it is not an elegant solution.
snippet:
object MyCallBack : MqttCallback {
public var message = ""
override fun messageArrived(topic: String?, message: MqttMessage?) {
this.message = message.toString()
Log.e(ContentValues.TAG,"mqtt Arrived: $message")
}
......
}
and a composable function used to display the information:
#Composable
fun ShowMessage() {
var myCallBack = MyCallBack //here i can access the updated information
var message by remember {
mutableStateOf(myCallBack.message)
Text("Changed ${myCallBack.message}", color = Color.White)
}
}
Thank you!
i have tried to use mutableStateOf() but it did not called for composition, i think it is not observable.
Related
I am kind of new to OOP (I think I understand the basics now), so I'm struggling with something:
I am currently making a personal iOS app that involves using events from the iPhone Calendar. I wanted to add properties to the built in EKEvent class but I read that's not possible with extensions.
So I decided to make my own Class, called Event, that will inherit from EKEvent and add the properties I want.
But I'm struggling with the initialization for this class: I would like to initialize Event from an instance of EKEvent since I'm getting EKEvents when I fetch the events of my Calendar, but I can't find a way to do it. I looked but didn't find a similar questions for Swift, they are using super.init() which is not what I want to do.
Here's my class, I found a workaround as you can see but I'd like to understand how to do it with inheritance if it's possible ^^
class Event { // Deleted the : EKEvent here to test my workaround
public var matiere: String = ""
public var matiereId: String = ""
public var prof: String = ""
public var salle: String = ""
public var type: String = ""
public var ekEvent: EKEvent
public var duration: DateInterval
public var hoursDuration: Int
public var minutesDuration: Int
init(ekEvent: EKEvent) {
/* Here is my workaround, if I need to access properties from EKEvent I'll use
self.ekEvent.startDate (for example) */
self.ekEvent = ekEvent
self.duration = DateInterval(start: ekEvent.startDate, end: ekEvent.endDate)
self.hoursDuration = Int(duration.duration) / 3600
self.minutesDuration = (Int(self.duration.duration) - (3600 * self.hoursDuration)) / 60
}
}
Thanks in advance and excuse my English I'm French =D
glad to hear you're getting into OOP!
So the way you've seen people doing this using super.init() is the most common and accepted way to achieve what you want, but in this case, the init function that you would want to override hasn't been marked as open.
This essentially means you can't override it and init your own variables, then call super.init. So your approach is completely fair in this case.
Streamline the process
so, as an idea, if you're aiming to try and make this a little more streamline, you could do something like this:
extension EKEvent {
func createEvent() -> Event {
.init(ekEvent: self)
}
}
This will allow you to now do this: ekEvent.createEvent() and then this will return an event object which you can use.
i am using Jetpack Compose 1.2.0 and Room 2.4.3
everything works well and my state changes when i use Read, Insert, Delete but i don't know why it does not work with update (when i navigate back or re enter the screen Its okay and i will get updated data)
this is my DAO
#Dao
abstract class MessageDAO : BaseDao<Message>() {
#Query("SELECT * FROM messages")
abstract fun getAllMessages(): LiveData<List<Message>>
#Insert(onConflict = OnConflictStrategy.IGNORE)
abstract override fun insert(obj: Message): Long
#Insert(onConflict = OnConflictStrategy.IGNORE)
abstract override fun insert(obj: MutableList<Message>?): MutableList<Long>
#Update
abstract override fun update(obj: Message)
#Update
abstract override fun update(obj: MutableList<Message>?)
#Delete
abstract override fun delete(obj: Message)
#Query("delete from messages where id in (:messageIDs)")
abstract fun delete(messageIDs: List<Long>)
}
also this my viewModel
#HiltViewModel
class MessagesViewModel #Inject constructor(
private val application: Application,
private val messageRepository: MessageRepository
) : ViewModel() {
fun sendMessage(message: Message) =
CoroutineScope(Dispatchers.IO).launch {
message.localId = messageRepository.insert(message)
}
fun editMessage(message: Message) =
CoroutineScope(Dispatchers.IO).launch {
messageRepository.update(message)
}
fun deleteMessage(message: Message) {
CoroutineScope(Dispatchers.IO).launch {
messageRepository.delete(message)
}
}
}
and this is my Composable function to show data
#Composable
fun Messaging(
navController: NavController,
to_user_id: String,
messagesViewModel: MessagesViewModel,
currentUserId: Long,
) {
val messages: List<Message> by messagesViewModel.getConversationMessages(to_user_id.toLong())
.observeAsState(
listOf()
)
Column {
MessagingHeader(
navController,
profileViewModel,
to_user_id.toLong(),
selection
)
Column(Modifier.weight(1f)) {
Column(
Modifier.verticalScroll(
state = rememberScrollState(),
reverseScrolling = true
)
) {
messages.forEach { message ->
Message(
currentUserId,
message
)
}
}
}
}
UPDATE for getConversationMessages func:
fun getConversationMessages(targetUserId: Long) =
messageDAO.getMessagesByTargetUserId(targetUserId)
and this getMessagesByTargetUserId func for my MessageDAO
#Query("SELECT * FROM messages WHERE receiver = :targetUserId OR sender = :targetUserId ORDER BY createdAt")
abstract fun getMessagesByTargetUserId(targetUserId: Long): LiveData<List<Message>>
It sounds like the issue you're experiencing is that the data in your Composable function is not being updated when you call the "update" method in your ViewModel. The reason for this is that the LiveData returned by the "getConversationMessages" function in your repository is not being updated when you call "update" in your ViewModel.
One solution to this issue is to use the "postValue" method instead of "setValue" when updating the value of the LiveData object in your repository. When you call "postValue", it will trigger the observer in your Composable function and update the UI with the latest data.
Another solution is to use the Transformations.map method in your Messaging function to convert the LiveData returned by the getConversationMessages to another LiveData type, and then observe it, this way when the data change inside the map the observer will be triggered.
For example:
val conversationLiveData = messagesViewModel.getConversationMessages(to_user_id.toLong())
val messages: List<Message> by Transformations.map(conversationLiveData){it}.observeAsState(listOf())
It's also possible that the issue is caused by a problem with the Room database's ability to notify the observer when the data is updated. One way to check this is to add a log statement in the "update" method in your ViewModel to ensure that it is being called when you expect it to be.
It's also possible that the issue is related to the use of CoroutineScope with Dispatchers.IO in your ViewModel. In this case it might be useful to try calling the update method with Dispatchers.Main instead.
In summary, the issue you're experiencing is likely related to the LiveData object not being updated when the update method is called. There are several possible solutions to this issue, including using the "postValue" method instead of "setValue" when updating the value of the LiveData object, using Transformations.map, making sure the update method is being called when it should be, or trying to call the update method with Dispatchers.Main instead of Dispatchers.IO in your ViewModel.
This question has effectively been asked before (Combine assign publisher to PassthroughSubject) but the answer assumed the architecture of the question's example was totally wrong.
I'm faced with the same issue, in what I feel is a different example case, and I'd dearly love clarity on the issue.
I have a viewmodel class which provides a public errorMessage publisher for the client layer to do what it will with. Just a string here. Privately this publisher is backed by a PassthroughSubject - I'm "entering the monad" from different places internally and so I want to send error messages imperatively at that stage.
One of my entry points here happens to be another Combine publisher. I'd like to just map and bind, as I would in RxSwift:
private let _errorMessageSubject = PublishSubject<String>()
public let errorMessageRail = _errorMessageSubject.asObservable()
private func setup() {
...
myEngine.errorMessages
.map { msg in "Internal error: \(msg)" }
.bind(to: _errorMessageSubject)
...
}
private func someOtherMethod() {
_errorMessageSubject.onNext("Surprise")
}
In Combine I'm not sure how to do it other than:
private let _errorMessageSubject = PassthroughSubject<String,Never>()
public let errorMessageRail = _errorMessageSubject.eraseToAnyPublisher()
private func setup() {
...
myEngine.errorMessages
.map { msg in "Internal error: \(msg)" }
.sink { [weak self] msg in
self?._errorMessageSubject.send(msg)
}
...
}
private func someOtherMethod() {
_errorMessageSubject.send("Surprise")
}
Before we get into chatting concurrency issues, let's say I'm always carefully pushing to _errorMessageSubject on a specific dispatch queue. I omit that from the code above for clarity.
So given this example, unless I'm missing something staggeringly obvious a flatMap won't help me here.
Am I stuck with this sink -> send dance?
Or is there some eye-watering code-smell about my public/private publisher/subject pattern (that I use a lot for bridging imperative with reactive architectures) and can some kind soul point me in the direction of self-improvement?
It sounds like what you want your rail to be the merge of _errorMessageSubject and another publisher. So I'll make errorMessageRail a variable so it can be changed (by this class only) after it's initialized:
public private(set) var errorMessageRail = _errorMessageSubject.eraseToAnyPublisher()
Then, in setup, you change the rail so it includes the additional stream:
func setup() {
...
errorMessageRail = _errorMessageSubject.merge( with:
myEngine.errorMessages
.map { msg in "Internal error: \(msg)" }
).eraseToAnyPublisher()
}
I would like to create a property wrapper for CurrentValueSubject. I have done this like that:
#propertyWrapper
public class CurrentValue<Value> {
public var wrappedValue: Value {
get { projectedValue.value }
set { projectedValue.value = newValue }
}
public var projectedValue: CurrentValueSubject<Value, Never>
public init(wrappedValue: Value) {
self.projectedValue = CurrentValueSubject(wrappedValue)
}
}
This works but there is a little thing I would like to change with it - use struct instead of class. The problem with using struct for this is that sometimes I could get Simultaneous accesses error. And I know why, this happens when in sink from this publisher I would try to read the value from wrapped value. So for example with code like this:
#CurrentValue
let test = 1
$test.sink { _ in
print(self.test)
}
And I more or less know why - because when projectedValue executes its observation, wrapped value is still in process of setting its value. In class this is ok, because it would just change the value, but with struct it actually modifies the struct itself, so Im trying to write and read from it at the same time.
My question is - is there some clever way to overcome this, while still using struct? I don't want to dispatch async.
Also I know that #Projected works similarly to this propertyWrapper, but there is a big difference - Projected executes on willSet, while CurrentValueSubject on didSet. And Projected has the same issue anyway.
I know that I can read the value inside the closure, but sometimes Im using this with various function calls, that might eventually use self.test instead.
Try my implementation
#propertyWrapper
class PublishedSubject<T> {
var wrappedValue: T {
didSet {
subject.send(wrappedValue)
}
}
var projectedValue: some Publisher<T, Never> {
subject
}
private lazy var subject = CurrentValueSubject<T, Never>(wrappedValue)
init(wrappedValue: T) {
self.wrappedValue = wrappedValue
}
}
I'm trying to create a way to build compassable objects in Swift. I feel like I'm almost there with what I have but it's still not 100% correct.
What I'm aiming for is to have a FlowController object that can create our UIViewControllers and then give them any of the dependencies that they need.
What I'd also like to do is make this work as loosely as possible.
I have a small example here that works but is not ideal. I'll explain...
Here are two objects that can be used as components... Wallet and User.
class Wallet {
func topUp(amount: Int) {
print("Top up wallet with £\(amount)")
}
}
class User {
func sayHello() {
Print("Hello, world!")
}
}
We then define a Component enum that has cases for each of these...
enum Component {
case Wallet
case User
}
... And a protocol that defines a method requiresComponents that returns an array of Components.
This is where the problem arises. In order for the "factory object" to put the components into a Composable object we need to define the user and wallet properties in the protocol also.
protocol Composable {
var user: User? {get set}
var wallet: Wallet? {get set}
func requiresComponents() -> [Component]
}
In an attempt to make these properties "optional" (not Optional) I have defined an extension to the Composable protocol that defines these vars as nil.
extension Composable {
var user: User? {
get {return nil}
set {}
}
var wallet: Wallet? {
get {return nil}
set {}
}
}
Now I declare the class that I want to make Composable. As you can see it requires the User component and declares the variable.
class SomeComposableClass: Composable {
var user: User?
func requiresComponents() -> [Component] {
return [.User]
}
}
Now the FlowController that will create these and add the components to them. You can see here that I have had to take the object, create a local var version of it and then return the updated object. I think this is because it doesn't know the type of objects that will be conforming to the protocol so the parameter can't be mutated.
class FlowController {
func addComponents<T: Composable>(toComposableObject object: T) -> T {
var localObject = object
for component in object.requiresComponents() {
switch component {
case .Wallet:
localObject.wallet = Wallet()
print("Wallet")
case .User:
localObject.user = User()
print("User")
}
}
return localObject
}
}
Here I create the objects.
let flowController = FlowController()
let composable = SomeComposableClass()
And here I add the components. In production this would be done all inside the FlowController.
flowController.addComponents(toComposableObject: composable) // prints "User" when adding the user component
compassable.user?.sayHello() // prints "Hello, world!"
As you can see, it works here. The user object is added.
However, as you can also see. Because I have declared the vars in the protocol the composable object also has a reference to a wallet component (although it will always be nil).
composable.wallet // nil
I feel like I'm about 95% of the way there with this but what I'd like to be able to do is improve how the properties are declared. What I'd like is for that last line... composable.wallet to be a compile error.
I could do this by moving the declaration of the properties out of the protocol but then I have the problem of not being able to add the properties to any object that conforms to the Composable protocol.
What would be awesome is for the factory object to be able to add the properties without relying on the declaration. Or even have some sort of guard that says "if this object has a property call user then add the user component to it". Or something like that.
If anyone knows how I could get the other 5% of this working it would be awesome. Like I said, this works, just not in an ideal way.
Thanks :D
Hacky Edit
Hmm... As a quick tacky, horrible, "no-one-should-do-this" edit. I have changed my protocol extension to be like this...
extension Composable {
var user: User? {
get {fatalError("Access user")}
set {fatalError("Set user")}
}
var wallet: Wallet? {
get {fatalError("Access wallet")}
set {fatalError("Set waller")}
}
}
Now at least the program will crash if I try to access a variable I have not defined. But it's still not ideal.
Edit after reading Daniel's blog
OK, I think I've done what I wanted. Just not sure that it's exactly Swifty. Although, I also think it might be. Looking for a second opinion :)
So, my components and protocols have become this...
// these are unchanged
class Wallet {
func topUp(amount: Int) {
print("Top up wallet with £\(amount)")
}
}
// each component gets a protocol
protocol WalletComposing {
var wallet: Wallet? {get set}
}
class User {
func sayHello() {
print("Hello, world!")
}
}
protocol UserComposing {
var user: User? {get set}
}
Now the factory method has changed...
// this is the bit I'm unsure about.
// I now have to check for conformance to each protocol
// and add the components accordingly.
// does this look OK?
func addComponents(toComposableObject object: AnyObject) {
if var localObject = object as? UserComposing {
localObject.user = User()
print("User")
}
if var localObject = object as? WalletComposing {
localObject.wallet = Wallet()
print("Wallet")
}
}
This allows me to do this...
class SomeComposableClass: UserComposing {
var user: User?
}
class OtherClass: UserComposing, WalletComposing {
var user: User?
var wallet: Wallet?
}
let flowController = FlowController()
let composable = SomeComposableClass()
flowController.addComponents(toComposableObject: composable)
composable.user?.sayHello()
composable.wallet?.topUp(amount: 20) // this is now a compile time error which is what I wanted :D
let other = OtherClass()
flowController.addComponents(toComposableObject: other)
other.user?.sayHello()
other.wallet?.topUp(amount: 10)
This seems like a good case for applying the Interface Segregation Principle
Specifically, rather than having a master Composable protocol, have many smaller protocols like UserComposing and WalletComposing. Then your concrete types that wish to compose those various traits, would just list their "requiredComponents" as protocols they conform to, i.e:
class FlowController : UserComposing, WalletComposing
I actually wrote a blog post that talks about this more extensively and gives more detailed examples at http://www.danielhall.io/a-swift-y-approach-to-dependency-injection
UPDATE:
Looking at the updated question and sample code, I would only suggest the following refinement:
Going back to your original design, it might make sense to define a base Composing protocol that requires any conforming class to create storage for composed traits as a dictionary. Something like this:
protocol Composing : class {
var traitDictionary:[String:Any] { get, set }
}
Then, use protocol extensions to add the actual composable trait as a computed property, which reduces the boilerplate of having to create those properties in every conforming class. This way any class can conform to any number of trait protocols without having to declare a specific var for each. Here's a more complete example implementation:
class FlowController {
static func userFor(instance:UserComposing) -> User {
return User()
}
static func walletFor(instance:WalletComposing) -> Wallet {
return Wallet()
}
}
protocol Composing : class {
var traitDictionary:[String:Any] { get, set }
}
protocol UserComposing : Composing {}
extension UserComposing {
var user:User {
get {
if let user = traitDictionary["user"] as? User {
return user
}
else {
let user = FlowController.userFor(self)
traitDictionary["user"] = user
return user
}
}
}
}
protocol WalletComposing {}
extension WalletComposing {
var wallet:Wallet {
get {
if let wallet = traitDictionary["wallet"] as? Wallet {
return wallet
}
else {
let wallet = FlowController.walletFor(self)
traitDictionary["wallet"] = wallet
return wallet
}
}
}
}
class AbstractComposing {
var traitDictionary = [String:Any]()
}
Not only does this get rid of those pesky optionals you have to unwrap everywhere, but it makes the injection of user and wallet implicit and automatic. That means that your classes will already have the right values for those traits even inside their own initializers, no need to explicitly pass each new instance to an instance of FlowController every time.
For example, your last code snippet would now become simply:
class SomeComposableClass: AbstractComposing, UserComposing {} // no need to declare var anymore
class OtherClass: AbstractComposing, UserComposing, WalletComposing {} //no vars here either!
let composable = SomeComposableClass() // No need to instantiate FlowController and pass in this instance
composable.user.sayHello() // No unwrapping the optional, this is guaranteed
composable.wallet.topUp(amount: 20) // this is still a compile time error which is what you wanted :D
let other = OtherClass() // No need to instantiate FlowController and pass in this instance
other.user.sayHello()
other.wallet.topUp(amount: 10) // It all "just works" ;)