-
Notifications
You must be signed in to change notification settings - Fork 299
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
42fb6dd
commit 16c7486
Showing
338 changed files
with
40,003 additions
and
0 deletions.
There are no files selected for viewing
483 changes: 483 additions & 0 deletions
483
0116-redacted-swiftui-pt2/Articles/Articles.xcodeproj/project.pbxproj
Large diffs are not rendered by default.
Oops, something went wrong.
7 changes: 7 additions & 0 deletions
7
...cted-swiftui-pt2/Articles/Articles.xcodeproj/project.xcworkspace/contents.xcworkspacedata
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
8 changes: 8 additions & 0 deletions
8
...pt2/Articles/Articles.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>IDEDidComputeMac32BitWarning</key> | ||
<true/> | ||
</dict> | ||
</plist> |
13 changes: 13 additions & 0 deletions
13
0116-redacted-swiftui-pt2/Articles/Shared/ActivityIndicator.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import SwiftUI | ||
|
||
struct ActivityIndicator: UIViewRepresentable { | ||
init() {} | ||
|
||
func makeUIView(context: Context) -> UIActivityIndicatorView { | ||
let view = UIActivityIndicatorView(style: .large) | ||
view.startAnimating() | ||
return view | ||
} | ||
|
||
func updateUIView(_ uiView: UIActivityIndicatorView, context: Context) {} | ||
} |
10 changes: 10 additions & 0 deletions
10
0116-redacted-swiftui-pt2/Articles/Shared/ArticlesApp.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import SwiftUI | ||
|
||
@main | ||
struct ArticlesApp: App { | ||
var body: some Scene { | ||
WindowGroup { | ||
VanillaArticlesView() | ||
} | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
0116-redacted-swiftui-pt2/Articles/Shared/Assets.xcassets/AccentColor.colorset/Contents.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"colors" : [ | ||
{ | ||
"idiom" : "universal" | ||
} | ||
], | ||
"info" : { | ||
"author" : "xcode", | ||
"version" : 1 | ||
} | ||
} |
148 changes: 148 additions & 0 deletions
148
0116-redacted-swiftui-pt2/Articles/Shared/Assets.xcassets/AppIcon.appiconset/Contents.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
{ | ||
"images" : [ | ||
{ | ||
"idiom" : "iphone", | ||
"scale" : "2x", | ||
"size" : "20x20" | ||
}, | ||
{ | ||
"idiom" : "iphone", | ||
"scale" : "3x", | ||
"size" : "20x20" | ||
}, | ||
{ | ||
"idiom" : "iphone", | ||
"scale" : "2x", | ||
"size" : "29x29" | ||
}, | ||
{ | ||
"idiom" : "iphone", | ||
"scale" : "3x", | ||
"size" : "29x29" | ||
}, | ||
{ | ||
"idiom" : "iphone", | ||
"scale" : "2x", | ||
"size" : "40x40" | ||
}, | ||
{ | ||
"idiom" : "iphone", | ||
"scale" : "3x", | ||
"size" : "40x40" | ||
}, | ||
{ | ||
"idiom" : "iphone", | ||
"scale" : "2x", | ||
"size" : "60x60" | ||
}, | ||
{ | ||
"idiom" : "iphone", | ||
"scale" : "3x", | ||
"size" : "60x60" | ||
}, | ||
{ | ||
"idiom" : "ipad", | ||
"scale" : "1x", | ||
"size" : "20x20" | ||
}, | ||
{ | ||
"idiom" : "ipad", | ||
"scale" : "2x", | ||
"size" : "20x20" | ||
}, | ||
{ | ||
"idiom" : "ipad", | ||
"scale" : "1x", | ||
"size" : "29x29" | ||
}, | ||
{ | ||
"idiom" : "ipad", | ||
"scale" : "2x", | ||
"size" : "29x29" | ||
}, | ||
{ | ||
"idiom" : "ipad", | ||
"scale" : "1x", | ||
"size" : "40x40" | ||
}, | ||
{ | ||
"idiom" : "ipad", | ||
"scale" : "2x", | ||
"size" : "40x40" | ||
}, | ||
{ | ||
"idiom" : "ipad", | ||
"scale" : "1x", | ||
"size" : "76x76" | ||
}, | ||
{ | ||
"idiom" : "ipad", | ||
"scale" : "2x", | ||
"size" : "76x76" | ||
}, | ||
{ | ||
"idiom" : "ipad", | ||
"scale" : "2x", | ||
"size" : "83.5x83.5" | ||
}, | ||
{ | ||
"idiom" : "ios-marketing", | ||
"scale" : "1x", | ||
"size" : "1024x1024" | ||
}, | ||
{ | ||
"idiom" : "mac", | ||
"scale" : "1x", | ||
"size" : "16x16" | ||
}, | ||
{ | ||
"idiom" : "mac", | ||
"scale" : "2x", | ||
"size" : "16x16" | ||
}, | ||
{ | ||
"idiom" : "mac", | ||
"scale" : "1x", | ||
"size" : "32x32" | ||
}, | ||
{ | ||
"idiom" : "mac", | ||
"scale" : "2x", | ||
"size" : "32x32" | ||
}, | ||
{ | ||
"idiom" : "mac", | ||
"scale" : "1x", | ||
"size" : "128x128" | ||
}, | ||
{ | ||
"idiom" : "mac", | ||
"scale" : "2x", | ||
"size" : "128x128" | ||
}, | ||
{ | ||
"idiom" : "mac", | ||
"scale" : "1x", | ||
"size" : "256x256" | ||
}, | ||
{ | ||
"idiom" : "mac", | ||
"scale" : "2x", | ||
"size" : "256x256" | ||
}, | ||
{ | ||
"idiom" : "mac", | ||
"scale" : "1x", | ||
"size" : "512x512" | ||
}, | ||
{ | ||
"idiom" : "mac", | ||
"scale" : "2x", | ||
"size" : "512x512" | ||
} | ||
], | ||
"info" : { | ||
"author" : "xcode", | ||
"version" : 1 | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
0116-redacted-swiftui-pt2/Articles/Shared/Assets.xcassets/Contents.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"info" : { | ||
"author" : "xcode", | ||
"version" : 1 | ||
} | ||
} |
179 changes: 179 additions & 0 deletions
179
0116-redacted-swiftui-pt2/Articles/Shared/Composable.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
import ComposableArchitecture | ||
import SwiftUI | ||
|
||
struct ArticlesState: Equatable { | ||
var articles: [Article] = [] | ||
var isLoading = false | ||
var readingArticle: Article? | ||
} | ||
|
||
enum ArticlesAction { | ||
case article(index: Int, action: ArticleAction) | ||
case articlesResponse([Article]?) | ||
case articleTapped(Article) | ||
case dismissArticle | ||
case onAppear | ||
} | ||
|
||
enum ArticleAction { | ||
case favoriteTapped | ||
case hideTapped | ||
case readLaterTapped | ||
} | ||
|
||
let articlesReducer = Reducer<ArticlesState, ArticlesAction, Void> { state, action, environment in | ||
switch action { | ||
case let .article(index: index, action: .favoriteTapped): | ||
state.articles[index].isFavorite.toggle() | ||
return .none | ||
|
||
case let .article(index: index, action: .hideTapped): | ||
state.articles[index].isHidden.toggle() | ||
return .none | ||
|
||
case let .article(index: index, action: .readLaterTapped): | ||
state.articles[index].willReadLater.toggle() | ||
return .none | ||
|
||
case let .articlesResponse(articles): | ||
state.isLoading = false | ||
state.articles = articles ?? [] | ||
return .none | ||
|
||
case let .articleTapped(article): | ||
state.readingArticle = article | ||
return .none | ||
|
||
case .dismissArticle: | ||
state.readingArticle = nil | ||
return .none | ||
|
||
case .onAppear: | ||
state.isLoading = true | ||
return Effect(value: .articlesResponse(liveArticles)) | ||
.delay(for: 4, scheduler: DispatchQueue.main) | ||
.eraseToEffect() | ||
} | ||
} | ||
|
||
struct ComposableArticlesView: View { | ||
let store: Store<ArticlesState, ArticlesAction> | ||
|
||
var body: some View { | ||
WithViewStore(self.store) { viewStore in | ||
NavigationView { | ||
List { | ||
if viewStore.isLoading { | ||
ActivityIndicator() | ||
.padding() | ||
.frame(maxWidth: .infinity) | ||
} | ||
|
||
ArticlesListView(store: self.store) | ||
} | ||
.sheet( | ||
item: viewStore.binding(get: \.readingArticle, send: .dismissArticle) | ||
) { article in | ||
NavigationView { | ||
ArticleDetailView(article: article) | ||
.navigationTitle(article.title) | ||
} | ||
} | ||
.navigationTitle("Articles") | ||
} | ||
.onAppear { viewStore.send(.onAppear) } | ||
} | ||
} | ||
} | ||
|
||
struct ArticlesListView: View { | ||
let store: Store<ArticlesState, ArticlesAction> | ||
|
||
var body: some View { | ||
WithViewStore(self.store) { viewStore in | ||
ForEachStore( | ||
self.store.scope(state: \.articles, action: ArticlesAction.article) | ||
) { articleStore in | ||
WithViewStore(articleStore) { articleViewStore in | ||
Button(action: { viewStore.send(.articleTapped(articleViewStore.state)) }) { | ||
ArticleRowView(store: articleStore) | ||
} | ||
.buttonStyle(PlainButtonStyle()) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
private struct ArticleRowView: View { | ||
let store: Store<Article, ArticleAction> | ||
|
||
var body: some View { | ||
WithViewStore(self.store) { viewStore in | ||
HStack(alignment: .top) { | ||
Image("") | ||
.frame(width: 80, height: 80) | ||
.background(Color(white: 0.9)) | ||
.padding([.trailing]) | ||
|
||
VStack(alignment: .leading) { | ||
Text(viewStore.title) | ||
.font(.title) | ||
|
||
Text(articleDateFormatter.string(from: viewStore.date)) | ||
.bold() | ||
|
||
Text(viewStore.blurb) | ||
.padding(.top, 6) | ||
|
||
HStack { | ||
Spacer() | ||
|
||
Button(action: { viewStore.send(.favoriteTapped) }) { | ||
Image(systemName: "star.fill") | ||
} | ||
.buttonStyle(PlainButtonStyle()) | ||
.foregroundColor(viewStore.isFavorite ? .red : .blue) | ||
.padding() | ||
|
||
Button(action: { viewStore.send(.readLaterTapped) }) { | ||
Image(systemName: "book.fill") | ||
} | ||
.buttonStyle(PlainButtonStyle()) | ||
.foregroundColor(viewStore.willReadLater ? .yellow : .blue) | ||
.padding() | ||
|
||
Button(action: { viewStore.send(.hideTapped) }) { | ||
Image(systemName: "eye.slash.fill") | ||
} | ||
.buttonStyle(PlainButtonStyle()) | ||
.foregroundColor(.blue) | ||
.padding() | ||
} | ||
} | ||
} | ||
.padding([.top, .bottom]) | ||
.buttonStyle(PlainButtonStyle()) | ||
} | ||
} | ||
} | ||
|
||
private struct ArticleDetailView: View { | ||
let article: Article | ||
|
||
var body: some View { | ||
Text(self.article.blurb) | ||
} | ||
} | ||
|
||
struct Composable_Previews: PreviewProvider { | ||
static var previews: some View { | ||
ComposableArticlesView( | ||
store: Store( | ||
initialState: ArticlesState(), | ||
reducer: articlesReducer, | ||
environment: () | ||
) | ||
) | ||
} | ||
} |
Oops, something went wrong.