The application allows users to get the top latest news sorted by categories and search the news of interest by making a query. When a row with a particular news has been tapped the app loads a web page with the news in the browser. A user can save news by swiping the row towards the left side of the screen. Saved news are available to a user on the separate “Saved news” screen. Saved news are appointed to sections with the respective date of save and can be removed from saved if a user decides so.
The project implements CleanSwift architecture and consist of 4 scenes:
- Sign in scene;
- Sign up scene;
- Profile scene;
- Saved news scene;
Each scene comprises a view, viewController, interactor, presenter, and model. Those classes create a specific data flow known as VIP. View Controller is responsible for updating UI and holds a weak reference to an interactor. Interactor processes business logic and holds a weak reference to a presenter. The presenter prepares data received from the interactor to be displayed in a viewController.
Navigation between different scenes is carried out through the Coordinator pattern. The pattern allows to encapsulate navigation logic in separate the class making it easy to create, configure and navigate to the next scene.
Networking and password management are implemented using Singleton. UserDefaults is used to persist user accounts in the device memory.
All view classes make heavy use of StackViews. Those stacks are initialized using an anonymous function making stacks neatly organized and easily constrained in the separate function lately. This is how NewsCell uses anonymous function to create cellStack:
private lazy var cellStack: UIStackView = {
let textStack = UIStackView(arrangedSubviews: [newsTitleLabel, newsDescriptionView])
textStack.axis = .vertical
textStack.spacing = 2
newsTitleLabel.setContentHuggingPriority(.required, for: .vertical)
newsDescriptionView.setContentHuggingPriority(.required, for: .vertical)
let stack = UIStackView(arrangedSubviews: [newsImageView, textStack, chevronImageView])
stack.axis = .horizontal
stack.spacing = 5
newsImageView.setContentHuggingPriority(.required, for: .vertical)
textStack.setContentHuggingPriority(.required, for: .vertical)
return stack
}()
Then we can use cellStack in functions that responsible for setting up views and constraints.
private func setViews() {
cardView.addSubViewsAndTamicOff([cellStack])
}
private func setConstrains() {
NSLayoutConstraint.activate([cellStack.topAnchor.constraint(equalTo: cardView.topAnchor, constant: 5),
cellStack.leadingAnchor.constraint(equalTo: cardView.leadingAnchor),
cellStack.trailingAnchor.constraint(equalTo: cardView.trailingAnchor),
cellStack.bottomAnchor.constraint(equalTo: cardView.bottomAnchor, constant: -5)])
}
To use the application user needs to create an account. The process begins when the "sign up" button is pressed. After that user ends up on a sign up sceen where data for user's account is to be provided. When necessary data has been provided and the "submit" button pressed log in screen comes back.
Log in scene checks if a user has entered the correct login and password. If there is a mistake in one of them the application will show the respective alert. When correct credentials have been provided user is taken to the profile screen where account details are displayed along with the top general news.
The screen consists of three main parts. The first one is user data which is displayed right under the navigation bar. The second one is a collection view containing a news categories bar and search bar to look for news globaly. The last one is table view which is responsible for showing news for chosen category or news found through global search.
When a particular category is selected it changes its appearance and scrolled to be in the center of the screen. When a user decides to search news by query, the news categories bar becomes deactivated until the user taps the "cancel" button in the search bar. When the query search is over news categories bar activates again and selects the category which was chosen before the query search had begun.
Class NewsService handles all logic related to fetching news. The class can be configured to fetch news by category or query. There are private isSearchingMode property and configureSearchingMode(_ query: String?, for category: SearchArticlesCategoryType?) method that help the class to switch between those two searching modes. If data is provided for the function's query parameter the class uses URL for query mode. If there is none, it uses URL to fetch news for the selected category.
final class NewsService {
private var isSearchingMode = false
private func configureSearchingMode(_ query: String?, for category: SearchArticlesCategoryType?) {
if let query = query {
isSearchingMode = true
self.query = query
}
if let category = category {
isSearchingMode = false
self.query = ""
searchArticlesCategory = category
}
}
Then fetchNewsModels(compleation: @escaping ([Article]) -> Void) function is called. It assembles URL, uses it to fetch JSON data and then decode it to swift's data model.
private func fetchNewsModels(compleation: @escaping ([Article]) -> Void) {
var url: URL!
if isSearchingMode {
guard let queryUrl = URL(string: link + String(pageNumber) + "&q=" + query + apiKey) else { return }
url = queryUrl
} else {
guard let categoryUrl = URL(string: link + String(pageNumber) + searchArticlesCategory.apiCategoryRequest + apiKey) else { return }
url = categoryUrl
}
session.dataTask(with: url) { [weak self] data, _ , _ in
guard let data = data,
let newsModel = try? self?.decoder.decode(NewsModel.self, from: data) else { return }
compleation(newsModel.articles)
}.resume()
}
The application monitors if a device is connected to the internet. If the connection is lost it will let user know about the problem.
To save any news user needs to swipe the row to the left. After that, a mark appears on the right side of the row signifying that the news has been successfully saved. Since the saved news can be met in several categories the code is designed to display the mark next to the right one despite the news can occupy a different position across different categories.
The scene is responsible for displaying saved news and removing those the user is not interested in anymore. Additionally, the news is divided into sections by the save date.