diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index fa2896d..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.gitignore b/.gitignore index 312d1f6..030d1ea 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,8 @@ DerivedData/ *.perspectivev3 !default.perspectivev3 xcuserdata/ +*.DS_Store +##xcshareddata ## Other *.moved-aside @@ -46,7 +48,9 @@ playground.xcworkspace # you should judge for yourself, the pros and cons are mentioned at: # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # -# Pods/ +iOSBooks/Pods/ +iOSBooks/iOSBooks.xcworkspace +iOSBooks/Podfile.lock # Carthage # diff --git a/iOSBooks/.DS_Store b/iOSBooks/.DS_Store deleted file mode 100644 index 7cb1ea8..0000000 Binary files a/iOSBooks/.DS_Store and /dev/null differ diff --git a/iOSBooks/Podfile b/iOSBooks/Podfile new file mode 100644 index 0000000..21ec943 --- /dev/null +++ b/iOSBooks/Podfile @@ -0,0 +1,19 @@ +# Uncomment the next line to define a global platform for your project +platform :ios, '12.0' + +target 'iOSBooks' do + # Comment the next line if you don't want to use dynamic frameworks + use_frameworks! + + # Pods for iOSBooks + pod 'PromiseKit' + pod 'IQKeyboardManagerSwift' + + target 'iOSBooksTests' do + inherit! :search_paths + # Pods for testing + pod 'Quick', '~> 2.1.0' + pod 'Nimble', '~> 8.0.1' + end + +end diff --git a/iOSBooks/iOSBooks.xcodeproj/project.pbxproj b/iOSBooks/iOSBooks.xcodeproj/project.pbxproj index b0482bb..4ee20cf 100644 --- a/iOSBooks/iOSBooks.xcodeproj/project.pbxproj +++ b/iOSBooks/iOSBooks.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 51; objects = { /* Begin PBXBuildFile section */ @@ -13,6 +13,18 @@ 0E3964DF2314BB4A0093738B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E3964DE2314BB4A0093738B /* Assets.xcassets */; }; 0E3964E22314BB4A0093738B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E3964E02314BB4A0093738B /* LaunchScreen.storyboard */; }; 0E3964ED2314BB4A0093738B /* iOSBooksTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3964EC2314BB4A0093738B /* iOSBooksTests.swift */; }; + 477DA993F6CBE89C179034EE /* Pods_iOSBooksTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28F20F388C60D311258C5680 /* Pods_iOSBooksTests.framework */; }; + 7447D35E2316C91700E01BD3 /* APIClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7447D35D2316C91700E01BD3 /* APIClient.swift */; }; + 7447D3602316CB1200E01BD3 /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7447D35F2316CB1200E01BD3 /* Reachability.swift */; }; + 7447D3622316DECA00E01BD3 /* BooksAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7447D3612316DECA00E01BD3 /* BooksAPI.swift */; }; + 7447D3642316E02500E01BD3 /* Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7447D3632316E02500E01BD3 /* Endpoint.swift */; }; + 7447D3662316E3C100E01BD3 /* BooksClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7447D3652316E3C100E01BD3 /* BooksClient.swift */; }; + 7447D3682316E57500E01BD3 /* Book.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7447D3672316E57500E01BD3 /* Book.swift */; }; + 7447D36A2316F11800E01BD3 /* BooksList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7447D3692316F11800E01BD3 /* BooksList.swift */; }; + 7447D36C2316F12800E01BD3 /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7447D36B2316F12800E01BD3 /* Item.swift */; }; + 7447D36E2316F13A00E01BD3 /* ImageLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7447D36D2316F13A00E01BD3 /* ImageLinks.swift */; }; + 7447D3702316F14D00E01BD3 /* SalesInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7447D36F2316F14D00E01BD3 /* SalesInfo.swift */; }; + 768F74E39672B1F50C632913 /* Pods_iOSBooks.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D2FDB2EC5907498F6A3C173 /* Pods_iOSBooks.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -36,6 +48,22 @@ 0E3964E82314BB4A0093738B /* iOSBooksTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iOSBooksTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 0E3964EC2314BB4A0093738B /* iOSBooksTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSBooksTests.swift; sourceTree = ""; }; 0E3964EE2314BB4A0093738B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 18F9E4DBC4A549B0B6E5DFAB /* Pods-iOSBooksTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iOSBooksTests.release.xcconfig"; path = "Target Support Files/Pods-iOSBooksTests/Pods-iOSBooksTests.release.xcconfig"; sourceTree = ""; }; + 275296C36B2B5A1EBD78A5F1 /* Pods-iOSBooks.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iOSBooks.release.xcconfig"; path = "Target Support Files/Pods-iOSBooks/Pods-iOSBooks.release.xcconfig"; sourceTree = ""; }; + 28F20F388C60D311258C5680 /* Pods_iOSBooksTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iOSBooksTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2D2FDB2EC5907498F6A3C173 /* Pods_iOSBooks.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iOSBooks.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 48CC3F84588BF21024860E64 /* Pods-iOSBooksTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iOSBooksTests.debug.xcconfig"; path = "Target Support Files/Pods-iOSBooksTests/Pods-iOSBooksTests.debug.xcconfig"; sourceTree = ""; }; + 7447D35D2316C91700E01BD3 /* APIClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIClient.swift; sourceTree = ""; }; + 7447D35F2316CB1200E01BD3 /* Reachability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reachability.swift; sourceTree = ""; }; + 7447D3612316DECA00E01BD3 /* BooksAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BooksAPI.swift; sourceTree = ""; }; + 7447D3632316E02500E01BD3 /* Endpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Endpoint.swift; sourceTree = ""; }; + 7447D3652316E3C100E01BD3 /* BooksClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BooksClient.swift; sourceTree = ""; }; + 7447D3672316E57500E01BD3 /* Book.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Book.swift; sourceTree = ""; }; + 7447D3692316F11800E01BD3 /* BooksList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BooksList.swift; sourceTree = ""; }; + 7447D36B2316F12800E01BD3 /* Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Item.swift; sourceTree = ""; }; + 7447D36D2316F13A00E01BD3 /* ImageLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageLinks.swift; sourceTree = ""; }; + 7447D36F2316F14D00E01BD3 /* SalesInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SalesInfo.swift; sourceTree = ""; }; + E6741F045EBC12BB315EE1CB /* Pods-iOSBooks.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iOSBooks.debug.xcconfig"; path = "Target Support Files/Pods-iOSBooks/Pods-iOSBooks.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -43,6 +71,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 768F74E39672B1F50C632913 /* Pods_iOSBooks.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -50,6 +79,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 477DA993F6CBE89C179034EE /* Pods_iOSBooksTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -62,6 +92,8 @@ 0E3964D62314BB470093738B /* iOSBooks */, 0E3964EB2314BB4A0093738B /* iOSBooksTests */, 0E3964D52314BB470093738B /* Products */, + E4186846F20EA500EF61D605 /* Pods */, + 46A8A270FB95E2805738E1EE /* Frameworks */, ); sourceTree = ""; }; @@ -77,6 +109,7 @@ 0E3964D62314BB470093738B /* iOSBooks */ = { isa = PBXGroup; children = ( + 7447D3712316F16E00E01BD3 /* Data */, 0E3964D72314BB470093738B /* AppDelegate.swift */, 0E3964D92314BB470093738B /* ViewController.swift */, 0E3964DB2314BB470093738B /* Main.storyboard */, @@ -96,6 +129,75 @@ path = iOSBooksTests; sourceTree = ""; }; + 46A8A270FB95E2805738E1EE /* Frameworks */ = { + isa = PBXGroup; + children = ( + 2D2FDB2EC5907498F6A3C173 /* Pods_iOSBooks.framework */, + 28F20F388C60D311258C5680 /* Pods_iOSBooksTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 7447D3712316F16E00E01BD3 /* Data */ = { + isa = PBXGroup; + children = ( + 7447D3752316F1A200E01BD3 /* Clients */, + 7447D3742316F19000E01BD3 /* Network */, + 7447D3732316F18400E01BD3 /* Endpoints */, + 7447D3722316F17900E01BD3 /* Models */, + ); + path = Data; + sourceTree = ""; + }; + 7447D3722316F17900E01BD3 /* Models */ = { + isa = PBXGroup; + children = ( + 7447D3672316E57500E01BD3 /* Book.swift */, + 7447D3692316F11800E01BD3 /* BooksList.swift */, + 7447D36B2316F12800E01BD3 /* Item.swift */, + 7447D36D2316F13A00E01BD3 /* ImageLinks.swift */, + 7447D36F2316F14D00E01BD3 /* SalesInfo.swift */, + ); + path = Models; + sourceTree = ""; + }; + 7447D3732316F18400E01BD3 /* Endpoints */ = { + isa = PBXGroup; + children = ( + 7447D3612316DECA00E01BD3 /* BooksAPI.swift */, + ); + path = Endpoints; + sourceTree = ""; + }; + 7447D3742316F19000E01BD3 /* Network */ = { + isa = PBXGroup; + children = ( + 7447D35D2316C91700E01BD3 /* APIClient.swift */, + 7447D35F2316CB1200E01BD3 /* Reachability.swift */, + 7447D3632316E02500E01BD3 /* Endpoint.swift */, + ); + path = Network; + sourceTree = ""; + }; + 7447D3752316F1A200E01BD3 /* Clients */ = { + isa = PBXGroup; + children = ( + 7447D3652316E3C100E01BD3 /* BooksClient.swift */, + ); + path = Clients; + sourceTree = ""; + }; + E4186846F20EA500EF61D605 /* Pods */ = { + isa = PBXGroup; + children = ( + E6741F045EBC12BB315EE1CB /* Pods-iOSBooks.debug.xcconfig */, + 275296C36B2B5A1EBD78A5F1 /* Pods-iOSBooks.release.xcconfig */, + 48CC3F84588BF21024860E64 /* Pods-iOSBooksTests.debug.xcconfig */, + 18F9E4DBC4A549B0B6E5DFAB /* Pods-iOSBooksTests.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -103,9 +205,11 @@ isa = PBXNativeTarget; buildConfigurationList = 0E3964F12314BB4A0093738B /* Build configuration list for PBXNativeTarget "iOSBooks" */; buildPhases = ( + 15C7FF8C9D9E1A03AF7AE485 /* [CP] Check Pods Manifest.lock */, 0E3964D02314BB470093738B /* Sources */, 0E3964D12314BB470093738B /* Frameworks */, 0E3964D22314BB470093738B /* Resources */, + 29B8A3AC18D11FEF43C48244 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -120,9 +224,11 @@ isa = PBXNativeTarget; buildConfigurationList = 0E3964F42314BB4A0093738B /* Build configuration list for PBXNativeTarget "iOSBooksTests" */; buildPhases = ( + B1160ABF1429D7BD27416220 /* [CP] Check Pods Manifest.lock */, 0E3964E42314BB4A0093738B /* Sources */, 0E3964E52314BB4A0093738B /* Frameworks */, 0E3964E62314BB4A0093738B /* Resources */, + 6A60DF844467B985FBAD6155 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -192,13 +298,104 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 15C7FF8C9D9E1A03AF7AE485 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-iOSBooks-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 29B8A3AC18D11FEF43C48244 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-iOSBooks/Pods-iOSBooks-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-iOSBooks/Pods-iOSBooks-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iOSBooks/Pods-iOSBooks-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 6A60DF844467B985FBAD6155 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-iOSBooksTests/Pods-iOSBooksTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-iOSBooksTests/Pods-iOSBooksTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iOSBooksTests/Pods-iOSBooksTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + B1160ABF1429D7BD27416220 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-iOSBooksTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 0E3964D02314BB470093738B /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7447D36C2316F12800E01BD3 /* Item.swift in Sources */, + 7447D3682316E57500E01BD3 /* Book.swift in Sources */, + 7447D3702316F14D00E01BD3 /* SalesInfo.swift in Sources */, + 7447D36E2316F13A00E01BD3 /* ImageLinks.swift in Sources */, 0E3964DA2314BB470093738B /* ViewController.swift in Sources */, 0E3964D82314BB470093738B /* AppDelegate.swift in Sources */, + 7447D3602316CB1200E01BD3 /* Reachability.swift in Sources */, + 7447D35E2316C91700E01BD3 /* APIClient.swift in Sources */, + 7447D3642316E02500E01BD3 /* Endpoint.swift in Sources */, + 7447D36A2316F11800E01BD3 /* BooksList.swift in Sources */, + 7447D3662316E3C100E01BD3 /* BooksClient.swift in Sources */, + 7447D3622316DECA00E01BD3 /* BooksAPI.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -358,11 +555,13 @@ }; 0E3964F22314BB4A0093738B /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = E6741F045EBC12BB315EE1CB /* Pods-iOSBooks.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 7AH86L34EQ; INFOPLIST_FILE = iOSBooks/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -376,11 +575,13 @@ }; 0E3964F32314BB4A0093738B /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 275296C36B2B5A1EBD78A5F1 /* Pods-iOSBooks.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 7AH86L34EQ; INFOPLIST_FILE = iOSBooks/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -394,6 +595,7 @@ }; 0E3964F52314BB4A0093738B /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 48CC3F84588BF21024860E64 /* Pods-iOSBooksTests.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; @@ -415,6 +617,7 @@ }; 0E3964F62314BB4A0093738B /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 18F9E4DBC4A549B0B6E5DFAB /* Pods-iOSBooksTests.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; diff --git a/iOSBooks/iOSBooks/Data/Clients/BooksClient.swift b/iOSBooks/iOSBooks/Data/Clients/BooksClient.swift new file mode 100644 index 0000000..3e291a3 --- /dev/null +++ b/iOSBooks/iOSBooks/Data/Clients/BooksClient.swift @@ -0,0 +1,23 @@ +// +// BooksClient.swift +// iOSBooks +// +// Created by Guilherme Antunes Ferreira on 28/08/19. +// Copyright © 2019 Guilherme Antunes. All rights reserved. +// + +import Foundation +import PromiseKit + +class BooksClient { + + let apiClient: APIClient + + init(apiClient: APIClient = APIClient()) { + self.apiClient = apiClient + } + + func fetchBooksList() -> Promise { + return apiClient.request(model: BooksList.self, BooksAPI.list.request) + } +} diff --git a/iOSBooks/iOSBooks/Data/Endpoints/BooksAPI.swift b/iOSBooks/iOSBooks/Data/Endpoints/BooksAPI.swift new file mode 100644 index 0000000..e598eb0 --- /dev/null +++ b/iOSBooks/iOSBooks/Data/Endpoints/BooksAPI.swift @@ -0,0 +1,67 @@ +// +// BooksAPI.swift +// iOSBooks +// +// Created by Guilherme Antunes Ferreira on 28/08/19. +// Copyright © 2019 Guilherme Antunes. All rights reserved. +// + +import Foundation + +enum BooksAPI: Endpoint { + + case list + + var path: String { + switch self { + case .list: + return "https://www.googleapis.com/books/v1/volumes" + } + } + + var headers: [String : String]? { + switch self { + case .list: + return nil + } + } + + var body: Data? { + switch self { + case .list: + return nil + } + } + + var httpMethod: String { + switch self { + case .list: + return "GET" + } + } + + var queryItems: [URLQueryItem]? { + switch self { + case .list: + let subjectItem = URLQueryItem(name: "q", value: "ios") + let maxResultsItem = URLQueryItem(name: "maxResults", value: "20") + let startIndexItem = URLQueryItem(name: "startIndex", value: "0") + return [subjectItem, startIndexItem, maxResultsItem] + } + } + + var request: URLRequest { + switch self { + case .list: + guard var components = URLComponents(string: path) else { fatalError() } + components.queryItems = queryItems + guard let url = components.url else { fatalError() } + var request = URLRequest(url: url) + request.allHTTPHeaderFields = headers + request.httpMethod = httpMethod + request.httpBody = body + return request + } + } +} + diff --git a/iOSBooks/iOSBooks/Data/Models/Book.swift b/iOSBooks/iOSBooks/Data/Models/Book.swift new file mode 100644 index 0000000..50078a8 --- /dev/null +++ b/iOSBooks/iOSBooks/Data/Models/Book.swift @@ -0,0 +1,24 @@ +// +// Book.swift +// iOSBooks +// +// Created by Guilherme Antunes Ferreira on 28/08/19. +// Copyright © 2019 Guilherme Antunes. All rights reserved. +// + +import Foundation + +class Book: Codable { + let authors: [String]? + let title: String? + let subtitle: String? + let description: String? + let imageLinks: ImageLinks? + + enum CodingKeys: String, CodingKey { + case authors, title, subtitle, description, imageLinks + } +} + + + diff --git a/iOSBooks/iOSBooks/Data/Models/BooksList.swift b/iOSBooks/iOSBooks/Data/Models/BooksList.swift new file mode 100644 index 0000000..2a2de78 --- /dev/null +++ b/iOSBooks/iOSBooks/Data/Models/BooksList.swift @@ -0,0 +1,18 @@ +// +// BooksList.swift +// iOSBooks +// +// Created by Guilherme Antunes Ferreira on 28/08/19. +// Copyright © 2019 Guilherme Antunes. All rights reserved. +// + +import Foundation + +class BooksList: Codable { + let items: [Item]? + let totalItems : Int? + + enum CodingKeys: String, CodingKey { + case items, totalItems + } +} diff --git a/iOSBooks/iOSBooks/Data/Models/ImageLinks.swift b/iOSBooks/iOSBooks/Data/Models/ImageLinks.swift new file mode 100644 index 0000000..3f19a98 --- /dev/null +++ b/iOSBooks/iOSBooks/Data/Models/ImageLinks.swift @@ -0,0 +1,19 @@ +// +// ImageLinks.swift +// iOSBooks +// +// Created by Guilherme Antunes Ferreira on 28/08/19. +// Copyright © 2019 Guilherme Antunes. All rights reserved. +// + +import Foundation + +class ImageLinks: Codable { + let smallThumbnail: String? + let thumbnail: String? + + enum CodingKeys: String, CodingKey { + case smallThumbnail, thumbnail + } + +} diff --git a/iOSBooks/iOSBooks/Data/Models/Item.swift b/iOSBooks/iOSBooks/Data/Models/Item.swift new file mode 100644 index 0000000..b06c4ed --- /dev/null +++ b/iOSBooks/iOSBooks/Data/Models/Item.swift @@ -0,0 +1,21 @@ +// +// Item.swift +// iOSBooks +// +// Created by Guilherme Antunes Ferreira on 28/08/19. +// Copyright © 2019 Guilherme Antunes. All rights reserved. +// + +import Foundation + +class Item: Codable { + let id: String? + let book: Book? + let salesInfo : SalesInfo? + + enum CodingKeys: String, CodingKey { + case book = "volumeInfo" + case id + case salesInfo = "saleInfo" + } +} diff --git a/iOSBooks/iOSBooks/Data/Models/SalesInfo.swift b/iOSBooks/iOSBooks/Data/Models/SalesInfo.swift new file mode 100644 index 0000000..1a395db --- /dev/null +++ b/iOSBooks/iOSBooks/Data/Models/SalesInfo.swift @@ -0,0 +1,14 @@ +// +// SalesInfo.swift +// iOSBooks +// +// Created by Guilherme Antunes Ferreira on 28/08/19. +// Copyright © 2019 Guilherme Antunes. All rights reserved. +// + +import Foundation + +class SalesInfo: Codable { + let country: String? + let buyLink: String? +} diff --git a/iOSBooks/iOSBooks/Data/Network/APIClient.swift b/iOSBooks/iOSBooks/Data/Network/APIClient.swift new file mode 100644 index 0000000..e5d2682 --- /dev/null +++ b/iOSBooks/iOSBooks/Data/Network/APIClient.swift @@ -0,0 +1,61 @@ +// +// APIClient.swift +// iOSBooks +// +// Created by Guilherme Antunes Ferreira on 28/08/19. +// Copyright © 2019 Guilherme Antunes. All rights reserved. +// + +import Foundation +import PromiseKit + +enum CustomError: Error { + case connectionError(String) + case mappingError(String) + case APIError(String) + case generalError(String) + case deallocatedClass(String) +} + +class APIClient { + + let connectionChecker: Reachability + let decoder: JSONDecoder + let session: URLSession + + init(connectionChecker: Reachability = Reachability(), decoder: JSONDecoder = JSONDecoder(), session: URLSession = URLSession.shared) { + self.connectionChecker = connectionChecker + self.decoder = decoder + self.session = session + } + + func request(model: T.Type, _ request: URLRequest) -> Promise { + return Promise { [weak self] seal in + guard let _self = self else { + print("Class deinitialized!") + return + } + + guard connectionChecker.isConnected else { + seal.reject(CustomError.connectionError("Por favor, verifique sua conexão com a internet!")) + return + } + + session.dataTask(with: request, completionHandler: { (data, response, error) in + guard let data = data else { + seal.reject(CustomError.APIError(error?.localizedDescription ?? "Ocorreu um erro inesperado, por favor, tente novamente!")) + return + } + + guard let model = try? _self.decoder.decode(T.self, from: data) else { + seal.reject(CustomError.mappingError("Houve um erro ao fazer o parse do modelo \(T.self)!")) + return + } + + seal.fulfill(model) + }).resume() + } + } + +} + diff --git a/iOSBooks/iOSBooks/Data/Network/Endpoint.swift b/iOSBooks/iOSBooks/Data/Network/Endpoint.swift new file mode 100644 index 0000000..61de764 --- /dev/null +++ b/iOSBooks/iOSBooks/Data/Network/Endpoint.swift @@ -0,0 +1,18 @@ +// +// Endpoint.swift +// iOSBooks +// +// Created by Guilherme Antunes Ferreira on 28/08/19. +// Copyright © 2019 Guilherme Antunes. All rights reserved. +// + +import Foundation + +protocol Endpoint { + var path: String { get } + var headers: [String: String]? { get } + var body: Data? { get } + var httpMethod: String { get } + var request: URLRequest { get } + var queryItems: [URLQueryItem]? { get } +} diff --git a/iOSBooks/iOSBooks/Data/Network/Reachability.swift b/iOSBooks/iOSBooks/Data/Network/Reachability.swift new file mode 100644 index 0000000..7657b05 --- /dev/null +++ b/iOSBooks/iOSBooks/Data/Network/Reachability.swift @@ -0,0 +1,36 @@ +// +// Reachability.swift +// iOSBooks +// +// Created by Guilherme Antunes Ferreira on 28/08/19. +// Copyright © 2019 Guilherme Antunes. All rights reserved. +// + +import Foundation +import SystemConfiguration + +class Reachability { + var isConnected: Bool { + var zeroAddress = sockaddr_in() + zeroAddress.sin_len = UInt8(MemoryLayout.size) + zeroAddress.sin_family = sa_family_t(AF_INET) + + guard let defaultRouteReachability = withUnsafePointer(to: &zeroAddress, { + $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { + SCNetworkReachabilityCreateWithAddress(nil, $0) + } + }) else { + return false + } + + var flags: SCNetworkReachabilityFlags = [] + if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) { + return false + } + + let isReachable = flags.contains(.reachable) + let needsConnection = flags.contains(.connectionRequired) + + return (isReachable && !needsConnection) + } +} diff --git a/iOSBooks/iOSBooks/ViewController.swift b/iOSBooks/iOSBooks/ViewController.swift index da791b1..9d3dfa9 100644 --- a/iOSBooks/iOSBooks/ViewController.swift +++ b/iOSBooks/iOSBooks/ViewController.swift @@ -7,14 +7,9 @@ // import UIKit +import PromiseKit class ViewController: UIViewController { - override func viewDidLoad() { - super.viewDidLoad() - // Do any additional setup after loading the view. - } - - }