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

Exam application content #329

Merged
merged 29 commits into from
Jul 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9682749
Install AlamofireNetworkActivityIndicator
ivan-magda Jul 18, 2018
38d97e5
Create knowledge graph plain objects
ivan-magda Jul 18, 2018
c329776
Create StepicResult enumeration
ivan-magda Jul 18, 2018
d131fd3
Create GraphService protocol
ivan-magda Jul 18, 2018
0b8b1ef
Decode JSON data
ivan-magda Jul 18, 2018
526a9c6
Refactor graph service implementation
ivan-magda Jul 19, 2018
f1aad93
Add common UIKit and Foundation extensions
ivan-magda Jul 19, 2018
8a69edf
Initial topics scene implementation
ivan-magda Jul 19, 2018
5a907a6
Fix tests
ivan-magda Jul 19, 2018
ba2edd2
Graph base models are now classes
ivan-magda Jul 19, 2018
409d64e
Refactor rename Vertex's data instance property to id
ivan-magda Jul 19, 2018
a85773b
Add dictionary subscript by index
ivan-magda Jul 20, 2018
d4bca10
Extend knowledge graph functionality
ivan-magda Jul 20, 2018
fe16bfe
Map graph response model to concrete knowledge graph
ivan-magda Jul 20, 2018
7869a51
Display actual graph data
ivan-magda Jul 20, 2018
daf7fd6
Add pull-to-refresh
ivan-magda Jul 20, 2018
587d511
Provide model as a parameter for the TopicsPresenter
ivan-magda Jul 23, 2018
61e8b7c
Test topics presenter view did load success refresh topics view called
ivan-magda Jul 23, 2018
008cdd0
Test topics presenter view did load failure display error
ivan-magda Jul 23, 2018
70ea727
Test topics presenter configure cell
ivan-magda Jul 23, 2018
f246420
Service components as a properties instead of functions
ivan-magda Jul 23, 2018
c89bc2f
Move graph's service resource URL to the class implementation
ivan-magda Jul 23, 2018
ed886f2
Install PromiseKit Alamofire extensions
ivan-magda Jul 23, 2018
2b48623
Use Alamofire in graph service implementation
ivan-magda Jul 23, 2018
777dda7
Use Promise as a return type of GraphService's obtainGraph()
ivan-magda Jul 23, 2018
0c3db15
Remove Dictionary+Subscript file
ivan-magda Jul 23, 2018
6587028
Remove String+Localized file
ivan-magda Jul 23, 2018
1a570b5
Refactor TopicsPresenter
ivan-magda Jul 23, 2018
f115ed0
Use TopicsPresenter's new selectTopic(with:) function insted of onTap…
ivan-magda Jul 24, 2018
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//

import UIKit
import AlamofireNetworkActivityIndicator

// MARK: AppDelegate: UIResponder, UIApplicationDelegate

Expand Down Expand Up @@ -40,6 +41,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}

rootNavigationManager.setup(with: window)
NetworkActivityIndicatorManager.shared.isEnabled = true

return true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,14 @@ final class RootNavigationManager {
// MARK: Public API

func setup(with window: UIWindow) {
let mainController = MainViewController()
mainController.userRegistrationService = serviceComponents.userRegistrationService
let navigationController = UINavigationController(rootViewController: mainController)
let controller = TopicsTableViewController()
controller.presenter = TopicsPresenterImpl(
view: controller,
model: KnowledgeGraph(),
userRegistrationService: serviceComponents.userRegistrationService,
graphService: serviceComponents.graphService
)
let navigationController = UINavigationController(rootViewController: controller)

window.rootViewController = navigationController
window.makeKeyAndVisible()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import Foundation

protocol ServiceComponents: class {

var userRegistrationService: UserRegistrationService { get }

var graphService: GraphService { get }
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,27 @@
import Foundation

final class ServiceComponentsAssembly: ServiceComponents {

let userRegistrationService: UserRegistrationService
private let authAPI: AuthAPI
private let stepicsAPI: StepicsAPI
private let profilesAPI: ProfilesAPI
private let defaultsStorageManager: DefaultsStorageManager
private let randomCredentialsGenerator: RandomCredentialsGenerator

init(authAPI: AuthAPI,
stepicsAPI: StepicsAPI,
profilesAPI: ProfilesAPI,
defaultsStorageManager: DefaultsStorageManager,
randomCredentialsGenerator: RandomCredentialsGenerator
) {
self.userRegistrationService = UserRegistrationServiceImplementation(
self.authAPI = authAPI
self.stepicsAPI = stepicsAPI
self.profilesAPI = profilesAPI
self.defaultsStorageManager = defaultsStorageManager
self.randomCredentialsGenerator = randomCredentialsGenerator
}

var userRegistrationService: UserRegistrationService {
return UserRegistrationServiceImplementation(
authAPI: authAPI,
stepicsAPI: stepicsAPI,
profilesAPI: profilesAPI,
Expand All @@ -27,4 +38,7 @@ final class ServiceComponentsAssembly: ServiceComponents {
)
}

var graphService: GraphService {
return GraphServiceImpl()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// GraphService.swift
// ExamEGERussian
//
// Created by Ivan Magda on 18/07/2018.
// Copyright © 2018 Alex Karpov. All rights reserved.
//

import Foundation
import PromiseKit

protocol GraphService: class {
func obtainGraph() -> Promise<KnowledgeGraphPlainObject>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// GraphServiceImpl.swift
// ExamEGERussian
//
// Created by Ivan Magda on 18/07/2018.
// Copyright © 2018 Alex Karpov. All rights reserved.
//

import Foundation
import PromiseKit
import Alamofire

final class GraphServiceImpl: GraphService {
private static let url = URL(string: "https://www.dropbox.com/s/l8n1wny8qu0gbqt/example.json?dl=1")!

func obtainGraph() -> Promise<KnowledgeGraphPlainObject> {
return Alamofire
.request(GraphServiceImpl.url)
.responseDecodable(KnowledgeGraphPlainObject.self)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// AbstractGraphBuilder.swift
// ExamEGERussian
//
// Created by Ivan Magda on 19/07/2018.
// Copyright © 2018 Alex Karpov. All rights reserved.
//

import Foundation

protocol AbstractGraphBuilder {
associatedtype T: Hashable
func build() -> AbstractGraph<T>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// AbstractGraphBuilderImpl.swift
// ExamEGERussian
//
// Created by Ivan Magda on 19/07/2018.
// Copyright © 2018 Alex Karpov. All rights reserved.
//

import Foundation

final class KnowledgeGraphBuilder: AbstractGraphBuilder {
private let graphPlainObject: KnowledgeGraphPlainObject

init(graphPlainObject: KnowledgeGraphPlainObject) {
self.graphPlainObject = graphPlainObject
}

func build() -> AbstractGraph<String> {
let graph = KnowledgeGraph()

graphPlainObject.topics.forEach {
graph.addVertex(KnowledgeGraphVertex(id: $0.id, title: $0.title))
}
graphPlainObject.topics
.filter { $0.requiredFor != nil }
.forEach { topic in
guard let (source, _) = graph[topic.id],
let (destination, _) = graph[topic.requiredFor!] else {
return
}
graph.add(from: source, to: destination)
}
graphPlainObject.topicsMap.forEach { topicMap in
guard let (vertex, _) = graph[topicMap.id] else { return }
vertex.lessons.append(contentsOf:
topicMap.lessons.map {
KnowledgeGraphLesson(id: $0.id, type: $0.type, courseId: $0.course)
}
)
}

return graph
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// UITableView+Registration.swift
// ExamEGERussian
//
// Created by Ivan Magda on 19/07/2018.
// Copyright © 2018 Alex Karpov. All rights reserved.
//

import UIKit

extension UITableView {
func registerNib(for cellClass: UITableViewCell.Type) {
register(cellClass.nib, forCellReuseIdentifier: cellClass.identifier)
}

func registerHeaderNib(for headerClass: UITableViewHeaderFooterView.Type) {
register(headerClass.nib, forHeaderFooterViewReuseIdentifier: headerClass.identifier)
}

func registerClass(for cellClass: UITableViewCell.Type) {
register(cellClass, forCellReuseIdentifier: cellClass.identifier)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// UITableView+Reuse.swift
// ExamEGERussian
//
// Created by Ivan Magda on 19/07/2018.
// Copyright © 2018 Alex Karpov. All rights reserved.
//

import UIKit.UITableView

extension UITableView {
func dequeueReusableCell<T: UITableViewCell>(for indexPath: IndexPath) -> T {
let identifier = T.identifier
return dequeueReusableCell(withIdentifier: identifier,
for: indexPath) as! T // swiftlint:disable:this force_cast
}

func dequeueReusableHeaderFooterView<T: UITableViewHeaderFooterView>() -> T {
let identifier = T.identifier
return dequeueReusableHeaderFooterView(withIdentifier: identifier) as! T // swiftlint:disable:this force_cast
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// UITableViewCell+ReusableCell.swift
// ExamEGERussian
//
// Created by Ivan Magda on 19/07/2018.
// Copyright © 2018 Alex Karpov. All rights reserved.
//

import Foundation

import UIKit

protocol ReusableCell {
static var identifier: String { get }
static var nib: UINib { get }
}

extension UITableViewCell: ReusableCell {
static var identifier: String {
return String(describing: self)
}

static var nib: UINib {
return UINib(nibName: identifier, bundle: nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Удобно, кстати 👍
Я бы вместо identifier, использовал String(describing: self), вдруг identifier поменяет формат (он всё таки больше reuseIdentifier).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Так тут и используется String(describing: self), спрятано.

protocol ReusableCell {
    static var identifier: String { get }
    static var nib: UINib { get }
}

...

static var identifier: String {
    return String(describing: self)
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Да, но это справедливо для соглашения "в качестве reuse identificator используем имя класса" :)
Завтра я захочу изменить его на "в качестве reuse identificator используем имя класса + суффикс -reuseId". Тогда в качестве nibName поле identificator уже не поюзать и все сломается.
Но да, это не очень реалистичный кейс и можно не обращать внимание

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Если мы захотим использовать идентификатор, отличный от имени класса, то всегда же можно переопределить это поле в наследнике.

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// UITableViewHeaderFooterView+ReusableCell.swift
// ExamEGERussian
//
// Created by Ivan Magda on 19/07/2018.
// Copyright © 2018 Alex Karpov. All rights reserved.
//

import Foundation

extension UITableViewHeaderFooterView: ReusableCell {
static var identifier: String {
return String(describing: self)
}

static var nib: UINib {
return UINib(nibName: identifier, bundle: nil)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// UIViewController+Alert.swift
// ExamEGERussian
//
// Created by Ivan Magda on 19/07/2018.
// Copyright © 2018 Alex Karpov. All rights reserved.
//

import UIKit

extension UIViewController {
func presentAlert(withTitle title: String?, message: String? = nil,
actionTitle: String? = NSLocalizedString("Ок", comment: ""),
action actionCallback: (() -> Void)? = nil) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: actionTitle, style: .cancel) { _ in
actionCallback?()
})
present(alert, animated: true, completion: nil)
}

func presentConfirmationAlert(withTitle title: String?, message: String? = nil,
buttonFirstTitle: String? = NSLocalizedString("Ок", comment: ""),
buttonSecondTitle: String? = NSLocalizedString("Cancel", comment: ""),
firstAction: (() -> Void)? = nil, secondAction: (() -> Void)? = nil) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: buttonFirstTitle, style: .default) { _ in
firstAction?()
})
alert.addAction(UIAlertAction(title: buttonSecondTitle, style: .default) { _ in
secondAction?()
})
present(alert, animated: true, completion: nil)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,8 @@
import Foundation

protocol RandomCredentialsGenerator {

var firstname: String { get }

var lastname: String { get }

var email: String { get }

var password: String { get }

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ struct RandomCredentialsGeneratorImplementation {
}

extension RandomCredentialsGeneratorImplementation: RandomCredentialsGenerator {

var firstname: String {
return StringHelper.generateRandomString(of: 6)
}
Expand All @@ -28,5 +27,4 @@ extension RandomCredentialsGeneratorImplementation: RandomCredentialsGenerator {
var password: String {
return StringHelper.generateRandomString(of: 16)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@ public class AbstractGraph<T: Hashable>: CustomStringConvertible {
fatalError("abstract property accessed")
}

public func addVertex(_ vertex: Vertex<T>) {
fatalError("abstract function called")
}

/// Utility method to create a vertex.
/// - parameter data: Data associated with the vertex.
public func createVertex(data: T) -> Vertex<T> {
/// - parameter id: ID associated with the vertex.
public func createVertex(id: T) -> Vertex<T> {
fatalError("abstract function called")
}

Expand Down
9 changes: 6 additions & 3 deletions ExamEGERussian/Sources/CoreLayer/Graph/Base/Edge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,24 @@

import Foundation

public struct Edge<T: Hashable> {
public class Edge<T: Hashable> {
public var source: Vertex<T>
public var destination: Vertex<T>

init(source: Vertex<T>, destination: Vertex<T>) {
self.source = source
self.destination = destination
}
}

extension Edge: Hashable {

public var hashValue: Int {
return "\(source)\(destination)".hashValue
}

static public func == (lhs: Edge<T>, rhs: Edge<T>) -> Bool {
return lhs.source == rhs.source && lhs.destination == rhs.destination
}

}

extension Edge: CustomStringConvertible {
Expand Down
Loading