iOS Photography App Dev Journal
This is a running log of my first-time experience developing a mobile application through React Native. The web portion is being built in Node.js and I am using MongoDB for data storage.
I will continue to update this everyday I work on something for this project.
Thursday, Aug 25th 2016
- Wrote a very barebones API for Landmark retrieval
- (no geo-spatial query implementation yet)
- Setup a new react-native application for the 1st time
- Using react-native-map
- Had to fix some import lines in the .h files for it to work
- Finally got the maps plugin working
- Able to query all landmark data from the API and display it on the map
Friday, Aug 26th 2016
- Implemented a tab-based navigation with 3 separate views
- Added Google Material Design icons
- Extended the map view to take up entire view
- Implemented an onPress handler for the Marker Callout
- Had to use a workaround using TouchableHighlight
- Changed terminal color to blue
Friday, Sep 2nd 2016
- First time using Sketch
- Worked mostly with laying out the UI in Sketch
- Drafted mockups / wireframes for the landmark display pop-over
Saturday, Sep 3rd 2016
- Drafted more mockups for different screens including:
- Landmark photo feed
- Explore page for nearby landmarks
Monday, Sep 5th 2016
- Worked on the onPress for the map markers
- Got a custom image onto the map as a marker
- Added a new modal library and linked it to the onPress event
Tuesday, Sep 6th 2016
- Implemented some of the landmark modal UI
Wednesday, Sep 7th 2016
Today’s post won’t be in bulletin point form as I have more stuff to document and write about.
Started writing some simple server-side code for the Photos objects. Decided for now that each Landmark object will contain a reference to a photo object via ID string. Will need to re-think this as I believe this decision will have major impact in the overall performance of the application. Maybe we need to store a mini photo object within the landmark object? The writes would be more complex but the reads and queries would be faster. Another potential option I was weighing was putting all leader images in it’s own separate collection. This would make the landmark point / click actions much faster as it’s a simple query. Can save the heavy lifting for when the user actually clicks into a landmark.
Eventually I should organize my react-native fetch(…) calls into their own objects much like how we use services in Angular for http requests.
Created a temporary mock s3 instance in a local folder within the application dir. Able to pass in url to the app and display dynamic images based on location marker.
Thursday, Sep 8th 2016
- Made the landmark modal area clickable
- Trying to figure out the Navigator component
- Having some trouble pushing a new scene to the stack.
- Will revisit tomorrow
Friday, Sep 9th 2016
- Completed navigation to new screen (from previous night)
- Added navigation bar
- Changed nav / status bar theme to be light-content
- Added back navigation to LandmarkView
- Map → Landmark Click → Landmark Render → Back to Map (DONE)
Monday, Sept 12th 2016
Time to start implementing the actual photo feed screen for each landmark. Trying to figure out the best way to handle photo retrieval.
- Added /api/photos/feed/:landmarkId route to the photos module to query photos by landmark ID
- Implemented photo feed layout that consumes the above API route
- Created a new react component: PhotoFeedImage
- Added like-box and username display for each photo
Right now when a landmark feed gets opened, a function loadPhotoFeed() is called to the GET /api/photos/feed/:landmarkId route. When the function returns, the state is updated which then re-renders the PhotoFeedImage components. This is a super barebones implementation at this point and is by no means “done”.
Inside the PhotoFeedImage, the image, username and # of likes are displayed and formatted to that component.
For right now, the images are being served by the web API and the assets are stored on a file path in my Node.js application directory as a placeholder until I get an S3 instance up.
Here are some TODOs I want to accomplish in the upcoming sessions:
- leader image specific styling
- implement infinite scroll loading
- design the ‘like’ feature
Tuesday, Sep 20th 2016
It’s been a week since I last worked on this. Couple of unexpected things popped up this week that kept me from this project. Anyhow, here we are again.
Might actually not even write any code today. Going to design the user likes feature. This may seem trivial to some and it’s so widely used across social media platforms that I often don’t even think about / consider how I would build out a system to do such a thing.
A couple of things right off the bat:
- The count on the UI will only update on user refresh
- How will I handle the consistency issue with the like count?
In other words, if 20 users all like a photo at around the same time, how do we keep incrementing and updating the like counter for that photo such that it stays in sync?
One idea that popped into my head: since our system is asynchronous, we could possibly have our like requests handled by some sort of a queue worker system. That is, anytime a user (on any given server) makes a request that changes a photo’s like counts, it gets funneled into a queue to be processed eventually. This solves our problem of data integrity across multiple machines but perhaps introduces a bottleneck depending on how the worker queue process / service is implemented. I will need to do more research on this matter.
The Use of Logs
Aha! I found a distributed systems article from the LinkedIn engineering blog that pretty much confirms my idea above using a log.
Basically a queue system where records are appended to the end of the log. Each entry is assigned a unique sequential log entry number. This ordering of records denotes time and can be thought of as a timestamp. This is great since we are decoupled from any physical clock.
Main purpose: Logs record what happened and when.
Solves two problems: ordering changes and distributing data.
A system that assumes an external log is present allows the individual systems to relinquish a lot of their own complexity and rely on the shared log.
- Handle data consistency (eventual or immediate) by sequencing updates to nodes
- Provide data replication between nodes
Have each node write to its own counter. When we need the counter value, we sum up all the individual counters.
Currently reading up on message queues. This requires a lot more planning than I originally thought. I want to layout by architecture concretely and flesh out more of the web services before I go back to the client-side.
Actually, upon further research it looks like MongoDB $inc should be OK? However, the other options are interesting and at the very least I learned some new things.
Wednesday, Sep 20th 2016
Read up on a lot of basic distributed computing concepts. Drafted up a preliminary architecture that implements a message queue to process writes to the database. Mainly did this as a means to solve the data consistency issue …. It’s 2AM…will finish writing this tomorrow.
The idea here below is pretty simple to start. We have one application server running an instance of the application backend in Node.js talking to our database server running an instance of MongoDB.
The message queue is there for future-proofing. Essentially the idea was, if the need to scale outward ever arose, data consistency would be a big pain-point. The user like system was actually the main reason I started doing research on that subject. Found a lot of good info but essentially one of the ways to get around this is to have all our write commands pushed into a worker queue. This solves the problem by ordering the commands to be executed as they come in one-by-one. However, this seems to be presenting itself as a bottleneck in the system. I need to do more research and I will decide after I do some testing.
In this current mockup, the image upload service is grouped into the same application server as the rest of the app. Should think about extracting the image upload out into its own service. This will help free up resources for reads.
Thursday, Sep 21st 2016
- Started work on the user likes feature
- User can now like a photo
- Added check to see if the photo is already liked by the user
- Increment the likes count on the photo object
So now the user can effectively ‘like’ a photo and the photo’s like count will be incremented to reflect that change.
Storing user likes data
One of the things I had to consider today was how to store a record of all the user’s previous likes. This is essential in the future when we implement more of the mobile UI since the ‘like’ button should look different depending on the state.
Of course the first thing I thought of (which is OBVIOUSLY wrong), is to store the like data in the photo object. Ok, so this was one of those ideas that I knew was wrong the second it popped into my mind. It’s pretty trivial at small scale but it doesn’t take long to realize that having a gigantic array in a document is not a good idea at large scale. Same goes for storing the user likes in the user object.
So then I started thinking about the relationship between a user and a favorited object. It’s quite a simple one and we can gather all the user likes with a simple db query. Thus, it made the most sense to store favorites in a separate collection.
The PhotoFavorites Collection
All the PhotoFavorites collection does is store unique relationships between a user_id and a photo_id. The JSON store literally looks like this:
Because of this, I now have all my photo favorites in its own bucket within the database. I can just run a simple query against the current user’s ID and retrieve all relevant likes across the entire application. I named the module PhotoFavorites because if in the future I decide to allow Landmark favoriting I can just create another LandmarkFavorites module.
So now I can make a request through Postman and see the new one-like photo result in the mobile app:
Monday, Sep 26th 2016
- Implemented the unfavorite feature to remove a favorite link and decrement the photos like count
Wednesday, Sep 27th 2016
- Going to try and implement user logins and accounts today.
- Got stuck on the Facebook SDK. LoginButton not showing up.
Saturday, Oct 1st 2016
- Figured out how to get the LoginButton to work. Had to perform some extra steps outside of the Facebook tutorial. Will add here or put that in a separate article later.
Sunday, Oct 2nd 2016
- Moved the login logic out to it’s own component (LoginView)
- Login will be optional (illustrated / described below)
Making login mandatory doesn’t seem to make sense for this application. Drawing from other apps (ex. Trulia), I think it makes more sense to allow users to browse the content if they do not wish to contribute. This is a deviation from say Instagram that requires all of its users to create an account and login.
Login Request Behavior
onApplicationLoad: ask if the user wants to login / create an account. Give the option to cancel.
onUserFavorite: Whenever the user tries to favorite without an account, launch the login / register view.
onUserAddLocation: Whenever the user tries to add a new location without an account, launch the login / register view.
Tuesday, Oct 4th 2016
Goal is to work on login flow. On initial opening of the app, it should prompt the user to login via facebook. However, if the user chooses the skip this step we should not force them to login and let them use the app. However, it might be fine to re-ask them to register with facebook later on after a period of time passes.
- working on the login check in index.ios.js
Wednesday, Oct 5th 2016
- Added full screen modal for user login prompt
- Made the navigator for the map/landmark view into its own component
- Moved the MapView into it’s own component
- Passing the user object from index.ios.js all the way down to the photo feed
- Added modal to the PhotoFeed component so that if the user tries to favorite a photo without an account, they will be prompted to login.
TODO: Document the components, how they connect, and what data they share.