Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
41e14a1
Initial project setup + .gitignore
Dec 12, 2019
cefa4d6
Project restructure: grouped resources, removed storyboard, added col…
Dec 12, 2019
b9f51b4
Added collection scene with stub card collection fetched
Dec 12, 2019
a7d03f2
Added card/sets entities; added local cards data source with tests
Dec 13, 2019
c3a1b4d
Added CollectionService responsible for fetching, sorting and filteri…
Dec 14, 2019
30c3d53
Proper collection scene dependencies injection; Card cell view stub
Dec 14, 2019
4503f9e
Added VIP for card view with image downloading and placeholder text
Dec 14, 2019
922c116
Http client with cancelation support
Dec 15, 2019
2c0e998
Image service for retrieving and caching images; card cell interactor…
Dec 15, 2019
e6c3ff8
Improved CardCell VIP test coverage
Dec 15, 2019
0b168a4
Prettied up collection/cell design
Dec 15, 2019
8e985e0
Added collection filtering; by default display only deathrattle & leg…
Dec 15, 2019
23a258f
Added stub details view controller; implemented basic routing
Dec 15, 2019
9e9eaaf
Details view routing + VIP
Dec 16, 2019
75c3f69
Detail view design, including name, image, flavor and favorite button
Dec 16, 2019
a952050
Metadata service responsible for storing and providing card metadata …
Dec 16, 2019
895c7d1
Cell view now displays favorite icons
Dec 16, 2019
6e77065
Improved test coverage for Details VIP and MetadataService
Dec 16, 2019
32501e9
Updated readme with developer notes
Dec 16, 2019
6c70d89
Use size ratio for favorite icon instead of an absolute value
Dec 16, 2019
f94f71e
Minor formatting improvements
Dec 17, 2019
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.idea
xcuserdata/

922 changes: 922 additions & 0 deletions HearthstoneHelper/HearthstoneHelper.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

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,49 @@
<?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>SchemeUserState</key>
<dict>
<key>HearthstoneHelper.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
<key>All in DetailsInteractorTests.swift.xcscheme</key>
<dict>
<key>isShown</key>
<false />
<key>orderHint</key>
<integer>1</integer>
</dict>
<key>All in LocalCollectionRetrieverTests.swift.xcscheme</key>
<dict>
<key>isShown</key>
<false />
<key>orderHint</key>
<integer>2</integer>
</dict>
<key>All in CollectionServiceTests.swift.xcscheme</key>
<dict>
<key>isShown</key>
<false />
<key>orderHint</key>
<integer>3</integer>
</dict>
<key>All in ImageServiceTests.swift.xcscheme</key>
<dict>
<key>isShown</key>
<false />
<key>orderHint</key>
<integer>4</integer>
</dict>
<key>All in MetadataServiceTests.swift.xcscheme</key>
<dict>
<key>isShown</key>
<false />
<key>orderHint</key>
<integer>5</integer>
</dict>
</dict>
</dict>
</plist>
25 changes: 25 additions & 0 deletions HearthstoneHelper/HearthstoneHelper/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// Created by Maxim Berezhnoy on 12/12/2019.
// Licensed under the MIT license
//
// Copyright (c) 2019 rencevio. All rights reserved.

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let dependencies = RootDependencies()

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let collectionViewController = dependencies.createCollectionViewController()

let navigationController = UINavigationController(rootViewController: collectionViewController.asViewController)

window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = navigationController
window?.makeKeyAndVisible()
return true
}
}

72 changes: 72 additions & 0 deletions HearthstoneHelper/HearthstoneHelper/Network/HttpClient.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// Created by Maxim Berezhnoy on 15/12/2019.
// Licensed under the MIT license
//
// Copyright (c) 2019 rencevio. All rights reserved.

import Foundation

protocol HttpCommunicator {
@discardableResult
func get(url: URL, completion: @escaping Http.TaskCompletion) -> Cancelable
}

enum Http {
typealias TaskCompletion = (Result<Data, RequestError>) -> Void

private typealias URLSessionTaskCompletion = (Data?, URLResponse?, Error?) -> Void

enum RequestError: Error {
case noData
case httpError(Error)
}

final class Client: HttpCommunicator {
private let urlSession: URLSession

init() {
urlSession = URLSession.shared
}

@discardableResult
func get(url: URL, completion: @escaping TaskCompletion) -> Cancelable {
let task = Task(urlTaskFactory: { sessionTaskCompletion in
urlSession.dataTask(with: url, completionHandler: sessionTaskCompletion)
}, completion: completion)

task.start()

return task
}
}

private class Task: Cancelable {
private let urlSessionTask: URLSessionDataTask

init(urlTaskFactory: (@escaping URLSessionTaskCompletion) -> URLSessionDataTask,
completion: @escaping TaskCompletion) {
self.urlSessionTask = urlTaskFactory { data, _, error in
guard let data = data else {
if let error = error {
completion(.failure(.httpError(error)))
} else {
completion(.failure(.noData))
}

return
}

completion(.success(data))
}
}

func start() {
assert(urlSessionTask.state == .suspended)
urlSessionTask.resume()
}

func cancel() {
urlSessionTask.cancel()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
48 changes: 48 additions & 0 deletions HearthstoneHelper/HearthstoneHelper/Resources/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?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>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
File renamed without changes.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions HearthstoneHelper/HearthstoneHelper/RootDependencies.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// Created by Maxim Berezhnoy on 12/12/2019.
// Licensed under the MIT license
//
// Copyright (c) 2019 rencevio. All rights reserved.

import struct Foundation.Data

final class RootDependencies {
let imageService: ImageProviding
let metadataService: MetadataProviding

init() {
let imageServiceFactory = ImageServiceFactory()
imageService = imageServiceFactory.create()

let metadataServiceFactory = MetadataServiceFactory()
metadataService = metadataServiceFactory.create()
}

func createCollectionViewController() -> CollectionDisplaying {
let collectionServiceFactory = CollectionServiceFactory()
let cardCellFactory = CardCellFactory()
let detailsFactory = DetailsFactory(imageService: imageService, metadataService: metadataService)

let collectionFactory = CollectionFactory(
cardCellFactory: cardCellFactory,
collectionServiceFactory: collectionServiceFactory,
detailsFactory: detailsFactory
)

return collectionFactory.createCollectionViewController(
imageService: imageService,
metadataService: metadataService)
}
}
Loading