The rise and fall of Crossed Paths on the App Store - my React Native experience
August 01, 2018
Part of levelling up for a lot of developers means doing side projects alongside paid work. Doing so opens up the colossal landscape of web development and enables us to work with technologies we may not usually be exposed to when doing day-to-day work. This post is a quick run down of a recent side project of mine which I used to experiment with different parts of Node and React Native. I’ll discuss what I aimed to achieve, what I learned in the process, and the important potential demise of the whole project due to Facebook’s new API rules.
Crossed Paths on the App Store
My side project is a mobile app called Crossed Paths. The Crossed Paths app is currently on the App Store for iOS, and has been on the App Store since September 2017. The app is nothing too fancy - it allows people to log in with Facebook which customises the dashboard with the users name and profile image from Facebook. The user is then able to pull down their checked in places/locations from both Facebook and Instagram at the tap of a button and view these locations on a map.
What makes the app interesting is that it allows the user to then browse through a list of their Facebook friends who are using the Crossed Paths app, and compare their checked-in locations with those of their friends. The app will then show the user locations where the user and their friends have in common, leading to the unveiling of coincidental trips anywhere in the world where the pair have been. These common locations are shown in a list, and also a map view.
The objectives
As mentioned before, the app was a side project to expand knowledge of React Native and Node. I’ve use React and Node for a while, but at the time of starting the build of Crossed Paths in March 2017 I hadn’t built much with React Native other than some very simple, unpolished apps. The aim was to build an app in React Native which was done to a professional standard, production ready, with the finishing touches, end-of-project hurdles and App Store distribution techniques under my belt.
Another goal of the app was to experiment with different API’s - specifically the Facebook and Instagram API’s, giving the app a level of rich content which I think is required for an app to be useful and worth downloading if it’s just an information based app. Consuming the API’s for Facebook and Instagram are quite different from one another, even though they are both owned by Facebook, so there was a lot to learn in that respect. As it turns out, the API connectivity and reliance on third party data would be the eventual downfall of Crossed Paths, which I’ll discuss later.
Finally, one of my favourite parts of a project is experimenting with application architecture for both client side and Node based projects, so this was a perfect opportunity for exactly that.
How it was built
There are many aspects of the app which are notable in terms of a significant/stand alone functionality, and also the technologies I decided to use. I’ll run through some of the technology stack and also go more in-depth afterwards about individual parts of the app.
Technology stack
As discussed, the app is built using React Native and Node.js as the base. Additionally, here are some technologies I’ve used which I feel are worth mentioning separately:
App:
- Redux - Used on the app to manage client side state. This was a great for storing the state of the logged-in user, as well as all of their locations from Facebook and Instagram. With the help of Redux Thunk and Redux Form, the app utilises client-side state in a natural way.
- Lottie - A powerful plugin for mobile applications allowing exported animations from After Effects to be used. This was used as an experiment initially and worked tremendously, so I’ll be using this more in the future.
- react-native-sms - A simple library to allow you to initiate a SMS message on the device from within the app.
- react-native-maps - Map integration into React Native.
Server:
- Passport - used to authenticate the user against Facebook and Instagram. This package greatly reduces the complexity of connecting to API’s for authentication, and helped keep the focus on the app rather than areas which can be hugely time consuming.
- FB - used to consume the Facebook API in Node.
- https - used to consume the Instagram API in Node.
- MongoDB and Mongoose - I decided to use a simple database setup here because there’s not a huge amount of relational data to consider with Crossed Paths.
- Cloudinary - an excellent online service which provides asset storage and a CDN services.
User authentication
A significant part of many apps is user authentication. I’m a big fan of exploring this in-depth at the right times as I enjoy working with the complexity and using technologies like JWT’s, sessions etc., however I felt this app was not one of those times. I’d instead decided to use the authentication library, Passport, to manage a large part of the authentication which was required for accessing data from Facebook and Instagram.
When the app is first opened, rather than asking for the user to create an account and risk losing their interest right away, I chose to have a Facebook login feature, allowing the user to log in with their Facebook account. This would help not only increase the likelihood of someone using the app, but also gain access to the user’s basic Facebook details straight away in order customise the app experience and start the process of getting the user locations.
The passport-facebook
library was used to implement this initial authentication, which then created a Mongo document for the user based on their unique Facebook ID. Inside this Mongo entry some basic user info is pulled through from Facebook to personalise the user experience, consisting of the Facebook ID, name, gender, and profile image. Once authenticated, each time the user logs in with Facebook the database is checked for the user and if they exist, their logged-in count increments by one.
Cloudinary
When experimenting with side projects like this, you often encounter unexpected problems. One such problem was that Facebook’s profile images are stored on CDN’s which change frequently, so after the project was live I noticed that people’s profile images were disappearing from the app.
I’d initially grabbed the profile image URL from the original Facebook authentication process when the user first logs in to the app, but in order to combat the image issue with Facebook I decided to use Cloudinary to store the profile images. On first log in, the profile image would be uploaded directly to Cloudinary and the new Cloudinary URL sent back to the Node server where it was saved in the user profile. Then, each time the user logs in the system would check if Facebook had changed the CDN for the users image, and update if necessary.
Aside from stopping the issue with expiring profile images, using Cloudinary also meant I was taking full advantage of a great CDN so images would load that little bit faster for everyone. I have users signed up from all around the world so this was a big advantage.
Retrieving the users locations
After logging in for the first time the user only has basic information stored on the Crossed Paths server. The whole point of the app is to get location data and cross-reference this with friends location data, so the challenge was to get as much location information as possible for each user. I’d decided to use Facebook and Instagram locations for this, which required using the relevant platform access token (obtained on user authentication) and accessing specific API endpoints.
For Facebook I used the fb
Node package which made working with the API a bit easier, and the https
package was used to access the Instagram API. They both required a similar approach to consume the data, including doing some on-the-fly pagination of results for the large sets. I had about 400 checkins on Facebook so this went through about 20 or so pages of results programatically. Here’s the snippet from the Facebook API:
All locations were stored in the secure MongoDB and could then be accessed by the user in the app to view their own locations.
Crossed Paths friends and their locations
When using Crossed Paths, you can’t just see everyone’s locations in the whole database. This is against privacy rules and is not allowed by either Facebook or Instagram. Instead, one is only able to see the locations of their social media friends. This is something I didn’t know initially, but makes sense as this is a similar situation with the Strava fitness app of which I really like the user experience. So much so that I decided to develop a similar process for Crossed Paths.
The part of the app which deals with comparing the locations is probably the most complex as you might imagine. This involves looping over each friend from Facebook and looking for their ID in the Crossed Paths database. Each friend is then run through the process of comparing each location within a given distance and time frame (depending on the filters set by the user) and a final array of locations is created and send back to the app. This final array is sent back to the client and used to display which locations are “crossed paths” between the two users.
One way to promote the app to other users is to have an area set up in the app which allows the user to send an automated SMS message to selected people in the device’s phone book. This is another feature used by Strava which I developed for Crossed Paths using the react-native-sms
package.
Map view
A few weeks after the app was launched on the App Store and around 70 having users registered, I decided to update the app and show a map view of the two users common locations. I was hoping to push the app forward and make it more visual with a few more ideas also lined up.
I’d thought the map integration was going to be easy, but I was wrong. Not because of placing markers and locations based on the array from the server - that was the easy part. The hard part was integrating the maps package in to React Native, specifically the Google Maps implementation which is not the default. Once installed, the Google Maps experience was very pleasant for both me and the end user!
I won’t go in to too much detail here as I documented my experience/obstacles in a recent post so check that out if you’re interested.
The fall of Crossed Paths
The title of this post hints at a rise and a fall of the Crossed Paths app. It didn’t rise too much up to the time of writing this, with a total of around 100 users, but it is certainly about to fall. The reason for this is due to Facebook’s new privacy overhaul which was brought on by the Cambridge Analytica scandal which was made public towards the start of 2018.
You may not know if you’ve not worked with the Facebook API or you’re not a developer (if it’s the latter then thanks for reading this far without getting bored), but in order to use the Facebook or Instagram API’s an app is required to set permissions which the end user of the app will approve before allowing your app to receive your data. There are about 25-30 permissions which an app can ask the user for, but you may only need to use a few of them. Crossed Paths uses the following:
public_profile
- this is the default permission which allows access to a users basic information like name and profile image. This information is used to personalise the app experience in Crossed Paths.email
- this is not public, but is required as a unique identifier of the user if they are to link their account to another service in the future, or send a welcome email in this case.user_friends
- this allows Crossed Paths to loop through the users friend list and see which ones are using the Crossed Paths app in order to construct the “crossed paths” array.user_tagged_places
- this gives Crossed Paths the ability to see where the logged in user has checked-in, or posted any content to Facebook with a location associated with it.
The last two permissions here (user_friends
and user_tagged_places
) are soon changing so any application requesting this information from a user requires that said application must submit their application for Facebook review and supply business credentials in order to continue using them. Up until now when a Facebook app like Crossed Paths goes in for review, you’re required to supply reasons for using the requested permissions and Facebook generally allow this, but due to new privacy rules, Facebook are only allowing sensitive information from users to be shared with established businesses. This means small developer teams and individuals like myself are no longer allowed to request this on the users behalf.
My feelings about this were mixed at the start, because now this means Crossed Paths will no longer be able to function, but my thoughts have somewhat changed recently. I’ve taken hundreds of peoples sensitive data and although I’ve stored this very securely and lawfully, there’s the chance some people out there (like that suggested by Cambridge Analytics scandal) can easily mis-use the data if not in a controlled environment. I hope the move by Facebook works out, but I imagine that there are many apps out there using these permissions which are going to be suffering the same consequences. The Facebook app landscape is about to go through a huge change, so let’s see how it goes.
Thanks for reading
I have all the source code available but I won’t add to Git as there are some private bits in there, so if anyone would like to see the files just get in touch on Twitter or drop me an email and I’ll gladly share.
Also a big thanks for Alex Johl who worked with me on getting the app from his head and in to the app store for just under a year.
Senior Engineer at Haven