Skip to content

Commit 5e43873

Browse files
committed
Add Bluesky
1 parent 5f6667c commit 5e43873

File tree

7 files changed

+166
-3
lines changed

7 files changed

+166
-3
lines changed

Images/bluesky.png

857 KB
Loading

README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
## News
22

3-
iOS news app in the style of Apollo, Apple News, Artifact, Axios, BBC, Cash App, CNN, Facebook, Facebook News, FastNews, Flipboard, Instagram, Lil News, NBC News, Particle, Reddit, Robinhood, The New York Times, The Washington Post, The Wall Street Journal, Threads, UIKit, X (Twitter) :newspaper:
3+
iOS news app in the style of Apollo, Apple News, Artifact, Axios, BBC, Bluesky, Cash App, CNN, Facebook, Facebook News, FastNews, Flipboard, Instagram, Lil News, NBC News, Particle, Reddit, Robinhood, The New York Times, The Washington Post, The Wall Street Journal, Threads, UIKit, X (Twitter) :newspaper:
44

55
![](https://github.com/dkhamsing/news/actions/workflows/xcodebuild-ios14.yml/badge.svg) ![](https://github.com/dkhamsing/news/actions/workflows/xcodebuild-ios15.yml/badge.svg) ![](https://github.com/dkhamsing/news/actions/workflows/xcodebuild-ios16.yml/badge.svg) ![](https://github.com/dkhamsing/news/actions/workflows/xcodebuild-ios17.yml/badge.svg) ![](https://github.com/dkhamsing/news/actions/workflows/xcodebuild-tvos13.yml/badge.svg)
66

@@ -9,7 +9,7 @@ iOS news app in the style of Apollo, Apple News, Artifact, Axios, BBC, Cash App,
99

1010
<img width=300 src=Images/demo.gif>
1111

12-
<img src=Images/apollo.png height=400> <img src=Images/applenews.png height=400> <img src=Images/artifact.png height=400> <img src=Images/axios.png height=400> <img src=Images/bbc.png height=400> <img src=Images/cashapp.png height=400> <img src=Images/cnn.png height=400> <img src=Images/facebook.png height=400> <img src=Images/facebooknews.png height=400> <img src=Images/flipboard.png height=400> <img src=Images/fastnews.png height=400> <img src=Images/instagram.png height=400> <img src=Images/lilnews.png height=400> <img src=Images/nbcnews.png height=400> <img src=Images/particle2.png height=400> <img src=Images/reddit.png height=400> <img src=Images/robinhood.png height=400> <img src=Images/nyt.png height=400> <img src=Images/threads.png height=400> <img src=Images/wsj.png height=400> <img src=Images/washingtonpost.png height=400> <img src=Images/uikit.png height=400> <img src=Images/x.png height=400>
12+
<img src=Images/apollo.png height=400> <img src=Images/applenews.png height=400> <img src=Images/artifact.png height=400> <img src=Images/axios.png height=400> <img src=Images/bbc.png height=400> <img src=Images/bluesky.png height=400> <img src=Images/cashapp.png height=400> <img src=Images/cnn.png height=400> <img src=Images/facebook.png height=400> <img src=Images/facebooknews.png height=400> <img src=Images/flipboard.png height=400> <img src=Images/fastnews.png height=400> <img src=Images/instagram.png height=400> <img src=Images/lilnews.png height=400> <img src=Images/nbcnews.png height=400> <img src=Images/particle2.png height=400> <img src=Images/reddit.png height=400> <img src=Images/robinhood.png height=400> <img src=Images/nyt.png height=400> <img src=Images/threads.png height=400> <img src=Images/wsj.png height=400> <img src=Images/washingtonpost.png height=400> <img src=Images/uikit.png height=400> <img src=Images/x.png height=400>
1313

1414
#### Bonus
1515

@@ -30,6 +30,7 @@ iOS news app in the style of Apollo, Apple News, Artifact, Axios, BBC, Cash App,
3030
- [Artifact](https://artifact.news)
3131
- [Axios](https://www.axios.com)
3232
- [BBC](https://www.bbc.com)
33+
- [Bluesky](https://bsky.app)
3334
- [Cash App](https://cash.app)
3435
- [CNN](https://www.cnn.com)
3536
- [Facebook](https://www.facebook.com)

Xcode/TheNews.xcodeproj/project.pbxproj

+16
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@
9595
D74AB4E72D6AA5CE008DF432 /* ParticleHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74AB4E52D6AA5CE008DF432 /* ParticleHandler.swift */; };
9696
D7563B882ADC56390093C3FF /* ThreadsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7563B862ADC56390093C3FF /* ThreadsCell.swift */; };
9797
D7563B892ADC56390093C3FF /* ThreadsHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7563B872ADC56390093C3FF /* ThreadsHandler.swift */; };
98+
D7E879102D7132D600370650 /* BlueskyCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E8790E2D7132D600370650 /* BlueskyCell.swift */; };
99+
D7E879112D7132D600370650 /* BlueskyHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E8790F2D7132D600370650 /* BlueskyHandler.swift */; };
98100
/* End PBXBuildFile section */
99101

100102
/* Begin PBXContainerItemProxy section */
@@ -214,6 +216,8 @@
214216
D74AB4E52D6AA5CE008DF432 /* ParticleHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParticleHandler.swift; sourceTree = "<group>"; };
215217
D7563B862ADC56390093C3FF /* ThreadsCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreadsCell.swift; sourceTree = "<group>"; };
216218
D7563B872ADC56390093C3FF /* ThreadsHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreadsHandler.swift; sourceTree = "<group>"; };
219+
D7E8790E2D7132D600370650 /* BlueskyCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlueskyCell.swift; sourceTree = "<group>"; };
220+
D7E8790F2D7132D600370650 /* BlueskyHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlueskyHandler.swift; sourceTree = "<group>"; };
217221
/* End PBXFileReference section */
218222

219223
/* Begin PBXFrameworksBuildPhase section */
@@ -486,6 +490,7 @@
486490
D7044E2F2AD7636200D5EC4A /* Artifact */,
487491
65580AA325904AE100D1287F /* Axios */,
488492
650785132587038E001ABDAE /* BBC */,
493+
D7E8790D2D7132D600370650 /* Bluesky */,
489494
65A79AB6277BD55300505313 /* CashApp */,
490495
650785352587038E001ABDAE /* CNN */,
491496
650785162587038E001ABDAE /* Facebook */,
@@ -588,6 +593,15 @@
588593
path = Threads;
589594
sourceTree = "<group>";
590595
};
596+
D7E8790D2D7132D600370650 /* Bluesky */ = {
597+
isa = PBXGroup;
598+
children = (
599+
D7E8790E2D7132D600370650 /* BlueskyCell.swift */,
600+
D7E8790F2D7132D600370650 /* BlueskyHandler.swift */,
601+
);
602+
path = Bluesky;
603+
sourceTree = "<group>";
604+
};
591605
/* End PBXGroup section */
592606

593607
/* Begin PBXNativeTarget section */
@@ -760,6 +774,7 @@
760774
650785522587038F001ABDAE /* CNNCell.swift in Sources */,
761775
650785402587038F001ABDAE /* RedditCell.swift in Sources */,
762776
650785472587038F001ABDAE /* UIKitCell.swift in Sources */,
777+
D7E879102D7132D600370650 /* BlueskyCell.swift in Sources */,
763778
65C3837825872490008F1411 /* NewsApi.swift in Sources */,
764779
655C9712258E657400095493 /* AppleNewsCell.swift in Sources */,
765780
65D47DA725941163009701BB /* FacebookNewsHandler.swift in Sources */,
@@ -769,6 +784,7 @@
769784
6560AE5324411C4A00581008 /* SceneDelegate.swift in Sources */,
770785
650785392587038F001ABDAE /* BBCHandler.swift in Sources */,
771786
65A79ABA277BD60500505313 /* CashAppHandler.swift in Sources */,
787+
D7E879112D7132D600370650 /* BlueskyHandler.swift in Sources */,
772788
650785462587038F001ABDAE /* UIKitHandler.swift in Sources */,
773789
650785492587038F001ABDAE /* NewsCollectionHandler.swift in Sources */,
774790
6507854D2587038F001ABDAE /* NYTCell.swift in Sources */,

Xcode/TheNews/NewsViewController.swift

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class NewsViewController: UIViewController {
1616
var handlerApollo = ApolloHandler()
1717
var handlerAxios = AxiosHandler()
1818
var handlerArtifact = ArtifactHandler()
19+
var handlerBluesky = BlueskyHandler()
1920
var handlerBBC = BBCHandler()
2021
var handlerCashApp = CashAppHandler()
2122
var handlerCNN = CNNHandler()
@@ -60,6 +61,7 @@ class NewsViewController: UIViewController {
6061
handlerApollo.articles = articles
6162
handlerArtifact.articles = articles
6263
handlerAxios.articles = articles
64+
handlerBluesky.articles = articles
6365
handlerBBC.articles = articles
6466
handlerCashApp.articles = articles
6567
handlerCNN.articles = articles
@@ -176,6 +178,9 @@ private extension NewsViewController {
176178
case .axios:
177179
viewTable.dataSource = handlerAxios
178180
viewTable.delegate = handlerAxios
181+
case .bluesky:
182+
viewTable.dataSource = handlerBluesky
183+
viewTable.delegate = handlerBluesky
179184
case .bbc:
180185
viewTable.dataSource = handlerBBC
181186
viewTable.delegate = handlerBBC

Xcode/TheNews/NewsViewModel.swift

+4-1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class NewsViewModel {
8080
applenews,
8181
artifact,
8282
axios,
83+
bluesky,
8384
bbc,
8485
cashapp,
8586
cnn,
@@ -126,7 +127,7 @@ class NewsViewModel {
126127
return "UIKit"
127128
case .bbc, .cnn:
128129
return self.rawValue.uppercased()
129-
case .apollo, .artifact, .axios, .facebook, .flipboard, .instagram, .reddit, .particle, .robinhood, .threads:
130+
case .apollo, .artifact, .axios, .bluesky, .facebook, .flipboard, .instagram, .reddit, .particle, .robinhood, .threads:
130131
return self.rawValue.capitalized
131132
}
132133
}
@@ -152,6 +153,8 @@ class NewsViewModel {
152153
return [AxiosCell.identifier]
153154
case .bbc:
154155
return [BBCCell.identifier]
156+
case .bluesky:
157+
return [BlueskyCell.identifier]
155158
case .cashapp:
156159
return [CashAppCell.identifier]
157160
case .cnn:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import UIKit
2+
3+
class BlueskyCell: NewsCell {
4+
5+
static let identifier: String = "BlueskyCell"
6+
private static let logoSize = CGSize(width: 34, height: 34)
7+
8+
let author = UILabel()
9+
let container = UIView()
10+
let url = UILabel()
11+
12+
override func config() {
13+
super.config()
14+
15+
logo.layer.cornerRadius = BlueskyCell.logoSize.width / 2
16+
logo.layer.masksToBounds = true
17+
18+
author.font = .preferredFont(forTextStyle: .headline)
19+
20+
ago.textColor = .secondaryLabel
21+
ago.font = .preferredFont(forTextStyle: .body)
22+
23+
url.textColor = .secondaryLabel
24+
url.font = .preferredFont(forTextStyle: .footnote)
25+
26+
container.layer.borderColor = UIColor.quaternaryLabel.cgColor
27+
container.layer.borderWidth = 1
28+
container.layer.cornerRadius = 10
29+
container.layer.masksToBounds = true
30+
31+
title.font = .preferredFont(forTextStyle: .headline)
32+
33+
[logo, author, ago, summary, container].forEach {
34+
contentView.addSubviewForAutoLayout($0)
35+
}
36+
37+
let imageHeight: CGFloat = 164
38+
let inset: CGFloat = 10
39+
NSLayoutConstraint.activate([
40+
logo.topAnchor.constraint(equalTo: contentView.topAnchor, constant: inset + 5),
41+
logo.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor),
42+
logo.heightAnchor.constraint(equalToConstant: BlueskyCell.logoSize.height),
43+
logo.widthAnchor.constraint(equalToConstant: BlueskyCell.logoSize.width),
44+
45+
author.topAnchor.constraint(equalTo: contentView.topAnchor, constant: inset),
46+
author.leadingAnchor.constraint(equalTo: logo.trailingAnchor, constant: inset),
47+
48+
ago.topAnchor.constraint(equalTo: contentView.topAnchor, constant: inset),
49+
ago.leadingAnchor.constraint(equalTo: author.trailingAnchor, constant: 8),
50+
contentView.trailingAnchor.constraint(equalTo: ago.trailingAnchor, constant: inset + 8),
51+
52+
summary.topAnchor.constraint(equalTo: ago.bottomAnchor, constant: inset - 5),
53+
summary.leadingAnchor.constraint(equalTo: logo.trailingAnchor, constant: inset),
54+
contentView.readableContentGuide.trailingAnchor.constraint(equalTo: summary.trailingAnchor),
55+
56+
container.topAnchor.constraint(equalTo: summary.bottomAnchor, constant: inset),
57+
container.leadingAnchor.constraint(equalTo: logo.trailingAnchor, constant: inset),
58+
contentView.readableContentGuide.trailingAnchor.constraint(equalTo: container.trailingAnchor),
59+
60+
contentView.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: inset + 5)
61+
])
62+
63+
let separatorView = UIView()
64+
separatorView.backgroundColor = .quaternaryLabel
65+
66+
let iconSize: CGFloat = 13
67+
let icon = UIImageView()
68+
69+
if let img = UIImage(systemName: "globe.asia.australia"),
70+
let globeImage: UIImage = img
71+
.applyingSymbolConfiguration(.init(
72+
font: .systemFont(ofSize: iconSize),
73+
scale: .default)) {
74+
icon.image = globeImage
75+
icon.tintColor = .secondaryLabel
76+
}
77+
78+
[articleImageView, url, title, separatorView, icon].forEach {
79+
container.addSubviewForAutoLayout($0)
80+
}
81+
82+
NSLayoutConstraint.activate([
83+
articleImageView.topAnchor.constraint(equalTo: container.topAnchor, constant: 0),
84+
articleImageView.leadingAnchor.constraint(equalTo: container.leadingAnchor),
85+
container.trailingAnchor.constraint(equalTo: articleImageView.trailingAnchor),
86+
articleImageView.heightAnchor.constraint(equalToConstant: imageHeight),
87+
88+
title.topAnchor.constraint(equalTo: articleImageView.bottomAnchor, constant: 8),
89+
title.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 8),
90+
container.trailingAnchor.constraint(equalTo: title.trailingAnchor, constant: 8),
91+
92+
separatorView.topAnchor.constraint(equalTo: title.bottomAnchor, constant: inset),
93+
separatorView.heightAnchor.constraint(equalToConstant: 1),
94+
separatorView.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 10),
95+
container.trailingAnchor.constraint(equalTo: separatorView.trailingAnchor, constant: 10),
96+
97+
icon.topAnchor.constraint(equalTo: separatorView.bottomAnchor, constant: inset + 1),
98+
icon.heightAnchor.constraint(equalToConstant: iconSize),
99+
icon.widthAnchor.constraint(equalToConstant: iconSize),
100+
icon.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: inset),
101+
102+
url.topAnchor.constraint(equalTo: separatorView.bottomAnchor, constant: inset),
103+
url.leadingAnchor.constraint(equalTo: icon.trailingAnchor, constant: 4),
104+
url.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -inset),
105+
106+
container.bottomAnchor.constraint(equalTo: url.bottomAnchor, constant: inset)
107+
])
108+
}
109+
110+
func load(article: Article) {
111+
author.text = article.author ?? article.source?.name
112+
summary.text = article.description
113+
title.text = article.titleDisplay
114+
ago.text = article.ago
115+
url.text = article.url?.host?.replacingOccurrences(of: "www.", with: "")
116+
load(urlString: article.urlToImage, downloader: ImageDownloader.shared)
117+
loadLogo(urlString: article.urlToSourceLogo, size: BlueskyCell.logoSize)
118+
}
119+
120+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import UIKit
2+
3+
class BlueskyHandler: NewsTableHandler {
4+
5+
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
6+
return articles.count
7+
}
8+
9+
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
10+
let cell = tableView.dequeueReusableCell(withIdentifier: BlueskyCell.identifier) as! BlueskyCell
11+
12+
let article = articles[indexPath.row]
13+
cell.load(article: article)
14+
15+
return cell
16+
}
17+
18+
}

0 commit comments

Comments
 (0)