Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider making push an optional plugin #292

Closed
ikbalkaya opened this issue Jan 11, 2022 · 10 comments
Closed

Consider making push an optional plugin #292

ikbalkaya opened this issue Jan 11, 2022 · 10 comments
Assignees

Comments

@ikbalkaya
Copy link
Contributor

ikbalkaya commented Jan 11, 2022

h3. Problem
We are facing with some functional issues which are related to our push notification implementation. We are also facing compatibility issues when another library is used alongside with ours. See #226

We have two main issues

We install our custom implementations to apps which uses our library. For example we declare services, broadcast receivers to Android apps and users unless they opt out in their manifest files. This situation creates a conflict described #226 and presents some inflexibility to users' side.

We are not using (or unable to use) popular library to lessen our burden dealing with internals of push notifications. This path was followed as this library would also expose iOS plugin too, but we do not want to support Firebase's iOS plugin.

Some thoughts on how we can address these problems

  • We can create a separate plugin (such as ably-push) that will expose a common interface for users to interact with. Then we can provide our default implementations. Firebase messaging seems to have adopted a similar pattern . This looks like a federated Plugin, but they seem to have implemented only their web plugin based on this interface. iOS and Android plugins are still in the same package.
  • I was trying to find a way to use official Firebase library and how Firebase library is designed make things a bit harder. "Create your own implementation of Firebase" seems to be their motto, which means that we conform their interface and provide our own implementation. iOS and Android implementation is part of the app facing library and it's difficult to design something based on only a single platform. "Create an implementation of ably push (eg. Firebase" was my thinking and we can let user opt in for our Firebase implementation if they want to use Ably capabilities in push code

To be discussed...

@ikbalkaya
Copy link
Contributor Author

ikbalkaya commented Jan 11, 2022

I have a theoretical idea on how we can solve this issue.

Considering we act as a broker between push provider and customer, we want to implement a generic interface as a separate package that will act as AblyPushReceiver so that we can implement custom receivers for later. Custom receivers themselves will reside into a separate package but will be using the common package as dependency to access AblyPushReceiver interface. On the ably side we can also import that common package and interact with Ably system through that (eg. push activation, deactivation, message receive etc). We can write our default receiver and provide it as an optional package. The aim is to give the customers the ability to choose receiver of their liking. However in cases where users not choosing a default receiver, we cannot process messages through our own receiver. In such cases we could inform users that our message brokership is not active anymore. But we can recommend them to delegate their messages through our common interface if they want Ably capabilities with push system.
If we go with common receiver / custom receivers route, we make our system a bit more flexible, providing an interface for any number of custom receivers. We should definitely publish a default Ably receiver in such a setting.

Some notes worth to take in such case,

  • Make a contract update between Ably and the customer (that's to explicitly provide information about how Ably deals with push notifications) and what they need to do if they want to operate their push messages through Ably. (Eg. update readme the need to add a push system if they want push functionality)
  • If users wants to use Firebase or a different library out of Ably platform, we shouldn't be intercepting. The user can delegate the message to our platform if they like. There might be cases where users use push notifications for different purposes and outside of Ably ecosystem. This is a flexibility for developers.
  • In cases where users use both an Ably receiver or a custom one, we should be doing what we need to do only when provided Ably receiver is invoked. In case of Firebase messaging there is no need to clash with Firebase. We would only process messages if a receiver is provided by client.
  • We would create a common package and make our system depend on it. That package would act as an interface for Ably receiver (or maybe Push adapter...) This would be the interface package between our system and other custom receivers. Might be worth to check whether a package could be created only with interfaces.
  • I am not sure how other plugins do, but it could be a good idea to put a warning (to IDE for example) for users if they have not provided a receiver. I know linters do it, but not sure if we can provide a custom linter on our package. Something like "you haven't added any receiver, your Ably push messages won't be received"
  • We should make sure that our receiver only receives messages through Ably platform.

@ikbalkaya ikbalkaya changed the title Consider making push an optional plugin and use official Firebase library Consider making push an optional plugin or use official Firebase library Jan 11, 2022
@ikbalkaya ikbalkaya changed the title Consider making push an optional plugin or use official Firebase library Consider making push an optional plugin Jan 11, 2022
@ikbalkaya ikbalkaya self-assigned this Jan 12, 2022
@ikbalkaya
Copy link
Contributor Author

ikbalkaya commented Jan 12, 2022

It maybe worth to experiment with the idea in a small project to verify / explore technical capabilities.

  1. Create a simple Plugin project (to represent a library like ably-flutter) that should have a push interface. Can be something like AblyPush intention being to deliver messages from push providers to Ably ecosystem communicating with native plugins .
  2. Create a pure Dart package that has interface (abstract class in Dart) definition of of push system(abstract class in Dart) . We can call it PushProvider. Also create a method so that we are able to register actions (or hooks). something like registerForMessage(some lambda function or interface....). Do something that Dart language provides.
  3. Make the simple plugin depend on the pure Dart plugin
  4. Create another Plugin that also depends on the pure Dart plugin. Create a simple implementation of PushProvider (eg. DefaultPushProvider). Make that plugin to support only Android
  5. Create a dart application that uses the simple plugin and default provider plugin (An example app will be created as part of plugin and that one can be used)
  6. Discover ways to how to properly add a provider to the plugin (eg. as a configuration property).
  7. In simple plugin project, create logic to wire the interfacing between push provider and AblyPush

@ikbalkaya
Copy link
Contributor Author

ikbalkaya commented Jan 12, 2022

I attempted to create a simple dependency diagram
Push provider dependency diagram

@QuintinWillison
Copy link
Contributor

@ikbalkaya, @KacperKluka, @AndyNicks and myself had a call this morning around this issue.

In terms of the current interfaces between the application developer's code and our current plugin API, it sounds like the approach diagramed above will not change that (i.e. it will not remove or mutate existing ably-flutter APIs designed to be used by the app).

My only concern is that we need to be careful about the developer experience with any change we make to adopt this new design. Even asking the app developer to add something to their code to explicitly specify our default-yet-optional push provider means that they will have two touch points to make it work: (1) Add dependency; (2) Instantiate in code and provide to core Ably implementation via a method call or property set. -- Can we avoid that? Could we follow an approach similar to SLF4J, perhaps, where the default is 'no-operation' unless a compatible plugin is added to the project, in which case that plugin (the 'Default Push Provider') kicks into action?

Relates to #118.

@ikbalkaya
Copy link
Contributor Author

I think we can minimise work done from developers' side provided we are clear on developer documentation. However I don't think we can (or we should) include default or non-default push providers without user including in their dependencies and declare the intention to use it. That shouldn't cause so much trouble as we will ask them to explicitly add a compatible plugin to their project and declare their intention to use it. In fact I think this will make the contract between us and customers clear and prevent potential confusion caused from Ably working with push notifications..

@ikbalkaya
Copy link
Contributor Author

ikbalkaya commented Jan 14, 2022

I will try to create a simple PoC to see whether we can proceed with this approach.

I will try to use endorsed / non-endorsed plugin approaches and see how they work

@ikbalkaya
Copy link
Contributor Author

ikbalkaya commented Jan 16, 2022

I thought below higher level concepts might help with verifying the effectiveness of such a design.

  1. A runtime platform that is the runtime an application is running on, processes incoming messages from a messaging platform such as Ably (through connections, channels etc) and also maybe through Firebase Cloud messaging. Those two messaging platforms can directly talk to one another (Eg. Ably publishing messages directly on FCM) But the important thing is to maintain platforms compatible with making it clear that the kind of messages they support are explicitly presented . Exposed through interfaces in our case
  2. Our library should only accept and process messages that are defined in our platform (that is the processing platform). Adapter like concepts might help with message translation between different platforms. Single shared interface (push provider in our case). Implementations will have to help with contract between platforms (eg activation, deactivation) and message translation (message send and receive). Keeping the high level contract should make it easier for us to reason about platform communication surface.
  3. We should aim for making message translation work comprehensive, meaning we should be supporting all message interaction possibilities (receive is in our case, Send is done through Ably, so we may not need send). So actually the common interface will always be the interface defined in Ably client spec. We will delegate the responsibility of message translations between Ably interface and a third party platform to another plugin. That will make it clear to our library users how we translate messages between platforms . It will also allow them to add multiple third party platforms that might interact with our system. They might implement their own custom plugin (meaning interface implementation ), or they can add an existing plugin and use it. Core library will always process messages through Ably defined interfaces, making sure that any given provider's contract lifecycle is maintained by Ably client spec.

@ikbalkaya
Copy link
Contributor Author

I created a repository to write a POC for this case https://github.com/ikbalkaya/PushInterfacePOC

@ikbalkaya
Copy link
Contributor Author

I was able to create a simple proof of concept that makes it possible to use our plugin with one or more push providers

@ikbalkaya
Copy link
Contributor Author

Closing this for now. The next step will be to actually implement this in our library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants