HustleTime has seen some major improvements in the latest release. I am going to walk you through the newest feature: the ignore tab, so you can see it in action and it’s intended use case.
Let’s say you’re in the West Village and you need an uptown F train to Queens. Any of the other orange trains (the ) won’t help you.
HustleTime, however, is only showing the next two trains on the track. Let’s highlight that station to bring it to the top.
Because we don’t care about the trains shown, let’s open up the menu to get to the ignore tab.
The default view is the favorite places, but by clicking the ignore symbol we can see all the train lines in the city. Something I’d still like to figure out is how to handle the lines. Currently the MTA groups the Franklin Avenue and Rockaway with the A/C/E lines, and the Grand Central-Times Square with the 1/2/3 line, so that’s how I’m laying them out here.
Now we can click on the lines we’d like to ignore. They’ll fade in opacity to indicate they’ve been ignored.
Selecting or de-selecting ignored trains will automatically update the arrival card (you can kinda see it behind the modal in the image above. But when we close it, we now see the desired trains.
We also notice that there’s a warning in the top left that lets the user know that she’s currently ignoring some trains.
In addition to the ignore feature, this update includes a number of other changes:
Major Changes:
adds a menu button replacing the old open modal button
adds tabs for favorite places and ignored trains
allows users to select trains to ignore – these trains will not appear in arrival notifications
Minor Features:
displays a warning if you are ignoring any train lines
Minor fixes:
improved actual search radius to more closely match displayed radius
added a few missing subway stations
removed font scaling from arrival notifications for legibility
Thanks for using HustleTime. Android update soon to come!
Apple is a bad dude. He’s a bully at times. Sure he can be nice at times, even sweet. When things are good with Apple, computers can seem amazing, fast, clean, and safe. But all that comes with a hefty pricetag. Apple needs you to update constantly, both hardware and software, and this can be challenging to lots of people out there.
I work on a hackintosh, so I have to be very careful about updating my OS – it could cause breaking changes to my system. And after reading a little about updating to Mojave to the most current version, I decided to forego updating to the last possible moment.
Unfortunately, I couldn’t keep xCode up to date, and I had updated my iPhone, so I couldn’t actually install my updates to HustleTime on my device.
I had to try to install the newest version of Mojave, and of course, I was met with intense resistance. It turned out I was using an older version of the Clover Bootloader, and had some older kexts that were causing display issues in the new update.
This issue took the better part of Friday and Sunday to fix. But with the help of some very good folks at /r/hackintosh, I was able to get up and running.
Now for the Debt Part:
I made the mistake of trying to code all of my ReactNative work for both Ios and Android out of the same directory. Being the noob that I am, I made some changes to the node modules in the process of trying to get Android up and running and I ended up breaking the Ios implementation.
For android, the problem was really that I couldn’t get the emulator to run on my mac. I ended up creating an entirely new project on a windows machine and building it there from scratch (with lots of cuts and pastes).
As for the Ios version, I made some temporary fixes, and was able to get it running on a rudimentary level, but something was broken deep inside HustleTime and I didn’t know what. It manifested like this: I could run in debug mode, but I couldn’t actually build the project in release mode. This is obviously a big problem, and in order to move forward with developing I needed to be able to test the project in release.
I searched high and wide for fixes, but nothing worked. I ended up giving up on fixing, and decided to rebuild the project in a completely different repo.
There are a number of problems with this approach: first off- I can’t just rebuild projects every time I make a breaking change. I need a way to fix things. But this seems to be a little bit of a problem for react-native. It’s losing steam in the community and there are fewer and fewer up to date resources for help. Lots of code is old and it’s hard to find solutions.
In any case, after rebuilding the project, I can’t get an ad to appear in the new version of HustleTime – I keep getting an error stating that the GoogleMobileAds Module can’t be found. It’s tremendously frustrating to have to spend so much additional time on a piece I had solved for previously.
HustleTime will be cleaner and faster and fresher than ever, I just need to iron out a few of the new kinks…
I’m excited to be rolling out a new version of HustleTime to the app store. It’s going to include some small Ui fixes, as well as a functional Ignore train feature.
Here’s your normal view: where we see some and trains rolling into High Street.
I’ll then go into the modal by clicking the Chevrons and we now have 2 tabs, a favorites as well as an ignore, I’ll click the ignore Icon and move to the ignore menu:
I’ll click on the train I want to ignore (in this case, the )
When we close the modal, the app will refresh; displaying arrivals that exclude the train, as well as display an icon indicating that you’re actively ignoring a train (this will display when ignoring any number of trains).
What this looks like from a programming perspective is such:
On the front end, App state includes an object that has a boolean for each train line. When I send an api call, I’m including any of those values that are marked true.
let ignoredTrains = []
let allTrains = Object.entries(this.state.shown)
allTrains.forEach( entry => entry[1] == false ? ignoredTrains.push(entry[0]) : null )
I’m transforming the object into an array that includes the keys for the objects where the boolean value is false, then including that array in the api call.
The back end receives this information and filters out all the departure which have ['train'] values that match the ignored trains. For reference, each departure is stored as its own object, looking something like the following:
Anyway, I’m super excited to be submitting this new feature to the app store for release (hopefully) next week! Then on to Android implementation, and then I’ll see what’s next.
So, I’m getting back to work on the next big feature, which is to allow the user to ignore specific train lines. I wasn’t 100% sure how to actually give the user an intuitive way to set this, but I’ve decided to build on my idea in my last post.
I’ve set the modal to have 2 ‘tabs’
the favorites tab
the ignore tab
My hope is that the user understands that each of the lines is a clickable object that will set that specific train to be ignored or not.
Each train line image is rendered as a <TouchableOpacity> which sets the state for each individual line to toggle a boolean. The opacity of the image rendered is influenced by the value of the bool:
I’ve clicked on the C and the F trains.
I’m pleased with this implementation so far. I still feel confused as to how to deal with the Franklin Avenue Shuttle and the Grand Central/Times Square Shuttle, but for now, I’ll leave that be.
The next steps:
save this information to UserDefaults so your preferences remain saved to your device
automatically load these preferences on open
include this information in the api call to the back end
set up the back end to be able to receive this array
change the redis search to disregard ignored lines
clean up some of the margins/padding etc.
I think this is a pretty exciting new feature and I’m sure people will use it. I personally find myself needing an F train in manhattan and when I open HustleTime and see two incoming M trains, I’m disappointed.
I’m actively working on this app, so if anyone has any suggestions for features that aren’t on my Trello Board please comment and let me know of a feature you’d like to see (and yes, I know people are hoping for buses).
You can get HustleTime on both Android and iPhone free!
In my last post, I talked a little about the occasional WireType error, and of course, over the past weekend, the BDFM feed was issuing corrupt data for over an hour.
I don’t have a real answer to how to deal with this sort of situation. The data I’m getting from the MTA lacks the necessary entity which contains arrivals and departures, so I don’t really have a choice but to not serve information for (in this case) the BDFM lines.
I also need to implement changes that allow for when a WireType error occurs. I started detailing this last week, but I think I finally have a temporary solution.
How I’m working this all out:
There are undoubtedly many ways to safely test code, especially when you have code in production. In my case, we’re starting to pick up a little steam:
just a little steam…
It’s just under 100 users! and that’s Android alone, on the AppStore, I’ve had about 60 installations in the last week. At the very beginning stages of getting an app ‘out there,’ I feel it’s important not to alienate our early adopters by breaking the working code. If I was in a vacuum, I could of course shut the whole thing down and make fixes. Not right now – even with my small group of 100-200 users.
What I’m doing is spinning up a local version of my back end and seeding my own redis manually – the actual server seeds redis (which stores all the MTA data) every minute with an active crontask.
To test- I’ll simulate an api call from a device with Postman a super simple way to manually make api calls to wherever!
HustleTime is looking for a latitude and longitude among other things, so I’m sending that information to my local server with postman and checking the results.
I was asking myself how I would simulate a WireType error, and I decided to ping the MTA api, and then manually muddle up the data. Fortunately for me, When I called the MTA’s 12345 feed, I got a WireType error off the bat! Here’s what the response looks like from the MTA when a WireType error occurs:
When I try to decode this message with Protobuf, it throws the WireType error that I’m so concerned about – so the first step will be to enact some error handling with WireType in mind.
For any given feed: the first beginnings of this look like this:
errors = []
.
.
.
begin
feed123456S = Transit_realtime::FeedMessage.decode(data123456S)
rescue Protobuf::InvalidWireType
errors = errors + 'skipped: 123456S (wireType) '
# Do something
end
once that decoding is done, I need to run my getArrivals(lineFeed, timeNow, data_array) method on each feed, and if there’s that WireType error, ruby will throw yet another error. So I need to either add a conditional in that method, or add some more error handling. I’m going to add some more error handling:
begin
getArrivals(feed123456S, time_now, data)
rescue
puts "did not seed 123"
# Do something
end
At this point I have the structure to handle the errors I’m seeing. Now I just actually need to do something about it. My DataHelper.rb file has some class methods including a retrieve_data function as such:
def self.retrieve_data
if @@data.length == 0
@@data = JSON.parse(@@redis.get(@@redis.get(@@data_key)))
end
@@data = JSON.parse(@@redis.get(@@redis.get(@@data_key)))
end
This basically grabs the data in redis and parses it. Since I’ve required DataHelper.rb into my CronHelper.rb file, I simply have to call the function and then sift through that data and grab all the data that pertains to the line that’s having the WireType error. It looks like this:
station_arrivals is the array that stores all the MTA and is what’s pushed into redis, so I’m basically calling all the old data, finding all the relevant arrivals and pushing it into station_arrivals to send back and save to redis.
Looking forward to further testing this and deploying to production in the next day or two!
Occasionally the MTA sends corrupt or incomplete data. It’s particularly frustrating for me because of the structure of how I’ve built HustleTime.
The MTA sends transit data through multiple GTFS feeds, the subway system (and SIRR) are covered by 9 individual feeds. My code looks a little like this:
As you can see, I’m touching each of these endpoints in one file, and assigning each feed to a variable which is then parsed by protobuf:
begin
feed123456S = Transit_realtime::FeedMessage.decode(data123456S)
rescue Protobuf::InvalidWireType
errors = errors + 'skipped: 123456S (wireType) '
end
What’s happening here is that a few times a week a feed starts sending corrupt data, and returns an InvalidWireType error. I’m catching this error and storing the error in an errors variable. The problem with this method is that when all the various feeds are agglomerated, if one feed received an error, that entire feed will be omitted from the station_arrivals object that is being sent to redis.
This all means that if there’s an error, all the old data is rewritten in Redis without the offending line at all- and this isn’t a great way to approach this since there is actually data that I could use.
Imagine HustleTime is seeding normally. The MTA sends about 1 hour of future arrivals when you hit their api. So if I am pinging the BDFM feed, I’ll see predictions for then next hour of arrivals at all stations along the Orange line. When you encounter a WireType error, all that old data would be rewritten, so I really need a way to go into the old data and grab those numbers, and splice them into the new data when an error is caught.
Remember, this only happens on occasion, but my good friend BanjoCat was good enough to set up some monitoring for me so I can see when it’s happening, and to be honest, it’s happening more than I would like. So I’ve shelved Ignoring trains to work on this issue next. I’m developing a few methods now that do what I need and I’m currently testing. Hopefully I can avoid WireType blackouts in the future.
You can get HustleTime on both Android and iPhone free!
The next big feature I’ve been meaning to add to HustleTime is a way for users to ignore specific train lines.
Let’s say you’re on lower level at West 4th street station in manhattan, and you need to take the to Brighton Beach but all you’re seeing in the HustleTime Ui is other trains that are of no use to you. This feature will allow you to ignore trains so that you’ll only see the train you need.
How can I accomplish this?
At it’s most basic level the Front end will need to send something to the back end and the back end will need to send the requested information sans the ignored stations. It will also be necessary to save the ignored lines into both state and the phone memory, so that a user will not need to select ignored trains every time they open the app. We also need to make sure the back-end is backwards compatible with prior versions without the ignore feature. Finally, we need a way for the user to actually select the stations they hope to ignore.
How to get started is always tricky for me: I never know where to start. As HustleTime gets more complex, it will become more challenging to decide how to begin modifying all the various moving parts – and I’m sure as I become more experienced as a developer, this process will become easier and easier.
Real Estate
The screen on a mobile device has very limited real-estate. I have a modal built into HustleTime already, and that shows you your saved favorite places.
Where can I add ignored lines??
With the limited amount of space, I think it will probably be best to display the ignore options in a separate tab of the menu. Something like this…
There, I’ll put checkboxes for the various lines, each checkbox will toggle a boolean value in state for each of these lines. I’ll then JSON.stringify that object when I save it to phone memory or send it to the back-end. In tomorrow’s post I’ll have something to show in regards to my progress on this feature.
As I push forward with new iterations of HustleTime, I wanted to create a blog to detail my progress, perhaps it will foster creativity, engagement with my small (but growing) audience, and / or simply serve to document my progress.
I am only a few days removed from my Android release, a painstakingly difficult process that led me down many dead ends, and eventually rebuilding the app in a completely different code repo.
The draw and selling points of React-Native eventually ended up failing me. Now that I’ve created the app, I can say that there are still a lot of advantages to building in React-Native, but it’s never as simple as it seems.
An example is in NSUserDefaults (Ios) and DefaultPreferences (android):
As you can see, the code is very similar, but one library can take an array, and the other forces me to use a string. It’s little things like this, coupled with various dependency issues – particularly how gradle dependency versioning works that really slowed me down.
I hope to blog more about how I improve the product in the future as well as a write a retrospective on my journey in future blog posts.