Logging in and displaying alerts
This commit is contained in:
parent
eddf11288c
commit
2ad43928db
@ -32,6 +32,9 @@
|
|||||||
7A40D5FF2693A91F009B0BC4 /* CocoaError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D5FD2693A91F009B0BC4 /* CocoaError.swift */; };
|
7A40D5FF2693A91F009B0BC4 /* CocoaError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D5FD2693A91F009B0BC4 /* CocoaError.swift */; };
|
||||||
7A40D6022694FF5D009B0BC4 /* Api.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D6012694FF5D009B0BC4 /* Api.swift */; };
|
7A40D6022694FF5D009B0BC4 /* Api.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D6012694FF5D009B0BC4 /* Api.swift */; };
|
||||||
7A40D6032694FF5D009B0BC4 /* Api.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D6012694FF5D009B0BC4 /* Api.swift */; };
|
7A40D6032694FF5D009B0BC4 /* Api.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D6012694FF5D009B0BC4 /* Api.swift */; };
|
||||||
|
7A40D60826998DCF009B0BC4 /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D60726998DCF009B0BC4 /* Alert.swift */; };
|
||||||
|
7A40D60926998DCF009B0BC4 /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D60726998DCF009B0BC4 /* Alert.swift */; };
|
||||||
|
7A40D60C2699A070009B0BC4 /* MockURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D60B2699A070009B0BC4 /* MockURLProtocol.swift */; };
|
||||||
7A683999269612EA00B2188A /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A683998269612EA00B2188A /* Response.swift */; };
|
7A683999269612EA00B2188A /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A683998269612EA00B2188A /* Response.swift */; };
|
||||||
7A68399A269612EA00B2188A /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A683998269612EA00B2188A /* Response.swift */; };
|
7A68399A269612EA00B2188A /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A683998269612EA00B2188A /* Response.swift */; };
|
||||||
7ACD05D72695C08A00557667 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ACD05D62695C08A00557667 /* Constants.swift */; };
|
7ACD05D72695C08A00557667 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ACD05D62695C08A00557667 /* Constants.swift */; };
|
||||||
@ -108,6 +111,8 @@
|
|||||||
7A40D5F52693A63A009B0BC4 /* SettingsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTests.swift; sourceTree = "<group>"; };
|
7A40D5F52693A63A009B0BC4 /* SettingsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTests.swift; sourceTree = "<group>"; };
|
||||||
7A40D5FD2693A91F009B0BC4 /* CocoaError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CocoaError.swift; sourceTree = "<group>"; };
|
7A40D5FD2693A91F009B0BC4 /* CocoaError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CocoaError.swift; sourceTree = "<group>"; };
|
||||||
7A40D6012694FF5D009B0BC4 /* Api.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Api.swift; sourceTree = "<group>"; };
|
7A40D6012694FF5D009B0BC4 /* Api.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Api.swift; sourceTree = "<group>"; };
|
||||||
|
7A40D60726998DCF009B0BC4 /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; };
|
||||||
|
7A40D60B2699A070009B0BC4 /* MockURLProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockURLProtocol.swift; sourceTree = "<group>"; };
|
||||||
7A683998269612EA00B2188A /* Response.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Response.swift; sourceTree = "<group>"; };
|
7A683998269612EA00B2188A /* Response.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Response.swift; sourceTree = "<group>"; };
|
||||||
7ACD05D62695C08A00557667 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
|
7ACD05D62695C08A00557667 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
|
||||||
7AEFAEEC26985A3400ED2C85 /* ACProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACProgressView.swift; sourceTree = "<group>"; };
|
7AEFAEEC26985A3400ED2C85 /* ACProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACProgressView.swift; sourceTree = "<group>"; };
|
||||||
@ -255,6 +260,7 @@
|
|||||||
7A40D5F42693A63A009B0BC4 /* AutoCat2Tests */ = {
|
7A40D5F42693A63A009B0BC4 /* AutoCat2Tests */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
7A40D60A2699A04F009B0BC4 /* Mocks */,
|
||||||
7A40D5F52693A63A009B0BC4 /* SettingsTests.swift */,
|
7A40D5F52693A63A009B0BC4 /* SettingsTests.swift */,
|
||||||
7AF552D82696E5C100578083 /* ApiTests.swift */,
|
7AF552D82696E5C100578083 /* ApiTests.swift */,
|
||||||
);
|
);
|
||||||
@ -265,6 +271,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
7A40D5FD2693A91F009B0BC4 /* CocoaError.swift */,
|
7A40D5FD2693A91F009B0BC4 /* CocoaError.swift */,
|
||||||
|
7A40D60726998DCF009B0BC4 /* Alert.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -278,6 +285,14 @@
|
|||||||
path = Utils;
|
path = Utils;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
7A40D60A2699A04F009B0BC4 /* Mocks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
7A40D60B2699A070009B0BC4 /* MockURLProtocol.swift */,
|
||||||
|
);
|
||||||
|
path = Mocks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
@ -474,6 +489,7 @@
|
|||||||
7AEFAEED26985A3400ED2C85 /* ACProgressView.swift in Sources */,
|
7AEFAEED26985A3400ED2C85 /* ACProgressView.swift in Sources */,
|
||||||
7A40D5A02691C6D8009B0BC4 /* AutoCat2App.swift in Sources */,
|
7A40D5A02691C6D8009B0BC4 /* AutoCat2App.swift in Sources */,
|
||||||
7A683999269612EA00B2188A /* Response.swift in Sources */,
|
7A683999269612EA00B2188A /* Response.swift in Sources */,
|
||||||
|
7A40D60826998DCF009B0BC4 /* Alert.swift in Sources */,
|
||||||
7A40D5A42691C6D8009B0BC4 /* Persistence.swift in Sources */,
|
7A40D5A42691C6D8009B0BC4 /* Persistence.swift in Sources */,
|
||||||
7A40D5ED2693A1EA009B0BC4 /* AuthVM.swift in Sources */,
|
7A40D5ED2693A1EA009B0BC4 /* AuthVM.swift in Sources */,
|
||||||
7A40D59E2691C6D8009B0BC4 /* AutoCat2.xcdatamodeld in Sources */,
|
7A40D59E2691C6D8009B0BC4 /* AutoCat2.xcdatamodeld in Sources */,
|
||||||
@ -494,6 +510,7 @@
|
|||||||
7AEFAEEE26985A3400ED2C85 /* ACProgressView.swift in Sources */,
|
7AEFAEEE26985A3400ED2C85 /* ACProgressView.swift in Sources */,
|
||||||
7A40D5A52691C6D8009B0BC4 /* Persistence.swift in Sources */,
|
7A40D5A52691C6D8009B0BC4 /* Persistence.swift in Sources */,
|
||||||
7A68399A269612EA00B2188A /* Response.swift in Sources */,
|
7A68399A269612EA00B2188A /* Response.swift in Sources */,
|
||||||
|
7A40D60926998DCF009B0BC4 /* Alert.swift in Sources */,
|
||||||
7A40D5E526924B0C009B0BC4 /* User.swift in Sources */,
|
7A40D5E526924B0C009B0BC4 /* User.swift in Sources */,
|
||||||
7A40D5EE2693A1EA009B0BC4 /* AuthVM.swift in Sources */,
|
7A40D5EE2693A1EA009B0BC4 /* AuthVM.swift in Sources */,
|
||||||
7A40D59F2691C6D8009B0BC4 /* AutoCat2.xcdatamodeld in Sources */,
|
7A40D59F2691C6D8009B0BC4 /* AutoCat2.xcdatamodeld in Sources */,
|
||||||
@ -524,6 +541,7 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
7A40D60C2699A070009B0BC4 /* MockURLProtocol.swift in Sources */,
|
||||||
7AF552D92696E5C100578083 /* ApiTests.swift in Sources */,
|
7AF552D92696E5C100578083 /* ApiTests.swift in Sources */,
|
||||||
7A40D5F62693A63A009B0BC4 /* SettingsTests.swift in Sources */,
|
7A40D5F62693A63A009B0BC4 /* SettingsTests.swift in Sources */,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -7,6 +7,7 @@ class ApiTests: XCTestCase {
|
|||||||
|
|
||||||
override func setUpWithError() throws {
|
override func setUpWithError() throws {
|
||||||
let sessionConfig = URLSessionConfiguration.default
|
let sessionConfig = URLSessionConfiguration.default
|
||||||
|
sessionConfig.protocolClasses = [MockURLProtocol.self]
|
||||||
let session = URLSession(configuration: sessionConfig)
|
let session = URLSession(configuration: sessionConfig)
|
||||||
self.api = Api(session: session)
|
self.api = Api(session: session)
|
||||||
}
|
}
|
||||||
@ -16,7 +17,7 @@ class ApiTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testLogin() async throws {
|
func testLogin() async throws {
|
||||||
let user = try await self.api.login()
|
let user = try await self.api.login(email: "", password: "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// func testPerformanceExample() throws {
|
// func testPerformanceExample() throws {
|
||||||
|
|||||||
44
AutoCat2Tests/Mocks/MockURLProtocol.swift
Normal file
44
AutoCat2Tests/Mocks/MockURLProtocol.swift
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
class MockURLProtocol: URLProtocol {
|
||||||
|
|
||||||
|
override class func canInit(with request: URLRequest) -> Bool {
|
||||||
|
// To check if this protocol can handle the given request.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
|
||||||
|
// Here you return the canonical version of the request but most of the time you pass the orignal one.
|
||||||
|
return request
|
||||||
|
}
|
||||||
|
|
||||||
|
override func startLoading() {
|
||||||
|
guard let handler = MockURLProtocol.requestHandler else {
|
||||||
|
fatalError("Handler is unavailable.")
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
// 2. Call handler with received request and capture the tuple of response and data.
|
||||||
|
let (response, data) = try handler(request)
|
||||||
|
|
||||||
|
// 3. Send received response to the client.
|
||||||
|
client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
|
||||||
|
|
||||||
|
if let data = data {
|
||||||
|
// 4. Send received data to the client.
|
||||||
|
client?.urlProtocol(self, didLoad: data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Notify request has been finished.
|
||||||
|
client?.urlProtocolDidFinishLoading(self)
|
||||||
|
} catch {
|
||||||
|
// 6. Notify received error.
|
||||||
|
client?.urlProtocol(self, didFailWithError: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func stopLoading() {
|
||||||
|
// This is called if the request gets canceled or completed.
|
||||||
|
print("")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,7 +4,7 @@ import CoreData
|
|||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if Settings.shared.user.token.isEmpty {
|
if Settings.shared.user.token.isEmpty {
|
||||||
AuthView(login: "", password: "")
|
AuthView()
|
||||||
} else {
|
} else {
|
||||||
EmptyView()
|
EmptyView()
|
||||||
}
|
}
|
||||||
|
|||||||
28
Shared/Extensions/Alert.swift
Normal file
28
Shared/Extensions/Alert.swift
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
enum AlertMessage: Identifiable {
|
||||||
|
case info(title: String, body: String)
|
||||||
|
case error(error: Error)
|
||||||
|
|
||||||
|
var id: Int {
|
||||||
|
switch self {
|
||||||
|
case .info(let title, let body):
|
||||||
|
return title.hashValue + body.hashValue
|
||||||
|
case .error(let error):
|
||||||
|
return error.localizedDescription.hashValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Alert {
|
||||||
|
init(_ message: AlertMessage) {
|
||||||
|
switch message {
|
||||||
|
case .info(let title, let body):
|
||||||
|
self.init(title: Text(title), message: Text(body))
|
||||||
|
case .error(let error):
|
||||||
|
let msg = (error as NSError).displayMessage
|
||||||
|
self.init(title: Text(msg.title), message: Text(msg.body))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -95,11 +95,11 @@ public class Api {
|
|||||||
|
|
||||||
// MARK: - AutoCat public API
|
// MARK: - AutoCat public API
|
||||||
|
|
||||||
public static func login(email: String, password: String) async throws -> User {
|
public func login(email: String, password: String) async throws -> User {
|
||||||
let body = [
|
let body = [
|
||||||
"email": email,
|
"email": email,
|
||||||
"password": password
|
"password": password
|
||||||
]
|
]
|
||||||
return try await self.shared.makeBodyRequest(api: "user/login", body: body)
|
return try await self.makeBodyRequest(api: "user/login", body: body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,6 @@ import Foundation
|
|||||||
|
|
||||||
public class AuthVM: ObservableObject {
|
public class AuthVM: ObservableObject {
|
||||||
public func login(user: String, password: String) async throws {
|
public func login(user: String, password: String) async throws {
|
||||||
|
Settings.shared.user = try await Api.shared.login(email: user, password: password)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,9 +3,10 @@ import SwiftUI
|
|||||||
struct AuthView: View {
|
struct AuthView: View {
|
||||||
@ObservedObject var viewModel = AuthVM()
|
@ObservedObject var viewModel = AuthVM()
|
||||||
|
|
||||||
@State var login: String
|
@State private var login: String = ""
|
||||||
@State var password: String
|
@State private var password: String = ""
|
||||||
@State var showProgress: Bool = false
|
@State private var showProgress: Bool = false
|
||||||
|
@State private var alert: AlertMessage? = nil
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
@ -14,11 +15,20 @@ struct AuthView: View {
|
|||||||
TextField("Login", text: $login)
|
TextField("Login", text: $login)
|
||||||
SecureField("Password", text: $password)
|
SecureField("Password", text: $password)
|
||||||
Button("Login") {
|
Button("Login") {
|
||||||
self.showProgress = true
|
|
||||||
async {
|
async {
|
||||||
|
do {
|
||||||
|
self.showProgress = true
|
||||||
try await self.viewModel.login(user: self.login, password: self.password)
|
try await self.viewModel.login(user: self.login, password: self.password)
|
||||||
|
self.showProgress = false
|
||||||
|
} catch {
|
||||||
|
self.showProgress = false
|
||||||
|
self.alert = .error(error: error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
.alert(item: $alert) { id in
|
||||||
|
Alert(id)
|
||||||
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
.buttonStyle(.bordered)
|
.buttonStyle(.bordered)
|
||||||
@ -35,9 +45,9 @@ struct AuthView: View {
|
|||||||
struct AuthView_Previews: PreviewProvider {
|
struct AuthView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
Group {
|
Group {
|
||||||
AuthView(login: "", password: "")
|
AuthView()
|
||||||
.previewDevice("iPhone 8")
|
.previewDevice("iPhone 8")
|
||||||
AuthView(login: "", password: "")
|
AuthView()
|
||||||
.preferredColorScheme(.dark)
|
.preferredColorScheme(.dark)
|
||||||
.previewDevice("iPhone 8")
|
.previewDevice("iPhone 8")
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user