Skip to content

Commit 1537470

Browse files
authored
UI polish (#309)
1 parent ae2262a commit 1537470

File tree

19 files changed

+177
-330
lines changed

19 files changed

+177
-330
lines changed

.github/workflows/ios_emerge_upload_main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66

77
jobs:
88
build:
9-
runs-on: macos-14
9+
runs-on: macos-15
1010

1111
defaults:
1212
run:
@@ -17,7 +17,7 @@ jobs:
1717
uses: actions/checkout@v4
1818

1919
- name: Select Xcode version
20-
run: sudo xcode-select -s '/Applications/Xcode_15.4.app/Contents/Developer'
20+
run: sudo xcode-select -s '/Applications/Xcode_16.2.app/Contents/Developer'
2121

2222
- name: Set up Ruby env
2323
uses: ruby/setup-ruby@v1

.github/workflows/ios_emerge_upload_pr.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77

88
jobs:
99
build:
10-
runs-on: macos-14
10+
runs-on: macos-15
1111

1212
defaults:
1313
run:
@@ -18,7 +18,7 @@ jobs:
1818
uses: actions/checkout@v4
1919

2020
- name: Select Xcode version
21-
run: sudo xcode-select -s '/Applications/Xcode_15.4.app/Contents/Developer'
21+
run: sudo xcode-select -s '/Applications/Xcode_16.2.app/Contents/Developer'
2222

2323
- name: Set up Ruby env
2424
uses: ruby/setup-ruby@v1

.github/workflows/ios_emerge_upload_snapshots.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ on:
99

1010
jobs:
1111
upload_emerge_snapshots:
12-
runs-on: macos-14
12+
runs-on: macos-15
1313

1414
defaults:
1515
run:
@@ -20,7 +20,7 @@ jobs:
2020
uses: actions/checkout@v4
2121

2222
- name: Select Xcode version
23-
run: sudo xcode-select -s '/Applications/Xcode_15.4.app/Contents/Developer'
23+
run: sudo xcode-select -s '/Applications/Xcode_16.2.app/Contents/Developer'
2424

2525
- name: Set up Ruby env
2626
uses: ruby/setup-ruby@v1
@@ -37,15 +37,15 @@ jobs:
3737
EMERGE_API_TOKEN: ${{ secrets.EMERGE_API_KEY }}
3838

3939
upload_swift_snapshot_testing_snapshots:
40-
runs-on: macos-14
40+
runs-on: macos-15
4141
defaults:
4242
run:
4343
working-directory: ./ios
4444
steps:
4545
- name: Checkout
4646
uses: actions/checkout@v4
4747
- name: Select Xcode version
48-
run: sudo xcode-select -s '/Applications/Xcode_15.4.app/Contents/Developer'
48+
run: sudo xcode-select -s '/Applications/Xcode_16.2.app/Contents/Developer'
4949
- name: Set up App Store Connect API Key
5050
run: |
5151
ABSOLUTE_KEY_PATH="$(pwd)/app_store_key.p8"
@@ -59,7 +59,7 @@ jobs:
5959
xcodebuild test \
6060
-scheme HackerNews \
6161
-sdk iphonesimulator \
62-
-destination 'platform=iOS Simulator,name=iPhone 15 Pro,OS=18.1' \
62+
-destination 'platform=iOS Simulator,name=iPhone 16 Pro,OS=18.1' \
6363
-only-testing:HackerNewsTests/SwiftSnapshotTest \
6464
-allowProvisioningUpdates \
6565
-authenticationKeyPath "$AUTH_KEY_PATH" \

ios/HackerNews.xcodeproj/project.pbxproj

Lines changed: 7 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@
109109
A42705AA2A4296BA0057E439 /* FeedScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedScreen.swift; sourceTree = "<group>"; };
110110
A42705AC2A429D2E0057E439 /* HNApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HNApi.swift; sourceTree = "<group>"; };
111111
A434C2DF2A8E75960002F488 /* WebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebView.swift; sourceTree = "<group>"; };
112-
A435EF3D2C08F2A9005BF473 /* HackerNews.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = HackerNews.entitlements; path = ../HackerNews.entitlements; sourceTree = "<group>"; };
113112
A439A8EB2D4194B100452940 /* Font.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Font.swift; sourceTree = "<group>"; };
113+
A439A8ED2D429E2500452940 /* HackerNews.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = HackerNews.entitlements; sourceTree = "<group>"; };
114114
A45C2C7D2A5DDE25009BC030 /* README.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.txt; sourceTree = "<group>"; };
115115
A45C2C7E2A5DDE25009BC030 /* Pluginfile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Pluginfile; sourceTree = "<group>"; };
116116
A45C2C7F2A5DDE25009BC030 /* Appfile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Appfile; sourceTree = "<group>"; };
@@ -216,8 +216,8 @@
216216
isa = PBXGroup;
217217
children = (
218218
A42705AC2A429D2E0057E439 /* HNApi.swift */,
219-
A423B0672BAE05FB00267DDB /* NetworkDebugger.swift */,
220219
EC70E1602D1DD82B00582023 /* HNWebClient.swift */,
220+
A423B0672BAE05FB00267DDB /* NetworkDebugger.swift */,
221221
);
222222
path = Network;
223223
sourceTree = "<group>";
@@ -257,14 +257,15 @@
257257
1DF162042A436E8D001A3F76 /* Network */,
258258
A439A8E72D418D1100452940 /* Settings */,
259259
A439A8E42D418CC900452940 /* Stories */,
260-
A439A8E22D418C9A00452940 /* Supporting Files */,
261260
1DF162012A436E6E001A3F76 /* Utils */,
262261
A439A8E62D418D0300452940 /* Web */,
263262
A42705A62A42949D0057E439 /* AppViewModel.swift */,
264263
A427057E2A4293B10057E439 /* ContentView.swift */,
265264
A427057C2A4293B10057E439 /* HNApp.swift */,
266265
A42705802A4293B20057E439 /* Assets.xcassets */,
267266
A42705832A4293B20057E439 /* Preview Assets.xcassets */,
267+
A45C2CBB2A5F0A41009BC030 /* Hacker-News-Info.plist */,
268+
A439A8ED2D429E2500452940 /* HackerNews.entitlements */,
268269
);
269270
path = HackerNews;
270271
sourceTree = "<group>";
@@ -289,23 +290,14 @@
289290
path = HackerNewsUITests;
290291
sourceTree = "<group>";
291292
};
292-
A439A8E22D418C9A00452940 /* Supporting Files */ = {
293-
isa = PBXGroup;
294-
children = (
295-
A435EF3D2C08F2A9005BF473 /* HackerNews.entitlements */,
296-
A45C2CBB2A5F0A41009BC030 /* Hacker-News-Info.plist */,
297-
);
298-
path = "Supporting Files";
299-
sourceTree = "<group>";
300-
};
301293
A439A8E32D418CB500452940 /* Comments */ = {
302294
isa = PBXGroup;
303295
children = (
304-
A48C0DE92A9819E00034CC0A /* CommentsViewModel.swift */,
305-
A48C0DE62A9818A50034CC0A /* CommentsScreen.swift */,
296+
ECC0BC8B2D39CDCB00ABB263 /* CommentComposer.swift */,
306297
A47309B52AA7D1F600201376 /* CommentRow.swift */,
307298
EC0B1C742D1A34110000C3AC /* CommentsHeader.swift */,
308-
ECC0BC8B2D39CDCB00ABB263 /* CommentComposer.swift */,
299+
A48C0DE62A9818A50034CC0A /* CommentsScreen.swift */,
300+
A48C0DE92A9819E00034CC0A /* CommentsViewModel.swift */,
309301
);
310302
path = Comments;
311303
sourceTree = "<group>";
@@ -465,7 +457,6 @@
465457
A42705762A4293B10057E439 /* Frameworks */,
466458
A42705772A4293B10057E439 /* Resources */,
467459
A42705AE2A44C35D0057E439 /* Strip Symbols */,
468-
F52059CED4FD476981550295 /* Upload Debug Symbols to Sentry */,
469460
A4D28AE92C237BAE007F20D0 /* Embed Frameworks */,
470461
);
471462
buildRules = (
@@ -623,22 +614,6 @@
623614
shellPath = /bin/sh;
624615
shellScript = "#!/bin/bash\nset -e\n\nif [ \"Release\" = \"${CONFIGURATION}\" ]; then\n # Path to the app directory\n APP_DIR_PATH=\"${BUILT_PRODUCTS_DIR}/${EXECUTABLE_FOLDER_PATH}\"\n # Strip main binary\n strip -rSTx \"${APP_DIR_PATH}/${EXECUTABLE_NAME}\"\n # Path to the Frameworks directory\n APP_FRAMEWORKS_DIR=\"${APP_DIR_PATH}/Frameworks\"\n\n # Strip symbols from frameworks, if Frameworks/ exists at all\n # ... as long as the framework is NOT signed by Apple\n if [ -d \"${APP_FRAMEWORKS_DIR}\" ]\n then\n find \"${APP_FRAMEWORKS_DIR}\" -type f -perm +111 -maxdepth 2 -mindepth 2 -exec bash -c '\n codesign -v -R=\"anchor apple\" \"{}\" &> /dev/null ||\n (\n echo \"Stripping {}\" &&\n if [ -w \"{}\" ]; then\n strip -rSTx \"{}\"\n else\n echo \"Warning: No write permission for {}\"\n fi\n )\n ' \\;\n fi\nfi\n";
625616
};
626-
F52059CED4FD476981550295 /* Upload Debug Symbols to Sentry */ = {
627-
isa = PBXShellScriptBuildPhase;
628-
alwaysOutOfDate = 1;
629-
buildActionMask = 2147483647;
630-
files = (
631-
);
632-
inputPaths = (
633-
"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}",
634-
);
635-
name = "Upload Debug Symbols to Sentry";
636-
outputPaths = (
637-
);
638-
runOnlyForDeploymentPostprocessing = 0;
639-
shellPath = /bin/sh;
640-
shellScript = "# This script is responsable to upload debug symbols and source context for Sentry.\nif which sentry-cli >/dev/null; then\nexport SENTRY_ORG=emerge-tools\nexport SENTRY_PROJECT=hackernews-ios\nERROR=$(sentry-cli debug-files upload --include-sources \"$DWARF_DSYM_FOLDER_PATH\" 2>&1 >/dev/null)\nif [ ! $? -eq 0 ]; then\necho \"warning: sentry-cli - $ERROR\"\nfi\nelse\necho \"warning: sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases\"\nfi\n";
641-
};
642617
/* End PBXShellScriptBuildPhase section */
643618

644619
/* Begin PBXSourcesBuildPhase section */

ios/HackerNews/AppViewModel.swift

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
//
77

88
import Foundation
9-
import SwiftUI
109
import SwiftData
10+
import SwiftUI
1111

1212
enum FeedType: CaseIterable {
1313
case top
@@ -119,9 +119,9 @@ enum StoryState: Identifiable {
119119

120120
var id: Int64 {
121121
switch self {
122-
case .loading(id: let id):
122+
case .loading(let id):
123123
return id
124-
case .loaded(content: let content):
124+
case .loaded(let content):
125125
return content.id
126126
case .nextPage:
127127
return Int64.max
@@ -134,27 +134,27 @@ enum AuthState {
134134
case loggedOut
135135
}
136136

137-
@MainActor
138-
class AppViewModel: ObservableObject {
137+
@Observable @MainActor
138+
final class AppViewModel {
139139

140140
enum AppNavigation: Codable, Hashable {
141141
case webLink(url: URL, title: String)
142142
case storyComments(story: Story)
143143
}
144144

145-
@Published var authState: AuthState
146-
@Published var showLoginSheet: Bool = false
147-
@Published var feedState = FeedState()
148-
@Published var navigationPath = NavigationPath()
149-
@Published var bookmarks: [Bookmark]
145+
var authState: AuthState
146+
var showLoginSheet: Bool = false
147+
var feedState = FeedState()
148+
var navigationPath = NavigationPath()
149+
var bookmarks: [Bookmark]
150150

151151
private let bookmarkStore: BookmarksDataStore
152152
private let api = HNApi()
153153
private let webClient = HNWebClient()
154154

155155
private var pager = Pager()
156156
private let cookieStorage = HTTPCookieStorage.shared
157-
private var loadingTask: Task<Void, Never>?
157+
@ObservationIgnored private var loadingTask: Task<Void, Never>?
158158

159159
init(bookmarkStore: BookmarksDataStore) {
160160
self.bookmarkStore = bookmarkStore
@@ -195,7 +195,7 @@ class AppViewModel: ObservableObject {
195195
}
196196
let nextPage = pager.nextPage()
197197
let items = await api.fetchPage(page: nextPage)
198-
feedState.stories.removeLast() // remove the loading view
198+
feedState.stories.removeLast() // remove the loading view
199199
feedState.stories += items.map { story in
200200
let bookmarked = bookmarkStore.containsBookmark(with: story.id)
201201
return .loaded(content: story.toStoryContent(bookmarked: bookmarked))
@@ -207,7 +207,6 @@ class AppViewModel: ObservableObject {
207207
bookmarks = bookmarkStore.fetchBookmarks()
208208
}
209209

210-
211210
private func isLoggedIn() -> Bool {
212211
return cookieStorage.cookies?.isEmpty == false
213212
}
@@ -217,7 +216,7 @@ class AppViewModel: ObservableObject {
217216
}
218217

219218
func gotoLogin() {
220-
if (authState == .loggedOut) {
219+
if authState == .loggedOut {
221220
showLoginSheet = true
222221
} else {
223222
cookieStorage.removeCookies()
@@ -252,7 +251,7 @@ class AppViewModel: ObservableObject {
252251
}
253252

254253
func loginSubmit(username: String, password: String) async {
255-
let status = await webClient.login(acct: username, pw: password )
254+
let status = await webClient.login(acct: username, pw: password)
256255
print("Login Status: \(status)")
257256
switch status {
258257
case .success:

ios/HackerNews/Auth/LoginScreen.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ enum LoginStatus {
1919
}
2020

2121
struct LoginScreen: View {
22-
@ObservedObject var model: AppViewModel
22+
@Binding var model: AppViewModel
2323
@State var loginState = LoginState()
2424

2525
var body: some View {
@@ -67,5 +67,8 @@ struct LoginScreen: View {
6767
}
6868

6969
#Preview {
70-
LoginScreen(model: AppViewModel(bookmarkStore: FakeBookmarkDataStore()))
70+
@Previewable @State var model = AppViewModel(
71+
bookmarkStore: FakeBookmarkDataStore()
72+
)
73+
LoginScreen(model: $model)
7174
}

ios/HackerNews/Bookmarks/BookmarksScreen.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ import Foundation
99
import SwiftUI
1010

1111
struct BookmarksScreen: View {
12-
@ObservedObject var model: AppViewModel
12+
@Binding var model: AppViewModel
1313

1414
var body: some View {
1515
Group {
1616
if model.bookmarks.isEmpty {
1717
ZStack {
18-
Text("Long press a story to bookmark it.")
18+
Text("Swipe a story to bookmark it.")
1919
.font(.ibmPlexSans(.medium, size: 18))
2020
}
2121
} else {
@@ -25,7 +25,7 @@ struct BookmarksScreen: View {
2525
.frame(height: 60)
2626
ForEach(model.bookmarks, id: \.id) { bookmark in
2727
StoryRow(
28-
model: model,
28+
model: $model,
2929
state: .loaded(content: bookmark.toStoryContent())
3030
)
3131

@@ -59,7 +59,9 @@ struct BookmarksScreen: View {
5959
}
6060

6161
#Preview {
62+
@Previewable @State var model = AppViewModel(
63+
bookmarkStore: FakeBookmarkDataStore())
6264
BookmarksScreen(
63-
model: AppViewModel(bookmarkStore: FakeBookmarkDataStore())
65+
model: $model
6466
)
6567
}

ios/HackerNews/Comments/CommentsScreen.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ struct CommentsScreen: View {
5050
ProgressView()
5151
.progressViewStyle(CircularProgressViewStyle())
5252
.scaleEffect(2)
53+
.padding(24)
5354
case .loaded(let comments):
5455
ForEach(comments, id: \.id) { commentState in
5556
CommentRow(
@@ -81,11 +82,11 @@ struct CommentsScreen: View {
8182
.padding(8)
8283
}
8384
.overlay(alignment: .bottom) {
84-
if (model.state.postCommentState != nil) {
85+
if model.state.postCommentState != nil {
8586
CommentComposer(
8687
state: Binding(
8788
get: { model.state.postCommentState! },
88-
set: { model.state.postCommentState = $0}
89+
set: { model.state.postCommentState = $0 }
8990
),
9091
goToLogin: {
9192
model.goToLogin()
@@ -111,7 +112,7 @@ struct CommentsScreen: View {
111112
let viewModel = CommentsViewModel(
112113
story: PreviewHelpers.makeFakeStory(kids: comments.map { $0.id }),
113114
auth: .loggedIn,
114-
navigation: {_ in}
115+
navigation: { _ in }
115116
)
116117
viewModel.state.comments = .loaded(comments: comments)
117118
return PreviewVariants {

0 commit comments

Comments
 (0)