Au Revoir Official Symfony Mailing List

goodbye-symfony-mailing-listOn the 24th June 2015 the official Symfony 2 mailing list became read only.

The reason for doing this is to reduce the many different ways of finding support for Symfony related problems. This sounds a little nutty – Fabien / Sensio Labs want to reduce the ways of finding help for Symfony projects?

Prior to this we had the mailing list, IRC, the official forums, not to mention the plethora of blog posts out there in Google land. This variety is both good and bad.

Firstly, the mailing list could be a little spammy. As with any public internet forum, inevitably it attracted recruitment types advertising garbage – I’m sure with not a great deal of effort I could dig out that guy who constantly spammed his .net positions to the list for a while. I can’t imagine his success percentage was incredibly high at the end of that campaign.

Having moderated (admittedly small) public forums in the past myself, I can fully appreciate the amount of behind the scenes work that must have gone in to keeping the list as clean as it was.

Secondly, the list wasn’t entirely beginner friendly. As a casual reader you would have believed a certain level of Symfony knowledge were required just to take part. That wasn’t actually the case but few were the basic questions on that list.

And lastly in terms of my gripes was the delay in posting and getting a reply. I believe all the posts were moderated towards the end, so even seeing your post show on the list took a while. Then others had to see it. I think most were doing what I did – getting the daily digest. This then relied on someone reading the digest and using the clunky Google interface to post your reply, which would also throw in your email address for all to see :/

So Long Symfony Mailing List, Hello StackOverflow

The new guidelines are to use Stack Overflow as the preferred and recommended medium for all Symfony related support questions.

Personally, I don’t have a Stack Overflow account. I know, burn me at the stake, right? Maybe it’s time to sign up and start collecting gold stars 😉

We – the community – are also requested to use the tag of Symfony instead of Symfony2 when tagging our posts.

This is interesting to me. My experience of searching for help on Symfony issues over the past few years has been always that Symfony throws up stuff for Symfony 1.x, whereas Symfony2 / Symfony 2 kinda guaranteed a result for the current version.

Ultimately I think moving to Stack Overflow is a good move. From my own business-y perspective I don’t like the idea of putting all my eggs in someone else’s basket, but for a community project like Symfony it’s probably the better of the two options (doing it vs keeping it as it was).

Stack Overflow has the infrastructure, moderation team, and user base to make this better all-round for the community.

Lastly, IRC isn’t affected by this. I’m not the hugest fan of IRC (I find it too distracting having a full on chat room running in the background), but some of the very best support I have received has come from the IRC channel.

Symfony is such a fantastic product and community I’m really glad to see it progressing forwards with no sign of slowing down any time soon. I just couldn’t let a mailing list I’ve been a subscriber / contributor too for the last two or more years go without saying goodbye.

(image credit: Steve Johnson – US Mail)

How to fix “You’ve already agreed to the Apple Developer Agreement”

I want to share a fix I stumbled upon after wasting hours Googling and 40 minutes sat in the queue for Apple’s Developer Program telephone support.

If you are getting the error:

You’ve already agreed to the Apple Developer Agreement.
Continue to Member Center or sign out.

No matter what I did, trying to continue on always redirected me to that same page.

In my case, the solution to this was that my email account was not verified. I had signed up to the developer program with a different email to the one registered to my Apple ID.

To fix this, I had to go to the Apple ID management page, log in, and then re-verify / validate my Apple ID.

This isn’t a difficult problem to solve when you know the solution. The frustrating part is that the error message given (or lack of) made tracking down the root cause really difficult.

Hopefully this will save you some time / hair loss in the future.

Feel The Fear and Launch It Anyway

TweetHours
TweetHours
Getting TweetHours to v1.0.0 v1.0.6 (ahem) hasn’t been easy. But, it has been a lot of fun. Before going any further though, allow me to give you a little back story.

TweetHours is a mobile app that quickly allows Twitter users to find interesting hours they can get involved with. If today was a Monday but one of the hours you were interested in didn’t start till 6pm on a Thursday then TweetHours will set a reminder for you so you don’t forget and miss out. Then, each following Thursday at 6pm you would get a reminder, and so on.

Now, aside from the technical aspect of building such an app, what has been most interesting to me on this project is that I didn’t come up with this idea.

Mobile Apps + Ideas = Eject, Eject!

Ideas are usually the place I start, but I’ve been bitten by this approach so many times before that this time I wanted to do things a little differently.

The problem with ideas is that everyone has them. And everyone who has them thinks that their ideas are better than everyone elses. It’s one of the psychological phenomenon where we believe ourselves to be better than we really are at everything we do. Male drivers are a great example. We are equal parts Hamilton, Mansel, and Fangio as soon as we are behind the wheel of any motor.

Mostly my ideas are great for me, but they are not viable businesses. richard-bransonThey solve my problems, but it would seem that from prior experience, my problems are shared by so few people that they could never be sold at such a scale as to attract a multi million pound buyout and start my subsequent retirement to Necker Island with Richard.

And apps attract ideas like 4chan attracts controversy. Honestly, when asked by anyone what it is that you do for a living, never say apps. People assume this means you must be eager to hear their app idea – which is frequently so vast in scale it makes the modern Facebook infrastructure appear as a mere side project – and then balk at the potential price for building said app starting somewhere in the six figure range.

I’m used to working with clients who have their businesses dictate what needs to be done. I’m also used to building ideas I come up with. In the former case, once the software is built, I am often surplus to requirements and as such, no longer getting paid. Boo. But such is life. And in the latter, well, once I’ve built my ideas I usually find there is actually no market for the tool or app I’ve lovingly created, and so I resign to the fact that simply doing the work was worthwhile so long as I learned something.

This approach sucks, and now that I’m consciously aware of it’s short-comings, I am actively working to avoid it.

Not My Idea, Don’t Blame Me

As mentioned though, TweetHours came about as a result of an apparent need that had not yet been fulfilled. I know, right?! We’ve made it all the way to 2015 and somehow there are still some ideas left that haven’t been turned into a mobile app.

That is to say, a potential customer (not really, I mean my wife) asked me for it. And others in the same or similar fields reacted positively to the sound of it. This is not to say I asked other people’s wives, but rather others in the b2b sector that my wife was working in at the time.

How hard can it be to do a Mobile App Launch?

As mentioned, the app premise is simple. A list of hours, tick the ones you are interested in, receive alerts when they start. Now, I’ve covered already both on this blog and on the tutorials site that in actuality, and of course as any normal software developer will tell you, it wasn’t as simple as it first appeared.

The alerts were tricky. I really should have RTFM more as all the answers to that problem were already well documented.

Getting the imagery sorted was another task. I chose to outsource this and was super pleased with the results. Design isn’t my forte and you really can’t go to market in 2015 with an MS paint monstrosity. And besides, I don’t even use Windows anymore so that would have meant bringing out the gimp.

I built half the app and then took on a couple of pieces of client work. When I returned to finish it near enough a year later I hit so many upgrade issues I ended up starting the project again using the newer dependency versions and porting one file over at a time. Thank God for having the forethought to write a decent set of tests.

Mobile App Launch

Probably the strangest problem of all though was fear. Fear of releasing my app baby into a world full of 1* reviews and Internet crazy. I’d imagine this is somewhat akin to how my parents felt the first time I went out for a drive alone after passing my driving test.

if you build it and then release it, they probably won’t come, but ffs, do release it

So rather than release I set up a beta program. I’d never had to do that side of app dev before so it was new to me.

What a palava.

Google force you into using their Groups setup which is haphazard enough, and then offer no way of giving your app away for free to beta testers. Madness. EA and Dice pulled a similar stunt on me back when Battlefield 4 came out on the pc. Paying full price for a bug ridden crash to desktop simulator left me feeling uneasy about repeating that experience by asking my friends and family to pay to test my app for me.

Still, even though I barely used the beta function, it covered all the same setup required to put the app out into the real world, so it wasn’t a complete waste of time.

I made a bit of a boob in that I accidentally bumped my version in the ionic config to v1.0.0 when going into beta. Hence, the app that has gone live is v1.0.6. What a noob.

I could come up with a ton of reasons I hesitated on going live with TweetHours. I’ve covered just a few. But finally, on the 16th June 2015, I promoted my app to prod.

It felt good.

Now I can sit back, relax, and begin thinking up another great idea 🙂

Fixing Ionic Icon and Splashscreen Being Stuck as Default

I want to share my fix for the Ionic icon and splashscreen issue I just hit upon.

The issue was that no matter what I did, deploying my Ionic app to my Android phone always ended up with an empty / blank / white splash screen and the default ionic icon.

Ionic default icon on Android
Ionic default icon on Android

No matter what I seemed to do, that darned icon would not change.

Before I go further however, it is definitely worth pointing out an awesome feature in Ionic – ionic resources – a command line utility which will take an icon and a splash screen image (.png files, Photoshop .psd files, or Illustrator .ai files) and create you all the correctly sized icons / splash screen images AND update your config.xml file for you. Of course, you need to have the two images to begin with – and if you don’t, check out somewhere like fiverr or odesk (ahem, upwork?) to have these files made for you.

From my project root, I put my icon.png and splash.png into a folder called resources and then ran the ionic resources command. Checking my config.xml after this shows something along the lines of:


  *snip* 
  <platform name="android">
    <icon density="ldpi" src="resources/android/icon/drawable-ldpi-icon.png"/>
    <icon density="mdpi" src="resources/android/icon/drawable-mdpi-icon.png"/>
    <icon density="hdpi" src="resources/android/icon/drawable-hdpi-icon.png"/>
    <icon density="xhdpi" src="resources/android/icon/drawable-xhdpi-icon.png"/>
    <icon density="xxhdpi" src="resources/android/icon/drawable-xxhdpi-icon.png"/>
    <icon density="xxxhdpi" src="resources/android/icon/drawable-xxxhdpi-icon.png"/>
    <splash density="land-ldpi" src="resources/android/splash/drawable-land-ldpi-screen.png"/>
    <splash density="land-mdpi" src="resources/android/splash/drawable-land-mdpi-screen.png"/>
    <splash density="land-hdpi" src="resources/android/splash/drawable-land-hdpi-screen.png"/>
    <splash density="land-xhdpi" src="resources/android/splash/drawable-land-xhdpi-screen.png"/>
    <splash density="land-xxhdpi" src="resources/android/splash/drawable-land-xxhdpi-screen.png"/>
    <splash density="land-xxxhdpi" src="resources/android/splash/drawable-land-xxxhdpi-screen.png"/>
    <splash density="port-ldpi" src="resources/android/splash/drawable-port-ldpi-screen.png"/>
    <splash density="port-mdpi" src="resources/android/splash/drawable-port-mdpi-screen.png"/>
    <splash density="port-hdpi" src="resources/android/splash/drawable-port-hdpi-screen.png"/>
    <splash density="port-xhdpi" src="resources/android/splash/drawable-port-xhdpi-screen.png"/>
    <splash density="port-xxhdpi" src="resources/android/splash/drawable-port-xxhdpi-screen.png"/>
    <splash density="port-xxxhdpi" src="resources/android/splash/drawable-port-xxxhdpi-screen.png"/>
  </platform>
*snip*

And each of those resources/android… whatever paths is an actual file, resized properly.

Ok, enough of the set up. As mentioned, all this looked right yet still every time I did an ionic run android, the icon would be the default icon, and the splash screen was empty / blank / white.

There are a number of apparent fixes to this problem online, but none seemed to work for me. The best I could tell, my ionic icon was cached somehow, but as I was building on the Mac, I didn’t have any form of Android tooling available to open the project and clean the install.

Ionic Icon Woes : The Solution

Though it took a lot of trial and error (precious hours I will never get back!), the solution to this problem was pretty easy.

I had noticed the widget id value in my config.xml was a bit odd. For some reason (which I haven’t figured out still), when Ionic had initially generated my project skeleton, it had added some numbers on at the end.


<widget id="com.ionicframework.tweethours24237" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
*snip*

All it took to fix my Ionic icon problem was to remove these numbers.

As soon as I did that, my icon changed to what I expected:

Ionic icon on Android displaying as expected
Ionic icon on Android displaying as expected

Result!

Splash Screen Solution

Unfortunately, this didn’t fix my splash screen issue.

But, as above, I had correctly generated the splash screen so this was a little puzzling. Everything seemed ok – and I did see the 3 seconds of white / blank / emptiness that implied the splash screen code was actually executing, just missing the expected image.

To fix this I added the cordova-plugin-splashscreen as mentioned here.

Remember to update your package.json file whenever you add a plugin, so you or your colleagues aren’t caught out by a missing plugin at some point in the future.

# /packages.json
  *snip*
  "cordovaPlugins": [
    "cordova-plugin-splashscreen",
  *snip*

To make this work I also needed to update my .run method:

# /www/js/app.js
.run(function($rootScope, $ionicPlatform, $cordovaSplashscreen){
    setTimeout(function() {
        $cordovaSplashscreen.hide()
    }, 3000);

After that, my splash screen worked as expected:

Custom Ionic Splash Screen on Android
Custom Ionic Splash Screen on Android

And finally, I was done 🙂

For reference, here is my ionic info on the off chance this is useful:

Cordova CLI: 5.0.0 
Gulp version: CLI version 3.8.1 
Gulp local: Local version 3.8.11 
Ionic Version: 1.0.0 
Ionic CLI Version: 1.4.6-beta.0 
Ionic App Lib Version: 0.0.22 
ios-deploy version: Not installed 
ios-sim version: 3.1.1 
OS: Mac OS X Yosemite 
Node Version: v0.12.4 
Xcode version: Xcode 6.3.1 
Build version 6D1002

Also, my base image sizes are as follows:

icon.png – 768px x 768px
splash.png – 2208px x 2208px

I also used this splash template – just make sure you get everything between the green grid lines, or you will get unwanted clipping on smaller screen sizes.

Angular Services with Karma and Jasmine Testing

viva las vegas - karma and jasmine go testing

Karma and Jasmine. Not the names of two girls I partied with in Vegas. Something even better.

Yes, let me introduce you to Jasmine testing, a Behaviour Driven Development testing framework. And her friend Karma, a spectacular test runner.

Now we are done with the introductions, let’s get down to detail. Testing an AngularJS / Ionic application is well documented and baked right in to the framework. That’s cool. Sometimes though, if your project reaches any sort of complexity above the tutorials, you are going to need to test Angular services that themselves have dependencies.

I found the documentation on this topic sparse and confusing. So hopefully this example can help shed a little more light on the subject.

The Situation

How can we unit test an Angular service that has another service (dependency) injected in to it?

Let’s say we have two services that we care about. Service A, and Service B.

Service A receives Service B via dependency injection.

How do we mock this up so that it becomes testable?

How can we fake the data coming back from Service B in a way that will allow us to make the dependency behave exactly how we want it to?

And how can we do this in such a way that our individual tests don’t become huge and unwieldy?

Before Each, Throw Me a Curve Ball

Assuming we have a service definition that looks like this:

“` language-javascript
‘use strict’;

angular.module(‘core’)

.service(‘TweetHoursStorage’, [‘storage’, function($localStorage) {
this.clearAll = function() {
$localStorage.clearAll();
};

// * snip *

}])
;
“`

Let’s say we have a test file that looks something like this:

(note: don’t copy / paste this, as it doesn’t work)

// app/modules/core/tests/services/tweet-hours-storage.service.test.js
'use strict';

describe('Service: TweetHoursStorage', function () {

    // load the service's module
    beforeEach(module('core'));

    var TweetHoursStorage;

    beforeEach(inject(function(_TweetHoursStorage_) {
        TweetHoursStorage = _TweetHoursStorage_;
    }));

    afterEach(function () {
        TweetHoursStorage.clearAll();
    });

    describe('update method test', function() {

        it('Tests the update method returns false if not passed an object', inject(function() {
            expect(TweetHoursStorage.update('blah')).toBeFalsy();
        }));

    // * snip *

When we run this, we will see an error similar to the following:

TypeError: queueableFn.fn.call is not a function

Why?

Well, it turns out there are two problems with the above code. One is more visible than the other, but we can fix both at the same time.

This issue occurs before we even hit our first test proper. This is a set up issue.

Firstly, beforeEach(module('core')); – this is redundant. We can remove this and combine with the second beforeEach(); method call just below.

Let’s do that:

// app/modules/core/tests/services/tweet-hours-storage.service.test.js
'use strict';

describe('Service: TweetHoursStorage', function () {

    var TweetHoursStorage,
        mockLocalStorage;

    // load the service's module
    beforeEach(function() {
        module('core', function($provide) {
            mockLocalStorage = jasmine.createSpyObj('mockLocalStorage', ['clearAll']);
            $provide.value('storage', mockLocalStorage);
        });

        inject(function(_TweetHoursStorage_) {
            TweetHoursStorage = _TweetHoursStorage_;
        });
    });

    afterEach(function () {
        TweetHoursStorage.clearAll();
    });

    describe('a clear all method test', function() {
        // * snip *
    });

Now, lines 11-14 go about creating a Jasmine spy object which allows us to pretend mockLocalStorage is really a real life, working as expected instance of $localStorage, which the service we are testing actually depends on – the line from our service definition above for reference:

.service('TweetHoursStorage', ['storage', function($localStorage) {

Once we implement this our tests should actually start running. They may / likely won’t be passing at this stage, but that bit is easier (in my opinion at least) than getting this set up the first time you do it.

Faking an API Call when Jasmine Testing

What about if we have a remote API that we need to get some data from?

How can we mock that out in a test?

 // app/modules/core/tests/services/selected-hours-to-notification-data-transformer.test.js

'use strict';

describe('Service: SelectedHoursToNotificationDataTransformer', function () {

    var SelectedHoursToNotificationDataTransformer,
        mockApiLookup,
        mockDateProcessing;


    // load the service's module
    beforeEach(function (){
        module('core', function($provide) {
            mockApiLookup = jasmine.createSpyObj('mockApiLookup', ['getAll']);
            mockApiLookup.getAll.and.callFake(function(){ return getFakeDataSet(); });
            mockDateProcessing = jasmine.createSpyObj('mockDateProcessing', ['process', 'nextStartDate']);

            $provide.value('ApiLookup', mockApiLookup);
            $provide.value('DateProcessing', mockDateProcessing);
        });

        inject(function(_SelectedHoursToNotificationDataTransformer_) {
            SelectedHoursToNotificationDataTransformer = _SelectedHoursToNotificationDataTransformer_;
        });
    });


    /**
     * This is the pretend result set that might be returned by a call to the API GET end point
     * @returns {*[]}
     */
    var start = moment().subtract(6, 'days');
    var getFakeDataSet = function () {
        return [
            { id: 4, title: 'event4', start: start, duration: 2 },
            { id: 5, title: 'event5', start: start, duration: 1 },
            { id: 8, title: 'event8', start: start, duration: 1 },
            { id: 99, title: 'event99', start: start, duration: 1 }
        ];
    };

This code may not be the most elegant or ninja-level JavaScript ever written, but by jingo it shows a few things that I would have found helpful a few months hence.

Line 15 – we are doing much the same as in the previous example.

This time though, we want to replace the real outcome with one we can control. Testing a remote API during our testing is not a good thing for many reasons including the API likely not being entirely tolerant to us repeatedly sending in the same fake data every time we run our Jasmine test suite. Also, speed, predictability, and many other factors.

First we declare the method as being available on our mock – line 15.

Then we tell Jasmine what we want anything that calls that method to get instead:

mockApiLookup.getAll.and.callFake(function(){ return getFakeDataSet(); });

Further down, starting on line 34, we do something that feels a little different to you if you’re used to working with PHP. We declare var getFakeDataSet which contains a yet to be executed function.

When that function is executed, it will return an array of JSON, representing the data we would anticipate receiving back from our remote API in the real world.

That data set will be entirely dependent on your project’s circumstances. My data is just for show.

So, back to line 16, where we return that data. The reason for me going into detail there is that it is entirely possible – and correct in some circumstances – that you would want to return the unresolved function.

mockApiLookup.getAll.and.callFake(function(){ return getFakeDataSet; });
// vs
mockApiLookup.getAll.and.callFake(function(){ return getFakeDataSet(); });

Just be careful of this one. It’s not specific to Jasmine testing, it’s a JavaScript thing in general, but it can be confusing.

I digress, but essentially the first line will return the function, unresolved until you call it by adding the () to the end of the variable holding that function. Confusing? A little. Tricky to spot for sure, when you inevitably make that mistake whilst coding.

Back to the example – line 19 and 20 show how to add multiple mocked dependencies should your service require it.

Jasmine Testing Cheat Sheet

The manual is pretty good on this topic.

But I also found this cheat sheet very helpful.