Skip to content

Commit 109dd0c

Browse files
committed
Initial commit
0 parents  commit 109dd0c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1843
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*.xcodeproj
2+
*.xcworkspace
3+
*.DS_Store
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//
2+
// AppDelegate.swift
3+
// SimpsonsQuotes
4+
//
5+
// Created by Zafar Ivaev on 09/08/23.
6+
//
7+
8+
import UIKit
9+
10+
@main
11+
class AppDelegate: UIResponder, UIApplicationDelegate {
12+
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
13+
return true
14+
}
15+
16+
// MARK: UISceneSession Lifecycle
17+
18+
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
19+
let sessionRole = connectingSceneSession.role
20+
let sceneConfig = UISceneConfiguration(name: nil, sessionRole: sessionRole)
21+
sceneConfig.delegateClass = SceneDelegate.self
22+
return sceneConfig
23+
}
24+
}

SimpsonsQuotes/Application/Info.plist

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleDevelopmentRegion</key>
6+
<string>$(DEVELOPMENT_LANGUAGE)</string>
7+
<key>CFBundleDisplayName</key>
8+
<string>SimpsonsQuotes</string>
9+
<key>CFBundleExecutable</key>
10+
<string>$(EXECUTABLE_NAME)</string>
11+
<key>CFBundleIdentifier</key>
12+
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
13+
<key>CFBundleInfoDictionaryVersion</key>
14+
<string>6.0</string>
15+
<key>CFBundleName</key>
16+
<string>$(PRODUCT_NAME)</string>
17+
<key>CFBundlePackageType</key>
18+
<string>APPL</string>
19+
<key>CFBundleShortVersionString</key>
20+
<string>1.0.0</string>
21+
<key>CFBundleVersion</key>
22+
<string>1</string>
23+
<key>NSAppTransportSecurity</key>
24+
<dict>
25+
<key>NSAllowsArbitraryLoads</key>
26+
<true/>
27+
</dict>
28+
<key>UILaunchStoryboardName</key>
29+
<string>LaunchScreen</string>
30+
<key>UISupportedInterfaceOrientations</key>
31+
<array>
32+
<string>UIInterfaceOrientationPortrait</string>
33+
</array>
34+
</dict>
35+
</plist>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
3+
<device id="retina6_12" orientation="portrait" appearance="light"/>
4+
<dependencies>
5+
<deployment identifier="iOS"/>
6+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21679"/>
7+
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
8+
<capability name="System colors in document resources" minToolsVersion="11.0"/>
9+
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
10+
</dependencies>
11+
<scenes>
12+
<!--View Controller-->
13+
<scene sceneID="EHf-IW-A2E">
14+
<objects>
15+
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
16+
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
17+
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
18+
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
19+
<subviews>
20+
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="obG-Y5-kRd">
21+
<rect key="frame" x="0.0" y="832" width="393" height="0.0"/>
22+
<fontDescription key="fontDescription" type="system" pointSize="17"/>
23+
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
24+
<nil key="highlightedColor"/>
25+
</label>
26+
</subviews>
27+
<viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
28+
<color key="backgroundColor" systemColor="systemCyanColor"/>
29+
<constraints>
30+
<constraint firstItem="Bcu-3y-fUS" firstAttribute="centerX" secondItem="obG-Y5-kRd" secondAttribute="centerX" id="5cz-MP-9tL"/>
31+
<constraint firstItem="obG-Y5-kRd" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" symbolic="YES" id="SfN-ll-jLj"/>
32+
<constraint firstAttribute="bottom" secondItem="obG-Y5-kRd" secondAttribute="bottom" constant="20" id="Y44-ml-fuU"/>
33+
</constraints>
34+
</view>
35+
</viewController>
36+
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
37+
</objects>
38+
<point key="canvasLocation" x="53" y="375"/>
39+
</scene>
40+
</scenes>
41+
<resources>
42+
<systemColor name="systemCyanColor">
43+
<color red="0.19607843137254902" green="0.67843137254901964" blue="0.90196078431372551" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
44+
</systemColor>
45+
</resources>
46+
</document>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//
2+
// SceneDelegate.swift
3+
// SimpsonsQuotes
4+
//
5+
// Created by Zafar Ivaev on 09/08/23.
6+
//
7+
8+
import UIKit
9+
import Navigator
10+
11+
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
12+
var window: UIWindow?
13+
var navigator: NavigatorProtocol?
14+
15+
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
16+
guard let windowScene = (scene as? UIWindowScene) else { return }
17+
18+
let window = UIWindow(windowScene: windowScene)
19+
let navigator = StartNavigator(window: window)
20+
self.navigator = navigator
21+
self.window = window
22+
navigator.start()
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//
2+
// Endpoint+Base.swift
3+
// Endpoints
4+
//
5+
// Created by Zafar Ivaev on 08/08/23.
6+
//
7+
8+
import Foundation
9+
10+
public extension Endpoint {
11+
var url: URL {
12+
var components = URLComponents()
13+
components.scheme = "https"
14+
components.host = "thesimpsonsquoteapi.glitch.me"
15+
components.path = "/\(path)"
16+
components.queryItems = queryItems
17+
18+
guard let url = components.url else {
19+
preconditionFailure("Invalid URL components: \(components)")
20+
}
21+
22+
return url
23+
}
24+
25+
var headers: [String: Any] {
26+
return [
27+
"Accept": "application/json",
28+
"Content-Type": "application/json"
29+
]
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//
2+
// Endpoint+Quotes.swift
3+
// Endpoints
4+
//
5+
// Created by Zafar Ivaev on 08/08/23.
6+
//
7+
8+
import Foundation
9+
10+
public extension Endpoint {
11+
static var quotes: Self {
12+
Endpoint(path: "quotes")
13+
}
14+
15+
static func quotes(count: Int) -> Self {
16+
Endpoint(
17+
path: "quotes",
18+
queryItems: [
19+
URLQueryItem(name: "count", value: "\(count)")
20+
]
21+
)
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//
2+
// Endpoint.swift
3+
// Endpoints
4+
//
5+
// Created by Zafar Ivaev on 08/08/23.
6+
//
7+
8+
import Foundation
9+
10+
public struct Endpoint {
11+
public var path: String
12+
public var queryItems: [URLQueryItem] = []
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//
2+
// AuthService.swift
3+
// Services
4+
//
5+
// Created by Zafar Ivaev on 20/08/23.
6+
//
7+
8+
import Foundation
9+
10+
public protocol AuthServiceProtocol {
11+
func signIn() async throws
12+
func signOut() async throws
13+
func isSignedIn() async -> Bool
14+
}
15+
16+
public final class AuthService: AuthServiceProtocol {
17+
18+
public init() {}
19+
20+
private let isSignedInKey = "isSignedIn"
21+
22+
public func signIn() async throws {
23+
try await Task.sleep(nanoseconds: 2_000_000_000)
24+
UserDefaults.standard.setValue(true, forKey: isSignedInKey)
25+
}
26+
27+
public func signOut() async throws {
28+
try await Task.sleep(nanoseconds: 2_000_000_000)
29+
UserDefaults.standard.setValue(false, forKey: isSignedInKey)
30+
}
31+
32+
public func isSignedIn() async -> Bool {
33+
UserDefaults.standard.bool(forKey: isSignedInKey)
34+
}
35+
}
36+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//
2+
// QuotesService.swift
3+
// Services
4+
//
5+
// Created by Zafar Ivaev on 08/08/23.
6+
//
7+
8+
import Foundation
9+
import Endpoints
10+
import Entities
11+
import Networker
12+
13+
public protocol QuotesServiceProtocol {
14+
func fetchQuotes(count: Int) async throws -> [Quote]
15+
}
16+
17+
public final class QuotesService: QuotesServiceProtocol {
18+
private let networker: NetworkerProtocol
19+
20+
public init(networker: NetworkerProtocol) {
21+
self.networker = networker
22+
}
23+
24+
public func fetchQuotes(count: Int) async throws -> [Quote] {
25+
let endpoint = Endpoint.quotes(count: count)
26+
return try await networker.get(type: [Quote].self, url: endpoint.url, headers: endpoint.headers)
27+
}
28+
}
29+
30+
public final class QuotesMockService: QuotesServiceProtocol {
31+
32+
public init() {}
33+
34+
public func fetchQuotes(count: Int) async throws -> [Quote] {
35+
try await Task.sleep(nanoseconds: 2_000_000_000)
36+
return [
37+
.init(quote: "Test quote 1", character: "Test character 1", image: nil),
38+
.init(quote: "Test quote 2", character: "Test character 2", image: nil),
39+
.init(quote: "Test quote 3", character: "Test character 3", image: nil),
40+
]
41+
}
42+
}
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// Quote.swift
3+
// Entities
4+
//
5+
// Created by Zafar Ivaev on 08/08/23.
6+
//
7+
8+
import Foundation
9+
10+
public struct Quote: Codable {
11+
public init(
12+
quote: String? = nil,
13+
character: String? = nil,
14+
image: String? = nil,
15+
characterDirection: String? = nil
16+
) {
17+
self.quote = quote
18+
self.character = character
19+
self.image = image
20+
self.characterDirection = characterDirection
21+
}
22+
23+
public let quote: String?
24+
public let character: String?
25+
public let image: String?
26+
public let characterDirection: String?
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//
2+
// Networker.swift
3+
// Networker
4+
//
5+
// Created by Zafar Ivaev on 08/08/23.
6+
//
7+
8+
import Foundation
9+
10+
public protocol NetworkerProtocol: AnyObject {
11+
typealias Headers = [String: Any]
12+
13+
func get<T: Decodable>(type: T.Type, url: URL, headers: Headers) async throws -> T
14+
}
15+
16+
public final class Networker: NetworkerProtocol {
17+
18+
public init () {}
19+
20+
public func get<T: Decodable>(type: T.Type, url: URL, headers: Headers) async throws -> T {
21+
var request = URLRequest(url: url)
22+
23+
headers.forEach { (key, value) in
24+
if let value = value as? String {
25+
request.setValue(value, forHTTPHeaderField: key)
26+
}
27+
}
28+
29+
let (data, _) = try await URLSession.shared.data(for: request)
30+
return try JSONDecoder().decode(type, from: data)
31+
}
32+
}

SimpsonsQuotes/Presentation/.DS_Store

6 KB
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"colors" : [
3+
{
4+
"idiom" : "universal"
5+
}
6+
],
7+
"info" : {
8+
"author" : "xcode",
9+
"version" : 1
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "appstore.png",
5+
"idiom" : "universal",
6+
"platform" : "ios",
7+
"size" : "1024x1024"
8+
}
9+
],
10+
"info" : {
11+
"author" : "xcode",
12+
"version" : 1
13+
}
14+
}
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"info" : {
3+
"author" : "xcode",
4+
"version" : 1
5+
}
6+
}

0 commit comments

Comments
 (0)