Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] Users screen with more samples #54

Merged
merged 3 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions NetworkingSampleApp/NetworkingSampleApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@
23EA9CF6292FB70A00B8E418 /* SampleAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23EA9CEB292FB70A00B8E418 /* SampleAPIError.swift */; };
23EA9CF7292FB70A00B8E418 /* SampleUserAuthResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23EA9CED292FB70A00B8E418 /* SampleUserAuthResponse.swift */; };
23EA9CF8292FB70A00B8E418 /* SampleUsersResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23EA9CEE292FB70A00B8E418 /* SampleUsersResponse.swift */; };
23EA9CF9292FB70A00B8E418 /* SampleUserResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23EA9CEF292FB70A00B8E418 /* SampleUserResponse.swift */; };
23EA9CF9292FB70A00B8E418 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23EA9CEF292FB70A00B8E418 /* User.swift */; };
23EA9CFA292FB70A00B8E418 /* SampleUserAuthRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23EA9CF1292FB70A00B8E418 /* SampleUserAuthRequest.swift */; };
23EA9CFB292FB70A00B8E418 /* SampleUserRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23EA9CF2292FB70A00B8E418 /* SampleUserRequest.swift */; };
587CD0EF2B27713700E3CB71 /* TaskButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587CD0EE2B27713700E3CB71 /* TaskButton.swift */; };
587CD0EC2B271CF800E3CB71 /* SampleCreateUserResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587CD0EB2B271CF800E3CB71 /* SampleCreateUserResponse.swift */; };
58C3E75E29B78EE6004FD1CD /* DownloadsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C3E75C29B78ED3004FD1CD /* DownloadsView.swift */; };
58C3E75F29B78EE8004FD1CD /* DownloadsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C3E75D29B78ED3004FD1CD /* DownloadsViewModel.swift */; };
58C3E76129B79259004FD1CD /* SampleDownloadRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C3E76029B79259004FD1CD /* SampleDownloadRouter.swift */; };
58C3E76529B7D709004FD1CD /* DownloadProgressViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C3E76429B7D709004FD1CD /* DownloadProgressViewModel.swift */; };
58D6976F2B21FF8300E6C529 /* UsersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D6976E2B21FF8300E6C529 /* UsersView.swift */; };
58D697712B21FF8E00E6C529 /* UsersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D697702B21FF8E00E6C529 /* UsersViewModel.swift */; };
58E4E0ED2982D884000ACBC0 /* SampleAuthorizationStorageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58E4E0EC2982D884000ACBC0 /* SampleAuthorizationStorageManager.swift */; };
58E4E0EF29843B42000ACBC0 /* NetworkingSampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58E4E0EE29843B42000ACBC0 /* NetworkingSampleApp.swift */; };
58E4E0F129850E86000ACBC0 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58E4E0F029850E86000ACBC0 /* ContentView.swift */; };
Expand Down Expand Up @@ -57,14 +60,17 @@
23EA9CEB292FB70A00B8E418 /* SampleAPIError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleAPIError.swift; sourceTree = "<group>"; };
23EA9CED292FB70A00B8E418 /* SampleUserAuthResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleUserAuthResponse.swift; sourceTree = "<group>"; };
23EA9CEE292FB70A00B8E418 /* SampleUsersResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleUsersResponse.swift; sourceTree = "<group>"; };
23EA9CEF292FB70A00B8E418 /* SampleUserResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleUserResponse.swift; sourceTree = "<group>"; };
23EA9CEF292FB70A00B8E418 /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = "<group>"; };
23EA9CF1292FB70A00B8E418 /* SampleUserAuthRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleUserAuthRequest.swift; sourceTree = "<group>"; };
23EA9CF2292FB70A00B8E418 /* SampleUserRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleUserRequest.swift; sourceTree = "<group>"; };
587CD0EB2B271CF800E3CB71 /* SampleCreateUserResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleCreateUserResponse.swift; sourceTree = "<group>"; };
587CD0EE2B27713700E3CB71 /* TaskButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskButton.swift; sourceTree = "<group>"; };
58C3E75C29B78ED3004FD1CD /* DownloadsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadsView.swift; sourceTree = "<group>"; };
58C3E75D29B78ED3004FD1CD /* DownloadsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadsViewModel.swift; sourceTree = "<group>"; };
58C3E76029B79259004FD1CD /* SampleDownloadRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleDownloadRouter.swift; sourceTree = "<group>"; };
58C3E76429B7D709004FD1CD /* DownloadProgressViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadProgressViewModel.swift; sourceTree = "<group>"; };
58D6976E2B21FF8300E6C529 /* UsersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsersView.swift; sourceTree = "<group>"; };
58D697702B21FF8E00E6C529 /* UsersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsersViewModel.swift; sourceTree = "<group>"; };
58E4E0EC2982D884000ACBC0 /* SampleAuthorizationStorageManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleAuthorizationStorageManager.swift; sourceTree = "<group>"; };
58E4E0EE29843B42000ACBC0 /* NetworkingSampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkingSampleApp.swift; sourceTree = "<group>"; };
58E4E0F029850E86000ACBC0 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -145,6 +151,7 @@
23A575ED25F8BF0E00617551 /* Scenes */ = {
isa = PBXGroup;
children = (
58D6976D2B21FF6A00E6C529 /* Users */,
58FB80C5298521DA0031FC59 /* Authorization */,
58C3E75B29B78ED3004FD1CD /* Download */,
B52674BB2A370D0D006D3B9C /* Upload */,
Expand Down Expand Up @@ -191,7 +198,7 @@
children = (
23EA9CED292FB70A00B8E418 /* SampleUserAuthResponse.swift */,
23EA9CEE292FB70A00B8E418 /* SampleUsersResponse.swift */,
23EA9CEF292FB70A00B8E418 /* SampleUserResponse.swift */,
587CD0EB2B271CF800E3CB71 /* SampleCreateUserResponse.swift */,
);
path = Responses;
sourceTree = "<group>";
Expand Down Expand Up @@ -225,6 +232,16 @@
path = Download;
sourceTree = "<group>";
};
58D6976D2B21FF6A00E6C529 /* Users */ = {
isa = PBXGroup;
children = (
23EA9CEF292FB70A00B8E418 /* User.swift */,
58D697702B21FF8E00E6C529 /* UsersViewModel.swift */,
58D6976E2B21FF8300E6C529 /* UsersView.swift */,
);
path = Users;
sourceTree = "<group>";
};
58FB80C5298521DA0031FC59 /* Authorization */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -356,21 +373,24 @@
58E4E0F129850E86000ACBC0 /* ContentView.swift in Sources */,
B52674BD2A370D1D006D3B9C /* UploadService.swift in Sources */,
58C3E76529B7D709004FD1CD /* DownloadProgressViewModel.swift in Sources */,
23EA9CF9292FB70A00B8E418 /* SampleUserResponse.swift in Sources */,
23EA9CF9292FB70A00B8E418 /* User.swift in Sources */,
DDD3AD1F2950E794006CB777 /* SampleAuthRouter.swift in Sources */,
DD887780293E33850065ED03 /* SampleErrorProcessor.swift in Sources */,
23EA9CFB292FB70A00B8E418 /* SampleUserRequest.swift in Sources */,
23EA9CFA292FB70A00B8E418 /* SampleUserAuthRequest.swift in Sources */,
58C3E76129B79259004FD1CD /* SampleDownloadRouter.swift in Sources */,
23EA9CF4292FB70A00B8E418 /* SampleUserRouter.swift in Sources */,
B52674BF2A370D33006D3B9C /* UploadItem.swift in Sources */,
58D6976F2B21FF8300E6C529 /* UsersView.swift in Sources */,
23EA9CF5292FB70A00B8E418 /* SampleAPIConstants.swift in Sources */,
58FB80C7298521FF0031FC59 /* AuthorizationView.swift in Sources */,
DD410D6F293F2E6E006D8E31 /* AuthorizationViewModel.swift in Sources */,
B52674BA2A370C15006D3B9C /* SampleUploadRouter.swift in Sources */,
58D697712B21FF8E00E6C529 /* UsersViewModel.swift in Sources */,
B52674C72A371046006D3B9C /* UploadItemView.swift in Sources */,
58C3E75F29B78EE8004FD1CD /* DownloadsViewModel.swift in Sources */,
58C3E75E29B78EE6004FD1CD /* DownloadsView.swift in Sources */,
587CD0EC2B271CF800E3CB71 /* SampleCreateUserResponse.swift in Sources */,
B52674C32A370E35006D3B9C /* UploadItemViewModel.swift in Sources */,
B52674C12A370DFF006D3B9C /* UploadsViewModel.swift in Sources */,
B52674C52A37102D006D3B9C /* UploadsView.swift in Sources */,
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// SampleCreateUserResponse.swift
// NetworkingSampleApp
//
// Created by Matej Molnár on 11.12.2023.
//

import Foundation

/// Data structure of sample API create user response
struct SampleCreateUserResponse: Codable {
let id: String
let name: String
let job: String
let createdAt: Date
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@

import Foundation

/// Data structure of sample API user list response
struct SampleUsersResponse: Codable {
let page: Int
let data: [SampleUserResponse]
/// Data structure of sample API get user response
struct SampleUserResponse: Codable {
let data: User
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ enum SampleUserRouter: Requestable {
case users(page: Int)
case user(userId: Int)
case createUser(user: SampleUserRequest)
case registerUser(user: SampleUserAuthRequest)

var baseURL: URL {
// swiftlint:disable:next force_unwrapping
Expand All @@ -25,24 +24,22 @@ enum SampleUserRouter: Requestable {
case .users, .createUser:
"users"
case let .user(userId):
"user/\(userId)"
case .registerUser:
"register"
"users/\(userId)"
}
}

var urlParameters: [String: Any]? {
switch self {
case let .users(page):
["page": page]
case .createUser, .registerUser, .user:
case .createUser, .user:
nil
}
}

var method: HTTPMethod {
switch self {
case .createUser, .registerUser:
case .createUser:
.post
case .users, .user:
.get
Expand All @@ -53,19 +50,15 @@ enum SampleUserRouter: Requestable {
switch self {
case let .createUser(user):
.encodable(user)
case let .registerUser(user):
.encodable(user)
case .users, .user:
nil
}
}

var isAuthenticationRequired: Bool {
switch self {
case .registerUser:
false
case .createUser, .users, .user:
true
false
}
}
}
3 changes: 3 additions & 0 deletions NetworkingSampleApp/NetworkingSampleApp/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import SwiftUI

enum NetworkingFeature: String, Hashable, CaseIterable {
case users
case authorization
case downloads
case uploads
Expand All @@ -24,6 +25,8 @@ struct ContentView: View {
.navigationTitle("Examples")
.navigationDestination(for: NetworkingFeature.self) { feature in
switch feature {
case .users:
UsersView()
case .authorization:
AuthorizationView()
case .downloads:
Expand Down
25 changes: 25 additions & 0 deletions NetworkingSampleApp/NetworkingSampleApp/Scenes/Users/User.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// SampleUserResponse.swift
// Networking sample app
//
// Created by Tomas Cejka on 07.04.2021.
//

import Foundation

/// Data structure of sample API user response
struct User: Codable, Identifiable {
enum CodingKeys: String, CodingKey {
case id
case email
case firstName = "first_name"
case lastName = "last_name"
case avatarURL = "avatar"
}

let id: Int
let email: String
let firstName: String
let lastName: String
let avatarURL: URL
}
120 changes: 120 additions & 0 deletions NetworkingSampleApp/NetworkingSampleApp/Scenes/Users/UsersView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//
// UsersView.swift
// NetworkingSampleApp
//
// Created by Matej Molnár on 07.12.2023.
//

import SwiftUI

struct UsersView: View {
@StateObject private var viewModel = UsersViewModel()

@State private var fromUserID: Int = 1
@State private var toUserID: Int = 3
@State private var parallelise = false
@State private var userName: String = ""
@State private var userJob: String = ""

var body: some View {
Form {
getUserView

createUserView
}
.navigationTitle("Users")
}
}

private extension UsersView {
var getUserView: some View {
Group {
Section {
HStack {
Text("From:")

TextField("From user ID", value: $fromUserID, formatter: NumberFormatter())
}

HStack {
Text("To:")

TextField("To user ID", value: $toUserID, formatter: NumberFormatter())
}

Toggle("Parallelise", isOn: $parallelise)
} header: {
Text("Get User by ID")
} footer: {
Button("Get Users") {
viewModel.getUsers(
in: fromUserID...toUserID,
parallelFetch: parallelise
)
}
.buttonStyle(.borderedProminent)
.frame(maxWidth: .infinity)
}

if !viewModel.users.isEmpty {
Section("Users") {
ForEach(viewModel.users) { user in
userCell(user)
}
}
}
}
}

var createUserView: some View {
Group {
Section {
TextField("Name", text: $userName)
TextField("Job", text: $userJob)
} header: {
Text("Create User with parameters")
} footer: {
Button("Create User") {
viewModel.createUser(name: userName, job: userJob)
}
.buttonStyle(.borderedProminent)
.frame(maxWidth: .infinity)
}

if let createdUser = viewModel.createdUser {
Section("Created User") {
Text("ID: \(createdUser.id)")
Text("Name: \(createdUser.name)")
Text("Job: \(createdUser.job)")
Text("Created at: \(createdUser.createdAt.formatted())")
}
}
}
}

func userCell(_ user: User) -> some View {
HStack(alignment: .center) {
AsyncImage(url: user.avatarURL) { image in
image
.resizable()
} placeholder: {
Color.gray
}
.frame(width: 70, height: 70)
.clipShape(Circle())

VStack(alignment: .leading) {
Text(user.firstName + " " + user.lastName)
.font(.subheadline)

Text(user.email)
.font(.footnote)
.foregroundStyle(.gray)
}
}
}
}

#Preview {
UsersView()
}
Loading