Different interfaces based on devices?

I just updated my issue filed on github meteor project about this.

The “solution” (using this loosely since I don’t know yet if there are other ramifications) is to remove autoupdate package as suggested in another post, and start the server for the mobile app with DDP_DEFAULT_CONNECTION_URL set to the desktop app.

Now everything works (except hot code pushes

UPDATE: and Cordova does not work at all, so this really isn’t a solution :frowning:

My company does this to some extent, and I accomplish this using a package I built based on bootstrap’s responsive helpers:

https://atmospherejs.com/plusmore/reactive-responsive-helpers

You can then do something like the following:

<template name="AppLayout">
    {{#visibleXs}}
        {{> mobileLayout}}
    {{/visibleXs}}
    {{#hiddenXs}}
        {{> largerLayout}}
    {{/hiddenXs}}
</template>

This works really well for us because we still have a lot of shared templates.

2 Likes

I am currently doing something similar, but that still means all the templates are being sent to the mobile device. And as my app has grown so has the size of the deployment bundle. Additionally this does not solve the problem with differing CSS/js libraries for mobile/desktop.

True.

Not an issue in our case. We use a heavily customized boostrap instead of libs for specific sizes (semantic-ui would work wonderfully as well) and I’d say at least 95% of the templates are the same. Really just a different menu template and different layout template. All content templates are responsive so I use mostly the same templates for both versions. I’m ok with people downloading an extra couple templates they won’t use to save the time of building an entirely separate app.

Most of our real logic is in microservices, so the client is pretty thin.

Obviously not a solution if you’re trying to get every ounce of performance you can, but for those willing to make a very small sacrifice to save a ton of time, it’s an option.

Two Meteor apps … use --server--mobile-server … problem solved.

@andrewreedy that is what I am playing with right now, but code reload is not happening on my cordova app. Once I start it with --mobile-server it no longer picks up new code …

OK, the only way I got it to work reliably was to explicitly DDP.connect and use the connection for my remote collections.

Meteor.remoteConnection = DDP.connect( <remote url> );
// make sure Accounts uses this so I can login
Accounts.connection = Meteor.remoteConnection;
// redeclare so it uses remote users
Meteor.users = new Meteor.Collection('users',{connection: Meteor.remoteConnection});
// now instantiate my collections and subscriptions 
Collection = new Meteor.Collection('collection,{connection: Meteor.remoteConnection});
Meteor.remoteConnection.subscribe('collection');

this way I can run my app as

meteor run ios

and still connect to the same app via web browser, and autoupdate works on the web and in Cordova.

4 Likes

Could you please elaborate on that? What are the differences between --server and --mobile-server?

As far as I understand --mobile-server is for Cordova apps to tell them to connect to a specific back-end, otherwise it would always connect to where it originally got the code from.

I don’t know what --server is, because none of the meteor options seem to support it.

Hey guys, I had this question for a long time, and tried many different things to try to accomplish it, but in the end it all becomes a lot of trouble and confusion.

Using Iron:Router and Meteor.isCordova is good until a certain point, afterwards it just gets more and more troublesome: Different styles, different layouts, and if you want to use bootstrap in your web app and want to use Ionic in your cordova app? lots of extra css and things here and there. So this was a major headache for me… until now…
just solved the issue in this thread today. Check it here: https://github.com/danielfbm/meteor-cordova-web-example

It is a very different way to create a meteor app, but I really like it, and according to this slideshow here this should be the way to go.

PS: Maybe for the first time you run the app you will get some error regarding the missing files in the .scss file. Just comment that and run it again. Afterwards those files will be present and then you can uncomment the @imports again

BTW: @jamgold your solution is also fantastic. Thank you very much for sharing!
Learned a lot reading!

4 Likes

that is pretty slick, I have to say

What about implementing something similar to Meteoric?

You could implement something similar to what Meteoric has done using navigator.userAgent along with Meteor.isClient and Meteor.isCordova. Their solution allows you to use {{#if isAndroid}}{{/if}} or {{#if isIOS}}{{/if}}. It’s implied that if isAndroid and isIOS are both false, the user is using a web browser - although that’s not 100% true, it is mostly true and can be used that way.

Here is a link to Meteoric’s solution: https://github.com/meteoric/meteor-ionic/tree/ab050a67c55d5507bd0878635c7d90ae48bf4dce/components/ionBody. And listed below is the exact code I am talking about:

Platform = {
  isIOS: function () {
    return (!!navigator.userAgent.match(/iPad/i) || !!navigator.userAgent.match(/iPhone/i) || !!navigator.userAgent.match(/iPod/i))
           || Session.get('platformOverride') === 'iOS';
  },

  isAndroid: function () {
    return navigator.userAgent.indexOf('Android') > 0
           || Session.get('platformOverride') === 'Android';
  }
};

Template.registerHelper('isIOS', function () {
  return Platform.isIOS();
});

Template.registerHelper('isAndroid', function () {
  return Platform.isAndroid();
});

Template.ionBody.helpers({
  platformClasses: function () {
    var classes = ['grade-a'];

    if (Meteor.isCordova) {
      classes.push('platform-cordova');
    }
    if (Meteor.isClient) {
      classes.push('platform-web');
    }
    if ((Meteor.isCordova && Platform.isIOS()) || Session.get('platformOverride') === 'iOS') {
      classes.push('platform-ios');
    }
    if ((Meteor.isCordova && Platform.isAndroid()) || Session.get('platformOverride') === 'Android') {
      classes.push('platform-android');
    }

    return classes.join(' ');
  }
});

You can see how the platformClasses template helper is used to apply CSS styles based on the current user’s device/platform here:

<template name="ionBody">
  <div class="ionic-body {{platformClasses}}">
    {{> UI.contentBlock}}
  </div>
</template>

You can also see Meteoric uses Session.get('platformOverride') as the default if the userAgent match returns false. However, they do not actually set that session variable anywhere in their code (You can check by searching for Session.set('platformOverride' in their github repo), so their full implementation of platform-specific code is included in the two snippets you see above.

Finally, you could use this in your templates like so:

<template name="navbar">
   {{#if isIOS}}
       <h1>This is my iOS navbar!</h1> 
   {{else}}
       {{#if isAndroid}}
           <h1>This is my Android navbar!</h1> 
       {{else}}
           <h1>This is my web navbar!</h1> 
       {{/if}}
   {{/if}}
</template>
3 Likes

hi, where can I read more about DDP connection between meteor and meteor app?

@danielfbm looks like a cleaner approach for building and maintaining larger applications. But i am trying to find out how these packages renders based on the platform, I could see the code being segregated as different packages but how the rendering happens in runtime? I couldn’t find the code for that in the example.

Thanks for sharing.

I can only add two links to this reply, so I will make references only to this repo
Let me try to break things up here so that it might make it easier to understand.

  • The mobile and web interfaces could be created inside the same package. In this case it is important to segregate the target while adding the files and dependent packages like this:

    //Cordova dependencies
    api.use([
    ‘meteoric:ionic’,
    ‘meteoric:ionic-sass’,
    ‘meteoric:ionicons-sass’
    ],‘web.cordova’);

    //Web dependencies
    api.use([
    ‘materialize:materialize’,
    ],‘web.browser’);

extracted from the package.js in the home package
Basically this would use different CSS files/libraries for each client type, instead of just trying to mix everything up and fix whatever comes out of it. Use case: “I want to use Ionic css files for my Cordova app, but bootstrap for my web app.”

  1. Rendering: in the Github repo I shared I am using iron:router to create the routes and help with rendering. It will still use Blaze to render, but I have full control of which routes should be created in each type of client. For the Cordova app in that repo I am using Meteoric so, you might see some more specific Meteoric helpers like @jdrorrer commented above

For the cordova app rendering happens like this:

  1. Iron:router finds the route ( in packages/home/cordova/routes/home.js)
  2. As the template name is not defined, Iron:router will render the template (in packages/home/cordova/home.html) with the same name as the route

And that’s basically what happens in the web as well, but because the files the web client receive are different, it will work only with the set of files it has at the moment:
route in packages/home/web/routes/home.js
template packages/home/web/home.html

If you don’t want to use this approach, you can still use the attribute Meteor.isCordova and using different routes, layouts and templates for your different clients, but that will not separate the CSS files/libraries you are sending to each client type. Also, remember that in the Cordova app all the assets will be downloaded for every hot code push, if I am not mistaken.

It will all come to the choices you make and how you will be able to maintain it. In my case, I suffered enough with puzzling my head around CSS tricks and hacks to make one and only CSS file, but it is not really worth the headache. In the end you might decide to go full native in the mobile part, and throw away all the cordova parts of your app in meteor. In that case, it is better to have it all separated from the beginning, just one of the benefits to illustrate.

I am not sure if I explained what you were asking, I am sorry . Let me know if it is not clear.

9 Likes

@danielfbm There cant be a better explanation than this. You nailed it. Thanks a lot for spending time to write this explanation. Makes sense to me now. I was thinking package.js just has the dependencies never expected that it will load based on platform. Now I understand how this whole thing work. Definitely a better approach than using isCordova.

Thanks

Thanks for sharing this awesome pattern.

Hi @jamgold ,have you ever tried the meteor-client-side package which mentioned in this github issue (which you attended to). If yes, does the result look promising ?

No, I didn’t try it.

Just if you want to know. I give it a try with my mobile app (Angular + Ionic) and very happy with the result :smiley:

1 Like