[gnome-software/wip/hughsie/GsAppList2: 1/4] Use GsAppList in more places



commit 7356c181c1f380772e8cae714c3b95394d136904
Author: Richard Hughes <richard hughsie com>
Date:   Wed May 18 17:36:21 2016 +0100

    Use GsAppList in more places
    
    This allows us to add a hashed index in the future for permance reasons, and to
    include a mutex to protect access from multiple threads. Also, by converting to
    a GPtrArray we can fix several memory leak issues.
    
    Plugins can trivially port using s/GList **/GsAppList */g

 src/gs-app-list.c                              |  207 +++++++++---
 src/gs-app-list.h                              |   29 +-
 src/gs-category.c                              |   85 ++---
 src/gs-category.h                              |   15 +-
 src/gs-cmd.c                                   |   65 ++--
 src/gs-plugin-loader-sync.c                    |   25 +-
 src/gs-plugin-loader-sync.h                    |   18 +-
 src/gs-plugin-loader.c                         |  450 ++++++++++++------------
 src/gs-plugin-loader.h                         |   28 +-
 src/gs-plugin.h                                |   32 +-
 src/gs-self-test.c                             |   72 ++--
 src/gs-shell-category.c                        |   35 +-
 src/gs-shell-extras.c                          |   16 +-
 src/gs-shell-installed.c                       |   16 +-
 src/gs-shell-moderate.c                        |    6 +-
 src/gs-shell-overview.c                        |   53 ++--
 src/gs-shell-search-provider.c                 |   14 +-
 src/gs-shell-search.c                          |    6 +-
 src/gs-shell-updates.c                         |   22 +-
 src/gs-sources-dialog.c                        |    6 +-
 src/gs-update-dialog.c                         |    8 +-
 src/gs-update-list.c                           |    7 +-
 src/gs-update-list.h                           |    3 +-
 src/gs-update-monitor.c                        |   30 +-
 src/plugins/gs-plugin-appstream.c              |   39 +-
 src/plugins/gs-plugin-dpkg.c                   |    2 +-
 src/plugins/gs-plugin-dummy.c                  |   10 +-
 src/plugins/gs-plugin-fedora-distro-upgrades.c |    2 +-
 src/plugins/gs-plugin-flatpak.c                |   10 +-
 src/plugins/gs-plugin-fwupd.c                  |    8 +-
 src/plugins/gs-plugin-limba.c                  |    6 +-
 src/plugins/gs-plugin-menu-spec-categories.c   |   12 +-
 src/plugins/gs-plugin-odrs.c                   |    2 +-
 src/plugins/gs-plugin-ostree.c                 |    2 +-
 src/plugins/gs-plugin-packagekit-history.c     |   39 +-
 src/plugins/gs-plugin-packagekit-local.c       |    2 +-
 src/plugins/gs-plugin-packagekit-offline.c     |    2 +-
 src/plugins/gs-plugin-packagekit-refine.c      |  127 ++++----
 src/plugins/gs-plugin-packagekit.c             |   16 +-
 src/plugins/gs-plugin-shell-extensions.c       |    4 +-
 src/plugins/gs-plugin-steam.c                  |    2 +-
 src/plugins/gs-plugin-systemd-updates.c        |   10 +-
 src/plugins/gs-plugin-xdg-app.c                |   10 +-
 src/plugins/packagekit-common.c                |    2 +-
 src/plugins/packagekit-common.h                |    2 +-
 45 files changed, 851 insertions(+), 706 deletions(-)
---
diff --git a/src/gs-app-list.c b/src/gs-app-list.c
index 19e4cd5..f8d97e4 100644
--- a/src/gs-app-list.c
+++ b/src/gs-app-list.c
@@ -32,61 +32,121 @@
 
 #include "gs-app-list.h"
 
+struct _GsAppList
+{
+       GObject                  parent_instance;
+       GPtrArray               *array;
+};
+
+G_DEFINE_TYPE (GsAppList, gs_app_list, G_TYPE_OBJECT)
+
 /**
  * gs_app_list_add:
- * @list: A pointer to a #GsAppList
+ * @list: A #GsAppList
  * @app: A #GsApp
  *
  * Adds an application to the list, adding a reference.
  **/
 void
-gs_app_list_add (GsAppList **list, GsApp *app)
+gs_app_list_add (GsAppList *list, GsApp *app)
 {
-       g_return_if_fail (list != NULL);
+       g_return_if_fail (GS_IS_APP_LIST (list));
        g_return_if_fail (GS_IS_APP (app));
-       *list = g_list_prepend (*list, g_object_ref (app));
+       g_ptr_array_add (list->array, g_object_ref (app));
 }
 
 /**
- * gs_app_list_free:
+ * gs_app_list_index:
  * @list: A #GsAppList
+ * @idx: An index into the list
  *
- * Frees the application list.
+ * Gets an application at a specific position in the list.
+ *
+ * Returns: (transfer none): a #GsApp, or %NULL if invalid
  **/
-void
-gs_app_list_free (GsAppList *list)
+GsApp *
+gs_app_list_index (GsAppList *list, guint idx)
+{
+       return GS_APP (g_ptr_array_index (list->array, idx));
+}
+
+/**
+ * gs_app_list_length:
+ * @list: A #GsAppList
+ *
+ * Gets the length of the application list.
+ *
+ * Returns: Integer
+ **/
+guint
+gs_app_list_length (GsAppList *list)
 {
-       g_list_free_full (list, (GDestroyNotify) g_object_unref);
+       g_return_val_if_fail (GS_IS_APP_LIST (list), 0);
+       return list->array->len;
 }
 
 /**
  * gs_app_list_filter:
- * @list: A pointer to a #GsAppList
+ * @list: A #GsAppList
  * @func: A #GsAppListFilterFunc
  * @user_data: the user pointer to pass to @func
  *
  * If func() returns TRUE for the GsApp, then the app is kept.
  **/
 void
-gs_app_list_filter (GsAppList **list, GsAppListFilterFunc func, gpointer user_data)
+gs_app_list_filter (GsAppList *list, GsAppListFilterFunc func, gpointer user_data)
 {
-       GsAppList *l;
-       GsAppList *new = NULL;
+       guint i;
        GsApp *app;
+       g_autoptr(GsAppList) old = NULL;
 
-       g_return_if_fail (list != NULL);
+       g_return_if_fail (GS_IS_APP_LIST (list));
        g_return_if_fail (func != NULL);
 
+       /* deep copy to a temp list and clear the current one */
+       old = gs_app_list_copy (list);
+       g_ptr_array_set_size (list->array, 0);
+
        /* see if any of the apps need filtering */
-       for (l = *list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       for (i = 0; i < old->array->len; i++) {
+               app = gs_app_list_index (old, i);
                if (func (app, user_data))
-                       gs_app_list_add (&new, app);
+                       gs_app_list_add (list, app);
        }
+}
+
+typedef struct {
+       GsAppListSortFunc        func;
+       gpointer                 user_data;
+} GsAppListSortHelper;
 
-       /* replace the list */
-       gs_app_list_free (*list);
-       *list = new;
+/**
+ * gs_app_list_sort_cb:
+ **/
+static gint
+gs_app_list_sort_cb (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+       GsApp *app1 = GS_APP (*(GsApp **) a);
+       GsApp *app2 = GS_APP (*(GsApp **) b);
+       GsAppListSortHelper *helper = (GsAppListSortHelper *) user_data;
+       return helper->func (app1, app2, user_data);
+}
+
+/**
+ * gs_app_list_sort:
+ * @list: A #GsAppList
+ * @func: A #GCompareFunc
+ *
+ * Sorts the application list.
+ **/
+void
+gs_app_list_sort (GsAppList *list, GsAppListSortFunc func, gpointer user_data)
+{
+       GsAppListSortHelper helper;
+       g_return_if_fail (GS_IS_APP_LIST (list));
+       helper.func = func;
+       helper.user_data = user_data;
+       g_ptr_array_sort_with_data (list->array, gs_app_list_sort_cb, &helper);
 }
 
 /**
@@ -95,49 +155,51 @@ gs_app_list_filter (GsAppList **list, GsAppListFilterFunc func, gpointer user_da
 static gint
 gs_app_list_randomize_cb (gconstpointer a, gconstpointer b, gpointer user_data)
 {
+       GsApp *app1 = GS_APP (*(GsApp **) a);
+       GsApp *app2 = GS_APP (*(GsApp **) b);
        const gchar *k1;
        const gchar *k2;
        g_autofree gchar *key = NULL;
 
        key = g_strdup_printf ("Plugin::sort-key[%p]", user_data);
-       k1 = gs_app_get_metadata_item ((GsApp *) a, key);
-       k2 = gs_app_get_metadata_item ((GsApp *) b, key);
+       k1 = gs_app_get_metadata_item (app1, key);
+       k2 = gs_app_get_metadata_item (app2, key);
        return g_strcmp0 (k1, k2);
 }
 
 /**
  * gs_app_list_randomize:
- * @list: A pointer to a #GsAppList
+ * @list: A #GsAppList
  *
  * Randomize the order of the list, but don't change the order until
  * the next day.
  **/
 void
-gs_app_list_randomize (GsAppList **list)
+gs_app_list_randomize (GsAppList *list)
 {
-       GsAppList *l;
+       guint i;
        GRand *rand;
        GsApp *app;
        gchar sort_key[] = { '\0', '\0', '\0', '\0' };
        g_autoptr(GDateTime) date = NULL;
        g_autofree gchar *key = NULL;
 
-       g_return_if_fail (list != NULL);
+       g_return_if_fail (GS_IS_APP_LIST (list));
 
        key = g_strdup_printf ("Plugin::sort-key[%p]", list);
        rand = g_rand_new ();
        date = g_date_time_new_now_utc ();
        g_rand_set_seed (rand, g_date_time_get_day_of_year (date));
-       for (l = *list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               app = gs_app_list_index (list, i);
                sort_key[0] = g_rand_int_range (rand, (gint32) 'A', (gint32) 'Z');
                sort_key[1] = g_rand_int_range (rand, (gint32) 'A', (gint32) 'Z');
                sort_key[2] = g_rand_int_range (rand, (gint32) 'A', (gint32) 'Z');
                gs_app_set_metadata (app, key, sort_key);
        }
-       *list = g_list_sort_with_data (*list, gs_app_list_randomize_cb, list);
-       for (l = *list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       g_ptr_array_sort_with_data (list->array, gs_app_list_randomize_cb, list);
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               app = gs_app_list_index (list, i);
                gs_app_set_metadata (app, key, NULL);
        }
        g_rand_free (rand);
@@ -145,44 +207,44 @@ gs_app_list_randomize (GsAppList **list)
 
 /**
  * gs_app_list_filter_duplicates:
- * @list: A pointer to a #GsAppList
+ * @list: A #GsAppList
  *
  * Filter any duplicate applications from the list.
  **/
 void
-gs_app_list_filter_duplicates (GsAppList **list)
+gs_app_list_filter_duplicates (GsAppList *list)
 {
-       GsAppList *l;
-       GsAppList *new = NULL;
+       guint i;
        GsApp *app;
        GsApp *found;
        const gchar *id;
        g_autoptr(GHashTable) hash = NULL;
+       g_autoptr(GsAppList) old = NULL;
+
+       g_return_if_fail (GS_IS_APP_LIST (list));
 
-       g_return_if_fail (list != NULL);
+       /* deep copy to a temp list and clear the current one */
+       old = gs_app_list_copy (list);
+       g_ptr_array_set_size (list->array, 0);
 
        /* create a new list with just the unique items */
        hash = g_hash_table_new (g_str_hash, g_str_equal);
-       for (l = *list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       for (i = 0; i < old->array->len; i++) {
+               app = gs_app_list_index (old, i);
                id = gs_app_get_id (app);
                if (id == NULL) {
-                       gs_app_list_add (&new, app);
+                       gs_app_list_add (list, app);
                        continue;
                }
                found = g_hash_table_lookup (hash, id);
                if (found == NULL) {
-                       gs_app_list_add (&new, app);
+                       gs_app_list_add (list, app);
                        g_hash_table_insert (hash, (gpointer) id,
                                             GUINT_TO_POINTER (1));
                        continue;
                }
                g_debug ("ignoring duplicate %s", id);
        }
-
-       /* replace the list */
-       gs_app_list_free (*list);
-       *list = new;
 }
 
 /**
@@ -196,7 +258,62 @@ gs_app_list_filter_duplicates (GsAppList **list)
 GsAppList *
 gs_app_list_copy (GsAppList *list)
 {
-       return g_list_copy_deep (list, (GCopyFunc) g_object_ref, NULL);
+       GsAppList *new;
+       guint i;
+
+       g_return_val_if_fail (GS_IS_APP_LIST (list), NULL);
+
+       new = gs_app_list_new ();
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               GsApp *app = gs_app_list_index (list, i);
+               gs_app_list_add (new, app);
+       }
+       return new;
+}
+
+/**
+ * gs_app_list_finalize:
+ **/
+static void
+gs_app_list_finalize (GObject *object)
+{
+       GsAppList *list = GS_APP_LIST (object);
+       g_ptr_array_unref (list->array);
+       G_OBJECT_CLASS (gs_app_list_parent_class)->finalize (object);
+}
+
+/**
+ * gs_app_list_class_init:
+ **/
+static void
+gs_app_list_class_init (GsAppListClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       object_class->finalize = gs_app_list_finalize;
+}
+
+/**
+ * gs_app_list_init:
+ **/
+static void
+gs_app_list_init (GsAppList *list)
+{
+       list->array = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
+}
+
+/**
+ * gs_app_list_new:
+ *
+ * Creates a new list.
+ *
+ * Returns: A newly allocated #GsAppList
+ **/
+GsAppList *
+gs_app_list_new (void)
+{
+       GsApp *app;
+       app = g_object_new (GS_TYPE_APP_LIST, NULL);
+       return GS_APP_LIST (app);
 }
 
 /* vim: set noexpandtab: */
diff --git a/src/gs-app-list.h b/src/gs-app-list.h
index ae4afca..ce90f9c 100644
--- a/src/gs-app-list.h
+++ b/src/gs-app-list.h
@@ -22,26 +22,37 @@
 #ifndef __GS_APP_LIST_H
 #define __GS_APP_LIST_H
 
+#include <glib-object.h>
+
 #include "gs-app.h"
 
 G_BEGIN_DECLS
 
-typedef GList GsAppList;
+#define GS_TYPE_APP_LIST (gs_app_list_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsAppList, gs_app_list, GS, APP_LIST, GObject)
 
 typedef gboolean (*GsAppListFilterFunc)                (GsApp          *app,
                                                 gpointer        user_data);
+typedef gboolean (*GsAppListSortFunc)          (GsApp          *app1,
+                                                GsApp          *app2,
+                                                gpointer        user_data);
 
-void            gs_app_list_add                (GsAppList      **list,
+void            gs_app_list_add                (GsAppList      *list,
                                                 GsApp          *app);
-void            gs_app_list_free               (GsAppList      *list);
-GList          *gs_app_list_copy               (GsAppList      *list);
-void            gs_app_list_filter             (GsAppList      **list,
+GsApp          *gs_app_list_index              (GsAppList      *list,
+                                                guint           idx);
+guint           gs_app_list_length             (GsAppList      *list);
+GsAppList      *gs_app_list_new                (void);
+GsAppList      *gs_app_list_copy               (GsAppList      *list);
+void            gs_app_list_filter             (GsAppList      *list,
                                                 GsAppListFilterFunc func,
                                                 gpointer        user_data);
-void            gs_app_list_filter_duplicates  (GsAppList      **list);
-void            gs_app_list_randomize          (GsAppList      **list);
-
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(GsAppList, gs_app_list_free)
+void            gs_app_list_sort               (GsAppList      *list,
+                                                GsAppListSortFunc func,
+                                                gpointer        user_data);
+void            gs_app_list_filter_duplicates  (GsAppList      *list);
+void            gs_app_list_randomize          (GsAppList      *list);
 
 G_END_DECLS
 
diff --git a/src/gs-category.c b/src/gs-category.c
index 68c8d94..c6447c4 100644
--- a/src/gs-category.c
+++ b/src/gs-category.c
@@ -42,7 +42,7 @@ struct _GsCategory
        gchar           *name;
        GsCategory      *parent;
        guint            size;
-       GList           *subcategories;
+       GPtrArray       *children;
 };
 
 G_DEFINE_TYPE (GsCategory, gs_category, G_TYPE_OBJECT)
@@ -130,19 +130,17 @@ gs_category_get_name (GsCategory *category)
  *
  * Find a child category with a specific ID.
  *
- * Returns: the #GsCategory, or %NULL
+ * Returns: (transfer none): the #GsCategory, or %NULL
  **/
 GsCategory *
 gs_category_find_child (GsCategory *category, const gchar *id)
 {
-       GList *l;
        GsCategory *tmp;
+       guint i;
 
        /* find the subcategory */
-       if (category->subcategories == NULL)
-               return NULL;
-       for (l = category->subcategories; l != NULL; l = l->next) {
-               tmp = GS_CATEGORY (l->data);
+       for (i = 0; i < category->children->len; i++) {
+               tmp = GS_CATEGORY (g_ptr_array_index (category->children, i));
                if (g_strcmp0 (id, gs_category_get_id (tmp)) == 0)
                        return tmp;
        }
@@ -165,33 +163,40 @@ gs_category_get_parent (GsCategory *category)
 }
 
 /**
- * gs_category_get_subcategories:
+ * gs_category_get_children:
  * @category: a #GsCategory
  *
- * Gets the list if subcategories for a category.
+ * Gets the list if children for a category.
  *
- * Return value: (element-type GsApp) (transfer container): A list of subcategories
+ * Return value: (element-type GsApp) (transfer none): A list of children
  **/
-GList *
-gs_category_get_subcategories (GsCategory *category)
+GPtrArray *
+gs_category_get_children (GsCategory *category)
 {
        g_return_val_if_fail (GS_IS_CATEGORY (category), NULL);
-       return g_list_copy (category->subcategories);
+       return category->children;
 }
 
 /**
- * gs_category_add_subcategory:
+ * gs_category_add_child:
  * @category: a #GsCategory
  * @subcategory: a #GsCategory
  *
  * Adds a child category to a parent category.
  **/
 void
-gs_category_add_subcategory (GsCategory *category, GsCategory *subcategory)
+gs_category_add_child (GsCategory *category, GsCategory *subcategory)
 {
        g_return_if_fail (GS_IS_CATEGORY (category));
-       category->subcategories = g_list_prepend (category->subcategories,
-                                                 g_object_ref (subcategory));
+       g_return_if_fail (GS_IS_CATEGORY (subcategory));
+
+       /* FIXME: do we need this? */
+       subcategory->parent = category;
+       g_object_add_weak_pointer (G_OBJECT (subcategory->parent),
+                                  (gpointer *) &subcategory->parent);
+
+       g_ptr_array_add (category->children,
+                        g_object_ref (subcategory));
 }
 
 /**
@@ -213,47 +218,29 @@ gs_category_get_sort_key (GsCategory *category)
 }
 
 /**
- * gs_category_sort_subcategories_cb:
+ * gs_category_sort_children_cb:
  **/
 static gint
-gs_category_sort_subcategories_cb (gconstpointer a, gconstpointer b)
+gs_category_sort_children_cb (gconstpointer a, gconstpointer b)
 {
-       GsCategory *ca = GS_CATEGORY ((gpointer) a);
-       GsCategory *cb = GS_CATEGORY ((gpointer) b);
+       GsCategory *ca = GS_CATEGORY (*(GsCategory **) a);
+       GsCategory *cb = GS_CATEGORY (*(GsCategory **) b);
        g_autofree gchar *id_a = gs_category_get_sort_key (ca);
        g_autofree gchar *id_b = gs_category_get_sort_key (cb);
        return g_strcmp0 (id_a, id_b);
 }
 
 /**
- * gs_category_sort_subcategories:
+ * gs_category_sort_children:
  * @category: a #GsCategory
  *
- * Sorts the list of subcategories.
+ * Sorts the list of children.
  **/
 void
-gs_category_sort_subcategories (GsCategory *category)
-{
-       /* nothing here */
-       if (category->subcategories == NULL)
-               return;
-
-       /* actually sort the data */
-       category->subcategories = g_list_sort (category->subcategories,
-                                          gs_category_sort_subcategories_cb);
-}
-
-static void
-gs_category_dispose (GObject *object)
+gs_category_sort_children (GsCategory *category)
 {
-       GsCategory *category = GS_CATEGORY (object);
-
-       if (category->subcategories != NULL) {
-               g_list_free_full (category->subcategories, g_object_unref);
-               category->subcategories = NULL;
-       }
-
-       G_OBJECT_CLASS (gs_category_parent_class)->dispose (object);
+       g_ptr_array_sort (category->children,
+                         gs_category_sort_children_cb);
 }
 
 static void
@@ -264,6 +251,7 @@ gs_category_finalize (GObject *object)
        if (category->parent != NULL)
                g_object_remove_weak_pointer (G_OBJECT (category->parent),
                                              (gpointer *) &category->parent);
+       g_ptr_array_unref (category->children);
        g_free (category->id);
        g_free (category->name);
 
@@ -274,18 +262,17 @@ static void
 gs_category_class_init (GsCategoryClass *klass)
 {
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
-       object_class->dispose = gs_category_dispose;
        object_class->finalize = gs_category_finalize;
 }
 
 static void
 gs_category_init (GsCategory *category)
 {
+       category->children = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
 }
 
 /**
  * gs_category_new:
- * @parent: a #GsCategory
  * @id: an ID, e.g. "all"
  * @name: a localised name
  *
@@ -294,7 +281,7 @@ gs_category_init (GsCategory *category)
  * Returns: the new #GsCategory
  **/
 GsCategory *
-gs_category_new (GsCategory *parent, const gchar *id, const gchar *name)
+gs_category_new (const gchar *id, const gchar *name)
 {
        GsCategory *category;
 
@@ -313,10 +300,6 @@ gs_category_new (GsCategory *parent, const gchar *id, const gchar *name)
        }
 
        category = g_object_new (GS_TYPE_CATEGORY, NULL);
-       category->parent = parent;
-       if (category->parent != NULL)
-               g_object_add_weak_pointer (G_OBJECT (category->parent),
-                                          (gpointer *) &category->parent);
        category->id = g_strdup (id);
        category->name = g_strdup (name);
        return GS_CATEGORY (category);
diff --git a/src/gs-category.h b/src/gs-category.h
index 9ac2ff3..3e3b65d 100644
--- a/src/gs-category.h
+++ b/src/gs-category.h
@@ -31,18 +31,19 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (GsCategory, gs_category, GS, CATEGORY, GObject)
 
-GsCategory     *gs_category_new                (GsCategory     *parent,
-                                                const gchar    *id,
+GsCategory     *gs_category_new                (const gchar    *id,
                                                 const gchar    *name);
 const gchar    *gs_category_get_id             (GsCategory     *category);
-GsCategory      *gs_category_get_parent                (GsCategory     *category);
+GsCategory     *gs_category_get_parent         (GsCategory     *category);
+const gchar    *gs_category_get_name           (GsCategory     *category);
+
 GsCategory     *gs_category_find_child         (GsCategory     *category,
                                                 const gchar    *id);
-const gchar    *gs_category_get_name           (GsCategory     *category);
-void            gs_category_sort_subcategories (GsCategory     *category);
-GList          *gs_category_get_subcategories  (GsCategory     *category);
-void            gs_category_add_subcategory    (GsCategory     *category,
+void            gs_category_sort_children      (GsCategory     *category);
+GPtrArray      *gs_category_get_children       (GsCategory     *category);
+void            gs_category_add_child          (GsCategory     *category,
                                                 GsCategory     *subcategory);
+
 guint           gs_category_get_size           (GsCategory     *category);
 void            gs_category_set_size           (GsCategory     *category,
                                                 guint           size);
diff --git a/src/gs-cmd.c b/src/gs-cmd.c
index 54905f5..5acba2d 100644
--- a/src/gs-cmd.c
+++ b/src/gs-cmd.c
@@ -34,17 +34,17 @@
  * gs_cmd_show_results_apps:
  **/
 static void
-gs_cmd_show_results_apps (GList *list)
+gs_cmd_show_results_apps (GsAppList *list)
 {
-       GList *l;
        GPtrArray *related;
        GsApp *app;
        GsApp *app_rel;
        guint i;
+       guint j;
 
-       for (l = list; l != NULL; l = l->next) {
+       for (j = 0; j < gs_app_list_length (list); j++) {
                g_autofree gchar *tmp = NULL;
-               app = GS_APP (l->data);
+               app = gs_app_list_index (list, j);
                tmp = gs_app_to_string (app);
                g_print ("%s\n", tmp);
                related = gs_app_get_related (app);
@@ -76,16 +76,16 @@ gs_cmd_pad_spaces (const gchar *text, guint length)
  * gs_cmd_show_results_categories:
  **/
 static void
-gs_cmd_show_results_categories (GList *list)
+gs_cmd_show_results_categories (GPtrArray *list)
 {
-       GList *l;
-       GList *subcats;
+       GPtrArray *subcats;
        GsCategory *cat;
        GsCategory *parent;
+       guint i;
 
-       for (l = list; l != NULL; l = l->next) {
+       for (i = 0; i < list->len; i++) {
                g_autofree gchar *tmp = NULL;
-               cat = GS_CATEGORY (l->data);
+               cat = GS_CATEGORY (g_ptr_array_index (list, i));
                parent = gs_category_get_parent (cat);
                if (parent != NULL){
                        g_autofree gchar *id = NULL;
@@ -99,7 +99,7 @@ gs_cmd_show_results_categories (GList *list)
                        tmp = gs_cmd_pad_spaces (gs_category_get_id (cat), 32);
                        g_print ("%s : %s\n",
                                 tmp, gs_category_get_name (cat));
-                       subcats = gs_category_get_subcategories (cat);
+                       subcats = gs_category_get_children (cat);
                        gs_cmd_show_results_categories (subcats);
                }
        }
@@ -196,8 +196,6 @@ gs_cmd_refresh_flag_from_string (const gchar *flag)
 int
 main (int argc, char **argv)
 {
-       GList *list = NULL;
-       GList *categories = NULL;
        GOptionContext *context;
        gboolean prefer_local = FALSE;
        gboolean ret;
@@ -210,6 +208,8 @@ main (int argc, char **argv)
        int status = 0;
        g_auto(GStrv) plugin_names = NULL;
        g_autoptr(GError) error = NULL;
+       g_autoptr(GsAppList) list = NULL;
+       g_autoptr(GPtrArray) categories = NULL;
        g_autoptr(GsDebug) debug = gs_debug_new ();
        g_autofree gchar *plugin_names_str = NULL;
        g_autofree gchar *refine_flags_str = NULL;
@@ -287,7 +287,7 @@ main (int argc, char **argv)
        if (argc == 2 && g_strcmp0 (argv[1], "installed") == 0) {
                for (i = 0; i < repeat; i++) {
                        if (list != NULL)
-                               gs_app_list_free (list);
+                               g_object_unref (list);
                        list = gs_plugin_loader_get_installed (plugin_loader,
                                                               refine_flags,
                                                               NULL,
@@ -300,7 +300,7 @@ main (int argc, char **argv)
        } else if (argc == 3 && g_strcmp0 (argv[1], "search") == 0) {
                for (i = 0; i < repeat; i++) {
                        if (list != NULL)
-                               gs_app_list_free (list);
+                               g_object_unref (list);
                        list = gs_plugin_loader_search (plugin_loader,
                                                        argv[2],
                                                        refine_flags,
@@ -320,7 +320,7 @@ main (int argc, char **argv)
                                                   NULL,
                                                   &error);
                if (ret)
-                       gs_app_list_add (&list, app);
+                       gs_app_list_add (list, app);
        } else if (argc == 3 && g_strcmp0 (argv[1], "refine") == 0) {
                app = gs_app_new (argv[2]);
                for (i = 0; i < repeat; i++) {
@@ -332,7 +332,7 @@ main (int argc, char **argv)
                        if (!ret)
                                break;
                }
-               gs_app_list_add (&list, app);
+               gs_app_list_add (list, app);
        } else if (argc == 3 && g_strcmp0 (argv[1], "launch") == 0) {
                app = gs_app_new (argv[2]);
                for (i = 0; i < repeat; i++) {
@@ -354,12 +354,12 @@ main (int argc, char **argv)
                if (app == NULL) {
                        ret = FALSE;
                } else {
-                       gs_app_list_add (&list, app);
+                       gs_app_list_add (list, app);
                }
        } else if (argc == 2 && g_strcmp0 (argv[1], "updates") == 0) {
                for (i = 0; i < repeat; i++) {
                        if (list != NULL)
-                               gs_app_list_free (list);
+                               g_object_unref (list);
                        list = gs_plugin_loader_get_updates (plugin_loader,
                                                             refine_flags,
                                                             NULL,
@@ -372,7 +372,7 @@ main (int argc, char **argv)
        } else if (argc == 2 && g_strcmp0 (argv[1], "upgrades") == 0) {
                for (i = 0; i < repeat; i++) {
                        if (list != NULL)
-                               gs_app_list_free (list);
+                               g_object_unref (list);
                        list = gs_plugin_loader_get_distro_upgrades (plugin_loader,
                                                                     refine_flags,
                                                                     NULL,
@@ -392,7 +392,7 @@ main (int argc, char **argv)
        } else if (argc == 2 && g_strcmp0 (argv[1], "popular") == 0) {
                for (i = 0; i < repeat; i++) {
                        if (list != NULL)
-                               gs_app_list_free (list);
+                               g_object_unref (list);
                        list = gs_plugin_loader_get_popular (plugin_loader,
                                                             refine_flags,
                                                             NULL,
@@ -405,7 +405,7 @@ main (int argc, char **argv)
        } else if (argc == 2 && g_strcmp0 (argv[1], "featured") == 0) {
                for (i = 0; i < repeat; i++) {
                        if (list != NULL)
-                               gs_app_list_free (list);
+                               g_object_unref (list);
                        list = gs_plugin_loader_get_featured (plugin_loader,
                                                              refine_flags,
                                                              NULL,
@@ -418,7 +418,7 @@ main (int argc, char **argv)
        } else if (argc == 2 && g_strcmp0 (argv[1], "get-categories") == 0) {
                for (i = 0; i < repeat; i++) {
                        if (categories != NULL)
-                               gs_app_list_free (categories);
+                               g_ptr_array_unref (categories);
                        categories = gs_plugin_loader_get_categories (plugin_loader,
                                                                      refine_flags,
                                                                      NULL,
@@ -432,16 +432,15 @@ main (int argc, char **argv)
                g_autoptr(GsCategory) category = NULL;
                g_auto(GStrv) split = NULL;
                split = g_strsplit (argv[2], "/", 2);
-               if (g_strv_length (split) == 1) {
-                       category = gs_category_new (NULL, split[0], NULL);
-               } else {
-                       g_autoptr(GsCategory) parent = NULL;
-                       parent = gs_category_new (NULL, split[0], NULL);
-                       category = gs_category_new (parent, split[1], NULL);
+               category = gs_category_new (split[0], NULL);
+               if (g_strv_length (split) == 2) {
+                       g_autoptr(GsCategory) child = NULL;
+                       child = gs_category_new (split[1], NULL);
+                       gs_category_add_child (category, child);
                }
                for (i = 0; i < repeat; i++) {
                        if (list != NULL)
-                               gs_app_list_free (list);
+                               g_object_unref (list);
                        list = gs_plugin_loader_get_category_apps (plugin_loader,
                                                                   category,
                                                                   refine_flags,
@@ -473,15 +472,15 @@ main (int argc, char **argv)
        }
 
        if (show_results) {
-               gs_cmd_show_results_apps (list);
-               gs_cmd_show_results_categories (categories);
+               if (list != NULL)
+                       gs_cmd_show_results_apps (list);
+               if (categories != NULL)
+                       gs_cmd_show_results_categories (categories);
        }
 out:
        if (profile != NULL)
                as_profile_dump (profile);
        g_option_context_free (context);
-       gs_app_list_free (list);
-       gs_app_list_free (categories);
        return status;
 }
 
diff --git a/src/gs-plugin-loader-sync.c b/src/gs-plugin-loader-sync.c
index 37ed2de..c1a6d79 100644
--- a/src/gs-plugin-loader-sync.c
+++ b/src/gs-plugin-loader-sync.c
@@ -47,7 +47,8 @@ gs_plugin_loader_get_app_by_id (GsPluginLoader *plugin_loader,
 /* tiny helper to help us do the async operation */
 typedef struct {
        GError          **error;
-       GList           *list;
+       GsAppList       *list;
+       GPtrArray       *catlist;
        GMainContext    *context;
        GMainLoop       *loop;
        gboolean         ret;
@@ -68,7 +69,7 @@ gs_plugin_loader_get_installed_finish_sync (GsPluginLoader *plugin_loader,
 /**
  * gs_plugin_loader_get_installed:
  **/
-GList *
+GsAppList *
 gs_plugin_loader_get_installed (GsPluginLoader *plugin_loader,
                                GsPluginRefineFlags flags,
                                GCancellable *cancellable,
@@ -113,7 +114,7 @@ gs_plugin_loader_search_finish_sync (GsPluginLoader *plugin_loader,
 /**
  * gs_plugin_loader_search:
  **/
-GList *
+GsAppList *
 gs_plugin_loader_search (GsPluginLoader *plugin_loader,
                         const gchar *value,
                         GsPluginRefineFlags flags,
@@ -160,7 +161,7 @@ gs_plugin_loader_get_updates_finish_sync (GsPluginLoader *plugin_loader,
 /**
  * gs_plugin_loader_get_updates:
  **/
-GList *
+GsAppList *
 gs_plugin_loader_get_updates (GsPluginLoader *plugin_loader,
                              GsPluginRefineFlags flags,
                              GCancellable *cancellable,
@@ -205,7 +206,7 @@ gs_plugin_loader_get_distro_upgrades_finish_sync (GsPluginLoader *plugin_loader,
 /**
  * gs_plugin_loader_get_distro_upgrades:
  **/
-GList *
+GsAppList *
 gs_plugin_loader_get_distro_upgrades (GsPluginLoader *plugin_loader,
                              GsPluginRefineFlags flags,
                              GCancellable *cancellable,
@@ -250,7 +251,7 @@ gs_plugin_loader_get_sources_finish_sync (GsPluginLoader *plugin_loader,
 /**
  * gs_plugin_loader_get_sources:
  **/
-GList *
+GsAppList *
 gs_plugin_loader_get_sources (GsPluginLoader *plugin_loader,
                              GsPluginRefineFlags flags,
                              GCancellable *cancellable,
@@ -295,7 +296,7 @@ gs_plugin_loader_get_popular_finish_sync (GsPluginLoader *plugin_loader,
 /**
  * gs_plugin_loader_get_popular:
  **/
-GList *
+GsAppList *
 gs_plugin_loader_get_popular (GsPluginLoader *plugin_loader,
                              GsPluginRefineFlags flags,
                              GCancellable *cancellable,
@@ -340,7 +341,7 @@ gs_plugin_loader_get_featured_finish_sync (GsPluginLoader *plugin_loader,
 /**
  * gs_plugin_loader_get_featured:
  **/
-GList *
+GsAppList *
 gs_plugin_loader_get_featured (GsPluginLoader *plugin_loader,
                               GsPluginRefineFlags flags,
                               GCancellable *cancellable,
@@ -376,7 +377,7 @@ gs_plugin_loader_get_categories_finish_sync (GsPluginLoader *plugin_loader,
                                             GAsyncResult *res,
                                             GsPluginLoaderHelper *helper)
 {
-       helper->list = gs_plugin_loader_get_categories_finish (plugin_loader,
+       helper->catlist = gs_plugin_loader_get_categories_finish (plugin_loader,
                                                               res,
                                                               helper->error);
        g_main_loop_quit (helper->loop);
@@ -385,7 +386,7 @@ gs_plugin_loader_get_categories_finish_sync (GsPluginLoader *plugin_loader,
 /**
  * gs_plugin_loader_get_categories:
  **/
-GList *
+GPtrArray *
 gs_plugin_loader_get_categories (GsPluginLoader *plugin_loader,
                                 GsPluginRefineFlags flags,
                                 GCancellable *cancellable,
@@ -413,7 +414,7 @@ gs_plugin_loader_get_categories (GsPluginLoader *plugin_loader,
        g_main_loop_unref (helper.loop);
        g_main_context_unref (helper.context);
 
-       return helper.list;
+       return helper.catlist;
 }
 
 static void
@@ -430,7 +431,7 @@ gs_plugin_loader_get_category_apps_finish_sync (GsPluginLoader *plugin_loader,
 /**
  * gs_plugin_loader_get_category_apps:
  **/
-GList *
+GsAppList *
 gs_plugin_loader_get_category_apps (GsPluginLoader *plugin_loader,
                                    GsCategory *category,
                                    GsPluginRefineFlags flags,
diff --git a/src/gs-plugin-loader-sync.h b/src/gs-plugin-loader-sync.h
index 03a3238..e643957 100644
--- a/src/gs-plugin-loader-sync.h
+++ b/src/gs-plugin-loader-sync.h
@@ -28,40 +28,40 @@
 
 G_BEGIN_DECLS
 
-GList          *gs_plugin_loader_get_installed         (GsPluginLoader *plugin_loader,
+GsAppList      *gs_plugin_loader_get_installed         (GsPluginLoader *plugin_loader,
                                                         GsPluginRefineFlags flags,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
-GList          *gs_plugin_loader_search                (GsPluginLoader *plugin_loader,
+GsAppList      *gs_plugin_loader_search                (GsPluginLoader *plugin_loader,
                                                         const gchar    *value,
                                                         GsPluginRefineFlags flags,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
-GList          *gs_plugin_loader_get_updates           (GsPluginLoader *plugin_loader,
+GsAppList      *gs_plugin_loader_get_updates           (GsPluginLoader *plugin_loader,
                                                         GsPluginRefineFlags flags,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
-GList          *gs_plugin_loader_get_distro_upgrades   (GsPluginLoader *plugin_loader,
+GsAppList      *gs_plugin_loader_get_distro_upgrades   (GsPluginLoader *plugin_loader,
                                                         GsPluginRefineFlags flags,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
-GList          *gs_plugin_loader_get_sources           (GsPluginLoader *plugin_loader,
+GsAppList      *gs_plugin_loader_get_sources           (GsPluginLoader *plugin_loader,
                                                         GsPluginRefineFlags flags,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
-GList          *gs_plugin_loader_get_popular           (GsPluginLoader *plugin_loader,
+GsAppList      *gs_plugin_loader_get_popular           (GsPluginLoader *plugin_loader,
                                                         GsPluginRefineFlags flags,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
-GList          *gs_plugin_loader_get_featured          (GsPluginLoader *plugin_loader,
+GsAppList      *gs_plugin_loader_get_featured          (GsPluginLoader *plugin_loader,
                                                         GsPluginRefineFlags flags,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
-GList          *gs_plugin_loader_get_categories        (GsPluginLoader *plugin_loader,
+GPtrArray      *gs_plugin_loader_get_categories        (GsPluginLoader *plugin_loader,
                                                         GsPluginRefineFlags flags,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
-GList          *gs_plugin_loader_get_category_apps     (GsPluginLoader *plugin_loader,
+GsAppList      *gs_plugin_loader_get_category_apps     (GsPluginLoader *plugin_loader,
                                                         GsCategory     *category,
                                                         GsPluginRefineFlags flags,
                                                         GCancellable   *cancellable,
diff --git a/src/gs-plugin-loader.c b/src/gs-plugin-loader.c
index 9ad19c2..b4585c6 100644
--- a/src/gs-plugin-loader.c
+++ b/src/gs-plugin-loader.c
@@ -71,16 +71,20 @@ typedef gboolean     (*GsPluginSetupFunc)           (GsPlugin       *plugin,
                                                         GError         **error);
 typedef gboolean        (*GsPluginSearchFunc)          (GsPlugin       *plugin,
                                                         gchar          **value,
-                                                        GList          **list,
+                                                        GsAppList      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 typedef gboolean        (*GsPluginCategoryFunc)        (GsPlugin       *plugin,
                                                         GsCategory     *category,
-                                                        GList          **list,
+                                                        GsAppList      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 typedef gboolean        (*GsPluginResultsFunc)         (GsPlugin       *plugin,
-                                                        GList          **list,
+                                                        GsAppList      *list,
+                                                        GCancellable   *cancellable,
+                                                        GError         **error);
+typedef gboolean        (*GsPluginCategoriesFunc)      (GsPlugin       *plugin,
+                                                        GPtrArray      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 typedef gboolean        (*GsPluginActionFunc)          (GsPlugin       *plugin,
@@ -93,7 +97,7 @@ typedef gboolean       (*GsPluginReviewFunc)          (GsPlugin       *plugin,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 typedef gboolean        (*GsPluginRefineFunc)          (GsPlugin       *plugin,
-                                                        GList          **list,
+                                                        GsAppList      *list,
                                                         GsPluginRefineFlags flags,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
@@ -108,12 +112,12 @@ typedef gboolean   (*GsPluginRefreshFunc  )       (GsPlugin       *plugin,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 typedef gboolean        (*GsPluginFileToAppFunc)       (GsPlugin       *plugin,
-                                                        GList          **list,
+                                                        GsAppList      *list,
                                                         GFile          *file,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 typedef gboolean        (*GsPluginUpdateFunc)          (GsPlugin       *plugin,
-                                                        GList          *apps,
+                                                        GsAppList      *apps,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 typedef void            (*GsPluginAdoptAppFunc)        (GsPlugin       *plugin,
@@ -122,7 +126,8 @@ typedef void                 (*GsPluginAdoptAppFunc)        (GsPlugin       *plugin,
 /* async state */
 typedef struct {
        const gchar                     *function_name;
-       GList                           *list;
+       GsAppList                       *list;
+       GPtrArray                       *catlist;
        GsPluginRefineFlags              flags;
        gchar                           *value;
        GFile                           *file;
@@ -143,9 +148,12 @@ gs_plugin_loader_free_async_state (GsPluginLoaderAsyncState *state)
                g_object_unref (state->review);
        if (state->file != NULL)
                g_object_unref (state->file);
+       if (state->list != NULL)
+               g_object_unref (state->list);
+       if (state->catlist != NULL)
+               g_ptr_array_unref (state->catlist);
 
        g_free (state->value);
-       gs_app_list_free (state->list);
        g_slice_free (GsPluginLoaderAsyncState, state);
 }
 
@@ -159,10 +167,10 @@ G_DEFINE_QUARK (gs-plugin-loader-error-quark, gs_plugin_loader_error)
  * gs_plugin_loader_app_sort_cb:
  **/
 static gint
-gs_plugin_loader_app_sort_cb (gconstpointer a, gconstpointer b)
+gs_plugin_loader_app_sort_cb (GsApp *app1, GsApp *app2, gpointer user_data)
 {
-       return g_strcmp0 (gs_app_get_name (GS_APP ((gpointer) a)),
-                         gs_app_get_name (GS_APP ((gpointer) b)));
+       return g_strcmp0 (gs_app_get_name (app1),
+                         gs_app_get_name (app2));
 }
 
 /**
@@ -211,11 +219,11 @@ gs_plugin_loader_action_stop (GsPluginLoader *plugin_loader, GsPlugin *plugin)
  * gs_plugin_loader_run_adopt:
  **/
 static void
-gs_plugin_loader_run_adopt (GsPluginLoader *plugin_loader, GList *list)
+gs_plugin_loader_run_adopt (GsPluginLoader *plugin_loader, GsAppList *list)
 {
        GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
-       GList *l;
        guint i;
+       guint j;
 
        /* go through each plugin in priority order */
        for (i = 0; i < priv->plugins->len; i++) {
@@ -228,8 +236,8 @@ gs_plugin_loader_run_adopt (GsPluginLoader *plugin_loader, GList *list)
                                 (gpointer *) &adopt_app_func);
                if (adopt_app_func == NULL)
                        continue;
-               for (l = list; l != NULL; l = l->next) {
-                       GsApp *app = GS_APP (l->data);
+               for (j = 0; j < gs_app_list_length (list); j++) {
+                       GsApp *app = gs_app_list_index (list, j);
                        if (gs_app_get_management_plugin (app) != NULL)
                                continue;
                        gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
@@ -250,13 +258,14 @@ gs_plugin_loader_run_adopt (GsPluginLoader *plugin_loader, GList *list)
 static gboolean
 gs_plugin_loader_run_refine (GsPluginLoader *plugin_loader,
                             const gchar *function_name_parent,
-                            GList **list,
+                            GsAppList *list,
                             GsPluginRefineFlags flags,
                             GCancellable *cancellable,
                             GError **error)
 {
        GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
-       GList *l;
+       guint i;
+       guint j;
        GPtrArray *addons;
        GPtrArray *related;
        GsApp *app;
@@ -264,18 +273,17 @@ gs_plugin_loader_run_refine (GsPluginLoader *plugin_loader,
        const gchar *function_name_app = "gs_plugin_refine_app";
        const gchar *function_name = "gs_plugin_refine";
        gboolean ret = TRUE;
-       guint i;
-       g_autoptr(GsAppList) addons_list = NULL;
        g_autoptr(GsAppList) freeze_list = NULL;
-       g_autoptr(GsAppList) related_list = NULL;
 
        /* freeze all apps */
-       freeze_list = gs_app_list_copy (*list);
-       for (l = freeze_list; l != NULL; l = l->next)
-               g_object_freeze_notify (G_OBJECT (l->data));
+       freeze_list = gs_app_list_copy (list);
+       for (i = 0; i < gs_app_list_length (freeze_list); i++) {
+               app = gs_app_list_index (freeze_list, i);
+               g_object_freeze_notify (G_OBJECT (app));
+       }
 
        /* try to adopt each application with a plugin */
-       gs_plugin_loader_run_adopt (plugin_loader, *list);
+       gs_plugin_loader_run_adopt (plugin_loader, list);
 
        /* run each plugin */
        for (i = 0; i < priv->plugins->len; i++) {
@@ -334,9 +342,9 @@ gs_plugin_loader_run_refine (GsPluginLoader *plugin_loader,
                        }
                }
                if (plugin_app_func != NULL) {
-                       for (l = *list; l != NULL; l = l->next) {
+                       for (j = 0; j < gs_app_list_length (list); j++) {
                                g_autoptr(GError) error_local = NULL;
-                               app = GS_APP (l->data);
+                               app = gs_app_list_index (list, j);
                                gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
                                ret = plugin_app_func (plugin, app, flags,
                                                       cancellable, &error_local);
@@ -362,24 +370,27 @@ gs_plugin_loader_run_refine (GsPluginLoader *plugin_loader,
 
        /* refine addons one layer deep */
        if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_ADDONS) > 0) {
+               g_autoptr(GsAppList) addons_list = NULL;
+
                flags &= ~GS_PLUGIN_REFINE_FLAGS_REQUIRE_ADDONS;
                flags &= ~GS_PLUGIN_REFINE_FLAGS_REQUIRE_REVIEWS;
                flags &= ~GS_PLUGIN_REFINE_FLAGS_REQUIRE_REVIEW_RATINGS;
-               for (l = *list; l != NULL; l = l->next) {
-                       app = GS_APP (l->data);
+               addons_list = gs_app_list_new ();
+               for (i = 0; i < gs_app_list_length (list); i++) {
+                       app = gs_app_list_index (list, i);
                        addons = gs_app_get_addons (app);
-                       for (i = 0; i < addons->len; i++) {
-                               GsApp *addon = g_ptr_array_index (addons, i);
+                       for (j = 0; j < addons->len; j++) {
+                               GsApp *addon = g_ptr_array_index (addons, j);
                                g_debug ("refining app %s addon %s",
                                         gs_app_get_id (app),
                                         gs_app_get_id (addon));
-                               gs_app_list_add (&addons_list, addon);
+                               gs_app_list_add (addons_list, addon);
                        }
                }
-               if (addons_list != NULL) {
+               if (gs_app_list_length (addons_list) > 0) {
                        ret = gs_plugin_loader_run_refine (plugin_loader,
                                                           function_name_parent,
-                                                          &addons_list,
+                                                          addons_list,
                                                           flags,
                                                           cancellable,
                                                           error);
@@ -390,22 +401,25 @@ gs_plugin_loader_run_refine (GsPluginLoader *plugin_loader,
 
        /* also do related packages one layer deep */
        if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_RELATED) > 0) {
+               g_autoptr(GsAppList) related_list = NULL;
+
                flags &= ~GS_PLUGIN_REFINE_FLAGS_REQUIRE_RELATED;
-               for (l = *list; l != NULL; l = l->next) {
-                       app = GS_APP (l->data);
+               related_list = gs_app_list_new ();
+               for (i = 0; i < gs_app_list_length (list); i++) {
+                       app = gs_app_list_index (list, i);
                        related = gs_app_get_related (app);
                        for (i = 0; i < related->len; i++) {
                                app = g_ptr_array_index (related, i);
                                g_debug ("refining related: %s[%s]",
                                         gs_app_get_id (app),
                                         gs_app_get_source_default (app));
-                               gs_app_list_add (&related_list, app);
+                               gs_app_list_add (related_list, app);
                        }
                }
                if (related_list != NULL) {
                        ret = gs_plugin_loader_run_refine (plugin_loader,
                                                           function_name_parent,
-                                                          &related_list,
+                                                          related_list,
                                                           flags,
                                                           cancellable,
                                                           error);
@@ -418,18 +432,20 @@ gs_plugin_loader_run_refine (GsPluginLoader *plugin_loader,
        ret = TRUE;
 out:
        /* now emit all the changed signals */
-       for (l = freeze_list; l != NULL; l = l->next)
-               g_object_thaw_notify (G_OBJECT (l->data));
+       for (i = 0; i < gs_app_list_length (freeze_list); i++) {
+               app = gs_app_list_index (freeze_list, i);
+               g_object_thaw_notify (G_OBJECT (app));
+       }
 
        return ret;
 }
 
-static GList *gs_plugin_loader_add_os_update_item (GList *list);
+static void gs_plugin_loader_add_os_update_item (GsAppList *list);
 
 /**
  * gs_plugin_loader_run_results:
  **/
-static GList *
+static GsAppList *
 gs_plugin_loader_run_results (GsPluginLoader *plugin_loader,
                              const gchar *function_name,
                              GsPluginRefineFlags flags,
@@ -437,7 +453,7 @@ gs_plugin_loader_run_results (GsPluginLoader *plugin_loader,
                              GError **error)
 {
        GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
-       GList *list = NULL;
+       g_autoptr(GsAppList) list = NULL;
        GsPluginResultsFunc plugin_func = NULL;
        GsPlugin *plugin;
        gboolean exists;
@@ -454,6 +470,7 @@ gs_plugin_loader_run_results (GsPluginLoader *plugin_loader,
        ptask = as_profile_start (priv->profile, "GsPlugin::*(%s)", function_name);
 
        /* run each plugin */
+       list = gs_app_list_new ();
        for (i = 0; i < priv->plugins->len; i++) {
                g_autoptr(GError) error_local = NULL;
                g_autoptr(AsProfileTask) ptask2 = NULL;
@@ -462,10 +479,8 @@ gs_plugin_loader_run_results (GsPluginLoader *plugin_loader,
                if (!gs_plugin_get_enabled (plugin))
                        continue;
                ret = g_cancellable_set_error_if_cancelled (cancellable, error);
-               if (ret) {
-                       ret = FALSE;
-                       goto out;
-               }
+               if (ret)
+                       return NULL;
 
                /* get symbol */
                exists = g_module_symbol (gs_plugin_get_module (plugin),
@@ -480,7 +495,7 @@ gs_plugin_loader_run_results (GsPluginLoader *plugin_loader,
                                           gs_plugin_get_name (plugin),
                                           function_name);
                gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
-               ret = plugin_func (plugin, &list, cancellable, &error_local);
+               ret = plugin_func (plugin, list, cancellable, &error_local);
                gs_plugin_loader_action_stop (plugin_loader, plugin);
                if (!ret) {
                        /* badly behaved plugin */
@@ -502,43 +517,38 @@ gs_plugin_loader_run_results (GsPluginLoader *plugin_loader,
        /* run refine() on each one */
        ret = gs_plugin_loader_run_refine (plugin_loader,
                                           function_name,
-                                          &list,
+                                          list,
                                           flags,
                                           cancellable,
                                           error);
        if (!ret)
-               goto out;
+               return NULL;
 
        /* coalesce all packages down into one os-update */
        if (g_strcmp0 (function_name, "gs_plugin_add_updates") == 0) {
-               list = gs_plugin_loader_add_os_update_item (list);
+               gs_plugin_loader_add_os_update_item (list);
                ret = gs_plugin_loader_run_refine (plugin_loader,
                                                   function_name,
-                                                  &list,
+                                                  list,
                                                   GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON,
                                                   cancellable,
                                                   error);
                if (!ret)
-                       goto out;
+                       return NULL;
        }
 
        /* filter package list */
-       gs_app_list_filter_duplicates (&list);
+       gs_app_list_filter_duplicates (list);
 
        /* no results */
-       if (list == NULL) {
+       if (gs_app_list_length (list) == 0) {
                g_set_error (error,
                             GS_PLUGIN_LOADER_ERROR,
                             GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
                             "no results to show");
-               goto out;
+               return NULL;
        }
-out:
-       if (!ret) {
-               gs_app_list_free (list);
-               list = NULL;
-       }
-       return list;
+       return g_steal_pointer (&list);
 }
 
 /**
@@ -870,27 +880,27 @@ gs_plugin_loader_merge_into_os_update (GsApp *app)
 /**
  * gs_plugin_loader_add_os_update_item:
  **/
-static GList *
-gs_plugin_loader_add_os_update_item (GList *list)
+static void
+gs_plugin_loader_add_os_update_item (GsAppList *list)
 {
        gboolean has_os_update = FALSE;
-       GList *l;
-       GsApp *app_os;
+       guint i;
        GsApp *app_tmp;
        g_autoptr(GError) error = NULL;
        g_autoptr(GdkPixbuf) pixbuf = NULL;
+       g_autoptr(GsApp) app_os = NULL;
        g_autoptr(AsIcon) ic = NULL;
 
        /* do we have any packages left that are not apps? */
-       for (l = list; l != NULL; l = l->next) {
-               app_tmp = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               app_tmp = gs_app_list_index (list, i);
                if (gs_plugin_loader_merge_into_os_update (app_tmp)) {
                        has_os_update = TRUE;
                        break;
                }
        }
        if (!has_os_update)
-               return list;
+               return;
 
        /* create new meta object */
        app_os = gs_app_new ("os-update.virtual");
@@ -910,8 +920,8 @@ gs_plugin_loader_add_os_update_item (GList *list)
        gs_app_set_description (app_os,
                                GS_APP_QUALITY_NORMAL,
                                gs_app_get_summary (app_os));
-       for (l = list; l != NULL; l = l->next) {
-               app_tmp = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               app_tmp = gs_app_list_index (list, i);
                if (!gs_plugin_loader_merge_into_os_update (app_tmp))
                        continue;
                gs_app_add_related (app_os, app_tmp);
@@ -920,8 +930,7 @@ gs_plugin_loader_add_os_update_item (GList *list)
        as_icon_set_kind (ic, AS_ICON_KIND_STOCK);
        as_icon_set_name (ic, "software-update-available-symbolic");
        gs_app_set_icon (app_os, ic);
-
-       return g_list_prepend (list, app_os);
+       gs_app_list_add (list, app_os);
 }
 
 /**
@@ -953,12 +962,12 @@ gs_plugin_loader_get_updates_thread_cb (GTask *task,
        }
 
        /* filter package list */
-       gs_app_list_filter_duplicates (&state->list);
+       gs_app_list_filter_duplicates (state->list);
 
        /* remove any packages that are not proper applications or
         * OS updates */
-       gs_app_list_filter (&state->list, gs_plugin_loader_app_is_valid, state);
-       if (state->list == NULL) {
+       gs_app_list_filter (state->list, gs_plugin_loader_app_is_valid, state);
+       if (gs_app_list_length (state->list) == 0) {
                g_task_return_new_error (task,
                                         GS_PLUGIN_LOADER_ERROR,
                                         GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
@@ -967,7 +976,7 @@ gs_plugin_loader_get_updates_thread_cb (GTask *task,
        }
 
        /* success */
-       g_task_return_pointer (task, gs_app_list_copy (state->list), (GDestroyNotify) gs_app_list_free);
+       g_task_return_pointer (task, g_object_ref (state->list), (GDestroyNotify) g_object_unref);
 }
 
 /**
@@ -1020,7 +1029,7 @@ gs_plugin_loader_get_updates_async (GsPluginLoader *plugin_loader,
  *
  * Return value: (element-type GsApp) (transfer full): A list of applications
  **/
-GList *
+GsAppList *
 gs_plugin_loader_get_updates_finish (GsPluginLoader *plugin_loader,
                                       GAsyncResult *res,
                                       GError **error)
@@ -1059,10 +1068,10 @@ gs_plugin_loader_get_distro_upgrades_thread_cb (GTask *task,
        }
 
        /* filter package list */
-       gs_app_list_filter_duplicates (&state->list);
+       gs_app_list_filter_duplicates (state->list);
 
        /* success */
-       g_task_return_pointer (task, gs_app_list_copy (state->list), (GDestroyNotify) gs_app_list_free);
+       g_task_return_pointer (task, g_object_ref (state->list), (GDestroyNotify) g_object_unref);
 }
 
 /**
@@ -1099,7 +1108,7 @@ gs_plugin_loader_get_distro_upgrades_async (GsPluginLoader *plugin_loader,
  *
  * Return value: (element-type GsApp) (transfer full): A list of applications
  **/
-GList *
+GsAppList *
 gs_plugin_loader_get_distro_upgrades_finish (GsPluginLoader *plugin_loader,
                                             GAsyncResult *res,
                                             GError **error)
@@ -1138,10 +1147,10 @@ gs_plugin_loader_get_unvoted_reviews_thread_cb (GTask *task,
        }
 
        /* filter package list */
-       gs_app_list_filter_duplicates (&state->list);
+       gs_app_list_filter_duplicates (state->list);
 
        /* success */
-       g_task_return_pointer (task, gs_app_list_copy (state->list), (GDestroyNotify) gs_app_list_free);
+       g_task_return_pointer (task, g_object_ref (state->list), (GDestroyNotify) g_object_unref);
 }
 
 /**
@@ -1178,7 +1187,7 @@ gs_plugin_loader_get_unvoted_reviews_async (GsPluginLoader *plugin_loader,
  *
  * Return value: (element-type GsApp) (transfer full): A list of applications
  **/
-GList *
+GsAppList *
 gs_plugin_loader_get_unvoted_reviews_finish (GsPluginLoader *plugin_loader,
                                             GAsyncResult *res,
                                             GError **error)
@@ -1217,10 +1226,10 @@ gs_plugin_loader_get_sources_thread_cb (GTask *task,
        }
 
        /* filter package list */
-       gs_app_list_filter_duplicates (&state->list);
+       gs_app_list_filter_duplicates (state->list);
 
        /* none left? */
-       if (state->list == NULL) {
+       if (gs_app_list_length (state->list) == 0) {
                g_task_return_new_error (task,
                                         GS_PLUGIN_LOADER_ERROR,
                                         GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
@@ -1229,7 +1238,7 @@ gs_plugin_loader_get_sources_thread_cb (GTask *task,
        }
 
        /* success */
-       g_task_return_pointer (task, gs_app_list_copy (state->list), (GDestroyNotify) gs_app_list_free);
+       g_task_return_pointer (task, g_object_ref (state->list), (GDestroyNotify) g_object_unref);
 }
 
 /**
@@ -1269,7 +1278,7 @@ gs_plugin_loader_get_sources_async (GsPluginLoader *plugin_loader,
  *
  * Return value: (element-type GsApp) (transfer full): A list of applications
  **/
-GList *
+GsAppList *
 gs_plugin_loader_get_sources_finish (GsPluginLoader *plugin_loader,
                                       GAsyncResult *res,
                                       GError **error)
@@ -1309,9 +1318,9 @@ gs_plugin_loader_get_installed_thread_cb (GTask *task,
        }
 
        /* filter package list */
-       gs_app_list_filter (&state->list, gs_plugin_loader_app_is_valid, state);
-       gs_app_list_filter (&state->list, gs_plugin_loader_app_is_valid_installed, state);
-       if (state->list == NULL) {
+       gs_app_list_filter (state->list, gs_plugin_loader_app_is_valid, state);
+       gs_app_list_filter (state->list, gs_plugin_loader_app_is_valid_installed, state);
+       if (gs_app_list_length (state->list) == 0) {
                g_task_return_new_error (task,
                                         GS_PLUGIN_LOADER_ERROR,
                                         GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
@@ -1320,7 +1329,7 @@ gs_plugin_loader_get_installed_thread_cb (GTask *task,
        }
 
        /* success */
-       g_task_return_pointer (task, gs_app_list_copy (state->list), (GDestroyNotify) gs_app_list_free);
+       g_task_return_pointer (task, g_object_ref (state->list), (GDestroyNotify) g_object_unref);
 }
 
 /**
@@ -1371,7 +1380,7 @@ gs_plugin_loader_get_installed_async (GsPluginLoader *plugin_loader,
  *
  * Return value: (element-type GsApp) (transfer full): A list of applications
  **/
-GList *
+GsAppList *
 gs_plugin_loader_get_installed_finish (GsPluginLoader *plugin_loader,
                                       GAsyncResult *res,
                                       GError **error)
@@ -1410,10 +1419,11 @@ gs_plugin_loader_get_popular_thread_cb (GTask *task,
                apps = g_settings_get_strv (priv->settings, "popular-overrides");
        if (apps != NULL && g_strv_length (apps) > 0) {
                guint i;
+               state->list = gs_app_list_new ();
                for (i = 0; apps[i] != NULL; i++) {
                        g_autoptr(GsApp) app = gs_app_new (apps[i]);
                        gs_app_add_quirk (app, AS_APP_QUIRK_MATCH_ANY_PREFIX);
-                       gs_app_list_add (&state->list, app);
+                       gs_app_list_add (state->list, app);
                }
        } else {
                /* do things that would block */
@@ -1429,10 +1439,10 @@ gs_plugin_loader_get_popular_thread_cb (GTask *task,
        }
 
        /* filter package list */
-       gs_app_list_filter (&state->list, gs_plugin_loader_app_is_valid, state);
-       gs_app_list_filter (&state->list, gs_plugin_loader_filter_qt_for_gtk, NULL);
-       gs_app_list_filter (&state->list, gs_plugin_loader_get_app_is_compatible, plugin_loader);
-       if (state->list == NULL) {
+       gs_app_list_filter (state->list, gs_plugin_loader_app_is_valid, state);
+       gs_app_list_filter (state->list, gs_plugin_loader_filter_qt_for_gtk, NULL);
+       gs_app_list_filter (state->list, gs_plugin_loader_get_app_is_compatible, plugin_loader);
+       if (gs_app_list_length (state->list) == 0) {
                g_task_return_new_error (task,
                                         GS_PLUGIN_LOADER_ERROR,
                                         GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
@@ -1441,7 +1451,7 @@ gs_plugin_loader_get_popular_thread_cb (GTask *task,
        }
 
        /* success */
-       g_task_return_pointer (task, gs_app_list_copy (state->list), (GDestroyNotify) gs_app_list_free);
+       g_task_return_pointer (task, g_object_ref (state->list), (GDestroyNotify) g_object_unref);
 }
 
 /**
@@ -1475,7 +1485,7 @@ gs_plugin_loader_get_popular_async (GsPluginLoader *plugin_loader,
  *
  * Return value: (element-type GsApp) (transfer full): A list of applications
  **/
-GList *
+GsAppList *
 gs_plugin_loader_get_popular_finish (GsPluginLoader *plugin_loader,
                                     GAsyncResult *res,
                                     GError **error)
@@ -1528,12 +1538,12 @@ gs_plugin_loader_get_featured_thread_cb (GTask *task,
 
        /* filter package list */
        if (g_getenv ("GNOME_SOFTWARE_FEATURED") != NULL) {
-               gs_app_list_filter (&state->list, gs_plugin_loader_featured_debug, NULL);
+               gs_app_list_filter (state->list, gs_plugin_loader_featured_debug, NULL);
        } else {
-               gs_app_list_filter (&state->list, gs_plugin_loader_app_is_valid, state);
-               gs_app_list_filter (&state->list, gs_plugin_loader_get_app_is_compatible, plugin_loader);
+               gs_app_list_filter (state->list, gs_plugin_loader_app_is_valid, state);
+               gs_app_list_filter (state->list, gs_plugin_loader_get_app_is_compatible, plugin_loader);
        }
-       if (state->list == NULL) {
+       if (gs_app_list_length (state->list) == 0) {
                g_task_return_new_error (task,
                                         GS_PLUGIN_LOADER_ERROR,
                                         GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
@@ -1542,7 +1552,7 @@ gs_plugin_loader_get_featured_thread_cb (GTask *task,
        }
 
        /* success */
-       g_task_return_pointer (task, gs_app_list_copy (state->list), (GDestroyNotify) gs_app_list_free);
+       g_task_return_pointer (task, g_object_ref (state->list), (GDestroyNotify) g_object_unref);
 }
 
 /**
@@ -1594,7 +1604,7 @@ gs_plugin_loader_get_featured_async (GsPluginLoader *plugin_loader,
  *
  * Return value: (element-type GsApp) (transfer full): A list of applications
  **/
-GList *
+GsAppList *
 gs_plugin_loader_get_featured_finish (GsPluginLoader *plugin_loader,
                                      GAsyncResult *res,
                                      GError **error)
@@ -1651,13 +1661,13 @@ gs_plugin_loader_convert_unavailable_app (GsApp *app, const gchar *search)
  * gs_plugin_loader_convert_unavailable:
  **/
 static void
-gs_plugin_loader_convert_unavailable (GList *list, const gchar *search)
+gs_plugin_loader_convert_unavailable (GsAppList *list, const gchar *search)
 {
-       GList *l;
+       guint i;
        GsApp *app;
 
-       for (l = list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               app = gs_app_list_index (list, i);
                if (gs_app_get_kind (app) != AS_APP_KIND_GENERIC)
                        continue;
                if (gs_app_get_state (app) != AS_APP_STATE_UNAVAILABLE)
@@ -1721,7 +1731,7 @@ gs_plugin_loader_search_thread_cb (GTask *task,
                                          gs_plugin_get_name (plugin),
                                          function_name);
                gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
-               ret = plugin_func (plugin, values, &state->list,
+               ret = plugin_func (plugin, values, state->list,
                                   cancellable, &error_local);
                gs_plugin_loader_action_stop (plugin_loader, plugin);
                if (!ret) {
@@ -1744,7 +1754,7 @@ gs_plugin_loader_search_thread_cb (GTask *task,
        /* run refine() on each one */
        ret = gs_plugin_loader_run_refine (plugin_loader,
                                           function_name,
-                                          &state->list,
+                                          state->list,
                                           state->flags,
                                           cancellable,
                                           &error);
@@ -1757,18 +1767,18 @@ gs_plugin_loader_search_thread_cb (GTask *task,
        gs_plugin_loader_convert_unavailable (state->list, state->value);
 
        /* filter package list */
-       gs_app_list_filter_duplicates (&state->list);
-       gs_app_list_filter (&state->list, gs_plugin_loader_app_is_valid, state);
-       gs_app_list_filter (&state->list, gs_plugin_loader_filter_qt_for_gtk, NULL);
-       gs_app_list_filter (&state->list, gs_plugin_loader_get_app_is_compatible, plugin_loader);
-       if (state->list == NULL) {
+       gs_app_list_filter_duplicates (state->list);
+       gs_app_list_filter (state->list, gs_plugin_loader_app_is_valid, state);
+       gs_app_list_filter (state->list, gs_plugin_loader_filter_qt_for_gtk, NULL);
+       gs_app_list_filter (state->list, gs_plugin_loader_get_app_is_compatible, plugin_loader);
+       if (gs_app_list_length (state->list) == 0) {
                g_task_return_new_error (task,
                                         GS_PLUGIN_LOADER_ERROR,
                                         GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
                                         "no search results to show");
                return;
        }
-       if (g_list_length (state->list) > 500) {
+       if (gs_app_list_length (state->list) > 500) {
                g_task_return_new_error (task,
                                         GS_PLUGIN_LOADER_ERROR,
                                         GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
@@ -1777,7 +1787,7 @@ gs_plugin_loader_search_thread_cb (GTask *task,
        }
 
        /* success */
-       g_task_return_pointer (task, gs_app_list_copy (state->list), (GDestroyNotify) gs_app_list_free);
+       g_task_return_pointer (task, g_object_ref (state->list), (GDestroyNotify) g_object_unref);
 }
 
 /**
@@ -1818,6 +1828,7 @@ gs_plugin_loader_search_async (GsPluginLoader *plugin_loader,
        /* save state */
        state = g_slice_new0 (GsPluginLoaderAsyncState);
        state->flags = flags;
+       state->list = gs_app_list_new ();
        state->value = g_strdup (value);
 
        /* run in a thread */
@@ -1831,7 +1842,7 @@ gs_plugin_loader_search_async (GsPluginLoader *plugin_loader,
  *
  * Return value: (element-type GsApp) (transfer full): A list of applications
  **/
-GList *
+GsAppList *
 gs_plugin_loader_search_finish (GsPluginLoader *plugin_loader,
                                GAsyncResult *res,
                                GError **error)
@@ -1889,7 +1900,7 @@ gs_plugin_loader_search_files_thread_cb (GTask *task,
                                          gs_plugin_get_name (plugin),
                                          function_name);
                gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
-               ret = plugin_func (plugin, values, &state->list,
+               ret = plugin_func (plugin, values, state->list,
                                   cancellable, &error_local);
                gs_plugin_loader_action_stop (plugin_loader, plugin);
                if (!ret) {
@@ -1912,7 +1923,7 @@ gs_plugin_loader_search_files_thread_cb (GTask *task,
        /* run refine() on each one */
        ret = gs_plugin_loader_run_refine (plugin_loader,
                                           function_name,
-                                          &state->list,
+                                          state->list,
                                           state->flags,
                                           cancellable,
                                           &error);
@@ -1925,19 +1936,19 @@ gs_plugin_loader_search_files_thread_cb (GTask *task,
        gs_plugin_loader_convert_unavailable (state->list, state->value);
 
        /* filter package list */
-       gs_app_list_filter_duplicates (&state->list);
-       gs_app_list_filter (&state->list, gs_plugin_loader_app_is_valid, state);
-       gs_app_list_filter (&state->list, gs_plugin_loader_app_is_non_installed, NULL);
-       gs_app_list_filter (&state->list, gs_plugin_loader_filter_qt_for_gtk, NULL);
-       gs_app_list_filter (&state->list, gs_plugin_loader_get_app_is_compatible, plugin_loader);
-       if (state->list == NULL) {
+       gs_app_list_filter_duplicates (state->list);
+       gs_app_list_filter (state->list, gs_plugin_loader_app_is_valid, state);
+       gs_app_list_filter (state->list, gs_plugin_loader_app_is_non_installed, NULL);
+       gs_app_list_filter (state->list, gs_plugin_loader_filter_qt_for_gtk, NULL);
+       gs_app_list_filter (state->list, gs_plugin_loader_get_app_is_compatible, plugin_loader);
+       if (gs_app_list_length (state->list) == 0) {
                g_task_return_new_error (task,
                                         GS_PLUGIN_LOADER_ERROR,
                                         GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
                                         "no search results to show");
                return;
        }
-       if (g_list_length (state->list) > 500) {
+       if (gs_app_list_length (state->list) > 500) {
                g_task_return_new_error (task,
                                         GS_PLUGIN_LOADER_ERROR,
                                         GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
@@ -1946,7 +1957,7 @@ gs_plugin_loader_search_files_thread_cb (GTask *task,
        }
 
        /* success */
-       g_task_return_pointer (task, gs_app_list_copy (state->list), (GDestroyNotify) gs_app_list_free);
+       g_task_return_pointer (task, g_object_ref (state->list), (GDestroyNotify) g_object_unref);
 }
 
 /**
@@ -1987,6 +1998,7 @@ gs_plugin_loader_search_files_async (GsPluginLoader *plugin_loader,
        /* save state */
        state = g_slice_new0 (GsPluginLoaderAsyncState);
        state->flags = flags;
+       state->list = gs_app_list_new ();
        state->value = g_strdup (value);
 
        /* run in a thread */
@@ -2000,7 +2012,7 @@ gs_plugin_loader_search_files_async (GsPluginLoader *plugin_loader,
  *
  * Return value: (element-type GsApp) (transfer full): A list of applications
  **/
-GList *
+GsAppList *
 gs_plugin_loader_search_files_finish (GsPluginLoader *plugin_loader,
                                       GAsyncResult *res,
                                       GError **error)
@@ -2058,7 +2070,7 @@ gs_plugin_loader_search_what_provides_thread_cb (GTask *task,
                                          gs_plugin_get_name (plugin),
                                          function_name);
                gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
-               ret = plugin_func (plugin, values, &state->list,
+               ret = plugin_func (plugin, values, state->list,
                                   cancellable, &error_local);
                gs_plugin_loader_action_stop (plugin_loader, plugin);
                if (!ret) {
@@ -2081,7 +2093,7 @@ gs_plugin_loader_search_what_provides_thread_cb (GTask *task,
        /* run refine() on each one */
        ret = gs_plugin_loader_run_refine (plugin_loader,
                                           function_name,
-                                          &state->list,
+                                          state->list,
                                           state->flags,
                                           cancellable,
                                           &error);
@@ -2094,19 +2106,19 @@ gs_plugin_loader_search_what_provides_thread_cb (GTask *task,
        gs_plugin_loader_convert_unavailable (state->list, state->value);
 
        /* filter package list */
-       gs_app_list_filter_duplicates (&state->list);
-       gs_app_list_filter (&state->list, gs_plugin_loader_app_is_valid, state);
-       gs_app_list_filter (&state->list, gs_plugin_loader_app_is_non_installed, NULL);
-       gs_app_list_filter (&state->list, gs_plugin_loader_filter_qt_for_gtk, NULL);
-       gs_app_list_filter (&state->list, gs_plugin_loader_get_app_is_compatible, plugin_loader);
-       if (state->list == NULL) {
+       gs_app_list_filter_duplicates (state->list);
+       gs_app_list_filter (state->list, gs_plugin_loader_app_is_valid, state);
+       gs_app_list_filter (state->list, gs_plugin_loader_app_is_non_installed, NULL);
+       gs_app_list_filter (state->list, gs_plugin_loader_filter_qt_for_gtk, NULL);
+       gs_app_list_filter (state->list, gs_plugin_loader_get_app_is_compatible, plugin_loader);
+       if (gs_app_list_length (state->list) == 0) {
                g_task_return_new_error (task,
                                         GS_PLUGIN_LOADER_ERROR,
                                         GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
                                         "no search results to show");
                return;
        }
-       if (g_list_length (state->list) > 500) {
+       if (gs_app_list_length (state->list) > 500) {
                g_task_return_new_error (task,
                                         GS_PLUGIN_LOADER_ERROR,
                                         GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
@@ -2115,7 +2127,7 @@ gs_plugin_loader_search_what_provides_thread_cb (GTask *task,
        }
 
        /* success */
-       g_task_return_pointer (task, gs_app_list_copy (state->list), (GDestroyNotify) gs_app_list_free);
+       g_task_return_pointer (task, g_object_ref (state->list), (GDestroyNotify) g_object_unref);
 }
 
 /**
@@ -2156,6 +2168,7 @@ gs_plugin_loader_search_what_provides_async (GsPluginLoader *plugin_loader,
        /* save state */
        state = g_slice_new0 (GsPluginLoaderAsyncState);
        state->flags = flags;
+       state->list = gs_app_list_new ();
        state->value = g_strdup (value);
 
        /* run in a thread */
@@ -2169,7 +2182,7 @@ gs_plugin_loader_search_what_provides_async (GsPluginLoader *plugin_loader,
  *
  * Return value: (element-type GsApp) (transfer full): A list of applications
  **/
-GList *
+GsAppList *
 gs_plugin_loader_search_what_provides_finish (GsPluginLoader *plugin_loader,
                                               GAsyncResult *res,
                                               GError **error)
@@ -2190,8 +2203,10 @@ gs_plugin_loader_search_what_provides_finish (GsPluginLoader *plugin_loader,
 static gint
 gs_plugin_loader_category_sort_cb (gconstpointer a, gconstpointer b)
 {
-       return g_strcmp0 (gs_category_get_name (GS_CATEGORY ((gpointer) a)),
-                         gs_category_get_name (GS_CATEGORY ((gpointer) b)));
+       GsCategory *cata = GS_CATEGORY (*(GsCategory **) a);
+       GsCategory *catb = GS_CATEGORY (*(GsCategory **) b);
+       return g_strcmp0 (gs_category_get_name (cata),
+                         gs_category_get_name (catb));
 }
 
 /**
@@ -2209,8 +2224,7 @@ gs_plugin_loader_get_categories_thread_cb (GTask *task,
        gboolean ret = TRUE;
        GsPluginLoaderAsyncState *state = (GsPluginLoaderAsyncState *) task_data;
        GsPlugin *plugin;
-       GsPluginResultsFunc plugin_func = NULL;
-       GList *l;
+       GsPluginCategoriesFunc plugin_func = NULL;
        guint i;
 
        /* run each plugin */
@@ -2233,7 +2247,7 @@ gs_plugin_loader_get_categories_thread_cb (GTask *task,
                                          gs_plugin_get_name (plugin),
                                          function_name);
                gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
-               ret = plugin_func (plugin, &state->list,
+               ret = plugin_func (plugin, state->catlist,
                                   cancellable, &error_local);
                gs_plugin_loader_action_stop (plugin_loader, plugin);
                if (!ret) {
@@ -2254,33 +2268,35 @@ gs_plugin_loader_get_categories_thread_cb (GTask *task,
        }
 
        /* ensure they all have an 'All' category */
-       for (l = state->list; l != NULL; l = l->next) {
-               GsCategory *parent = GS_CATEGORY (l->data);
+       for (i = 0; i < state->catlist->len; i++) {
+               GsCategory *parent = GS_CATEGORY (g_ptr_array_index (state->catlist, i));
                if (g_strcmp0 (gs_category_get_id (parent), "Addons") == 0)
                        continue;
                if (gs_category_find_child (parent, "all") == NULL) {
                        g_autoptr(GsCategory) child = NULL;
-                       child = gs_category_new (parent, "all", NULL);
-                       gs_category_add_subcategory (parent, child);
+                       child = gs_category_new ("all", NULL);
+                       gs_category_add_child (parent, child);
                        /* this is probably valid... */
                        gs_category_set_size (child, gs_category_get_size (parent));
                }
                if (gs_category_find_child (parent, "featured") == NULL) {
                        g_autoptr(GsCategory) child = NULL;
-                       child = gs_category_new (parent, "featured", NULL);
-                       gs_category_add_subcategory (parent, child);
+                       child = gs_category_new ("featured", NULL);
+                       gs_category_add_child (parent, child);
                        /* this is probably valid... */
                        gs_category_set_size (child, 5);
                }
        }
 
        /* sort by name */
-       state->list = g_list_sort (state->list, gs_plugin_loader_category_sort_cb);
-       for (l = state->list; l != NULL; l = l->next)
-               gs_category_sort_subcategories (GS_CATEGORY (l->data));
+       g_ptr_array_sort (state->catlist, gs_plugin_loader_category_sort_cb);
+       for (i = 0; i < state->catlist->len; i++) {
+               GsCategory *cat = GS_CATEGORY (g_ptr_array_index (state->catlist, i));
+               gs_category_sort_children (cat);
+       }
 
        /* success */
-       if (state->list == NULL) {
+       if (state->catlist->len == 0) {
                g_task_return_new_error (task,
                                         GS_PLUGIN_LOADER_ERROR,
                                         GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
@@ -2289,7 +2305,7 @@ gs_plugin_loader_get_categories_thread_cb (GTask *task,
        }
 
        /* success */
-       g_task_return_pointer (task, gs_app_list_copy (state->list), (GDestroyNotify) gs_app_list_free);
+       g_task_return_pointer (task, g_ptr_array_ref (state->catlist), (GDestroyNotify) g_ptr_array_unref);
 }
 
 /**
@@ -2314,6 +2330,7 @@ gs_plugin_loader_get_categories_async (GsPluginLoader *plugin_loader,
        /* save state */
        state = g_slice_new0 (GsPluginLoaderAsyncState);
        state->flags = flags;
+       state->catlist = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
 
        /* run in a thread */
        task = g_task_new (plugin_loader, cancellable, callback, user_data);
@@ -2326,7 +2343,7 @@ gs_plugin_loader_get_categories_async (GsPluginLoader *plugin_loader,
  *
  * Return value: (element-type GsCategory) (transfer full): A list of applications
  **/
-GList *
+GPtrArray *
 gs_plugin_loader_get_categories_finish (GsPluginLoader *plugin_loader,
                                        GAsyncResult *res,
                                        GError **error)
@@ -2380,7 +2397,7 @@ gs_plugin_loader_get_category_apps_thread_cb (GTask *task,
                                          gs_plugin_get_name (plugin),
                                          function_name);
                gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
-               ret = plugin_func (plugin, state->category, &state->list,
+               ret = plugin_func (plugin, state->category, state->list,
                                   cancellable, &error_local);
                gs_plugin_loader_action_stop (plugin_loader, plugin);
                if (!ret) {
@@ -2403,7 +2420,7 @@ gs_plugin_loader_get_category_apps_thread_cb (GTask *task,
        /* run refine() on each one */
        ret = gs_plugin_loader_run_refine (plugin_loader,
                                           function_name,
-                                          &state->list,
+                                          state->list,
                                           state->flags,
                                           cancellable,
                                           &error);
@@ -2413,12 +2430,12 @@ gs_plugin_loader_get_category_apps_thread_cb (GTask *task,
        }
 
        /* filter package list */
-       gs_app_list_filter_duplicates (&state->list);
-       gs_app_list_filter (&state->list, gs_plugin_loader_app_is_non_compulsory, NULL);
-       gs_app_list_filter (&state->list, gs_plugin_loader_app_is_valid, state);
-       gs_app_list_filter (&state->list, gs_plugin_loader_filter_qt_for_gtk, NULL);
-       gs_app_list_filter (&state->list, gs_plugin_loader_get_app_is_compatible, plugin_loader);
-       if (state->list == NULL) {
+       gs_app_list_filter_duplicates (state->list);
+       gs_app_list_filter (state->list, gs_plugin_loader_app_is_non_compulsory, NULL);
+       gs_app_list_filter (state->list, gs_plugin_loader_app_is_valid, state);
+       gs_app_list_filter (state->list, gs_plugin_loader_filter_qt_for_gtk, NULL);
+       gs_app_list_filter (state->list, gs_plugin_loader_get_app_is_compatible, plugin_loader);
+       if (gs_app_list_length (state->list) == 0) {
                g_task_return_new_error (task,
                                         GS_PLUGIN_LOADER_ERROR,
                                         GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
@@ -2427,10 +2444,10 @@ gs_plugin_loader_get_category_apps_thread_cb (GTask *task,
        }
 
        /* sort, just in case the UI doesn't do this */
-       state->list = g_list_sort (state->list, gs_plugin_loader_app_sort_cb);
+       gs_app_list_sort (state->list, gs_plugin_loader_app_sort_cb, NULL);
 
        /* success */
-       g_task_return_pointer (task, gs_app_list_copy (state->list), (GDestroyNotify) gs_app_list_free);
+       g_task_return_pointer (task, g_object_ref (state->list), (GDestroyNotify) g_object_unref);
 }
 
 /**
@@ -2471,6 +2488,7 @@ gs_plugin_loader_get_category_apps_async (GsPluginLoader *plugin_loader,
        /* save state */
        state = g_slice_new0 (GsPluginLoaderAsyncState);
        state->flags = flags;
+       state->list = gs_app_list_new ();
        state->category = g_object_ref (category);
 
        /* run in a thread */
@@ -2484,7 +2502,7 @@ gs_plugin_loader_get_category_apps_async (GsPluginLoader *plugin_loader,
  *
  * Return value: (element-type GsApp) (transfer full): A list of applications
  **/
-GList *
+GsAppList *
 gs_plugin_loader_get_category_apps_finish (GsPluginLoader *plugin_loader,
                                           GAsyncResult *res,
                                           GError **error)
@@ -2509,15 +2527,16 @@ gs_plugin_loader_app_refine_thread_cb (GTask *task,
                                       GCancellable *cancellable)
 {
        GError *error = NULL;
-       GList *list = NULL;
+       GsAppList *list;
        GsPluginLoaderAsyncState *state = (GsPluginLoaderAsyncState *) task_data;
        GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (object);
        gboolean ret;
 
-       gs_app_list_add (&list, state->app);
+       list = gs_app_list_new ();
+       gs_app_list_add (list, state->app);
        ret = gs_plugin_loader_run_refine (plugin_loader,
                                           NULL,
-                                          &list,
+                                          list,
                                           state->flags,
                                           cancellable,
                                           &error);
@@ -2529,7 +2548,7 @@ gs_plugin_loader_app_refine_thread_cb (GTask *task,
        /* success */
        g_task_return_boolean (task, TRUE);
 out:
-       gs_app_list_free (list);
+       g_object_unref (list);
 }
 
 /**
@@ -2637,10 +2656,11 @@ gs_plugin_loader_app_action_thread_cb (GTask *task,
                }
 
                /* refine again to make sure we pick up new source id */
-               gs_app_list_add (&list, state->app);
+               list = gs_app_list_new ();
+               gs_app_list_add (list, state->app);
                ret = gs_plugin_loader_run_refine (plugin_loader,
                                                   state->function_name,
-                                                  &list,
+                                                  list,
                                                   GS_PLUGIN_REFINE_FLAGS_REQUIRE_ORIGIN,
                                                   cancellable,
                                                   &error);
@@ -2761,12 +2781,12 @@ static gboolean
 load_install_queue (GsPluginLoader *plugin_loader, GError **error)
 {
        GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
-       GList *list = NULL;
        gboolean ret = TRUE;
        guint i;
        g_autofree gchar *contents = NULL;
        g_autofree gchar *file = NULL;
        g_auto(GStrv) names = NULL;
+       g_autoptr(GsAppList) list = NULL;
 
        /* load from file */
        file = g_build_filename (g_get_user_data_dir (),
@@ -2774,13 +2794,13 @@ load_install_queue (GsPluginLoader *plugin_loader, GError **error)
                                 "install-queue",
                                 NULL);
        if (!g_file_test (file, G_FILE_TEST_EXISTS))
-               goto out;
+               return TRUE;
        g_debug ("loading install queue from %s", file);
-       ret = g_file_get_contents (file, &contents, NULL, error);
-       if (!ret)
-               goto out;
+       if (!g_file_get_contents (file, &contents, NULL, error))
+               return FALSE;
 
        /* add each app-id */
+       list = gs_app_list_new ();
        names = g_strsplit (contents, "\n", 0);
        for (i = 0; names[i]; i++) {
                g_autoptr(GsApp) app = NULL;
@@ -2795,23 +2815,21 @@ load_install_queue (GsPluginLoader *plugin_loader, GError **error)
                g_mutex_unlock (&priv->pending_apps_mutex);
 
                g_debug ("adding pending app %s", gs_app_get_id (app));
-               gs_app_list_add (&list, app);
+               gs_app_list_add (list, app);
        }
 
        /* refine */
-       if (list != NULL) {
+       if (gs_app_list_length (list) > 0) {
                ret = gs_plugin_loader_run_refine (plugin_loader,
                                                   NULL,
-                                                  &list,
+                                                  list,
                                                   GS_PLUGIN_REFINE_FLAGS_DEFAULT,
                                                   NULL, //FIXME?
                                                   error);
                if (!ret)
-                       goto out;
+                       return FALSE;
        }
-out:
-       gs_app_list_free (list);
-       return ret;
+       return TRUE;
 }
 
 static void
@@ -2930,8 +2948,8 @@ gs_plugin_loader_app_action_async (GsPluginLoader *plugin_loader,
 
        /* handle with a fake list */
        if (action == GS_PLUGIN_LOADER_ACTION_UPDATE) {
-               g_autoptr(GsAppList) list = NULL;
-               gs_app_list_add (&list, app);
+               g_autoptr(GsAppList) list = gs_app_list_new ();
+               gs_app_list_add (list, app);
                gs_plugin_loader_update_async (plugin_loader, list,
                                               cancellable, callback,
                                               user_data);
@@ -3077,19 +3095,18 @@ gs_plugin_loader_app_action_finish (GsPluginLoader *plugin_loader,
 /**
  * gs_plugin_loader_get_pending:
  **/
-GPtrArray *
+GsAppList *
 gs_plugin_loader_get_pending (GsPluginLoader *plugin_loader)
 {
        GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
-       GPtrArray *array;
+       GsAppList *array;
        guint i;
 
-       array = g_ptr_array_new_with_free_func ((GFreeFunc) g_object_unref);
-
+       array = gs_app_list_new ();
        g_mutex_lock (&priv->pending_apps_mutex);
        for (i = 0; i < priv->pending_apps->len; i++) {
                GsApp *app = g_ptr_array_index (priv->pending_apps, i);
-               g_ptr_array_add (array, g_object_ref (app));
+               gs_app_list_add (array, app);
        }
        g_mutex_unlock (&priv->pending_apps_mutex);
 
@@ -3722,7 +3739,6 @@ gs_plugin_loader_set_network_status (GsPluginLoader *plugin_loader,
                                     gboolean online)
 {
        GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
-       GList *l;
        GsApp *app;
        guint i;
        g_autoptr(GsAppList) queue = NULL;
@@ -3738,14 +3754,15 @@ gs_plugin_loader_set_network_status (GsPluginLoader *plugin_loader,
                return;
 
        g_mutex_lock (&priv->pending_apps_mutex);
+       queue = gs_app_list_new ();
        for (i = 0; i < priv->pending_apps->len; i++) {
                app = g_ptr_array_index (priv->pending_apps, i);
                if (gs_app_get_state (app) == AS_APP_STATE_QUEUED_FOR_INSTALL)
-                       gs_app_list_add (&queue, app);
+                       gs_app_list_add (queue, app);
        }
        g_mutex_unlock (&priv->pending_apps_mutex);
-       for (l = queue; l; l = l->next) {
-               app = l->data;
+       for (i = 0; i < gs_app_list_length (queue); i++) {
+               app = gs_app_list_index (queue, i);
                gs_plugin_loader_app_action_async (plugin_loader,
                                                   app,
                                                   GS_PLUGIN_LOADER_ACTION_INSTALL,
@@ -3921,11 +3938,11 @@ gs_plugin_loader_file_to_app_thread_cb (GTask *task,
        const gchar *function_name = "gs_plugin_file_to_app";
        gboolean ret = TRUE;
        GError *error = NULL;
-       GList *l;
+       guint i;
+       guint j;
        GsPluginLoaderAsyncState *state = (GsPluginLoaderAsyncState *) task_data;
        GsPlugin *plugin;
        GsPluginFileToAppFunc plugin_func = NULL;
-       guint i;
 
        /* run each plugin */
        for (i = 0; i < priv->plugins->len; i++) {
@@ -3947,7 +3964,7 @@ gs_plugin_loader_file_to_app_thread_cb (GTask *task,
                                          gs_plugin_get_name (plugin),
                                          function_name);
                gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
-               ret = plugin_func (plugin, &state->list, state->file,
+               ret = plugin_func (plugin, state->list, state->file,
                                   cancellable, &error_local);
                gs_plugin_loader_action_stop (plugin_loader, plugin);
                if (!ret) {
@@ -3968,8 +3985,8 @@ gs_plugin_loader_file_to_app_thread_cb (GTask *task,
        }
 
        /* set the local file on any of the returned results */
-       for (l = state->list; l != NULL; l = l->next) {
-               GsApp *app = GS_APP (l->data);
+       for (j = 0; j < gs_app_list_length (state->list); j++) {
+               GsApp *app = gs_app_list_index (state->list, j);
                if (gs_app_get_local_file (app) == NULL)
                        gs_app_set_local_file (app, state->file);
        }
@@ -3977,7 +3994,7 @@ gs_plugin_loader_file_to_app_thread_cb (GTask *task,
        /* run refine() on each one */
        ret = gs_plugin_loader_run_refine (plugin_loader,
                                           function_name,
-                                          &state->list,
+                                          state->list,
                                           state->flags,
                                           cancellable,
                                           &error);
@@ -3987,8 +4004,8 @@ gs_plugin_loader_file_to_app_thread_cb (GTask *task,
        }
 
        /* filter package list */
-       gs_app_list_filter_duplicates (&state->list);
-       if (state->list == NULL) {
+       gs_app_list_filter_duplicates (state->list);
+       if (gs_app_list_length (state->list) == 0) {
                g_task_return_new_error (task,
                                         GS_PLUGIN_LOADER_ERROR,
                                         GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
@@ -3997,8 +4014,8 @@ gs_plugin_loader_file_to_app_thread_cb (GTask *task,
        }
 
        /* check the apps have an icon set */
-       for (l = state->list; l != NULL; l = l->next) {
-               GsApp *app = GS_APP (l->data);
+       for (j = 0; j < gs_app_list_length (state->list); j++) {
+               GsApp *app = gs_app_list_index (state->list, j);
                if (gs_app_get_icon (app) == NULL) {
                        g_autoptr(AsIcon) ic = as_icon_new ();
                        as_icon_set_kind (ic, AS_ICON_KIND_STOCK);
@@ -4013,7 +4030,7 @@ gs_plugin_loader_file_to_app_thread_cb (GTask *task,
        /* run refine() on each one again to pick up any icons */
        ret = gs_plugin_loader_run_refine (plugin_loader,
                                           function_name,
-                                          &state->list,
+                                          state->list,
                                           GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON,
                                           cancellable,
                                           &error);
@@ -4023,7 +4040,7 @@ gs_plugin_loader_file_to_app_thread_cb (GTask *task,
        }
 
        /* success */
-       if (g_list_length (state->list) != 1) {
+       if (gs_app_list_length (state->list) != 1) {
                g_task_return_new_error (task,
                                         GS_PLUGIN_LOADER_ERROR,
                                         GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
@@ -4031,7 +4048,7 @@ gs_plugin_loader_file_to_app_thread_cb (GTask *task,
                                         g_file_get_path (state->file));
                return;
        }
-       g_task_return_pointer (task, g_object_ref (state->list->data), (GDestroyNotify) g_object_unref);
+       g_task_return_pointer (task, g_object_ref (gs_app_list_index (state->list, 0)), (GDestroyNotify) 
g_object_unref);
 }
 
 /**
@@ -4066,6 +4083,7 @@ gs_plugin_loader_file_to_app_async (GsPluginLoader *plugin_loader,
        /* save state */
        state = g_slice_new0 (GsPluginLoaderAsyncState);
        state->flags = flags;
+       state->list = gs_app_list_new ();
        state->file = g_object_ref (file);
 
        /* run in a thread */
@@ -4155,7 +4173,7 @@ gs_plugin_loader_update_thread_cb (GTask *task,
        /* run each plugin, per-app version */
        function_name = "gs_plugin_update_app";
        for (i = 0; i < priv->plugins->len; i++) {
-               GList *l;
+               guint j;
 
                plugin = g_ptr_array_index (priv->plugins, i);
                if (!gs_plugin_get_enabled (plugin))
@@ -4170,8 +4188,8 @@ gs_plugin_loader_update_thread_cb (GTask *task,
                        continue;
 
                /* for each app */
-               for (l = state->list; l != NULL; l = l->next) {
-                       GsApp *app = GS_APP (l->data);
+               for (j = 0; j < gs_app_list_length (state->list); j++) {
+                       GsApp *app = gs_app_list_index (state->list, j);
                        g_autoptr(AsProfileTask) ptask = NULL;
                        g_autoptr(GError) error_local = NULL;
 
@@ -4215,7 +4233,7 @@ gs_plugin_loader_update_thread_cb (GTask *task,
  **/
 void
 gs_plugin_loader_update_async (GsPluginLoader *plugin_loader,
-                              GList *apps,
+                              GsAppList *apps,
                               GCancellable *cancellable,
                               GAsyncReadyCallback callback,
                               gpointer user_data)
@@ -4228,7 +4246,7 @@ gs_plugin_loader_update_async (GsPluginLoader *plugin_loader,
 
        /* save state */
        state = g_slice_new0 (GsPluginLoaderAsyncState);
-       state->list = g_list_copy_deep (apps, (GCopyFunc) g_object_ref, NULL);
+       state->list = gs_app_list_copy (apps);
 
        /* run in a thread */
        task = g_task_new (plugin_loader, cancellable, callback, user_data);
diff --git a/src/gs-plugin-loader.h b/src/gs-plugin-loader.h
index 7586ca7..184cab4 100644
--- a/src/gs-plugin-loader.h
+++ b/src/gs-plugin-loader.h
@@ -79,7 +79,7 @@ void           gs_plugin_loader_get_installed_async   (GsPluginLoader *plugin_loader,
                                                         GCancellable   *cancellable,
                                                         GAsyncReadyCallback callback,
                                                         gpointer        user_data);
-GList          *gs_plugin_loader_get_installed_finish  (GsPluginLoader *plugin_loader,
+GsAppList      *gs_plugin_loader_get_installed_finish  (GsPluginLoader *plugin_loader,
                                                         GAsyncResult   *res,
                                                         GError         **error);
 void            gs_plugin_loader_get_updates_async     (GsPluginLoader *plugin_loader,
@@ -87,7 +87,7 @@ void           gs_plugin_loader_get_updates_async     (GsPluginLoader *plugin_loader,
                                                         GCancellable   *cancellable,
                                                         GAsyncReadyCallback callback,
                                                         gpointer        user_data);
-GList          *gs_plugin_loader_get_updates_finish    (GsPluginLoader *plugin_loader,
+GsAppList      *gs_plugin_loader_get_updates_finish    (GsPluginLoader *plugin_loader,
                                                         GAsyncResult   *res,
                                                         GError         **error);
 void            gs_plugin_loader_get_distro_upgrades_async (GsPluginLoader     *plugin_loader,
@@ -95,7 +95,7 @@ void           gs_plugin_loader_get_distro_upgrades_async (GsPluginLoader     *plugin_loader
                                                         GCancellable   *cancellable,
                                                         GAsyncReadyCallback callback,
                                                         gpointer        user_data);
-GList          *gs_plugin_loader_get_distro_upgrades_finish (GsPluginLoader    *plugin_loader,
+GsAppList      *gs_plugin_loader_get_distro_upgrades_finish (GsPluginLoader    *plugin_loader,
                                                         GAsyncResult   *res,
                                                         GError         **error);
 void            gs_plugin_loader_get_unvoted_reviews_async (GsPluginLoader     *plugin_loader,
@@ -103,7 +103,7 @@ void                 gs_plugin_loader_get_unvoted_reviews_async (GsPluginLoader     
*plugin_loader
                                                         GCancellable   *cancellable,
                                                         GAsyncReadyCallback callback,
                                                         gpointer        user_data);
-GList          *gs_plugin_loader_get_unvoted_reviews_finish (GsPluginLoader    *plugin_loader,
+GsAppList      *gs_plugin_loader_get_unvoted_reviews_finish (GsPluginLoader    *plugin_loader,
                                                         GAsyncResult   *res,
                                                         GError         **error);
 void            gs_plugin_loader_get_sources_async     (GsPluginLoader *plugin_loader,
@@ -111,7 +111,7 @@ void                 gs_plugin_loader_get_sources_async     (GsPluginLoader 
*plugin_loader,
                                                         GCancellable   *cancellable,
                                                         GAsyncReadyCallback callback,
                                                         gpointer        user_data);
-GList          *gs_plugin_loader_get_sources_finish    (GsPluginLoader *plugin_loader,
+GsAppList      *gs_plugin_loader_get_sources_finish    (GsPluginLoader *plugin_loader,
                                                         GAsyncResult   *res,
                                                         GError         **error);
 void            gs_plugin_loader_get_popular_async     (GsPluginLoader *plugin_loader,
@@ -119,7 +119,7 @@ void                 gs_plugin_loader_get_popular_async     (GsPluginLoader 
*plugin_loader,
                                                         GCancellable   *cancellable,
                                                         GAsyncReadyCallback callback,
                                                         gpointer        user_data);
-GList          *gs_plugin_loader_get_popular_finish    (GsPluginLoader *plugin_loader,
+GsAppList      *gs_plugin_loader_get_popular_finish    (GsPluginLoader *plugin_loader,
                                                         GAsyncResult   *res,
                                                         GError         **error);
 void            gs_plugin_loader_get_featured_async    (GsPluginLoader *plugin_loader,
@@ -127,7 +127,7 @@ void                 gs_plugin_loader_get_featured_async    (GsPluginLoader 
*plugin_loader,
                                                         GCancellable   *cancellable,
                                                         GAsyncReadyCallback callback,
                                                         gpointer        user_data);
-GList          *gs_plugin_loader_get_featured_finish   (GsPluginLoader *plugin_loader,
+GsAppList      *gs_plugin_loader_get_featured_finish   (GsPluginLoader *plugin_loader,
                                                         GAsyncResult   *res,
                                                         GError         **error);
 void            gs_plugin_loader_get_categories_async  (GsPluginLoader *plugin_loader,
@@ -135,7 +135,7 @@ void                 gs_plugin_loader_get_categories_async  (GsPluginLoader 
*plugin_loader,
                                                         GCancellable   *cancellable,
                                                         GAsyncReadyCallback callback,
                                                         gpointer        user_data);
-GList          *gs_plugin_loader_get_categories_finish (GsPluginLoader *plugin_loader,
+GPtrArray      *gs_plugin_loader_get_categories_finish (GsPluginLoader *plugin_loader,
                                                         GAsyncResult   *res,
                                                         GError         **error);
 void            gs_plugin_loader_get_category_apps_async (GsPluginLoader       *plugin_loader,
@@ -144,7 +144,7 @@ void                 gs_plugin_loader_get_category_apps_async (GsPluginLoader       
*plugin_loader,
                                                         GCancellable   *cancellable,
                                                         GAsyncReadyCallback callback,
                                                         gpointer        user_data);
-GList          *gs_plugin_loader_get_category_apps_finish (GsPluginLoader      *plugin_loader,
+GsAppList      *gs_plugin_loader_get_category_apps_finish (GsPluginLoader      *plugin_loader,
                                                         GAsyncResult   *res,
                                                         GError         **error);
 void            gs_plugin_loader_search_async          (GsPluginLoader *plugin_loader,
@@ -153,7 +153,7 @@ void                 gs_plugin_loader_search_async          (GsPluginLoader 
*plugin_loader,
                                                         GCancellable   *cancellable,
                                                         GAsyncReadyCallback callback,
                                                         gpointer        user_data);
-GList          *gs_plugin_loader_search_finish         (GsPluginLoader *plugin_loader,
+GsAppList      *gs_plugin_loader_search_finish         (GsPluginLoader *plugin_loader,
                                                         GAsyncResult   *res,
                                                         GError         **error);
 void            gs_plugin_loader_search_files_async    (GsPluginLoader *plugin_loader,
@@ -162,7 +162,7 @@ void                 gs_plugin_loader_search_files_async    (GsPluginLoader 
*plugin_loader,
                                                         GCancellable   *cancellable,
                                                         GAsyncReadyCallback callback,
                                                         gpointer        user_data);
-GList          *gs_plugin_loader_search_files_finish   (GsPluginLoader *plugin_loader,
+GsAppList      *gs_plugin_loader_search_files_finish   (GsPluginLoader *plugin_loader,
                                                         GAsyncResult   *res,
                                                         GError         **error);
 void            gs_plugin_loader_search_what_provides_async (GsPluginLoader    *plugin_loader,
@@ -171,7 +171,7 @@ void                 gs_plugin_loader_search_what_provides_async (GsPluginLoader    
*plugin_loade
                                                         GCancellable   *cancellable,
                                                         GAsyncReadyCallback callback,
                                                         gpointer        user_data);
-GList          *gs_plugin_loader_search_what_provides_finish (GsPluginLoader   *plugin_loader,
+GsAppList      *gs_plugin_loader_search_what_provides_finish (GsPluginLoader   *plugin_loader,
                                                         GAsyncResult   *res,
                                                         GError         **error);
 void            gs_plugin_loader_file_to_app_async     (GsPluginLoader *plugin_loader,
@@ -184,7 +184,7 @@ GsApp               *gs_plugin_loader_file_to_app_finish    (GsPluginLoader 
*plugin_loader,
                                                         GAsyncResult   *res,
                                                         GError         **error);
 void            gs_plugin_loader_update_async          (GsPluginLoader *plugin_loader,
-                                                        GList          *apps,
+                                                        GsAppList      *apps,
                                                         GCancellable   *cancellable,
                                                         GAsyncReadyCallback callback,
                                                         gpointer        user_data);
@@ -236,7 +236,7 @@ void                 gs_plugin_loader_refresh_async         (GsPluginLoader 
*plugin_loader,
                                                         GCancellable   *cancellable,
                                                         GAsyncReadyCallback callback,
                                                         gpointer        user_data);
-GPtrArray      *gs_plugin_loader_get_pending           (GsPluginLoader *plugin_loader);
+GsAppList      *gs_plugin_loader_get_pending           (GsPluginLoader *plugin_loader);
 void            gs_plugin_loader_set_network_status    (GsPluginLoader *plugin_loader,
                                                         gboolean        online);
 gboolean        gs_plugin_loader_get_plugin_supported  (GsPluginLoader *plugin_loader,
diff --git a/src/gs-plugin.h b/src/gs-plugin.h
index 49d12d0..1015567 100644
--- a/src/gs-plugin.h
+++ b/src/gs-plugin.h
@@ -250,17 +250,17 @@ void               gs_plugin_adopt_app                    (GsPlugin       *plugin,
                                                         GsApp          *app);
 gboolean        gs_plugin_add_search                   (GsPlugin       *plugin,
                                                         gchar          **values,
-                                                        GList          **list,
+                                                        GsAppList      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 gboolean        gs_plugin_add_search_files             (GsPlugin       *plugin,
                                                         gchar          **values,
-                                                        GList          **list,
+                                                        GsAppList      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 gboolean        gs_plugin_add_search_what_provides     (GsPlugin       *plugin,
                                                         gchar          **values,
-                                                        GList          **list,
+                                                        GsAppList      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 const gchar    **gs_plugin_order_after                 (GsPlugin       *plugin);
@@ -270,48 +270,48 @@ gboolean   gs_plugin_setup                        (GsPlugin       *plugin,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 gboolean        gs_plugin_add_installed                (GsPlugin       *plugin,
-                                                        GList          **list,
+                                                        GsAppList      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 gboolean        gs_plugin_add_updates                  (GsPlugin       *plugin,
-                                                        GList          **list,
+                                                        GsAppList      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 gboolean        gs_plugin_add_distro_upgrades          (GsPlugin       *plugin,
-                                                        GList          **list,
+                                                        GsAppList      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 gboolean        gs_plugin_add_sources                  (GsPlugin       *plugin,
-                                                        GList          **list,
+                                                        GsAppList      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 gboolean        gs_plugin_add_updates_historical       (GsPlugin       *plugin,
-                                                        GList          **list,
+                                                        GsAppList      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 gboolean        gs_plugin_add_categories               (GsPlugin       *plugin,
-                                                        GList          **list,
+                                                        GPtrArray      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 gboolean        gs_plugin_add_category_apps            (GsPlugin       *plugin,
                                                         GsCategory     *category,
-                                                        GList          **list,
+                                                        GsAppList      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 gboolean        gs_plugin_add_popular                  (GsPlugin       *plugin,
-                                                        GList          **list,
+                                                        GsAppList      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 gboolean        gs_plugin_add_featured                 (GsPlugin       *plugin,
-                                                        GList          **list,
+                                                        GsAppList      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 gboolean        gs_plugin_add_unvoted_reviews          (GsPlugin       *plugin,
-                                                        GList          **list,
+                                                        GsAppList      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 gboolean        gs_plugin_refine                       (GsPlugin       *plugin,
-                                                        GList          **list,
+                                                        GsAppList      *list,
                                                         GsPluginRefineFlags flags,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
@@ -400,12 +400,12 @@ gboolean   gs_plugin_refresh                      (GsPlugin       *plugin,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 gboolean        gs_plugin_file_to_app                  (GsPlugin       *plugin,
-                                                        GList          **list,
+                                                        GsAppList      *list,
                                                         GFile          *file,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 gboolean        gs_plugin_update                       (GsPlugin       *plugin,
-                                                        GList          *apps,
+                                                        GsAppList      *apps,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 
diff --git a/src/gs-self-test.c b/src/gs-self-test.c
index 8062d2c..2947a5c 100644
--- a/src/gs-self-test.c
+++ b/src/gs-self-test.c
@@ -105,50 +105,52 @@ gs_app_list_filter_cb (GsApp *app, gpointer user_data)
 static void
 gs_plugin_func (void)
 {
-       GList *list = NULL;
-       GList *list_dup;
-       GList *list_remove = NULL;
+       GsAppList *list;
+       GsAppList *list_dup;
+       GsAppList *list_remove;
        GsApp *app;
 
        /* add a couple of duplicate IDs */
        app = gs_app_new ("a");
-       gs_app_list_add (&list, app);
+       list = gs_app_list_new ();
+       gs_app_list_add (list, app);
        g_object_unref (app);
 
        /* test refcounting */
-       g_assert_cmpstr (gs_app_get_id (GS_APP (list->data)), ==, "a");
+       g_assert_cmpstr (gs_app_get_id (gs_app_list_index (list, 0)), ==, "a");
        list_dup = gs_app_list_copy (list);
-       gs_app_list_free (list);
-       g_assert_cmpint (g_list_length (list_dup), ==, 1);
-       g_assert_cmpstr (gs_app_get_id (GS_APP (list_dup->data)), ==, "a");
-       gs_app_list_free (list_dup);
+       g_object_unref (list);
+       g_assert_cmpint (gs_app_list_length (list_dup), ==, 1);
+       g_assert_cmpstr (gs_app_get_id (gs_app_list_index (list_dup, 0)), ==, "a");
+       g_object_unref (list_dup);
 
        /* test removing obects */
        app = gs_app_new ("a");
-       gs_app_list_add (&list_remove, app);
+       list_remove = gs_app_list_new ();
+       gs_app_list_add (list_remove, app);
        g_object_unref (app);
        app = gs_app_new ("b");
-       gs_app_list_add (&list_remove, app);
+       gs_app_list_add (list_remove, app);
        g_object_unref (app);
        app = gs_app_new ("c");
-       gs_app_list_add (&list_remove, app);
+       gs_app_list_add (list_remove, app);
        g_object_unref (app);
-       g_assert_cmpint (g_list_length (list_remove), ==, 3);
-       gs_app_list_filter (&list_remove, gs_app_list_filter_cb, NULL);
-       g_assert_cmpint (g_list_length (list_remove), ==, 1);
-       g_assert_cmpstr (gs_app_get_id (GS_APP (list_remove->data)), ==, "b");
+       g_assert_cmpint (gs_app_list_length (list_remove), ==, 3);
+       gs_app_list_filter (list_remove, gs_app_list_filter_cb, NULL);
+       g_assert_cmpint (gs_app_list_length (list_remove), ==, 1);
+       g_assert_cmpstr (gs_app_get_id (gs_app_list_index (list_remove, 0)), ==, "b");
 
        /* test removing duplicates */
        app = gs_app_new ("b");
-       gs_app_list_add (&list_remove, app);
+       gs_app_list_add (list_remove, app);
        g_object_unref (app);
        app = gs_app_new ("b");
-       gs_app_list_add (&list_remove, app);
+       gs_app_list_add (list_remove, app);
        g_object_unref (app);
-       gs_app_list_filter_duplicates (&list_remove);
-       g_assert_cmpint (g_list_length (list_remove), ==, 1);
-       g_assert_cmpstr (gs_app_get_id (GS_APP (list_remove->data)), ==, "b");
-       gs_app_list_free (list_remove);
+       gs_app_list_filter_duplicates (list_remove);
+       g_assert_cmpint (gs_app_list_length (list_remove), ==, 1);
+       g_assert_cmpstr (gs_app_get_id (gs_app_list_index (list_remove, 0)), ==, "b");
+       g_object_unref (list_remove);
 }
 
 static void
@@ -323,8 +325,8 @@ gs_plugin_loader_updates_func (GsPluginLoader *plugin_loader)
        g_assert (list != NULL);
 
        /* make sure there are two entries */
-       g_assert_cmpint (g_list_length (list), ==, 2);
-       app = g_list_nth_data (list, 0);
+       g_assert_cmpint (gs_app_list_length (list), ==, 2);
+       app = gs_app_list_index (list, 0);
        g_assert_cmpstr (gs_app_get_id (app), ==, "chiron.desktop");
        g_assert_cmpint (gs_app_get_kind (app), ==, AS_APP_KIND_DESKTOP);
        g_assert_cmpint (gs_app_get_state (app), ==, AS_APP_STATE_UPDATABLE_LIVE);
@@ -332,7 +334,7 @@ gs_plugin_loader_updates_func (GsPluginLoader *plugin_loader)
        g_assert_cmpint (gs_app_get_update_urgency (app), ==, AS_URGENCY_KIND_HIGH);
 
        /* get the virtual non-apps OS update */
-       app = g_list_nth_data (list, 1);
+       app = gs_app_list_index (list, 1);
        g_assert_cmpstr (gs_app_get_id (app), ==, "os-update.virtual");
        g_assert_cmpstr (gs_app_get_name (app), ==, "OS Updates");
        g_assert_cmpstr (gs_app_get_summary (app), ==, "Includes performance, stability and security 
improvements.");
@@ -358,8 +360,8 @@ gs_plugin_loader_distro_upgrades_func (GsPluginLoader *plugin_loader)
        g_assert (list != NULL);
 
        /* make sure there is one entry */
-       g_assert_cmpint (g_list_length (list), ==, 1);
-       app = GS_APP (list->data);
+       g_assert_cmpint (gs_app_list_length (list), ==, 1);
+       app = gs_app_list_index (list, 0);
        g_assert_cmpstr (gs_app_get_id (app), ==, "org.fedoraproject.release-rawhide.upgrade");
        g_assert_cmpint (gs_app_get_kind (app), ==, AS_APP_KIND_OS_UPGRADE);
        g_assert_cmpint (gs_app_get_state (app), ==, AS_APP_STATE_AVAILABLE);
@@ -412,8 +414,8 @@ gs_plugin_loader_installed_func (GsPluginLoader *plugin_loader)
        g_assert (list != NULL);
 
        /* make sure there is one entry */
-       g_assert_cmpint (g_list_length (list), ==, 1);
-       app = GS_APP (list->data);
+       g_assert_cmpint (gs_app_list_length (list), ==, 1);
+       app = gs_app_list_index (list, 0);
        g_assert_cmpstr (gs_app_get_id (app), ==, "zeus.desktop");
        g_assert_cmpint (gs_app_get_kind (app), ==, AS_APP_KIND_DESKTOP);
        g_assert_cmpint (gs_app_get_state (app), ==, AS_APP_STATE_INSTALLED);
@@ -471,8 +473,8 @@ gs_plugin_loader_search_func (GsPluginLoader *plugin_loader)
        g_assert (list != NULL);
 
        /* make sure there is one entry, the parent app */
-       g_assert_cmpint (g_list_length (list), ==, 1);
-       app = GS_APP (list->data);
+       g_assert_cmpint (gs_app_list_length (list), ==, 1);
+       app = gs_app_list_index (list, 0);
        g_assert_cmpstr (gs_app_get_id (app), ==, "zeus.desktop");
        g_assert_cmpint (gs_app_get_kind (app), ==, AS_APP_KIND_DESKTOP);
 }
@@ -681,8 +683,8 @@ gs_plugin_loader_flatpak_func (GsPluginLoader *plugin_loader)
                                                &error);
        g_assert_no_error (error);
        g_assert (sources != NULL);
-       g_assert_cmpint (g_list_length (sources), ==, 1);
-       app = GS_APP (sources->data);
+       g_assert_cmpint (gs_app_list_length (sources), ==, 1);
+       app = gs_app_list_index (sources, 0);
        g_assert_cmpstr (gs_app_get_id (app), ==, "test");
        g_assert_cmpint (gs_app_get_kind (app), ==, AS_APP_KIND_SOURCE);
 
@@ -705,8 +707,8 @@ gs_plugin_loader_flatpak_func (GsPluginLoader *plugin_loader)
        g_assert (list != NULL);
 
        /* make sure there is one entry, the flatpak app */
-       g_assert_cmpint (g_list_length (list), ==, 1);
-       app = GS_APP (list->data);
+       g_assert_cmpint (gs_app_list_length (list), ==, 1);
+       app = gs_app_list_index (list, 0);
        g_assert_cmpstr (gs_app_get_id (app), ==, "org.test.Chiron.desktop");
        g_assert_cmpint (gs_app_get_kind (app), ==, AS_APP_KIND_DESKTOP);
        g_assert_cmpint (gs_app_get_state (app), ==, AS_APP_STATE_AVAILABLE);
diff --git a/src/gs-shell-category.c b/src/gs-shell-category.c
index 7701891..8b6c280 100644
--- a/src/gs-shell-category.c
+++ b/src/gs-shell-category.c
@@ -86,8 +86,7 @@ gs_shell_category_get_apps_cb (GObject *source_object,
                               GAsyncResult *res,
                               gpointer user_data)
 {
-       gint i = 0;
-       GList *l;
+       guint i;
        GsApp *app;
        GtkWidget *tile;
        GsShellCategory *self = GS_SHELL_CATEGORY (user_data);
@@ -107,8 +106,8 @@ gs_shell_category_get_apps_cb (GObject *source_object,
                return;
        }
 
-       for (l = list, i = 0; l != NULL; l = l->next, i++) {
-               app = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               app = gs_app_list_index (list, i);
                tile = gs_app_tile_new (app);
                g_signal_connect (tile, "clicked",
                                  G_CALLBACK (app_tile_clicked), self);
@@ -180,20 +179,16 @@ gs_shell_category_create_filter_list (GsShellCategory *self,
                                      GsCategory *subcategory)
 {
        GtkWidget *row;
-       GList *l;
        GsCategory *s;
-       g_autoptr(GList) list = NULL;
+       guint i;
+       GPtrArray *children;
 
        gs_container_remove_all (GTK_CONTAINER (self->category_detail_box));
-
-       list = gs_category_get_subcategories (category);
-       if (!list)
-               return;
-
        gs_container_remove_all (GTK_CONTAINER (self->listbox_filter));
 
-       for  (l = list; l; l = l->next) {
-               s = l->data;
+       children = gs_category_get_children (category);
+       for (i = 0; i < children->len; i++) {
+               s = GS_CATEGORY (g_ptr_array_index (children, i));
                if (gs_category_get_size (s) < 1)
                        continue;
                row = gtk_label_new (gs_category_get_name (s));
@@ -209,10 +204,10 @@ gs_shell_category_create_filter_list (GsShellCategory *self,
 void
 gs_shell_category_set_category (GsShellCategory *self, GsCategory *category)
 {
+       GPtrArray *children = NULL;
        GsCategory *sub;
        GsCategory *selected = NULL;
-       GList *l;
-       g_autoptr(GList) list = NULL;
+       guint i;
 
        /* this means we've come from the app-view -> back */
        if (self->category == category)
@@ -223,9 +218,9 @@ gs_shell_category_set_category (GsShellCategory *self, GsCategory *category)
        self->category = g_object_ref (category);
 
        /* select favourites by default */
-       list = gs_category_get_subcategories (category);
-       for (l = list; l != NULL; l = l->next) {
-               sub = GS_CATEGORY (l->data);
+       children = gs_category_get_children (category);
+       for (i = 0; i < children->len; i++) {
+               sub = GS_CATEGORY (g_ptr_array_index (children, i));
                if (g_strcmp0 (gs_category_get_id (sub), "favourites") == 0) {
                        selected = sub;
                        break;
@@ -233,8 +228,8 @@ gs_shell_category_set_category (GsShellCategory *self, GsCategory *category)
        }
 
        /* okay, no favourites, so just select the first entry */
-       if (selected == NULL && list != NULL)
-               selected = GS_CATEGORY (list->data);
+       if (selected == NULL && children->len > 0)
+               selected = GS_CATEGORY (g_ptr_array_index (children, 0));
 
        /* find apps in this group */
        gs_shell_category_create_filter_list (self, category, selected);
diff --git a/src/gs-shell-extras.c b/src/gs-shell-extras.c
index 1ab8ab9..b7f783a 100644
--- a/src/gs-shell-extras.c
+++ b/src/gs-shell-extras.c
@@ -516,7 +516,7 @@ search_files_cb (GObject *source_object,
        SearchData *search_data = (SearchData *) user_data;
        GsShellExtras *self = search_data->self;
        g_autoptr(GsAppList) list = NULL;
-       GList *l;
+       guint i;
        GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source_object);
        g_autoptr(GError) error = NULL;
 
@@ -533,7 +533,7 @@ search_files_cb (GObject *source_object,
 
                        g_debug ("extras: no search result for %s, showing as missing", search_data->title);
                        app = create_missing_app (search_data);
-                       list = g_list_prepend (list, app);
+                       gs_app_list_add (list, app);
                } else {
                        g_autofree gchar *str = NULL;
 
@@ -546,8 +546,8 @@ search_files_cb (GObject *source_object,
                }
        }
 
-       for (l = list; l != NULL; l = l->next) {
-               GsApp *app = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               GsApp *app = gs_app_list_index (list, i);
 
                g_debug ("%s\n\n", gs_app_to_string (app));
                gs_shell_extras_add_app (self, app, search_data);
@@ -614,7 +614,7 @@ get_search_what_provides_cb (GObject *source_object,
        SearchData *search_data = (SearchData *) user_data;
        GsShellExtras *self = search_data->self;
        g_autoptr(GsAppList) list = NULL;
-       GList *l;
+       guint i;
        GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source_object);
        g_autoptr(GError) error = NULL;
 
@@ -631,7 +631,7 @@ get_search_what_provides_cb (GObject *source_object,
 
                        g_debug ("extras: no search result for %s, showing as missing", search_data->title);
                        app = create_missing_app (search_data);
-                       list = g_list_prepend (list, app);
+                       gs_app_list_add (list, app);
                } else {
                        g_autofree gchar *str = NULL;
 
@@ -644,8 +644,8 @@ get_search_what_provides_cb (GObject *source_object,
                }
        }
 
-       for (l = list; l != NULL; l = l->next) {
-               GsApp *app = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               GsApp *app = gs_app_list_index (list, i);
 
                g_debug ("%s\n\n", gs_app_to_string (app));
                gs_shell_extras_add_app (self, app, search_data);
diff --git a/src/gs-shell-installed.c b/src/gs-shell-installed.c
index 92bc7c2..65a8a75 100644
--- a/src/gs-shell-installed.c
+++ b/src/gs-shell-installed.c
@@ -188,7 +188,7 @@ gs_shell_installed_get_installed_cb (GObject *source_object,
                                     GAsyncResult *res,
                                     gpointer user_data)
 {
-       GList *l;
+       guint i;
        GsApp *app;
        GsShellInstalled *self = GS_SHELL_INSTALLED (user_data);
        GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source_object);
@@ -209,8 +209,8 @@ gs_shell_installed_get_installed_cb (GObject *source_object,
                        g_warning ("failed to get installed apps: %s", error->message);
                goto out;
        }
-       for (l = list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               app = gs_app_list_index (list, i);
                gs_shell_installed_add_app (self, app);
        }
 out:
@@ -480,21 +480,21 @@ gs_shell_installed_pending_apps_changed_cb (GsPluginLoader *plugin_loader,
        GsApp *app;
        GtkWidget *widget;
        guint i;
-       g_autoptr(GPtrArray) pending = NULL;
+       g_autoptr(GsAppList) pending = NULL;
 
        widget = GTK_WIDGET (gtk_builder_get_object (self->builder,
                                                     "button_installed_counter"));
        pending = gs_plugin_loader_get_pending (plugin_loader);
-       if (pending->len == 0) {
+       if (gs_app_list_length (pending) == 0) {
                gtk_widget_hide (widget);
        } else {
                g_autofree gchar *label = NULL;
                gtk_widget_show (widget);
-               label = g_strdup_printf ("%d", pending->len);
+               label = g_strdup_printf ("%d", gs_app_list_length (pending));
                gtk_label_set_label (GTK_LABEL (widget), label);
        }
-       for (i = 0; i < pending->len; i++) {
-               app = GS_APP (g_ptr_array_index (pending, i));
+       for (i = 0; i < gs_app_list_length (pending); i++) {
+               app = gs_app_list_index (pending, i);
                /* Be careful not to add pending apps more than once. */
                if (gs_shell_installed_has_app (self, app) == FALSE)
                        gs_shell_installed_add_app (self, app);
diff --git a/src/gs-shell-moderate.c b/src/gs-shell-moderate.c
index 482cb90..a2992cf 100644
--- a/src/gs-shell-moderate.c
+++ b/src/gs-shell-moderate.c
@@ -143,7 +143,7 @@ gs_shell_moderate_get_unvoted_reviews_cb (GObject *source_object,
                                          GAsyncResult *res,
                                          gpointer user_data)
 {
-       GList *l;
+       guint i;
        GsApp *app;
        GsShellModerate *self = GS_SHELL_MODERATE (user_data);
        GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source_object);
@@ -168,8 +168,8 @@ gs_shell_moderate_get_unvoted_reviews_cb (GObject *source_object,
                        g_warning ("failed to get moderate apps: %s", error->message);
                return;
        }
-       for (l = list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               app = gs_app_list_index (list, i);
                gs_shell_moderate_add_app (self, app);
        }
 }
diff --git a/src/gs-shell-overview.c b/src/gs-shell-overview.c
index 2f91112..30cd1cf 100644
--- a/src/gs-shell-overview.c
+++ b/src/gs-shell-overview.c
@@ -127,9 +127,8 @@ gs_shell_overview_get_popular_cb (GObject *source_object,
        GsShellOverview *self = GS_SHELL_OVERVIEW (user_data);
        GsShellOverviewPrivate *priv = gs_shell_overview_get_instance_private (self);
        GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source_object);
-       GList *l;
+       guint i;
        GsApp *app;
-       gint i;
        GtkWidget *tile;
        g_autoptr(GError) error = NULL;
        g_autoptr(GsAppList) list = NULL;
@@ -144,13 +143,13 @@ gs_shell_overview_get_popular_cb (GObject *source_object,
                goto out;
        }
        /* Don't show apps from the category that's currently featured as the category of the day */
-       gs_app_list_filter (&list, filter_category, priv->category_of_day);
-       gs_app_list_randomize (&list);
+       gs_app_list_filter (list, filter_category, priv->category_of_day);
+       gs_app_list_randomize (list);
 
        gs_container_remove_all (GTK_CONTAINER (priv->box_popular));
 
-       for (l = list, i = 0; l != NULL && i < N_TILES; l = l->next, i++) {
-               app = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (list) && i < N_TILES; i++) {
+               app = gs_app_list_index (list, i);
                tile = gs_popular_tile_new (app);
                g_signal_connect (tile, "clicked",
                          G_CALLBACK (popular_tile_clicked), self);
@@ -177,9 +176,8 @@ gs_shell_overview_get_popular_rotating_cb (GObject *source_object,
        GsShellOverview *self = load_data->self;
        GsShellOverviewPrivate *priv = gs_shell_overview_get_instance_private (self);
        GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source_object);
-       GList *l;
+       guint i;
        GsApp *app;
-       gint i;
        GtkWidget *tile;
        g_autoptr(GError) error = NULL;
        g_autoptr(GsAppList) list = NULL;
@@ -192,21 +190,23 @@ gs_shell_overview_get_popular_rotating_cb (GObject *source_object,
                gtk_widget_hide (priv->popular_rotating_heading);
                gtk_widget_hide (priv->box_popular_rotating);
                goto out;
-       } else if (g_list_length (list) < N_TILES) {
-               g_warning ("hiding recommended applications: found only %d to show, need at least %d", 
g_list_length (list), N_TILES);
+       } else if (gs_app_list_length (list) < N_TILES) {
+               g_warning ("hiding recommended applications: "
+                          "found only %d to show, need at least %d",
+                          gs_app_list_length (list), N_TILES);
                gtk_widget_hide (priv->popular_rotating_heading);
                gtk_widget_hide (priv->box_popular_rotating);
                goto out;
        }
-       gs_app_list_randomize (&list);
+       gs_app_list_randomize (list);
 
        gtk_widget_show (priv->popular_rotating_heading);
        gtk_widget_show (priv->box_popular_rotating);
 
        gs_container_remove_all (GTK_CONTAINER (priv->box_popular_rotating));
 
-       for (l = list, i = 0; l != NULL && i < N_TILES; l = l->next, i++) {
-               app = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (list) && i < N_TILES; i++) {
+               app = gs_app_list_index (list, i);
                tile = gs_popular_tile_new (app);
                g_signal_connect (tile, "clicked",
                          G_CALLBACK (popular_tile_clicked), self);
@@ -255,19 +255,25 @@ gs_shell_overview_get_featured_cb (GObject *source_object,
 
        if (g_getenv ("GNOME_SOFTWARE_FEATURED") == NULL) {
                /* Don't show apps from the category that's currently featured as the category of the day */
-               gs_app_list_filter (&list, filter_category, priv->category_of_day);
-               gs_app_list_randomize (&list);
+               gs_app_list_filter (list, filter_category, priv->category_of_day);
+               gs_app_list_randomize (list);
        }
 
        gs_container_remove_all (GTK_CONTAINER (priv->bin_featured));
        gtk_widget_set_visible (priv->featured_heading, list != NULL);
        if (list == NULL) {
-               g_warning ("failed to get featured apps: %s", error ? error->message : "no apps to show");
+               g_warning ("failed to get featured apps: %s",
+                          error->message);
+               goto out;
+       }
+       if (gs_app_list_length (list) == 0) {
+               g_warning ("failed to get featured apps: "
+                          "no apps to show");
                goto out;
        }
 
        /* at the moment, we only care about the first app */
-       app = GS_APP (list->data);
+       app = gs_app_list_index (list, 0);
        tile = gs_feature_tile_new (app);
        g_signal_connect (tile, "clicked",
                          G_CALLBACK (feature_tile_clicked), self);
@@ -307,12 +313,12 @@ gs_shell_overview_get_categories_cb (GObject *source_object,
        GsShellOverview *self = GS_SHELL_OVERVIEW (user_data);
        GsShellOverviewPrivate *priv = gs_shell_overview_get_instance_private (self);
        GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source_object);
-       GList *l;
+       guint i;
        GsCategory *cat;
        GtkWidget *tile;
        gboolean has_category = FALSE;
        g_autoptr(GError) error = NULL;
-       g_autoptr(GsAppList) list = NULL;
+       g_autoptr(GPtrArray) list = NULL;
 
        list = gs_plugin_loader_get_categories_finish (plugin_loader, res, &error);
        if (list == NULL) {
@@ -322,8 +328,8 @@ gs_shell_overview_get_categories_cb (GObject *source_object,
        }
        gs_container_remove_all (GTK_CONTAINER (priv->flowbox_categories));
 
-       for (l = list; l; l = l->next) {
-               cat = GS_CATEGORY (l->data);
+       for (i = 0; i < list->len; i++) {
+               cat = GS_CATEGORY (g_ptr_array_index (list, i));
                if (gs_category_get_size (cat) == 0)
                        continue;
                tile = gs_category_tile_new (cat);
@@ -413,8 +419,9 @@ gs_shell_overview_load (GsShellOverview *self)
                g_autoptr(GsCategory) category = NULL;
                g_autoptr(GsCategory) featured_category = NULL;
 
-               category = gs_category_new (NULL, category_of_day, NULL);
-               featured_category = gs_category_new (category, "featured", NULL);
+               category = gs_category_new (category_of_day, NULL);
+               featured_category = gs_category_new ("featured", NULL);
+               gs_category_add_child (category, featured_category);
 
                load_data = g_slice_new0 (LoadData);
                load_data->category = g_object_ref (category);
diff --git a/src/gs-shell-search-provider.c b/src/gs-shell-search-provider.c
index fb44ed4..5eab9e3 100644
--- a/src/gs-shell-search-provider.c
+++ b/src/gs-shell-search-provider.c
@@ -60,11 +60,11 @@ pending_search_free (PendingSearch *search)
  * search_sort_by_kudo_cb:
  **/
 static gint
-search_sort_by_kudo_cb (gconstpointer a, gconstpointer b)
+search_sort_by_kudo_cb (GsApp *app1, GsApp *app2, gpointer user_data)
 {
        guint pa, pb;
-       pa = gs_app_get_kudos_percentage (GS_APP ((gpointer) a));
-       pb = gs_app_get_kudos_percentage (GS_APP ((gpointer) b));
+       pa = gs_app_get_kudos_percentage (app1);
+       pb = gs_app_get_kudos_percentage (app2);
        if (pa < pb)
                return 1;
        else if (pa > pb)
@@ -79,7 +79,7 @@ search_done_cb (GObject *source,
 {
        PendingSearch *search = user_data;
        GsShellSearchProvider *self = search->provider;
-       GList *l;
+       guint i;
        GVariantBuilder builder;
        g_autoptr(GsAppList) list = NULL;
 
@@ -92,11 +92,11 @@ search_done_cb (GObject *source,
        }
 
        /* sort by kudos, as there is no ratings data by default */
-       list = g_list_sort (list, search_sort_by_kudo_cb);
+       gs_app_list_sort (list, search_sort_by_kudo_cb, NULL);
 
        g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
-       for (l = list; l != NULL; l = l->next) {
-               GsApp *app = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               GsApp *app = gs_app_list_index (list, i);
                if (gs_app_get_state (app) != AS_APP_STATE_AVAILABLE)
                        continue;
                g_variant_builder_add (&builder, "s", gs_app_get_id (app));
diff --git a/src/gs-shell-search.c b/src/gs-shell-search.c
index 5062dc1..acb2395 100644
--- a/src/gs-shell-search.c
+++ b/src/gs-shell-search.c
@@ -92,7 +92,7 @@ gs_shell_search_get_search_cb (GObject *source_object,
                                     GAsyncResult *res,
                                     gpointer user_data)
 {
-       GList *l;
+       guint i;
        GsApp *app;
        GsShellSearch *self = GS_SHELL_SEARCH (user_data);
        GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source_object);
@@ -120,8 +120,8 @@ gs_shell_search_get_search_cb (GObject *source_object,
 
        gs_stop_spinner (GTK_SPINNER (self->spinner_search));
        gtk_stack_set_visible_child_name (GTK_STACK (self->stack_search), "results");
-       for (l = list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               app = gs_app_list_index (list, i);
                app_row = gs_app_row_new (app);
                g_signal_connect (app_row, "button-clicked",
                                  G_CALLBACK (gs_shell_search_app_row_clicked_cb),
diff --git a/src/gs-shell-updates.c b/src/gs-shell-updates.c
index f7e72d8..975e4bc 100644
--- a/src/gs-shell-updates.c
+++ b/src/gs-shell-updates.c
@@ -473,7 +473,7 @@ gs_shell_updates_get_updates_cb (GsPluginLoader *plugin_loader,
                                 GAsyncResult *res,
                                 GsShellUpdates *self)
 {
-       GList *l;
+       guint i;
        GtkWidget *widget;
        g_autoptr(GError) error = NULL;
        g_autoptr(GsAppList) list = NULL;
@@ -484,8 +484,8 @@ gs_shell_updates_get_updates_cb (GsPluginLoader *plugin_loader,
        list = gs_plugin_loader_get_updates_finish (plugin_loader, res, &error);
        self->all_updates_are_live = TRUE;
        self->any_require_reboot = FALSE;
-       for (l = list; l != NULL; l = l->next) {
-               GsApp *app = GS_APP (l->data);
+       for (i = 0; list != NULL && i < gs_app_list_length (list); i++) {
+               GsApp *app = gs_app_list_index (list, i);
                if (gs_app_get_state (app) != AS_APP_STATE_UPDATABLE_LIVE)
                        self->all_updates_are_live = FALSE;
                if (gs_app_has_quirk (app, AS_APP_QUIRK_NEEDS_REBOOT))
@@ -508,7 +508,7 @@ gs_shell_updates_get_updates_cb (GsPluginLoader *plugin_loader,
        widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "button_updates_counter"));
        if (list != NULL && !gs_update_monitor_is_managed ()) {
                g_autofree gchar *text = NULL;
-               text = g_strdup_printf ("%d", g_list_length (list));
+               text = g_strdup_printf ("%d", gs_app_list_length (list));
                gtk_label_set_label (GTK_LABEL (widget), text);
                gtk_widget_show (widget);
        } else {
@@ -568,8 +568,10 @@ gs_shell_updates_get_upgrades_cb (GObject *source_object,
                                           error->message);
                        }
                }
+       } else if (gs_app_list_length (list) == 0) {
+               g_debug ("updates-shell: no upgrades to show");
        } else {
-               GsApp *app = GS_APP (list->data);
+               GsApp *app = gs_app_list_index (list, 0);
                g_debug ("got upgrade %s", gs_app_get_id (app));
                gs_upgrade_banner_set_app (GS_UPGRADE_BANNER (self->upgrade_banner), app);
                gs_shell_updates_set_flag (self, GS_SHELL_UPDATES_FLAG_HAS_UPGRADES);
@@ -937,7 +939,7 @@ gs_shell_updates_reboot_failed_cb (GObject *source, GAsyncResult *res, gpointer
 {
        GsShellUpdates *self = GS_SHELL_UPDATES (user_data);
        g_autoptr(GError) error = NULL;
-       g_autoptr(GList) apps = NULL;
+       g_autoptr(GsAppList) apps = NULL;
        g_autoptr(GVariant) retval = NULL;
 
        /* get result */
@@ -953,7 +955,7 @@ gs_shell_updates_reboot_failed_cb (GObject *source, GAsyncResult *res, gpointer
        /* cancel trigger */
        apps = gs_update_list_get_apps (GS_UPDATE_LIST (self->list_box_updates));
        gs_plugin_loader_app_action_async (self->plugin_loader,
-                                          GS_APP (apps->data),
+                                          gs_app_list_index (apps, 0),
                                           GS_PLUGIN_LOADER_ACTION_UPDATE_CANCEL,
                                           self->cancellable,
                                           cancel_trigger_failed_cb,
@@ -1012,7 +1014,7 @@ gs_shell_updates_button_update_all_cb (GtkButton      *button,
                                       GsShellUpdates *self)
 {
        g_autoptr(GError) error = NULL;
-       g_autoptr(GList) apps = NULL;
+       g_autoptr(GsAppList) apps = NULL;
 
        /* do the offline update */
        apps = gs_update_list_get_apps (GS_UPDATE_LIST (self->list_box_updates));
@@ -1110,7 +1112,7 @@ upgrade_reboot_failed_cb (GObject *source,
 {
        GsShellUpdates *self = (GsShellUpdates *) user_data;
        g_autoptr(GError) error = NULL;
-       g_autoptr(GList) apps = NULL;
+       g_autoptr(GsAppList) apps = NULL;
        g_autoptr(GVariant) retval = NULL;
 
        /* get result */
@@ -1126,7 +1128,7 @@ upgrade_reboot_failed_cb (GObject *source,
        /* cancel trigger */
        apps = gs_update_list_get_apps (GS_UPDATE_LIST (self->list_box_updates));
        gs_plugin_loader_app_action_async (self->plugin_loader,
-                                          GS_APP (apps->data),
+                                          gs_app_list_index (apps, 0),
                                           GS_PLUGIN_LOADER_ACTION_UPDATE_CANCEL,
                                           self->cancellable,
                                           cancel_trigger_failed_cb,
diff --git a/src/gs-sources-dialog.c b/src/gs-sources-dialog.c
index adc71c4..0430f3a 100644
--- a/src/gs-sources-dialog.c
+++ b/src/gs-sources-dialog.c
@@ -147,7 +147,7 @@ get_sources_cb (GsPluginLoader *plugin_loader,
                GAsyncResult *res,
                GsSourcesDialog *dialog)
 {
-       GList *l;
+       guint i;
        GsApp *app;
        g_autoptr(GError) error = NULL;
        g_autoptr(GsAppList) list = NULL;
@@ -180,8 +180,8 @@ get_sources_cb (GsPluginLoader *plugin_loader,
 
        /* add each */
        gtk_stack_set_visible_child_name (GTK_STACK (dialog->stack), "sources");
-       for (l = list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               app = gs_app_list_index (list, i);
                add_source (GTK_LIST_BOX (dialog->listbox), app);
        }
 }
diff --git a/src/gs-update-dialog.c b/src/gs-update-dialog.c
index 3f75cb9..ae8bcb8 100644
--- a/src/gs-update-dialog.c
+++ b/src/gs-update-dialog.c
@@ -168,7 +168,7 @@ get_installed_updates_cb (GsPluginLoader *plugin_loader,
                           GAsyncResult *res,
                           GsUpdateDialog *dialog)
 {
-       GList *l;
+       guint i;
        guint64 install_date;
        g_autoptr(GsAppList) list = NULL;
        g_autoptr(GError) error = NULL;
@@ -198,7 +198,7 @@ get_installed_updates_cb (GsPluginLoader *plugin_loader,
        }
 
        /* set the header title using any one of the applications */
-       install_date = gs_app_get_install_date (GS_APP (list->data));
+       install_date = gs_app_get_install_date (gs_app_list_index (list, 0));
        if (install_date > 0) {
                GtkWidget *header;
                g_autoptr(GDateTime) date = NULL;
@@ -220,9 +220,9 @@ get_installed_updates_cb (GsPluginLoader *plugin_loader,
        gtk_stack_set_visible_child_name (GTK_STACK (dialog->stack), "installed-updates-list");
 
        gs_container_remove_all (GTK_CONTAINER (dialog->list_box_installed_updates));
-       for (l = list; l != NULL; l = l->next) {
+       for (i = 0; i < gs_app_list_length (list); i++) {
                gs_update_list_add_app (GS_UPDATE_LIST (dialog->list_box_installed_updates),
-                                       GS_APP (l->data));
+                                       gs_app_list_index (list, i));
        }
 }
 
diff --git a/src/gs-update-list.c b/src/gs-update-list.c
index 2c29b14..fc30330 100644
--- a/src/gs-update-list.c
+++ b/src/gs-update-list.c
@@ -77,17 +77,18 @@ gs_update_list_add_app (GsUpdateList *update_list,
        gtk_widget_show (app_row);
 }
 
-GList *
+GsAppList *
 gs_update_list_get_apps (GsUpdateList *update_list)
 {
-       GList *apps = NULL;
+       GsAppList *apps;
        GList *l;
        g_autoptr(GList) children = NULL;
 
+       apps = gs_app_list_new ();
        children = gtk_container_get_children (GTK_CONTAINER (update_list));
        for (l = children; l != NULL; l = l->next) {
                GsAppRow *app_row = GS_APP_ROW (l->data);
-               apps = g_list_prepend (apps, gs_app_row_get_app (app_row));
+               gs_app_list_add (apps, gs_app_row_get_app (app_row));
        }
        return apps;
 }
diff --git a/src/gs-update-list.h b/src/gs-update-list.h
index b35c3ec..96e3351 100644
--- a/src/gs-update-list.h
+++ b/src/gs-update-list.h
@@ -25,6 +25,7 @@
 #include <gtk/gtk.h>
 
 #include "gs-app.h"
+#include "gs-app-list.h"
 
 G_BEGIN_DECLS
 
@@ -42,7 +43,7 @@ struct _GsUpdateListClass
 GtkWidget      *gs_update_list_new             (void);
 void            gs_update_list_add_app         (GsUpdateList   *update_list,
                                                 GsApp          *app);
-GList          *gs_update_list_get_apps        (GsUpdateList   *update_list);
+GsAppList      *gs_update_list_get_apps        (GsUpdateList   *update_list);
 
 G_END_DECLS
 
diff --git a/src/gs-update-monitor.c b/src/gs-update-monitor.c
index cceaedd..594b596 100644
--- a/src/gs-update-monitor.c
+++ b/src/gs-update-monitor.c
@@ -108,11 +108,11 @@ notify_offline_update_available (GsUpdateMonitor *monitor)
 static gboolean
 has_important_updates (GsAppList *apps)
 {
-       GList *l;
+       guint i;
        GsApp *app;
 
-       for (l = apps; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (apps); i++) {
+               app = gs_app_list_index (apps, i);
                if (gs_app_get_update_urgency (app) == AS_URGENCY_KIND_CRITICAL ||
                    gs_app_get_update_urgency (app) == AS_URGENCY_KIND_HIGH)
                        return TRUE;
@@ -153,7 +153,7 @@ get_updates_finished_cb (GObject *object,
                         gpointer data)
 {
        GsUpdateMonitor *monitor = data;
-       GList *l;
+       guint i;
        GsApp *app;
        guint64 security_timestamp = 0;
        guint64 security_timestamp_old = 0;
@@ -174,8 +174,8 @@ get_updates_finished_cb (GObject *object,
        /* find security updates, or clear timestamp if there are now none */
        g_settings_get (monitor->settings,
                        "security-timestamp", "x", &security_timestamp_old);
-       for (l = apps; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (apps); i++) {
+               app = gs_app_list_index (apps, i);
                if (gs_app_get_metadata_item (app, "is-security") != NULL) {
                        security_timestamp = g_get_monotonic_time ();
                        break;
@@ -186,7 +186,7 @@ get_updates_finished_cb (GObject *object,
                                "security-timestamp", "x", security_timestamp);
        }
 
-       g_debug ("Got %d updates", g_list_length (apps));
+       g_debug ("Got %d updates", gs_app_list_length (apps));
 
        if (has_important_updates (apps) ||
            no_updates_for_a_week (monitor)) {
@@ -227,7 +227,7 @@ get_upgrades_finished_cb (GObject *object,
                return;
 
        /* just get the first result : FIXME, do we sort these by date? */
-       app = GS_APP (apps->data);
+       app = gs_app_list_index (apps, 0);
 
        /* TRANSLATORS: this is a distro upgrade, the replacement would be the
         * distro name, e.g. 'Fedora' */
@@ -441,8 +441,16 @@ get_updates_historical_cb (GObject *object, GAsyncResult *res, gpointer data)
                return;
        }
 
+       /* no results */
+       if (gs_app_list_length (apps) == 0) {
+               g_debug ("no historical updates; withdrawing notification");
+               g_application_withdraw_notification (monitor->application,
+                                                    "updates-available");
+               return;
+       }
+
        /* have we notified about this before */
-       app = GS_APP (apps->data);
+       app = gs_app_list_index (apps, 0);
        g_settings_get (monitor->settings,
                        "install-timestamp", "x", &time_last_notified);
        if (time_last_notified >= gs_app_get_install_date (app))
@@ -451,11 +459,11 @@ get_updates_historical_cb (GObject *object, GAsyncResult *res, gpointer data)
        /* TRANSLATORS: title when we've done offline updates */
        title = ngettext ("Software Update Installed",
                          "Software Updates Installed",
-                         g_list_length (apps));
+                         gs_app_list_length (apps));
        /* TRANSLATORS: message when we've done offline updates */
        message = ngettext ("An important OS update has been installed.",
                            "Important OS updates have been installed.",
-                           g_list_length (apps));
+                           gs_app_list_length (apps));
 
        notification = g_notification_new (title);
        g_notification_set_body (notification, message);
diff --git a/src/plugins/gs-plugin-appstream.c b/src/plugins/gs-plugin-appstream.c
index 13f4b9c..99f946b 100644
--- a/src/plugins/gs-plugin-appstream.c
+++ b/src/plugins/gs-plugin-appstream.c
@@ -334,7 +334,7 @@ gs_plugin_refine_from_pkgname (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_add_distro_upgrades (GsPlugin *plugin,
-                              GList **list,
+                              GsAppList *list,
                               GCancellable *cancellable,
                               GError **error)
 {
@@ -392,7 +392,7 @@ gs_plugin_refine_app (GsPlugin *plugin,
 gboolean
 gs_plugin_add_category_apps (GsPlugin *plugin,
                             GsCategory *category,
-                            GList **list,
+                            GsAppList *list,
                             GCancellable *cancellable,
                             GError **error)
 {
@@ -446,7 +446,7 @@ gs_plugin_add_category_apps (GsPlugin *plugin,
  */
 static gboolean
 gs_plugin_add_search_item (GsPlugin *plugin,
-                          GList **list,
+                          GsAppList *list,
                           AsApp *item,
                           gchar **values,
                           GCancellable *cancellable,
@@ -485,7 +485,7 @@ gs_plugin_add_search_item (GsPlugin *plugin,
 gboolean
 gs_plugin_add_search (GsPlugin *plugin,
                      gchar **values,
-                     GList **list,
+                     GsAppList *list,
                      GCancellable *cancellable,
                      GError **error)
 {
@@ -517,7 +517,7 @@ gs_plugin_add_search (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_add_installed (GsPlugin *plugin,
-                        GList **list,
+                        GsAppList *list,
                         GCancellable *cancellable,
                         GError **error)
 {
@@ -548,27 +548,26 @@ gs_plugin_add_installed (GsPlugin *plugin,
  * gs_plugin_add_categories_for_app:
  */
 static void
-gs_plugin_add_categories_for_app (GList *list, AsApp *app)
+gs_plugin_add_categories_for_app (GPtrArray *list, AsApp *app)
 {
-       GList *l;
-       GList *l2;
+       guint i, j;
        GsCategory *category;
        GsCategory *parent;
        gboolean found_subcat;
 
        /* does it match the main category */
-       for (l = list; l != NULL; l = l->next) {
-               g_autoptr(GList) children = NULL;
-               parent = GS_CATEGORY (l->data);
+       for (i = 0; i < list->len; i++) {
+               GPtrArray *children;
+               parent = GS_CATEGORY (g_ptr_array_index (list, i));
                if (!as_app_has_category (app, gs_category_get_id (parent)))
                        continue;
                gs_category_increment_size (parent);
 
                /* does it match any sub-categories */
                found_subcat = FALSE;
-               children = gs_category_get_subcategories (parent);
-               for (l2 = children; l2 != NULL; l2 = l2->next) {
-                       category = GS_CATEGORY (l2->data);
+               children = gs_category_get_children (parent);
+               for (j = 0; j < children->len; j++) {
+                       category = GS_CATEGORY (g_ptr_array_index (children, j));
                        if (!as_app_has_category (app, gs_category_get_id (category)))
                                continue;
                        gs_category_increment_size (category);
@@ -580,8 +579,8 @@ gs_plugin_add_categories_for_app (GList *list, AsApp *app)
                if (!found_subcat) {
                        category = gs_category_find_child (parent, "other");
                        if (category == NULL) {
-                               category = gs_category_new (parent, "other", NULL);
-                               gs_category_add_subcategory (parent, category);
+                               category = gs_category_new ("other", NULL);
+                               gs_category_add_child (parent, category);
                                g_object_unref (category);
                        }
                        as_app_add_category (app, gs_category_get_id (category));
@@ -595,7 +594,7 @@ gs_plugin_add_categories_for_app (GList *list, AsApp *app)
  */
 gboolean
 gs_plugin_add_categories (GsPlugin *plugin,
-                         GList **list,
+                         GPtrArray *list,
                          GCancellable *cancellable,
                          GError **error)
 {
@@ -615,7 +614,7 @@ gs_plugin_add_categories (GsPlugin *plugin,
                        continue;
                if (as_app_get_priority (app) < 0)
                        continue;
-               gs_plugin_add_categories_for_app (*list, app);
+               gs_plugin_add_categories_for_app (list, app);
        }
        return TRUE;
 }
@@ -625,7 +624,7 @@ gs_plugin_add_categories (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_add_popular (GsPlugin *plugin,
-                      GList **list,
+                      GsAppList *list,
                       GCancellable *cancellable,
                       GError **error)
 {
@@ -658,7 +657,7 @@ gs_plugin_add_popular (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_add_featured (GsPlugin *plugin,
-                       GList **list,
+                       GsAppList *list,
                        GCancellable *cancellable,
                        GError **error)
 {
diff --git a/src/plugins/gs-plugin-dpkg.c b/src/plugins/gs-plugin-dpkg.c
index c709c3b..0e38390 100644
--- a/src/plugins/gs-plugin-dpkg.c
+++ b/src/plugins/gs-plugin-dpkg.c
@@ -44,7 +44,7 @@ gs_plugin_initialize (GsPlugin *plugin)
  */
 gboolean
 gs_plugin_file_to_app (GsPlugin *plugin,
-                      GList **list,
+                      GsAppList *list,
                       GFile *file,
                       GCancellable *cancellable,
                       GError **error)
diff --git a/src/plugins/gs-plugin-dummy.c b/src/plugins/gs-plugin-dummy.c
index 2d3ff08..a1db60b 100644
--- a/src/plugins/gs-plugin-dummy.c
+++ b/src/plugins/gs-plugin-dummy.c
@@ -147,7 +147,7 @@ gs_plugin_dummy_poll_cb (gpointer user_data)
 gboolean
 gs_plugin_add_search (GsPlugin *plugin,
                      gchar **values,
-                     GList **list,
+                     GsAppList *list,
                      GCancellable *cancellable,
                      GError **error)
 {
@@ -199,7 +199,7 @@ gs_plugin_add_search (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_add_updates (GsPlugin *plugin,
-                      GList **list,
+                      GsAppList *list,
                       GCancellable *cancellable,
                       GError **error)
 {
@@ -267,7 +267,7 @@ gs_plugin_add_updates (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_add_installed (GsPlugin *plugin,
-                        GList **list,
+                        GsAppList *list,
                         GCancellable *cancellable,
                         GError **error)
 {
@@ -472,7 +472,7 @@ gs_plugin_refine_app (GsPlugin *plugin,
 gboolean
 gs_plugin_add_category_apps (GsPlugin *plugin,
                             GsCategory *category,
-                            GList **list,
+                            GsAppList *list,
                             GCancellable *cancellable,
                             GError **error)
 {
@@ -494,7 +494,7 @@ gs_plugin_add_category_apps (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_add_distro_upgrades (GsPlugin *plugin,
-                              GList **list,
+                              GsAppList *list,
                               GCancellable *cancellable,
                               GError **error)
 {
diff --git a/src/plugins/gs-plugin-fedora-distro-upgrades.c b/src/plugins/gs-plugin-fedora-distro-upgrades.c
index 9b821cd..dc96aa1 100644
--- a/src/plugins/gs-plugin-fedora-distro-upgrades.c
+++ b/src/plugins/gs-plugin-fedora-distro-upgrades.c
@@ -300,7 +300,7 @@ parse_pkgdb_collections_data (const gchar *data,
  */
 gboolean
 gs_plugin_add_distro_upgrades (GsPlugin *plugin,
-                              GList **list,
+                              GsAppList *list,
                               GCancellable *cancellable,
                               GError **error)
 {
diff --git a/src/plugins/gs-plugin-flatpak.c b/src/plugins/gs-plugin-flatpak.c
index e5cfd16..9a26803 100644
--- a/src/plugins/gs-plugin-flatpak.c
+++ b/src/plugins/gs-plugin-flatpak.c
@@ -147,7 +147,7 @@ gs_app_set_flatpak_kind (GsApp *app, FlatpakRefKind kind)
  */
 gboolean
 gs_plugin_add_popular (GsPlugin *plugin,
-                      GList **list,
+                      GsAppList *list,
                       GCancellable *cancellable,
                       GError **error)
 {
@@ -466,7 +466,7 @@ gs_plugin_flatpak_progress_cb (const gchar *status,
  */
 gboolean
 gs_plugin_add_installed (GsPlugin *plugin,
-                        GList **list,
+                        GsAppList *list,
                         GCancellable *cancellable,
                         GError **error)
 {
@@ -515,7 +515,7 @@ gs_plugin_add_installed (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_add_sources (GsPlugin *plugin,
-                      GList **list,
+                      GsAppList *list,
                       GCancellable *cancellable,
                       GError **error)
 {
@@ -600,7 +600,7 @@ gs_plugin_add_source (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_add_updates (GsPlugin *plugin,
-                      GList **list,
+                      GsAppList *list,
                       GCancellable *cancellable,
                       GError **error)
 {
@@ -1434,7 +1434,7 @@ gs_plugin_update_app (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_file_to_app (GsPlugin *plugin,
-                      GList **list,
+                      GsAppList *list,
                       GFile *file,
                       GCancellable *cancellable,
                       GError **error)
diff --git a/src/plugins/gs-plugin-fwupd.c b/src/plugins/gs-plugin-fwupd.c
index 80209c7..d4d41d0 100644
--- a/src/plugins/gs-plugin-fwupd.c
+++ b/src/plugins/gs-plugin-fwupd.c
@@ -308,7 +308,7 @@ gs_plugin_fwupd_new_app_from_results (FwupdResult *res)
  */
 static gboolean
 gs_plugin_add_update_app (GsPlugin *plugin,
-                         GList **list,
+                         GsAppList *list,
                          FwupdResult *res,
                          GError **error)
 {
@@ -420,7 +420,7 @@ gs_plugin_add_update_app (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_add_updates_historical (GsPlugin *plugin,
-                                 GList **list,
+                                 GsAppList *list,
                                  GCancellable *cancellable,
                                  GError **error)
 {
@@ -461,7 +461,7 @@ gs_plugin_add_updates_historical (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_add_updates (GsPlugin *plugin,
-                      GList **list,
+                      GsAppList *list,
                       GCancellable *cancellable,
                       GError **error)
 {
@@ -781,7 +781,7 @@ gs_plugin_update_app (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_file_to_app (GsPlugin *plugin,
-                      GList **list,
+                      GsAppList *list,
                       GFile *file,
                       GCancellable *cancellable,
                       GError **error)
diff --git a/src/plugins/gs-plugin-limba.c b/src/plugins/gs-plugin-limba.c
index 71813e4..b13bfbe 100644
--- a/src/plugins/gs-plugin-limba.c
+++ b/src/plugins/gs-plugin-limba.c
@@ -273,7 +273,7 @@ gs_plugin_app_install (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_add_sources (GsPlugin *plugin,
-                      GList **list,
+                      GsAppList *list,
                       GCancellable *cancellable,
                       GError **error)
 {
@@ -355,7 +355,7 @@ gs_plugin_app_from_pki (LiPkgInfo *pki)
  */
 gboolean
 gs_plugin_add_installed (GsPlugin *plugin,
-                        GList **list,
+                        GsAppList *list,
                         GCancellable *cancellable,
                         GError **error)
 {
@@ -392,7 +392,7 @@ gs_plugin_add_installed (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_add_updates (GsPlugin *plugin,
-                       GList **list,
+                       GsAppList *list,
                        GCancellable *cancellable,
                        GError **error)
 {
diff --git a/src/plugins/gs-plugin-menu-spec-categories.c b/src/plugins/gs-plugin-menu-spec-categories.c
index f7f3b4c..bd143ef 100644
--- a/src/plugins/gs-plugin-menu-spec-categories.c
+++ b/src/plugins/gs-plugin-menu-spec-categories.c
@@ -37,7 +37,7 @@
  */
 gboolean
 gs_plugin_add_categories (GsPlugin *plugin,
-                         GList **list,
+                         GPtrArray *list,
                          GCancellable *cancellable,
                          GError **error)
 {
@@ -51,17 +51,15 @@ gs_plugin_add_categories (GsPlugin *plugin,
        for (i = 0; msdata[i].path != NULL; i++) {
                tmp = g_strstr_len (msdata[i].path, -1, "::");
                if (tmp == NULL) {
-                       category = gs_category_new (NULL,
-                                                   msdata[i].path,
+                       category = gs_category_new (msdata[i].path,
                                                    gettext(msdata[i].text));
-                       *list = g_list_prepend (*list, category);
+                       g_ptr_array_add (list, category);
                        g_snprintf(msgctxt, 100, "Menu subcategory of %s", msdata[i].text);
                } else {
                        g_autoptr(GsCategory) sub = NULL;
-                       sub = gs_category_new (category,
-                                              tmp + 2,
+                       sub = gs_category_new (tmp + 2,
                                               g_dpgettext2(GETTEXT_PACKAGE, msgctxt, msdata[i].text));
-                       gs_category_add_subcategory (category, sub);
+                       gs_category_add_child (category, sub);
                }
        }
 
diff --git a/src/plugins/gs-plugin-odrs.c b/src/plugins/gs-plugin-odrs.c
index 86d8f5c..a776492 100644
--- a/src/plugins/gs-plugin-odrs.c
+++ b/src/plugins/gs-plugin-odrs.c
@@ -952,7 +952,7 @@ gs_plugin_create_app_dummy (const gchar *id)
  */
 gboolean
 gs_plugin_add_unvoted_reviews (GsPlugin *plugin,
-                              GList **list,
+                              GsAppList *list,
                               GCancellable *cancellable,
                               GError **error)
 {
diff --git a/src/plugins/gs-plugin-ostree.c b/src/plugins/gs-plugin-ostree.c
index 8df8473..c593041 100644
--- a/src/plugins/gs-plugin-ostree.c
+++ b/src/plugins/gs-plugin-ostree.c
@@ -100,7 +100,7 @@ gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
  */
 gboolean
 gs_plugin_add_sources (GsPlugin *plugin,
-                      GList **list,
+                      GsAppList *list,
                       GCancellable *cancellable,
                       GError **error)
 {
diff --git a/src/plugins/gs-plugin-packagekit-history.c b/src/plugins/gs-plugin-packagekit-history.c
index 52840bc..9e8093e 100644
--- a/src/plugins/gs-plugin-packagekit-history.c
+++ b/src/plugins/gs-plugin-packagekit-history.c
@@ -138,14 +138,14 @@ gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
 
 static gboolean
 gs_plugin_packagekit_refine (GsPlugin *plugin,
-                            GList *list,
+                            GsAppList *list,
                             GCancellable *cancellable,
                             GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
        gboolean ret;
        GError *error_local = NULL;
-       GList *l;
+       guint j;
        GsApp *app;
        guint i = 0;
        GVariantIter iter;
@@ -155,13 +155,13 @@ gs_plugin_packagekit_refine (GsPlugin *plugin,
        g_autoptr(GVariant) tuple = NULL;
 
        /* get an array of package names */
-       package_names = g_new0 (const gchar *, g_list_length (list) + 1);
-       for (l = list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       package_names = g_new0 (const gchar *, gs_app_list_length (list) + 1);
+       for (j = 0; j < gs_app_list_length (list); j++) {
+               app = gs_app_list_index (list, j);
                package_names[i++] = gs_app_get_source_default (app);
        }
 
-       g_debug ("getting history for %i packages", g_list_length (list));
+       g_debug ("getting history for %i packages", gs_app_list_length (list));
        result = g_dbus_connection_call_sync (priv->connection,
                                              "org.freedesktop.PackageKit",
                                              "/org/freedesktop/PackageKit",
@@ -182,8 +182,8 @@ gs_plugin_packagekit_refine (GsPlugin *plugin,
 
                        /* just set this to something non-zero so we don't keep
                         * trying to call GetPackageHistory */
-                       for (l = list; l != NULL; l = l->next) {
-                               app = GS_APP (l->data);
+                       for (i = 0; i < gs_app_list_length (list); i++) {
+                               app = gs_app_list_index (list, i);
                                gs_app_set_install_date (app, GS_APP_INSTALL_DATE_UNKNOWN);
                        }
                } else if (g_error_matches (error_local,
@@ -191,8 +191,8 @@ gs_plugin_packagekit_refine (GsPlugin *plugin,
                                            G_IO_ERROR_TIMED_OUT)) {
                        g_debug ("No history as PackageKit took too long: %s",
                                 error_local->message);
-                       for (l = list; l != NULL; l = l->next) {
-                               app = GS_APP (l->data);
+                       for (i = 0; i < gs_app_list_length (list); i++) {
+                               app = gs_app_list_index (list, i);
                                gs_app_set_install_date (app, GS_APP_INSTALL_DATE_UNKNOWN);
                        }
                }
@@ -206,8 +206,8 @@ gs_plugin_packagekit_refine (GsPlugin *plugin,
 
        /* get any results */
        tuple = g_variant_get_child_value (result, 0);
-       for (l = list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               app = gs_app_list_index (list, i);
                ret = g_variant_lookup (tuple,
                                        gs_app_get_source_default (app),
                                        "@aa{sv}",
@@ -243,31 +243,32 @@ gs_plugin_packagekit_refine (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_refine (GsPlugin *plugin,
-                 GList **list,
+                 GsAppList *list,
                  GsPluginRefineFlags flags,
                  GCancellable *cancellable,
                  GError **error)
 {
        gboolean ret;
-       GList *l;
+       guint i;
        GsApp *app;
        GPtrArray *sources;
-       g_autoptr(GList) packages = NULL;
+       g_autoptr(GsAppList) packages = NULL;
 
        if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_HISTORY) == 0)
                return TRUE;
 
        /* add any missing history data */
-       for (l = *list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       packages = gs_app_list_new ();
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               app = gs_app_list_index (list, i);
                sources = gs_app_get_sources (app);
                if (sources->len == 0)
                        continue;
                if (gs_app_get_install_date (app) != 0)
                        continue;
-               packages = g_list_prepend (packages, app);
+               gs_app_list_add (packages, app);
        }
-       if (packages != NULL) {
+       if (gs_app_list_length (packages) > 0) {
                ret = gs_plugin_packagekit_refine (plugin,
                                                   packages,
                                                   cancellable,
diff --git a/src/plugins/gs-plugin-packagekit-local.c b/src/plugins/gs-plugin-packagekit-local.c
index 8dc984f..741b4c3 100644
--- a/src/plugins/gs-plugin-packagekit-local.c
+++ b/src/plugins/gs-plugin-packagekit-local.c
@@ -185,7 +185,7 @@ gs_plugin_packagekit_refresh_guess_app_id (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_file_to_app (GsPlugin *plugin,
-                      GList **list,
+                      GsAppList *list,
                       GFile *file,
                       GCancellable *cancellable,
                       GError **error)
diff --git a/src/plugins/gs-plugin-packagekit-offline.c b/src/plugins/gs-plugin-packagekit-offline.c
index 85c7653..fa8a785 100644
--- a/src/plugins/gs-plugin-packagekit-offline.c
+++ b/src/plugins/gs-plugin-packagekit-offline.c
@@ -101,7 +101,7 @@ gs_plugin_packagekit_convert_error (GError **error,
  */
 gboolean
 gs_plugin_add_updates_historical (GsPlugin *plugin,
-                                 GList **list,
+                                 GsAppList *list,
                                  GCancellable *cancellable,
                                  GError **error)
 {
diff --git a/src/plugins/gs-plugin-packagekit-refine.c b/src/plugins/gs-plugin-packagekit-refine.c
index e19aba0..738fc9e 100644
--- a/src/plugins/gs-plugin-packagekit-refine.c
+++ b/src/plugins/gs-plugin-packagekit-refine.c
@@ -277,27 +277,27 @@ gs_plugin_packagekit_resolve_packages_app (GsPlugin *plugin,
  **/
 static gboolean
 gs_plugin_packagekit_resolve_packages (GsPlugin *plugin,
-                                      GList *list,
+                                      GsAppList *list,
                                       GCancellable *cancellable,
                                       GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       GList *l;
        GPtrArray *sources;
        GsApp *app;
        const gchar *pkgname;
        guint i;
+       guint j;
        ProgressData data;
        g_autoptr(PkResults) results = NULL;
        g_autoptr(GPtrArray) package_ids = NULL;
        g_autoptr(GPtrArray) packages = NULL;
 
        package_ids = g_ptr_array_new_with_free_func (g_free);
-       for (l = list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               app = gs_app_list_index (list, i);
                sources = gs_app_get_sources (app);
-               for (i = 0; i < sources->len; i++) {
-                       pkgname = g_ptr_array_index (sources, i);
+               for (j = 0; j < sources->len; j++) {
+                       pkgname = g_ptr_array_index (sources, j);
                        g_ptr_array_add (package_ids, g_strdup (pkgname));
                }
        }
@@ -319,8 +319,8 @@ gs_plugin_packagekit_resolve_packages (GsPlugin *plugin,
 
        /* get results */
        packages = pk_results_get_package_array (results);
-       for (l = list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               app = gs_app_list_index (list, i);
                if (gs_app_get_local_file (app) != NULL)
                        continue;
                gs_plugin_packagekit_resolve_packages_app (plugin, packages, app);
@@ -400,26 +400,24 @@ gs_plugin_packagekit_fixup_update_description (const gchar *text)
  */
 static gboolean
 gs_plugin_packagekit_refine_updatedetails (GsPlugin *plugin,
-                                          GList *list,
+                                          GsAppList *list,
                                           GCancellable *cancellable,
                                           GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
        const gchar *package_id;
-       GList *l;
+       guint j;
        GsApp *app;
        guint i = 0;
-       guint size;
        PkUpdateDetail *update_detail;
        ProgressData data;
        g_autofree const gchar **package_ids = NULL;
        g_autoptr(PkResults) results = NULL;
        g_autoptr(GPtrArray) array = NULL;
 
-       size = g_list_length (list);
-       package_ids = g_new0 (const gchar *, size + 1);
-       for (l = list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       package_ids = g_new0 (const gchar *, gs_app_list_length (list) + 1);
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               app = gs_app_list_index (list, i);
                package_id = gs_app_get_source_id_default (app);
                package_ids[i++] = package_id;
        }
@@ -439,8 +437,8 @@ gs_plugin_packagekit_refine_updatedetails (GsPlugin *plugin,
 
        /* set the update details for the update */
        array = pk_results_get_update_detail_array (results);
-       for (l = list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       for (j = 0; j < gs_app_list_length (list); j++) {
+               app = gs_app_list_index (list, j);
                package_id = gs_app_get_source_id_default (app);
                for (i = 0; i < array->len; i++) {
                        const gchar *tmp;
@@ -544,27 +542,26 @@ gs_plugin_packagekit_refine_details_app (GsPlugin *plugin,
  */
 static gboolean
 gs_plugin_packagekit_refine_details (GsPlugin *plugin,
-                                    GList *list,
+                                    GsAppList *list,
                                     GCancellable *cancellable,
                                     GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       GList *l;
        GPtrArray *source_ids;
        GsApp *app;
        const gchar *package_id;
-       guint i;
+       guint i, j;
        ProgressData data;
        g_autoptr(GPtrArray) array = NULL;
        g_autoptr(GPtrArray) package_ids = NULL;
        g_autoptr(PkResults) results = NULL;
 
        package_ids = g_ptr_array_new_with_free_func (g_free);
-       for (l = list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               app = gs_app_list_index (list, i);
                source_ids = gs_app_get_source_ids (app);
-               for (i = 0; i < source_ids->len; i++) {
-                       package_id = g_ptr_array_index (source_ids, i);
+               for (j = 0; j < source_ids->len; j++) {
+                       package_id = g_ptr_array_index (source_ids, j);
                        g_ptr_array_add (package_ids, g_strdup (package_id));
                }
        }
@@ -585,8 +582,8 @@ gs_plugin_packagekit_refine_details (GsPlugin *plugin,
 
        /* set the update details for the update */
        array = pk_results_get_details_array (results);
-       for (l = list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               app = gs_app_list_index (list, i);
                gs_plugin_packagekit_refine_details_app (plugin, array, app);
        }
        return TRUE;
@@ -597,12 +594,12 @@ gs_plugin_packagekit_refine_details (GsPlugin *plugin,
  */
 static gboolean
 gs_plugin_packagekit_refine_update_urgency (GsPlugin *plugin,
-                                            GList *list,
-                                            GCancellable *cancellable,
-                                            GError **error)
+                                           GsAppList *list,
+                                           GCancellable *cancellable,
+                                           GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       GList *l;
+       guint i;
        GsApp *app;
        const gchar *package_id;
        PkBitfield filter;
@@ -626,9 +623,9 @@ gs_plugin_packagekit_refine_update_urgency (GsPlugin *plugin,
 
        /* set the update severity for the app */
        sack = pk_results_get_package_sack (results);
-       for (l = list; l != NULL; l = l->next) {
+       for (i = 0; i < gs_app_list_length (list); i++) {
                g_autoptr (PkPackage) pkg = NULL;
-               app = GS_APP (l->data);
+               app = gs_app_list_index (list, i);
                package_id = gs_app_get_source_id_default (app);
                if (package_id == NULL)
                        continue;
@@ -687,21 +684,22 @@ gs_plugin_refine_app_needs_details (GsPlugin *plugin, GsPluginRefineFlags flags,
  */
 static gboolean
 gs_plugin_refine_require_details (GsPlugin *plugin,
-                                 GList *list,
+                                 GsAppList *list,
                                  GsPluginRefineFlags flags,
                                  GCancellable *cancellable,
                                  GError **error)
 {
-       GList *l;
+       guint i;
        GsApp *app;
        gboolean ret = TRUE;
-       g_autoptr(GList) list_tmp = NULL;
+       g_autoptr(GsAppList) list_tmp = NULL;
        g_autoptr(AsProfileTask) ptask = NULL;
 
        ptask = as_profile_start_literal (gs_plugin_get_profile (plugin),
                                          "packagekit-refine[source->license]");
-       for (l = list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       list_tmp = gs_app_list_new ();
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               app = gs_app_list_index (list, i);
                if (gs_app_get_kind (app) == AS_APP_KIND_WEB_APP)
                        continue;
                if (g_strcmp0 (gs_app_get_management_plugin (app), "packagekit") != 0)
@@ -710,9 +708,9 @@ gs_plugin_refine_require_details (GsPlugin *plugin,
                        continue;
                if (!gs_plugin_refine_app_needs_details (plugin, flags, app))
                        continue;
-               list_tmp = g_list_prepend (list_tmp, app);
+               gs_app_list_add (list_tmp, app);
        }
-       if (list_tmp == NULL)
+       if (gs_app_list_length (list_tmp) == 0)
                return TRUE;
        ret = gs_plugin_packagekit_refine_details (plugin,
                                                   list_tmp,
@@ -803,7 +801,7 @@ gs_plugin_packagekit_refine_distro_upgrade (GsPlugin *plugin,
                                            GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       GList *l;
+       guint i;
        GsApp *app2;
        ProgressData data;
        g_autoptr(PkResults) results = NULL;
@@ -823,12 +821,13 @@ gs_plugin_packagekit_refine_distro_upgrade (GsPlugin *plugin,
                                            error);
        if (!gs_plugin_packagekit_results_valid (results, error))
                return FALSE;
-       if (!gs_plugin_packagekit_add_results (plugin, &list, results, error))
+       list = gs_app_list_new ();
+       if (!gs_plugin_packagekit_add_results (plugin, list, results, error))
                return FALSE;
 
        /* add each of these as related applications */
-       for (l = list; l != NULL; l = l->next) {
-               app2 = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               app2 = gs_app_list_index (list, i);
                if (gs_app_get_state (app2) != AS_APP_STATE_AVAILABLE)
                        continue;
                gs_app_add_related (app, app2);
@@ -841,26 +840,26 @@ gs_plugin_packagekit_refine_distro_upgrade (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_refine (GsPlugin *plugin,
-                 GList **list,
+                 GsAppList *list,
                  GsPluginRefineFlags flags,
                  GCancellable *cancellable,
                  GError **error)
 {
-       GList *l;
+       guint i;
        GPtrArray *sources;
        GsApp *app;
        const gchar *tmp;
        gboolean ret = TRUE;
-       g_autoptr(GList) resolve_all = NULL;
-       g_autoptr(GList) updatedetails_all = NULL;
+       g_autoptr(GsAppList) resolve_all = NULL;
+       g_autoptr(GsAppList) updatedetails_all = NULL;
        AsProfileTask *ptask = NULL;
 
        /* when we need the cannot-be-upgraded applications, we implement this
         * by doing a UpgradeSystem(SIMULATE) which adds the removed packages
         * to the related-apps list with a state of %AS_APP_STATE_AVAILABLE */
        if (flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_UPGRADE_REMOVED) {
-               for (l = *list; l != NULL; l = l->next) {
-                       app = GS_APP (l->data);
+               for (i = 0; i < gs_app_list_length (list); i++) {
+                       app = gs_app_list_index (list, i);
                        if (gs_app_get_kind (app) != AS_APP_KIND_OS_UPGRADE)
                                continue;
                        if (!gs_plugin_packagekit_refine_distro_upgrade (plugin,
@@ -874,8 +873,9 @@ gs_plugin_refine (GsPlugin *plugin,
        /* can we resolve in one go? */
        ptask = as_profile_start_literal (gs_plugin_get_profile (plugin),
                                          "packagekit-refine[name->id]");
-       for (l = *list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       resolve_all = gs_app_list_new ();
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               app = gs_app_list_index (list, i);
                if (gs_app_get_kind (app) == AS_APP_KIND_WEB_APP)
                        continue;
                tmp = gs_app_get_management_plugin (app);
@@ -888,10 +888,10 @@ gs_plugin_refine (GsPlugin *plugin,
                    gs_plugin_refine_requires_package_id (app, flags) ||
                    gs_plugin_refine_requires_origin (app, flags) ||
                    gs_plugin_refine_requires_version (app, flags)) {
-                       resolve_all = g_list_prepend (resolve_all, app);
+                       gs_app_list_add (resolve_all, app);
                }
        }
-       if (resolve_all != NULL) {
+       if (gs_app_list_length (resolve_all) > 0) {
                ret = gs_plugin_packagekit_resolve_packages (plugin,
                                                             resolve_all,
                                                             cancellable,
@@ -904,11 +904,11 @@ gs_plugin_refine (GsPlugin *plugin,
        /* set the package-id for an installed desktop file */
        ptask = as_profile_start_literal (gs_plugin_get_profile (plugin),
                                          "packagekit-refine[installed-filename->id]");
-       for (l = *list; l != NULL; l = l->next) {
+       for (i = 0; i < gs_app_list_length (list); i++) {
                g_autofree gchar *fn = NULL;
                if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_SETUP_ACTION) == 0)
                        continue;
-               app = GS_APP (l->data);
+               app = gs_app_list_index (list, i);
                if (gs_app_get_source_id_default (app) != NULL)
                        continue;
                tmp = gs_app_get_id (app);
@@ -943,17 +943,18 @@ gs_plugin_refine (GsPlugin *plugin,
        /* any update details missing? */
        ptask = as_profile_start_literal (gs_plugin_get_profile (plugin),
                                          "packagekit-refine[id->update-details]");
-       for (l = *list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
+       updatedetails_all = gs_app_list_new ();
+       for (i = 0; i < gs_app_list_length (list); i++) {
+               app = gs_app_list_index (list, i);
                if (gs_app_get_state (app) != AS_APP_STATE_UPDATABLE)
                        continue;
                tmp = gs_app_get_management_plugin (app);
                if (tmp != NULL && g_strcmp0 (tmp, "packagekit") != 0)
                        continue;
                if (gs_plugin_refine_requires_update_details (app, flags))
-                       updatedetails_all = g_list_prepend (updatedetails_all, app);
+                       gs_app_list_add (updatedetails_all, app);
        }
-       if (updatedetails_all != NULL) {
+       if (gs_app_list_length (updatedetails_all) > 0) {
                ret = gs_plugin_packagekit_refine_updatedetails (plugin,
                                                                 updatedetails_all,
                                                                 cancellable,
@@ -965,7 +966,7 @@ gs_plugin_refine (GsPlugin *plugin,
 
        /* any important details missing? */
        ret = gs_plugin_refine_require_details (plugin,
-                                               *list,
+                                               list,
                                                flags,
                                                cancellable,
                                                error);
@@ -975,9 +976,9 @@ gs_plugin_refine (GsPlugin *plugin,
        /* get the update severity */
        if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_UPDATE_SEVERITY) > 0) {
                ret = gs_plugin_packagekit_refine_update_urgency (plugin,
-                                                                  *list,
-                                                                  cancellable,
-                                                                  error);
+                                                                 list,
+                                                                 cancellable,
+                                                                 error);
                if (!ret)
                        goto out;
        }
diff --git a/src/plugins/gs-plugin-packagekit.c b/src/plugins/gs-plugin-packagekit.c
index 7e2e9c8..176225a 100644
--- a/src/plugins/gs-plugin-packagekit.c
+++ b/src/plugins/gs-plugin-packagekit.c
@@ -112,7 +112,7 @@ gs_plugin_packagekit_progress_cb (PkProgress *progress,
  */
 gboolean
 gs_plugin_add_installed (GsPlugin *plugin,
-                        GList **list,
+                        GsAppList *list,
                         GCancellable *cancellable,
                         GError **error)
 {
@@ -157,7 +157,7 @@ gs_plugin_add_sources_related (GsPlugin *plugin,
                               GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       GList *l;
+       guint i;
        GsApp *app;
        GsApp *app_tmp;
        PkBitfield filter;
@@ -187,14 +187,14 @@ gs_plugin_add_sources_related (GsPlugin *plugin,
        if (!gs_plugin_packagekit_results_valid (results, error))
                return FALSE;
        ret = gs_plugin_packagekit_add_results (plugin,
-                                               &installed,
+                                               installed,
                                                results,
                                                error);
        if (!ret)
                return FALSE;
-       for (l = installed; l != NULL; l = l->next) {
+       for (i = 0; i < gs_app_list_length (installed); i++) {
                g_auto(GStrv) split = NULL;
-               app = GS_APP (l->data);
+               app = gs_app_list_index (installed, i);
                split = pk_package_id_split (gs_app_get_source_id_default (app));
                if (g_str_has_prefix (split[PK_PACKAGE_ID_DATA], "installed:")) {
                        id = split[PK_PACKAGE_ID_DATA] + 10;
@@ -214,7 +214,7 @@ gs_plugin_add_sources_related (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_add_sources (GsPlugin *plugin,
-                      GList **list,
+                      GsAppList *list,
                       GCancellable *cancellable,
                       GError **error)
 {
@@ -632,7 +632,7 @@ gs_plugin_app_remove (GsPlugin *plugin,
 gboolean
 gs_plugin_add_search_files (GsPlugin *plugin,
                             gchar **search,
-                            GList **list,
+                            GsAppList *list,
                             GCancellable *cancellable,
                             GError **error)
 {
@@ -669,7 +669,7 @@ gs_plugin_add_search_files (GsPlugin *plugin,
 gboolean
 gs_plugin_add_search_what_provides (GsPlugin *plugin,
                                     gchar **search,
-                                    GList **list,
+                                    GsAppList *list,
                                     GCancellable *cancellable,
                                     GError **error)
 {
diff --git a/src/plugins/gs-plugin-shell-extensions.c b/src/plugins/gs-plugin-shell-extensions.c
index 7638533..53cfabd 100644
--- a/src/plugins/gs-plugin-shell-extensions.c
+++ b/src/plugins/gs-plugin-shell-extensions.c
@@ -255,7 +255,7 @@ gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
  */
 gboolean
 gs_plugin_add_installed (GsPlugin *plugin,
-                        GList **list,
+                        GsAppList *list,
                         GCancellable *cancellable,
                         GError **error)
 {
@@ -856,7 +856,7 @@ gs_plugin_launch (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_add_categories (GsPlugin *plugin,
-                         GList **list,
+                         GPtrArray *list,
                          GCancellable *cancellable,
                          GError **error)
 {
diff --git a/src/plugins/gs-plugin-steam.c b/src/plugins/gs-plugin-steam.c
index 05d96bb..d1fb779 100644
--- a/src/plugins/gs-plugin-steam.c
+++ b/src/plugins/gs-plugin-steam.c
@@ -985,7 +985,7 @@ gs_plugin_launch (GsPlugin *plugin, GsApp *app,
 gboolean
 gs_plugin_add_search (GsPlugin *plugin,
                      gchar **values,
-                     GList **list,
+                     GsAppList *list,
                      GCancellable *cancellable,
                      GError **error)
 {
diff --git a/src/plugins/gs-plugin-systemd-updates.c b/src/plugins/gs-plugin-systemd-updates.c
index bf1c1a0..67bdfe5 100644
--- a/src/plugins/gs-plugin-systemd-updates.c
+++ b/src/plugins/gs-plugin-systemd-updates.c
@@ -90,7 +90,7 @@ gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
  */
 gboolean
 gs_plugin_add_updates (GsPlugin *plugin,
-                      GList **list,
+                      GsAppList *list,
                       GCancellable *cancellable,
                       GError **error)
 {
@@ -137,15 +137,15 @@ gs_plugin_add_updates (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_update (GsPlugin *plugin,
-                 GList *apps,
+                 GsAppList *apps,
                  GCancellable *cancellable,
                  GError **error)
 {
-       GList *l;
+       guint i;
 
        /* any apps to process offline */
-       for (l = apps; l != NULL; l = l->next) {
-               GsApp *app = GS_APP (l->data);
+       for (i = 0; i < gs_app_list_length (apps); i++) {
+               GsApp *app = gs_app_list_index (apps, i);
 
                /* only process this app if was created by this plugin */
                if (g_strcmp0 (gs_app_get_management_plugin (app), "packagekit") != 0)
diff --git a/src/plugins/gs-plugin-xdg-app.c b/src/plugins/gs-plugin-xdg-app.c
index d732ca1..1bbf081 100644
--- a/src/plugins/gs-plugin-xdg-app.c
+++ b/src/plugins/gs-plugin-xdg-app.c
@@ -139,7 +139,7 @@ gs_app_set_xdgapp_kind (GsApp *app, XdgAppRefKind kind)
  */
 gboolean
 gs_plugin_add_popular (GsPlugin *plugin,
-                      GList **list,
+                      GsAppList *list,
                       GCancellable *cancellable,
                       GError **error)
 {
@@ -445,7 +445,7 @@ gs_plugin_xdg_app_progress_cb (const gchar *status,
  */
 gboolean
 gs_plugin_add_installed (GsPlugin *plugin,
-                        GList **list,
+                        GsAppList *list,
                         GCancellable *cancellable,
                         GError **error)
 {
@@ -494,7 +494,7 @@ gs_plugin_add_installed (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_add_sources (GsPlugin *plugin,
-                      GList **list,
+                      GsAppList *list,
                       GCancellable *cancellable,
                       GError **error)
 {
@@ -539,7 +539,7 @@ gs_plugin_add_sources (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_add_updates (GsPlugin *plugin,
-                      GList **list,
+                      GsAppList *list,
                       GCancellable *cancellable,
                       GError **error)
 {
@@ -1363,7 +1363,7 @@ gs_plugin_update_app (GsPlugin *plugin,
  */
 gboolean
 gs_plugin_file_to_app (GsPlugin *plugin,
-                      GList **list,
+                      GsAppList *list,
                       GFile *file,
                       GCancellable *cancellable,
                       GError **error)
diff --git a/src/plugins/packagekit-common.c b/src/plugins/packagekit-common.c
index 91ab368..17a2e37 100644
--- a/src/plugins/packagekit-common.c
+++ b/src/plugins/packagekit-common.c
@@ -189,7 +189,7 @@ gs_plugin_packagekit_results_valid (PkResults *results, GError **error)
  */
 gboolean
 gs_plugin_packagekit_add_results (GsPlugin *plugin,
-                                 GList **list,
+                                 GsAppList *list,
                                  PkResults *results,
                                  GError **error)
 {
diff --git a/src/plugins/packagekit-common.h b/src/plugins/packagekit-common.h
index 590e416..4293545 100644
--- a/src/plugins/packagekit-common.h
+++ b/src/plugins/packagekit-common.h
@@ -33,7 +33,7 @@ G_BEGIN_DECLS
 GsPluginStatus         packagekit_status_enum_to_plugin_status (PkStatusEnum    status);
 
 gboolean       gs_plugin_packagekit_add_results        (GsPlugin       *plugin,
-                                                        GList          **list,
+                                                        GsAppList      *list,
                                                         PkResults      *results,
                                                         GError         **error);
 gboolean       gs_plugin_packagekit_convert_gerror     (GError         **error);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]