Working with the AppInstalled and AppUninstalling events in SharePoint Apps

Some quick notes about remote app events receivers I have picked up when working with apps in SharePoint Online.

When is AppInstalled triggered?

When you add an app in SharePoint Online it shows in Site Content as being added. Installing an app in itself is nothing special, so you might expect the AppInstalled event to trigger pretty much instantly, but not so. There appears to be some kind of queue system employed here. Usually the queue is short and the app installs quickly, but sometimes I have had to wait for several minutes before the installation started.

AppInstalled can be triggered several times

If your app needs to deploy a number of items to the host web it may take a while to complete the installation. Other times your installer could be slow because the server is under heavy load or you are debugging. Even if your app is still up and running and performing the installation and thus communicating with SharePoint, if it does not complete the installation within a certain timespan SharePoint will think that the installation failed and try again. SharePoint retries up to four times by design. How long is this timespan you ask? It varies. Usually I see retries in about 15-30 seconds in my logs. But it is unpredictable and sometimes it seems to retry in as short as a few seconds. As an example, here is an extract from a log session in one of my apps:

There does not seem to be a way to tell SharePoint that your installation will take time and ask it to be patient, so you need to handle these retries in some way. You should start by building a robust installer that can handle situations when elements are already installed. This is always a good thing, but not enough for the above stated problem, because there could be a race condition: how would you know if another thread has not just begun installing the element your thread wants to install?

Another way could be to let your server threads communicate with each other, as suggested by AkhileshN. If the first call is still running, subsequent calls could halt or return directly. I tried this and it works, but you run into another problem. The first call will either succeed or fail. The subsequent calls can not simply return success, since SharePoint will think that install succeeded even if the first call later returns failure. And how do you know that SharePoint is still listening to the first call? This can quickly become hard to manage. Myself I decided not to use this approach.
My solution was to only perform a few basic tasks in the AppInstalled event. I mostly put validation checks there to make sure that it is ok to install the app on the specific host web. Then the user will have to open up the app and complete the installation from there. Sometimes I do provision a few elements in the AppInstalled event though, since this will improve the user experience. Just make sure you know what you are doing and test thoroughly so you understand what is happening.

AppUninstalling event does not trigger?

If you’ve tried to listen to the AppUninstalling event in your SharePoint app but never gotten it to fire you’re probably not alone. The AppInstalled event is triggered when you add the app in Site Content, so you’d expect AppUninstalling to be triggered when the app is removed from there. Not so, and here is why: When you remove an app it is placed in the Recycle Bin, so you have to remove it from there too. But AppUninstalling will still not trigger! If you go to Site Settings > Site Collection Administration > Recycle Bin you will see that you actually have two recycle bins. “End user Recycle Bin items” is the one you previously emptied. But there is also “Deleted from end user Recycle Bin”. Open this and you’ll find your “deleted” apps here. Empty this recycle bin too, and now the AppUninstalling event should trigger!

In SharePoint you have two recycle bins

Trying to prepare myself for delivering a talk

I’m starting to get slightly nervous now. Decided that I was going to hold a talk at Geek Girl Meetup Oresund and now I have to do it. Since I seems to be unable to hold lectures and lessons without knowing monumentally more than what I’m actually going to talk about, I have quite a lot of work to do.

I have named the talk “How to go from idea to webapp in 15 minutes”, or something like it, and it is about how to set up grunt, SASS, and JavaScript structure in under 20 minutes. And also, the will be pictures of kittens, probably.

I hope someone shows up and I hope I do great and that the kittens are cute.

A mixin for everything and all on retina screens

Problem: You have styles you want for regular screens, and styles you want for retina screens. Mostly images with different sizes, but sometimes font sizes or widths.

I was faced with this recently and got annoyed at the amount of repeating I had to do. First it’s the fuck ugly syntax of the media queries, then it’s the browser compatibility, and then I have to repeat it all over my Sass document for different screen sizes and everything else that can change

Queue trying to build a handy mixin for it all

This is what I came up with:

It’s a mixin that takes three arguments. The first ($maxwidth) handles the actual media query and when the rule should be applied. The second ($attribute) handles what attribute is affected. Here you put font-size, background-image or something like it. And the last one handles the value of the attribute, so what size should the font be, or what background image do you want.

It is quite handy! Just use it whenever you want something styled for a retina screen. Since you can reuse it for all attributes and screen sizes, it shourtens the code you have to write quite a lot.

Using it like this:

Will compile to this:

I used it for my app Counting Down and as far as I know it doesn’t work in LESS

Generate Guid easily with ReSharper

Sometimes the smallest things make our life a lot easier.

Like this tiny – but great – feature from ReSharper for generating Guids. Simply write nguid in any file, no matter the type, and press tab.

guid1

ReSharper will then generate a new Guid for you, and even let you choose a format.

guid2

Just one of those small, nice things making ReSharper a delight to work with. =)

(Thanks for the tip Villy)