Different interfaces based on devices?

@davethe0nly because it wouldn’t work. Whenever I called it in startup, my subscriptions would be empty. Worthwhile finding out why, but for now I am just happy that this works.

This is clearly a hack, and as I found out at the DevShop yesterday not 100% supported. I am about to file a github issue on the meteor project about this.

Microservices in https://github.com/meteorhacks/cluster provide another approach to this, and IMO would be a better long term strategy for greater scale when it makes sense to break the backend up into separate apps.

2 Likes

Juggll is a Meteor app. I know they render different templates for different devices. They run a Meteor app where everything is shared except template files. I’m not 100% sure how they’re doing it. I guess it’s somewhere around the router.

@davethe0nly I have been looking into this some more. As per meteor api docs

On a server, the function will run as soon as the server process is finished starting. On a client, the function will run as soon as the DOM is ready.

The startup callbacks are called in the same order as the calls to Meteor.startup were made.

On a client, startup callbacks from packages will be called first, followed by templates from your .html files, followed by your application code.

so all bootstrap actions that depend on the connection have already run (my explanation). The best way to actually get this done was if it was possible to set

__meteor_runtime_config__.DDP_DEFAULT_CONNECTION_URL

to the desired value, but I have not yet been able to figure out how to do that.

Just filed an issue with the meteor project

2 Likes

Thanks for fighting this out.

You approach is very interesting and i’m watching this closely.

This is exactly my question here:

Please let us know what you figure out. Thanks!

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.