# Handheld Business Watcher News App
A neat-designed iOS Newsfeed application which contains richful most-recent news about business stocks, built from scratch in UIkit, incorporated with newapi.org APIs to a custom UI and cache images and CGRect.
## ***[Copyright and Commercial Use Disclaimer](https://github.com/KrystalZhang612/KrystalZhang-Handheld-Business-Watcher-News-App/blob/main/README.md#please-carefully-read-licensemd-about-the-open-source-restrictions-and-the-personal-use-policy-of-this-project-under-gpl-30-license-any-commericial-uses-on-this-project-by-other-than-the-owner-krystalzhang612-or-the-authorized-users-and-organizations-including-unauthorized-modifications-forks-pull-requests-and-other-commercial-related-uses-will-be-subjected-to-copyright-violation-with-sebsequent-legal-concerns)***
### ***Please carefully read [LICENSE.md](https://github.com/KrystalZhang612/KrystalZhang-Handheld-Business-Watcher-News-App/blob/main/LICENSE) about the Open Source restrictions and the personal use policy of this project under [GPL-3.0 license](https://www.gnu.org/licenses/gpl-3.0.en.html), any commericial uses on this project by other than the owner [@KrystalZhang612](https://github.com/KrystalZhang612) or the authorized users and organizations, will be subjected to copyright violation with sebsequent legal concerns.***
## Handheld Business Watcher News App Overview:
# Build
[Method to Run & Test the Project Locally](https://github.com/KrystalZhang612/KrystalZhang-Handheld-Business-Watcher-News-App/blob/main/README.md#method-to-run--test-the-project-locally)
[Prerequisites & Setups](https://github.com/KrystalZhang612/KrystalZhang-Handheld-Business-Watcher-News-App/blob/main/README.md#prerequisites--setups)
[Debugging&Troubleshooting](https://github.com/KrystalZhang612/KrystalZhang-Handheld-Business-Watcher-News-App/blob/main/README.md#debuggingtroubleshooting)
[Synchronous Developing Notes](https://github.com/KrystalZhang612/KrystalZhang-Handheld-Business-Watcher-News-App/blob/main/README.md#synchronous-developing-notes)
[Testing Result](https://github.com/KrystalZhang612/KrystalZhang-Handheld-Business-Watcher-News-App/blob/main/README.md#testing-results)
[Tags and Topics](https://github.com/KrystalZhang612/KrystalZhang-Handheld-Business-Watcher-News-App/blob/main/README.md#tags-and-topics)
# Contribution
[Author](https://github.com/KrystalZhang612/KrystalZhang-Handheld-Business-Watcher-News-App/blob/main/README.md#author)
# Compatibility
| OS | Supported |
| ------- | ------------------ |
| iOS 10-iOS 15.5 | :white_check_mark: |
| iOS 16+ | :x: |
| macOS Mojave | |
| macOS Monterey | |
# Method to Run & Test the Project Locally
### Download the entire project to local directory
Create a new file named `APICaller.swift` in the root directory of the project`:
```swift
import Foundation
final class APICaller {
static let shared = APICaller()
struct Constants {
static let topHeadlinesURL = URL(string:
"https://newsapi.org/v2/top-headlines?country=us&category=business&apiKey=YOUR OWN API KEY")
}
private init(){}
public func getTopStories(completion: @escaping (Result<[Article], Error>) -> Void) {
guard let url = Constants.topHeadlinesURL else {
return
}
let task = URLSession.shared.dataTask(with: url) { data, _, error in
if let error = error{
completion(.failure(error))
}
else if let data = data {
do {
let result = try JSONDecoder().decode(APIResponse.self, from: data)
print("Articles: \(result.articles.count)")
completion(.success(result.articles))
}
catch {
completion(.failure(error))
}
}
}
task.resume()
}
}
//Models
struct APIResponse: Codable {
let articles: [Article]
}
struct Article: Codable {
let source: Source
let title: String
let description: String?
let url: String?
let urlToImage: String?
let publishedAt: String
}
struct Source: Codable {
let name: String
}
```
Replace `YOUR OWN API KEY` with your own obtained API Key.
### Xcode must be 13.4 and higher versions with all Xcode dependencies updated.
### Compatible with `iOS 10-iOS 15.5`
### Not compatiable with `iOS 16+`
### Run the project, choose Simulator `iPhone 12 Pro Max iOS 14.4` for best compatiability.
# Developing Languages, Tools, and Techniques Needed
[Xcode 13.4.1 iOS 10-15.5 (iOS 16 causes Assertion Errors)](https://developer.apple.com/documentation/xcode-release-notes/xcode-13_4_1-release-notes)
Best testing simulator version to avoid errors: `iPhone12 Pro Max iOS 14.4`
[News API](https://newsapi.org/docs/get-started#search)
[SwiftUI](https://developer.apple.com/xcode/swiftui/)
[SafariServices Documentation](https://developer.apple.com/documentation/safariservices Swift 5 https://developer.apple.com/swift/)
`Note: Xcode project renaming rule: change info.plist File in Build Settings.`
# Prerequisites & Setups
Obtain an API url from newsapi.org for the most-recent TechCrunch news.
Create a new object to call APIs [APICaller.swift](https://github.com/KrystalZhang612/KrystalZhang-Handheld-Business-Watcher-News-App/blob/main/Handheld-Business-Watcher-News-App/APICaller.swift):
Handle potential errors with completion method, resume task:
```Swift
public func getTopStories(completion: @escaping{...) {
...
if let error = error{
completion(.failure(error))
}
else if let data = data {
do {
let result = try JSONDecoder().decode(String.self,
} catch {
completion(.failure(error))
}
task.resume()
```
Create some API responses models:
```Swift
struct APIResponse: Codable {
let articles: [Article]
}
struct Article: Codable {
let source: Source
let title: String
let description: String?
let url: String?
let urlToImage: String?
let publishedAt: String
}
struct Source: Codable {
let name: String }
```
# Synchronous Developing Notes
## ***Table View:***
```Swift
func tableView (_ tableView: UITableView, numberOfRowsInSection
section: Int)-> Int {
return 0 }
func tableView (_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(
withIdentifier: "cell",
for: indexPath
)
cell.textLabel?.text = "Something"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath:
IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
```
Also create a frame for table view:
```Swift
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.frame = view.bounds
}
```
Create a new Cocoa Touch file [NewsTableViewCell.swift](https://github.com/KrystalZhang612/KrystalZhang-Handheld-Business-Watcher-News-App/blob/main/Handheld-Business-Watcher-News-App/NewsTableViewCell.swift):
```Swift
class NewsTableViewCell: UITableViewCell {
static let identifier = "NewsTableViewCell"
override init(style: UITableViewCell.CellStyle, reuseIdentifier:
String?){
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required init?(coder: NSCoder){
fatalError()
}
override func layoutSubviews(){
super.layoutSubviews()
}
override func prepareForReuse(){
super.prepareForReuse()
}
```
Configure the cell with viewModel:
Create viewModel first:
```Swift
class NewsTableViewCellViewModel {
let title: String
let subtitle: String
let imageURL: URL?
let imageData: Data? = nil
init(
title: String,
subtitle: String,
imageURL: URL?
){
self.title = title self.subtitle = subtitle self.imageURL = imageURL
}
```
Then add viewModel configuration:
```Swift
func configure(with viewModel: NewsTableViewCellViewModel){
newsTitleLabel.text = viewModel.title
subtitleLabel.text = viewModel.subtitle
```
Also configure the images:
```Swift
if let data = viewModel.imageData {
newsImageView.image = UIImage(data: data)
}
```
Now the top 20 news headlines fetched and loaded successfully:
[news headlines fetched.PNG](https://github.com/KrystalZhang612/KrystalZhang-Handheld-Business-Watcher-News-App/blob/main/testing-result-Handheld-Business-Watcher-News-App/news%20headlines%20fetched.PNG)
Fetch images by downloading the urls:
```Swift
//fetch
URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
guard let data = data, error == nil else {
return
}
viewModel.imageData = data
DispatchQueue.main.async {
self?.newsImageView.image = UIImage(data: data)
}
}.resume()
```
Now news headlines, details and images along are properly aligned:
[news details and images are properly aligned.PNG](https://github.com/KrystalZhang612/KrystalZhang-Handheld-Business-Watcher-News-App/blob/main/testing-result-Handheld-Business-Watcher-News-App/news%20details%20and%20images%20are%20properly%20aligned.PNG)
Import Safari Service:
```Swift
import SafariServices
```
Make Safari service presented:
```Swift
func tableView(_ tableView: UITableView, didSelectRowAt indexPath:
IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let article = articles[indexPath.row)
guard let url = URL(string: article.url ?? "") else {
return }
let vc = SFSafariViewController(url: url)
present(vc, animated: true)
}
```
Now all the visible news are clickable and will redirect us to its safari viewing page:
[redirected to safari viewer.PNG](https://github.com/KrystalZhang612/KrystalZhang-Handheld-Business-Watcher-News-App/blob/main/testing-result-Handheld-Business-Watcher-News-App/redirected%20to%20safari%20viewer.PNG)
# Debugging&Troubleshooting
- ERROR: Title not showing in Xcode simulator. DEBUGGING: In `Main.storyboard`,
`Editor-> Embed in -> Navigation Controller`.
- Fatal Error:
Error Message: `[Assert] UINavigationBar decoded as unlocked for
UINavigationController, or navigationBar delegate set up incorrectly.
Inconsistent configuration may cause problems.
navigationController=
,
navigationBar=>
delegate=0x7f7e5a00d400` DEBUGGING: iOS 16 incompatibility error. Try downloading Simulator packages <= iOS 15.5.
# Testing Results
# Tags and Topics
api, swift, xcode, cgrect, safari-services, swoftui, swift5, newsapi-org, uikit.
# Author
Krystal Zhang
https://github.com/KrystalZhang612
*This file was generated by [Handheld-Business-Watcher-News-AppApp-readme](https://github.com/KrystalZhang612/KrystalZhang-Handheld-Business-Watcher-News-App/blob/main/README.md), on October 30, 2022.