diff --git a/F-Droid/src/org/fdroid/fdroid/localrepo/SwapState.java b/F-Droid/src/org/fdroid/fdroid/localrepo/SwapState.java
index 1783eb000..802de5cd9 100644
--- a/F-Droid/src/org/fdroid/fdroid/localrepo/SwapState.java
+++ b/F-Droid/src/org/fdroid/fdroid/localrepo/SwapState.java
@@ -7,6 +7,9 @@ import android.support.annotation.NonNull;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
 
 public class SwapState {
 
@@ -18,13 +21,19 @@ public class SwapState {
     public static final int STEP_SHOW_NFC    = 4;
     public static final int STEP_WIFI_QR     = 5;
 
-    private int step;
 
     @NonNull
     private final Context context;
 
-    private SwapState(@NonNull Context context) {
+    @NonNull
+    private Set<String> appsToSwap;
+
+    private int step;
+
+    private SwapState(@NonNull Context context, @SwapStep int step, @NonNull Set<String> appsToSwap) {
         this.context = context;
+        this.step = step;
+        this.appsToSwap = appsToSwap;
     }
 
     /**
@@ -38,27 +47,93 @@ public class SwapState {
 
     public SwapState setStep(@SwapStep int step) {
         this.step = step;
-        persist();
+        persistStep();
         return this;
     }
 
-    private static final String KEY_STEP = "step";
+    public Set<String> getAppsToSwap() {
+        return appsToSwap;
+    }
+
+    private static final String KEY_STEP         = "step";
+    private static final String KEY_APPS_TO_SWAP = "appsToSwap";
+
+    private static SwapState instance;
 
     @NonNull
     public static SwapState load(@NonNull Context context) {
-        SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE);
+        if (instance == null) {
+            SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE);
 
-        @SwapStep int step = preferences.getInt(KEY_STEP, STEP_INTRO);
+            @SwapStep int step = preferences.getInt(KEY_STEP, STEP_INTRO);
+            Set<String> appsToSwap = deserializePackages(preferences.getString(KEY_APPS_TO_SWAP, ""));
 
-        return new SwapState(context)
-                .setStep(step);
+            instance = new SwapState(context, step, appsToSwap);
+        }
+
+        return instance;
     }
 
-    private void persist() {
-        SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_APPEND);
-        preferences.edit()
-                .putInt(KEY_STEP, step)
-                .commit();
+    private SharedPreferences persistence() {
+        return context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_APPEND);
+    }
+
+    private void persistStep() {
+        persistence().edit().putInt(KEY_STEP, step).commit();
+    }
+
+    private void persistAppsToSwap() {
+        persistence().edit().putString(KEY_APPS_TO_SWAP, serializePackages(appsToSwap)).commit();
+    }
+
+    /**
+     * Replacement for {@link android.content.SharedPreferences.Editor#putStringSet(String, Set)}
+     * which is only available in API >= 11.
+     * Package names are reverse-DNS-style, so they should only have alpha numeric values. Thus,
+     * this uses a comma as the separator.
+     * @see SwapState#deserializePackages(String)
+     */
+    private static String serializePackages(Set<String> packages) {
+        StringBuilder sb = new StringBuilder();
+        for (String pkg : packages) {
+            if (sb.length() > 0) {
+                sb.append(',');
+            }
+            sb.append(pkg);
+        }
+        return sb.toString();
+    }
+
+    /**
+     * @see SwapState#deserializePackages(String)
+     */
+    private static Set<String> deserializePackages(String packages) {
+        Set<String> set = new HashSet<>();
+        Collections.addAll(set, packages.split(","));
+        return set;
+    }
+
+    public void ensureFDroidSelected() {
+        String fdroid = context.getPackageName();
+        if (!hasSelectedPackage(fdroid)) {
+            selectPackage(fdroid);
+        }
+    }
+
+    public boolean hasSelectedPackage(String packageName) {
+        return appsToSwap.contains(packageName);
+    }
+
+    public void selectPackage(String packageName) {
+        appsToSwap.add(packageName);
+        persistAppsToSwap();
+    }
+
+    public void deselectPackage(String packageName) {
+        if (appsToSwap.contains(packageName)) {
+            appsToSwap.remove(packageName);
+        }
+        persistAppsToSwap();
     }
 
     /**
diff --git a/F-Droid/src/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java b/F-Droid/src/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java
index 72e8e4b37..1dab7b974 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java
@@ -45,8 +45,7 @@ public class SwapWorkflowActivity extends FragmentActivity {
         /** @return The step that this view represents. */
         @SwapState.SwapStep int getStep();
 
-        // TODO: Handle back presses with a method like this:
-        // @SwapState.SwapStep int getPreviousStep();
+        @SwapState.SwapStep int getPreviousStep();
     }
 
     private static final int CONNECT_TO_SWAP = 1;
@@ -57,6 +56,17 @@ public class SwapWorkflowActivity extends FragmentActivity {
     private UpdateAsyncTask updateSwappableAppsTask = null;
     private Timer shutdownLocalRepoTimer;
 
+    @Override
+    public void onBackPressed() {
+        if (currentView.getStep() == SwapState.STEP_INTRO) {
+            finish();
+        } else {
+            int nextStep = currentView.getPreviousStep();
+            state.setStep(nextStep);
+            showRelevantView();
+        }
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -105,6 +115,10 @@ public class SwapWorkflowActivity extends FragmentActivity {
         }
     }
 
+    public SwapState getState() {
+        return state;
+    }
+
     private void showNfc() {
         if (!attemptToShowNfc()) {
             showWifiQr();
@@ -128,10 +142,12 @@ public class SwapWorkflowActivity extends FragmentActivity {
         inflateInnerView(R.layout.swap_select_apps);
     }
 
-    // TODO: Pass in the selected apps, then we can figure out whether they have changed.
-    public void onAppsSelected(Set<String> selectedApps) {
+    // TODO: Figure out whether they have changed since last time UpdateAsyncTask was run.
+    // If the local repo is running, then we can ask it what apps it is swapping and compare with that.
+    // Otherwise, probably will need to scan the file system.
+    public void onAppsSelected() {
         if (updateSwappableAppsTask == null && !hasPreparedLocalRepo) {
-            updateSwappableAppsTask = new UpdateAsyncTask(this, selectedApps);
+            updateSwappableAppsTask = new UpdateAsyncTask(state.getAppsToSwap());
             updateSwappableAppsTask.execute();
         } else {
             showJoinWifi();
@@ -247,10 +263,10 @@ public class SwapWorkflowActivity extends FragmentActivity {
         @NonNull
         private final Context context;
 
-        public UpdateAsyncTask(Context c, @NonNull Set<String> apps) {
+        public UpdateAsyncTask(@NonNull Set<String> apps) {
             context = SwapWorkflowActivity.this.getApplicationContext();
             selectedApps = apps;
-            progressDialog = new ProgressDialog(c);
+            progressDialog = new ProgressDialog(context);
             progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
             progressDialog.setTitle(R.string.updating);
             sharingUri = Utils.getSharingUri(FDroidApp.repo);
diff --git a/F-Droid/src/org/fdroid/fdroid/views/swap/views/JoinWifiView.java b/F-Droid/src/org/fdroid/fdroid/views/swap/views/JoinWifiView.java
index e77d0b9a6..44a16c4e4 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/swap/views/JoinWifiView.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/swap/views/JoinWifiView.java
@@ -46,7 +46,6 @@ public class JoinWifiView extends RelativeLayout implements SwapWorkflowActivity
     }
 
     private SwapWorkflowActivity getActivity() {
-        // TODO: Try and find a better way to get to the SwapActivity, which makes less asumptions.
         return (SwapWorkflowActivity)getContext();
     }
 
@@ -61,7 +60,8 @@ public class JoinWifiView extends RelativeLayout implements SwapWorkflowActivity
         });
         refreshWifiState();
 
-        // TODO: Listen for "Connecting..." state and reflect that in the view too.
+        // TODO: This is effectively swap state management code, shouldn't be isolated to the
+        // WifiStateChangeService, but should be bundled with the main swap state handling code.
         LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
                 new BroadcastReceiver() {
                     @Override
@@ -74,6 +74,7 @@ public class JoinWifiView extends RelativeLayout implements SwapWorkflowActivity
 
     }
 
+    // TODO: Listen for "Connecting..." state and reflect that in the view too.
     private void refreshWifiState() {
         TextView descriptionView = (TextView) findViewById(R.id.text_description);
         ImageView wifiIcon = (ImageView) findViewById(R.id.wifi_icon);
@@ -123,4 +124,9 @@ public class JoinWifiView extends RelativeLayout implements SwapWorkflowActivity
     public int getStep() {
         return SwapState.STEP_JOIN_WIFI;
     }
+
+    @Override
+    public int getPreviousStep() {
+        return SwapState.STEP_SELECT_APPS;
+    }
 }
diff --git a/F-Droid/src/org/fdroid/fdroid/views/swap/views/NfcView.java b/F-Droid/src/org/fdroid/fdroid/views/swap/views/NfcView.java
index b3a566203..482e23f8d 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/swap/views/NfcView.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/swap/views/NfcView.java
@@ -38,7 +38,6 @@ public class NfcView extends RelativeLayout implements SwapWorkflowActivity.Inne
     }
 
     private SwapWorkflowActivity getActivity() {
-        // TODO: Try and find a better way to get to the SwapActivity, which makes less asumptions.
         return (SwapWorkflowActivity)getContext();
     }
 
@@ -73,4 +72,9 @@ public class NfcView extends RelativeLayout implements SwapWorkflowActivity.Inne
     public int getStep() {
         return SwapState.STEP_SHOW_NFC;
     }
+
+    @Override
+    public int getPreviousStep() {
+        return SwapState.STEP_JOIN_WIFI;
+    }
 }
diff --git a/F-Droid/src/org/fdroid/fdroid/views/swap/views/SelectAppsView.java b/F-Droid/src/org/fdroid/fdroid/views/swap/views/SelectAppsView.java
index 2917e8c02..b07347fc7 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/swap/views/SelectAppsView.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/swap/views/SelectAppsView.java
@@ -32,15 +32,11 @@ import android.widget.ImageView;
 import android.widget.ListView;
 import android.widget.TextView;
 
-import org.fdroid.fdroid.FDroidApp;
 import org.fdroid.fdroid.R;
 import org.fdroid.fdroid.data.InstalledAppProvider;
-import org.fdroid.fdroid.localrepo.LocalRepoManager;
 import org.fdroid.fdroid.localrepo.SwapState;
 import org.fdroid.fdroid.views.swap.SwapWorkflowActivity;
 
-import java.util.HashSet;
-
 public class SelectAppsView extends ListView implements
         SwapWorkflowActivity.InnerView,
         LoaderManager.LoaderCallbacks<Cursor>,
@@ -67,6 +63,10 @@ public class SelectAppsView extends ListView implements
         return (SwapWorkflowActivity)getContext();
     }
 
+    private SwapState getState() {
+        return getActivity().getState();
+    }
+
     private static final int LOADER_INSTALLED_APPS = 253341534;
 
     private AppListAdapter adapter;
@@ -95,17 +95,6 @@ public class SelectAppsView extends ListView implements
         // either reconnect with an existing loader or start a new one
         getActivity().getSupportLoaderManager().initLoader(LOADER_INSTALLED_APPS, null, this);
 
-        // build list of existing apps from what is on the file system
-        if (FDroidApp.selectedApps == null) {
-            FDroidApp.selectedApps = new HashSet<>();
-            for (String filename : LocalRepoManager.get(getActivity()).repoDir.list()) {
-                if (filename.matches(".*\\.apk")) {
-                    String packageName = filename.substring(0, filename.indexOf("_"));
-                    FDroidApp.selectedApps.add(packageName);
-                }
-            }
-        }
-
         setOnItemClickListener(new AdapterView.OnItemClickListener() {
             public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
                 if (position > 0) {
@@ -128,7 +117,7 @@ public class SelectAppsView extends ListView implements
         nextMenuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
             @Override
             public boolean onMenuItemClick(MenuItem item) {
-                getActivity().onAppsSelected(FDroidApp.selectedApps);
+                getActivity().onAppsSelected();
                 return true;
             }
         });
@@ -148,13 +137,18 @@ public class SelectAppsView extends ListView implements
         return SwapState.STEP_SELECT_APPS;
     }
 
+    @Override
+    public int getPreviousStep() {
+        return SwapState.STEP_INTRO;
+    }
+
     private void toggleAppSelected(int position) {
         Cursor c = (Cursor) adapter.getItem(position - 1);
         String packageName = c.getString(c.getColumnIndex(InstalledAppProvider.DataColumns.APP_ID));
-        if (FDroidApp.selectedApps.contains(packageName)) {
-            FDroidApp.selectedApps.remove(packageName);
+        if (getState().hasSelectedPackage(packageName)) {
+            getState().deselectPackage(packageName);
         } else {
-            FDroidApp.selectedApps.add(packageName);
+            getState().selectPackage(packageName);
         }
     }
 
@@ -179,17 +173,13 @@ public class SelectAppsView extends ListView implements
     public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
         adapter.swapCursor(cursor);
 
-        String fdroid = loader.getContext().getPackageName();
         for (int i = 0; i < getCount() - 1; i++) {
             Cursor c = ((Cursor) getItemAtPosition(i + 1));
             String packageName = c.getString(c.getColumnIndex(InstalledAppProvider.DataColumns.APP_ID));
-            if (TextUtils.equals(packageName, fdroid)) {
-                setItemChecked(i + 1, true); // always include FDroid
-            } else {
-                for (String selected : FDroidApp.selectedApps) {
-                    if (TextUtils.equals(packageName, selected)) {
-                        setItemChecked(i + 1, true);
-                    }
+            getState().ensureFDroidSelected();
+            for (String selected : getState().getAppsToSwap()) {
+                if (TextUtils.equals(packageName, selected)) {
+                    setItemChecked(i + 1, true);
                 }
             }
         }
diff --git a/F-Droid/src/org/fdroid/fdroid/views/swap/views/StartSwapView.java b/F-Droid/src/org/fdroid/fdroid/views/swap/views/StartSwapView.java
index a681f3de7..c251dd0bf 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/swap/views/StartSwapView.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/swap/views/StartSwapView.java
@@ -17,6 +17,12 @@ import org.fdroid.fdroid.views.swap.SwapWorkflowActivity;
 
 public class StartSwapView extends LinearLayout implements SwapWorkflowActivity.InnerView {
 
+    // TODO: Is there a way to guarangee which of these constructors the inflater will call?
+    // Especially on different API levels? It would be nice to only have the one which accepts
+    // a Context, but I'm not sure if that is correct or not. As it stands, this class provides
+    // constructurs which match each of the ones available in the parent class.
+    // The same is true for the other views in the swap process too.
+
     public StartSwapView(Context context) {
         super(context);
     }
@@ -62,4 +68,13 @@ public class StartSwapView extends LinearLayout implements SwapWorkflowActivity.
     public int getStep() {
         return SwapState.STEP_INTRO;
     }
+
+    @Override
+    public int getPreviousStep() {
+        // TODO: Currently this is handleed by the SwapWorkflowActivity as a special case, where
+        // if getStep is STEP_INTRO, don't even bother asking for getPreviousStep. But that is a
+        // bit messy. It would be nicer if this was handled using the same mechanism as everything
+        // else.
+        return SwapState.STEP_INTRO;
+    }
 }
diff --git a/F-Droid/src/org/fdroid/fdroid/views/swap/views/WifiQrView.java b/F-Droid/src/org/fdroid/fdroid/views/swap/views/WifiQrView.java
index 47120a492..fc451de9b 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/swap/views/WifiQrView.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/swap/views/WifiQrView.java
@@ -60,7 +60,6 @@ public class WifiQrView extends ScrollView implements SwapWorkflowActivity.Inner
     }
 
     private SwapWorkflowActivity getActivity() {
-        // TODO: Try and find a better way to get to the SwapActivity, which makes less asumptions.
         return (SwapWorkflowActivity)getContext();
     }
 
@@ -78,6 +77,9 @@ public class WifiQrView extends ScrollView implements SwapWorkflowActivity.Inner
         openQr.setOnClickListener(new Button.OnClickListener() {
             @Override
             public void onClick(View v) {
+                // TODO: Should probably ask the activity or some other class to do this for us.
+                // The view should be dumb and only know how to show things and delegate things to
+                // other classes that know how to do things.
                 IntentIntegrator integrator = new IntentIntegrator(getActivity());
                 integrator.initiateScan();
             }
@@ -91,6 +93,9 @@ public class WifiQrView extends ScrollView implements SwapWorkflowActivity.Inner
             }
         });
 
+        // TODO: As with the JoinWifiView, this should be refactored to be part of the SwapState.
+        // Otherwise, we are left with SwapState, LocalRepoService, WifiStateChangeService, and
+        // some static variables in FDroidApp all which manage the state for swap.
         LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
                 new BroadcastReceiver() {
                     @Override
@@ -112,6 +117,12 @@ public class WifiQrView extends ScrollView implements SwapWorkflowActivity.Inner
         return SwapState.STEP_WIFI_QR;
     }
 
+    @Override
+    public int getPreviousStep() {
+        // TODO: Find a way to make this optionally go back to the NFC screen if appropriate.
+        return SwapState.STEP_JOIN_WIFI;
+    }
+
     private void setUIFromWifi() {
 
         if (TextUtils.isEmpty(FDroidApp.repo.address))