Catching Up
Hello all!
It’s been a little while since the last update on LegisLink, and I wanted to take a moment to reconnect and share what’s been happening behind the scenes. As many of you can guess, building an app from scratch is a journey filled with many twists and turns, and sometimes life’s own transitions can lead to unexpected pauses.
Over the past few months, I took a step back from both the app development and this blog as I transitioned into a new job. It is a full stack role that is similar in some ways to my previous job. This change brought about its own set of challenges and adjustments, but it also offered new perspectives and inspirations that I’m excited to bring into LegisLink.
While the app and this blog was on a brief hiatus, I’ve been reflecting on the vision for LegisLink and how it can evolve to better serve its users. The time away has not only refreshed my enthusiasm but has also provided valuable insights into how to enhance the app’s functionality and user experience.
In this blog post, I’ll delve into where LegisLink stands now, outline some of the progress made prior to the hiatus, and share the roadmap for the exciting updates and features planned ahead. Thank you for your patience and continued support as we navigate this development journey together. Let’s dive into the current state of LegisLink and explore what’s next for our mission to keep you connected with civic shenanigans.
Refactoring the App to be More Compliant with MVVM Architecture
One of the major updates in the development of LegisLink has been the comprehensive refactoring of the codebase to embrace a more organized MVVM (Model-View-ViewModel) architecture. Previously, the code for managing various features of the app was largely centralized within a single, massive MyRepsViewModel
. This mistake was easy to make because several features of the app are oriented around finding and storing information about a user's representatives. However, this is not sustainable long-term so a good amount of development time was spent fixing this. And while it may not be perfect, it is a step forward. To break it down, here are the features whose code largely resided in the MyRepsViewModel
:
Finding and storing committee assignment data
Finding and storing election contribution data
Finding and storing sponsored legislation data
Finding and storing legislator data
Clearly, the integration of these diverse features into one ViewModel led to a complex and unwieldy structure with numerous dependencies, making it challenging to maintain and extend. So, how did I fix it?
The refactoring process involved breaking down the monolithic MyRepsViewModel
into more specialized and modular ViewModels. Each major feature of the app now has its dedicated ViewModel: FindMyRepsViewModel
, RepCommitteesViewModel
, RepSponsoredLegislationViewModel
, and RepContributorsViewModel
are all handled separately.
This reorganization not only clarifies the responsibilities of each ViewModel but also reduces the interdependencies between them. This also has performance implications. Instead of all of the operations for finding contributors, committees, legislation etc. being ran for all 3 federal reps at the startup of the app, the operations are only ran when they are needed by the user. This is also true for the API calls being utilized.
This endeavor came from other parts of the app already being coded in a better MVVM manner. For instance, the New York Times feed, which integrates the New York Times API to display top congressional news, and legislation feed, which tracks updates from the Congress.gov website. These features were handled separately, allowing them to remain isolated and less affected by the complexity of the other components. In order to make the impact of the update more clear, here is a simple diagram of how these components were before:
To contrast this, here is a diagram after the refactoring:
By adopting this refined MVVM architecture, I have significantly improved the modularity and maintainability of the codebase. Each ViewModel now focuses on a specific aspect of the app’s functionality, making it easier to debug, test, and enhance individual components without risking unintended consequences elsewhere. This structured approach not only streamlines ongoing development but also lays a solid foundation for integrating future features and updates. The separation of concerns achieved through this refactor ensures that as LegisLink grows and evolves, its underlying architecture remains robust and adaptable.
Creating Development & Production Build Configurations
This was surprisingly difficult. I had to learn more about how Xcode builds and packages things, as well as ensure that I could deploy to Xcode Cloud properly. The reason why I even wanted to work on this was to develop some foundation for shipping the app, and that will definitely require making distinctions between development and production environments. Here are some of the changes I made
Created a directory titled Development Environment
The contents of this (locally) are a
DevelopmentBuildConfig
file, a .plist file calledDEVELOPMENT
and a test plan calledLegisLink-DEV
DevelopmentBuildConfig
is not stored in source control and has the API credentials for development. When building, Xcode finds this file and uses anEnvironment.Swift
file to set the values.DEVELOPMENT
has key value pairs for all of the api keys and environment variablesLegisLink-DEV
is a test plan that can be executed locally or in Xcode Cloud
There is a similarly structured directory titled Production Environment that has the same files, except for a .plist file. Notably because I haven't created separate keys or resources for production yet.
For deploying to the cloud, the process differs slightly. Keys are stored as environment variables in Xcode Cloud, and a script titled
ci_post_clone
assigns those variables to theDevelopmentBuildConfig
file. From there Xcode Cloud is able to run similarly to the local version.
Now, I am able to properly run dev builds locally and in the cloud. As development continues, I will build upon this process. Below is a screenshot of a successful build screen in Xcode Cloud:
Going forward, I want to integrate Xcode Cloud builds and test with GitHub actions on pushes, pull requests etc..
The Trial & Tribulations of Acquiring Legislator Data
Frankly, one of the most difficult tasks with creating this app has been automating the acquisition of legislator information. First, it is important to note that my data source for information comes from this GitHub repository. Here, all of the data I need for this congress is located in various YAML files. For development purposes, it is sufficient to download the folder from GitHub and throw it into the project and point to it locally. However, this is not suitable for a future production version when things can change by the day. So, I worked on a solution to this problem for approximately 2 months. My first idea was a C# API that would request the data from the repo and send it to the the app upon request. This ended up feeling like overkill, so I abandoned that solution rather quickly. Next, I considered building an app in AWS with EC2 to do a similar task. This ended up being unwieldy and buggy, so I settled upon my third and best solution.
After deciding to stick with AWS (Azure and GCP did not work at all when I tried them earlier in the process) I decided to store the files for the congress in an S3 bucket. Then, I wrote a short and sweet AWS Lambda function in Python that would update the S3 bucket from the repo independently from the app or user. This was done by cloning the contents of the repo directory into the S3 bucket. Using the AWS Swift SDK, I then wrote a helper class that would download the contents of the S3 bucket into the app on startup, and thus be usable. Below is a diagram to illustrate this:
This endeavor took much trial and error, but is largely successful. I am presently running into an issue where I can't view my Lambda functions though. Whenever I open the Lambda service in the AWS portal, none of the functions are listed and there is simply an "Unknown Error" message displayed. At some point, I'll have to try and fix that.
Future Work
There are several exciting features and improvements on the horizon. One of the primary focuses will be implementing user profiles and login functionality. This addition will allow users to create personalized accounts. This feature is one of the remaining core functionalities of the app that once implemented, will step it forward to being an MVP.
Adding secure login capabilities and support for Okta linked accounts will also pave the way for enhanced data management and user engagement, as it will facilitate a more customized and interactive interface that evolves with each user's needs.
Another key area of future development involves enhancing the app’s usability with quality-of-life features. I'd like to introduce loading bars and smooth transitions to improve the overall user experience. These elements will provide visual feedback and create a more fluid interaction with the app, making navigation and data retrieval feel more seamless and intuitive.
Of course, I also would like to resolve the issue with not being able to access my Lambda functions.
Conclusion
In wrapping things up, I’m really excited about the direction LegisLink is heading. Refactoring the code has made a big difference, and I’m looking forward to rolling out new features like user profiles and those quality-of-life enhancements. I’m admittedly not looking forward to chatting with tech support in order to resolve the AWS issues.
Thanks a ton for your patience and support through this transition—it really means a lot to me. I plan to post more regular dev logs going forward.
AI Disclosure
For the sake of transparency - I am disclosing that I did use ChatGPT to help me write some of this blog. I used it to create rough drafts of some portions of the blog and to help me create an outline. The vast majority of the work for this blog is still created by me, as are all images used besides the header. I am hoping that by using AI to help me write these blogs, I will be able to publish them more regularly.