Created
September 18, 2020 12:20
-
-
Save jdan/f2b13eca0cd49518e068a500e5ddd5b6 to your computer and use it in GitHub Desktop.
jdan app
This file contains hidden or 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
// | |
// ContentView.swift | |
// Shared | |
// | |
// Created by Jordan Scales on 7/25/20. | |
// | |
import SwiftUI | |
import SwiftUIRefresh | |
struct Response: Codable { | |
struct Includes: Codable { | |
let Asset: [Asset] | |
} | |
let total: Int | |
let skip: Int | |
let limit: Int | |
let items: [Item] | |
let includes: Includes | |
} | |
struct MainApp: View { | |
let response: Response | |
let onRefresh: () -> Void | |
@State private var isRefreshing = false | |
var body: some View { | |
List { | |
ForEach(response.items, id: \.sys.id) { item in | |
PostView( | |
post: item.fields, | |
assets: response.includes.Asset | |
).listRowInsets(EdgeInsets()) | |
} | |
} | |
.pullToRefresh(isShowing: $isRefreshing) { | |
onRefresh() | |
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { | |
self.isRefreshing = false | |
} | |
} | |
} | |
} | |
struct ContentView_Previews: PreviewProvider { | |
static var previews: some View { | |
MainApp(response: testResponse!, onRefresh: {}) | |
} | |
} | |
struct ContentView: View { | |
@State private var response: Response? = nil | |
func loadData() { | |
guard let url = URL(string: "[REDACTED]") else { | |
print("Invalid URL") | |
return | |
} | |
let request = URLRequest(url: url) | |
URLSession.shared.dataTask(with: request) { data, response, error in | |
if let data = data { | |
do { | |
let decodedResponse = try JSONDecoder().decode(Response.self, from: data) | |
DispatchQueue.main.async { | |
self.response = decodedResponse | |
} | |
return | |
} catch { | |
print(error) | |
} | |
} | |
}.resume() | |
} | |
var body: some View { | |
if response != nil { | |
MainApp(response: response!, onRefresh: loadData) | |
} else { | |
Text("Loading...").onAppear(perform: loadData) | |
} | |
} | |
} |
This file contains hidden or 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
// | |
// Post.swift | |
// jdan | |
// | |
// Created by Jordan Scales on 7/25/20. | |
// | |
import SwiftUI | |
struct Item: Codable { | |
let sys: Metadata | |
let fields: Post | |
} | |
struct Metadata: Codable { | |
var id: String | |
var createdAt: Date | |
var updatedAt: Date | |
init(from decoder: Decoder) throws { | |
let values = try decoder.container(keyedBy: CodingKeys.self) | |
id = try values.decode(String.self, forKey: .id) | |
let strCreatedAt = try values.decode(String.self, forKey: .createdAt) | |
let strUpdatedAt = try values.decode(String.self, forKey: .updatedAt) | |
let dateFormatter = DateFormatter() | |
dateFormatter.locale = Locale(identifier: "en_US_POSIX") | |
// 2020-07-25T01:36:36.946Z | |
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSZ" | |
createdAt = dateFormatter.date(from: strCreatedAt)! | |
updatedAt = dateFormatter.date(from: strUpdatedAt)! | |
} | |
} | |
struct Asset: Codable { | |
struct AssetMetaData: Codable { | |
var id: String | |
var createdAt: Date | |
var updatedAt: Date | |
init(from decoder: Decoder) throws { | |
let values = try decoder.container(keyedBy: CodingKeys.self) | |
id = try values.decode(String.self, forKey: .id) | |
let strCreatedAt = try values.decode(String.self, forKey: .createdAt) | |
let strUpdatedAt = try values.decode(String.self, forKey: .updatedAt) | |
let dateFormatter = DateFormatter() | |
dateFormatter.locale = Locale(identifier: "en_US_POSIX") | |
// 2020-07-25T01:36:36.946Z | |
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSZ" | |
createdAt = dateFormatter.date(from: strCreatedAt)! | |
updatedAt = dateFormatter.date(from: strUpdatedAt)! | |
} | |
init(id: String) { | |
self.id = id | |
self.createdAt = Date() | |
self.updatedAt = Date() | |
} | |
} | |
struct Dimensions: Codable { | |
let width: Int | |
let height: Int | |
} | |
struct Details: Codable { | |
let size: Int | |
let image: Dimensions | |
} | |
struct File: Codable { | |
let url: String | |
let details: Details | |
let fileName: String | |
let contentType: String | |
} | |
struct Image: Codable { | |
let title: String | |
let description: String | |
let file: File | |
} | |
var sys: AssetMetaData | |
var fields: Image | |
init(id: String) { | |
self.sys = AssetMetaData(id: id) | |
self.fields = Image( | |
title: "Title", | |
description: "Description", | |
file: File( | |
url: "example.com", | |
details: Details( | |
size: 100, | |
image: Dimensions(width: 10, height: 10) | |
), | |
fileName: "file", | |
contentType: "image/png" | |
) | |
) | |
} | |
} | |
struct AssetReference: Codable { | |
struct Item: Codable { | |
let id: String | |
} | |
var sys: Item | |
init(id: String) { | |
self.sys = Item(id: id) | |
} | |
} | |
struct Post: Codable { | |
var date: Date | |
var title: String | |
var body: String | |
var gallery: [AssetReference]? | |
var score: Int | |
init(title: String, date: Date, body: String, gallery: [AssetReference]) { | |
self.date = date | |
self.title = title | |
self.body = body | |
self.gallery = gallery | |
self.score = 0 | |
} | |
init(from decoder: Decoder) throws { | |
let values = try decoder.container(keyedBy: CodingKeys.self) | |
title = try values.decode(String.self, forKey: .title) | |
body = try values.decode(String.self, forKey: .body) | |
gallery = try? // gallery might be missing | |
values.decode(Array<AssetReference>.self, forKey: .gallery) | |
score = try values.decode(Int.self, forKey: .score) | |
let strDate = try values.decode(String.self, forKey: .date) | |
let dateFormatter = DateFormatter() | |
dateFormatter.locale = Locale(identifier: "en_US_POSIX") | |
// 2020-07-25T08:57-04:00 | |
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mmZZZZZ" | |
date = dateFormatter.date(from: strDate)! | |
} | |
} | |
extension Date { | |
func timeAgo() -> String { | |
let formatter = DateComponentsFormatter() | |
formatter.unitsStyle = .full | |
formatter.allowedUnits = [.year, .month, .day, .hour, .minute, .second] | |
formatter.zeroFormattingBehavior = .dropAll | |
formatter.maximumUnitCount = 1 | |
return String(format: formatter.string(from: self, to: Date()) ?? "", locale: .current) | |
} | |
} | |
struct PostView: View { | |
let post: Post | |
let assets: [Asset] | |
var body: some View { | |
VStack(alignment: .leading, spacing: 20) { | |
HStack(alignment: .top) { | |
Image("avatar") | |
.resizable() | |
.frame(width: 40, height: 40) | |
.cornerRadius(20) | |
VStack(alignment: .leading) { | |
Text("jdan").fontWeight(.bold) | |
Text(post.title).font(.subheadline) | |
} | |
Spacer() | |
Text(post.date.timeAgo()) | |
.font(.caption) | |
.foregroundColor(Color(UIColor.secondaryLabel)) | |
}.padding(.horizontal) | |
Text(post.body) | |
.font(.title3) | |
.fontWeight(.light) | |
.padding(.horizontal) | |
if post.gallery != nil && post.gallery!.count > 0 { | |
ScrollView(.horizontal, showsIndicators: false) { | |
HStack(spacing: 8) { | |
ForEach(post.gallery!, id: \.sys.id) { galleryItem in | |
URLImage( | |
withURL: assets | |
.first(where: { $0.sys.id == galleryItem.sys.id })! | |
.fields.file.url | |
) | |
} | |
}.padding(.horizontal) | |
} | |
} | |
}.padding(.vertical) | |
} | |
} | |
struct PostView_Previews: PreviewProvider { | |
static var previews: some View { | |
Group { | |
PostView( | |
post: Post(title: "just vibin", date: Date(), body: "Hello, world!", gallery: []), | |
assets: [] | |
).padding().previewLayout(.sizeThatFits) | |
PostView( | |
post: Post(title: "just vibin", date: Date(), body: "Hello, world!", gallery: [ | |
AssetReference(id: "1") | |
]), | |
assets: [ | |
Asset(id: "1"), | |
Asset(id: "2") | |
] | |
) | |
.previewLayout(.fixed(width: 414, height: 500)) | |
.previewDisplayName("One image") | |
PostView( | |
post: Post( | |
title: "just vibin", | |
date: Date(), | |
body: "Hello, world!", | |
gallery: [ | |
AssetReference(id: "1"), | |
AssetReference(id: "2"), | |
AssetReference(id: "3") | |
] | |
), | |
assets: [ | |
Asset(id: "1"), | |
Asset(id: "2"), | |
Asset(id: "3") | |
] | |
) | |
.previewLayout(.fixed(width: 414, height: 500)) | |
.previewDisplayName("Multiple images") | |
} | |
} | |
} |
This file contains hidden or 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
// | |
// URLImage.swift | |
// jdan | |
// | |
// Created by Jordan Scales on 7/25/20. | |
// | |
import SwiftUI | |
class ImageLoader: ObservableObject { | |
@Published var data: Data? | |
init(urlString: String) { | |
// Why do I need to put https in myself | |
guard let url = URL(string: "https:\(urlString)") else { return } | |
let task = URLSession.shared.dataTask(with: url) { data, response, error in | |
guard let data = data else { return } | |
DispatchQueue.main.async { | |
self.data = data | |
} | |
} | |
task.resume() | |
} | |
} | |
struct URLImage: View { | |
#if !targetEnvironment(simulator) | |
@ObservedObject var imageLoader: ImageLoader | |
#endif | |
@State var image: UIImage = UIImage() | |
init(withURL url: String) { | |
#if !targetEnvironment(simulator) | |
imageLoader = ImageLoader(urlString: url) | |
#endif | |
} | |
let screenWidth = UIScreen.main.bounds.size.width - 32 | |
var body: some View { | |
VStack { | |
#if targetEnvironment(simulator) | |
Image("me") | |
.resizable() | |
.aspectRatio(contentMode: .fit) | |
.frame(width: screenWidth, height: screenWidth) | |
#else | |
Image(uiImage: imageLoader.data != nil ? UIImage(data:imageLoader.data!)! : UIImage()) | |
.resizable() | |
.aspectRatio(contentMode: .fit) | |
.frame(width: screenWidth, height: screenWidth) | |
#endif | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment