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

Show topic lessons #332

Merged
merged 14 commits into from
Jul 30, 2018
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "logo.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
authAPI: AuthAPI(),
stepicsAPI: StepicsAPI(),
profilesAPI: ProfilesAPI(),
coursesAPI: CoursesAPI(),
enrollmentsAPI: EnrollmentsAPI(),
lessonsAPI: LessonsAPI(),
defaultsStorageManager: DefaultsStorageManager(),
randomCredentialsGenerator: RandomCredentialsGeneratorImplementation()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@

import UIKit

// MARK: RootNavigationManager

final class RootNavigationManager {

// MARK: - Instance Variables
// MARK: - Instance Properties

private unowned let serviceComponents: ServiceComponents
private weak var navigationController: UINavigationController?
private let knowledgeGraph = KnowledgeGraph()

// MARK: Init

Expand All @@ -28,14 +28,44 @@ final class RootNavigationManager {
let controller = TopicsTableViewController()
controller.presenter = TopicsPresenterImpl(
view: controller,
model: KnowledgeGraph(),
knowledgeGraph: knowledgeGraph,
router: self,
userRegistrationService: serviceComponents.userRegistrationService,
graphService: serviceComponents.graphService
)
let navigationController = UINavigationController(rootViewController: controller)
navigationController = UINavigationController(rootViewController: controller)

window.rootViewController = navigationController
window.makeKeyAndVisible()
}

}

// MARK: - RootNavigationManager: TopicsRouter -

extension RootNavigationManager: TopicsRouter {
func showLessonsForTopicWithId(_ id: String) {
let controller = LessonsTableViewController()
let presenter = LessonsPresenterImpl(
view: controller,
router: self,
topicId: id,
knowledgeGraph: knowledgeGraph,
lessonsService: serviceComponents.lessonsService,
courseService: serviceComponents.courseService,
enrollmentService: serviceComponents.enrollmentService
)
controller.presenter = presenter
controller.title = knowledgeGraph[id]?.key.title

navigationController?.pushViewController(controller, animated: true)
}
}

// MARK: - RootNavigationManager: LessonsRouter -

extension RootNavigationManager: LessonsRouter {
func showStepsForLessonWith(_ id: Int) {
print("\(#function) \(id)")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ import Foundation
protocol ServiceComponents: class {
var userRegistrationService: UserRegistrationService { get }
var graphService: GraphService { get }
var lessonsService: LessonsService { get }
var courseService: CourseService { get }
var enrollmentService: EnrollmentService { get }
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,27 @@ final class ServiceComponentsAssembly: ServiceComponents {
private let authAPI: AuthAPI
private let stepicsAPI: StepicsAPI
private let profilesAPI: ProfilesAPI
private let coursesAPI: CoursesAPI
private let enrollmentsAPI: EnrollmentsAPI
private let lessonsAPI: LessonsAPI
private let defaultsStorageManager: DefaultsStorageManager
private let randomCredentialsGenerator: RandomCredentialsGenerator

init(authAPI: AuthAPI,
stepicsAPI: StepicsAPI,
profilesAPI: ProfilesAPI,
coursesAPI: CoursesAPI,
enrollmentsAPI: EnrollmentsAPI,
lessonsAPI: LessonsAPI,
defaultsStorageManager: DefaultsStorageManager,
randomCredentialsGenerator: RandomCredentialsGenerator
) {
self.authAPI = authAPI
self.stepicsAPI = stepicsAPI
self.profilesAPI = profilesAPI
self.coursesAPI = coursesAPI
self.enrollmentsAPI = enrollmentsAPI
self.lessonsAPI = lessonsAPI
self.defaultsStorageManager = defaultsStorageManager
self.randomCredentialsGenerator = randomCredentialsGenerator
}
Expand All @@ -41,4 +50,16 @@ final class ServiceComponentsAssembly: ServiceComponents {
var graphService: GraphService {
return GraphServiceImpl()
}

var lessonsService: LessonsService {
return LessonsServiceImpl(lessonsAPI: lessonsAPI)
}

var courseService: CourseService {
return CourseServiceImpl(coursesAPI: coursesAPI)
}

var enrollmentService: EnrollmentService {
return EnrollmentServiceImpl(enrollmentsAPI: enrollmentsAPI)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// CourseService.swift
// ExamEGERussian
//
// Created by Ivan Magda on 26/07/2018.
// Copyright © 2018 Alex Karpov. All rights reserved.
//

import Foundation
import PromiseKit

protocol CourseService: class {
/// Method is used to fetch Course objects from Stepik API
///
/// - Parameter ids: Ids Course objects
/// - Returns: Promise with a result of an array of Course objects from API.
func fetchCourses(with ids: [Int]) -> Promise<[Course]>
/// Method is used to obtain Course objects from cache with ids
///
/// - Parameter ids: Ids Course objects
/// - Returns: Promise with a result of an array of Course objects from cache.
func obtainCourses(with ids: [Int]) -> Promise<[Course]>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// CourseServiceImpl.swift
// ExamEGERussian
//
// Created by Ivan Magda on 26/07/2018.
// Copyright © 2018 Alex Karpov. All rights reserved.
//

import Foundation
import PromiseKit

final class CourseServiceImpl: CourseService {
private let coursesAPI: CoursesAPI

init(coursesAPI: CoursesAPI) {
self.coursesAPI = coursesAPI
}

func fetchCourses(with ids: [Int]) -> Promise<[Course]> {
return obtainCourses(with: ids).then { courses in
self.coursesAPI.retrieve(ids: ids, existing: courses)
}
}

func obtainCourses(with ids: [Int]) -> Promise<[Course]> {
return Course.fetchAsync(ids)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// EnrollmentService.swift
// ExamEGERussian
//
// Created by Ivan Magda on 26/07/2018.
// Copyright © 2018 Alex Karpov. All rights reserved.
//

import Foundation
import PromiseKit

protocol EnrollmentService: class {
/// Method is used to joining Course object with specified id.
///
/// - Parameter course: Course to join.
/// - Returns: Promise when fullfilled.
func joinCourse(_ course: Course) -> Promise<Course>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// EnrollmentServiceImpl.swift
// ExamEGERussian
//
// Created by Ivan Magda on 26/07/2018.
// Copyright © 2018 Alex Karpov. All rights reserved.
//

import Foundation
import PromiseKit

final class EnrollmentServiceImpl: EnrollmentService {
private let enrollmentsAPI: EnrollmentsAPI

init(enrollmentsAPI: EnrollmentsAPI) {
self.enrollmentsAPI = enrollmentsAPI
}

func joinCourse(_ course: Course) -> Promise<Course> {
return enrollmentsAPI.joinCourse(course).then { _ -> Promise<Course> in
.value(course)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@ import Foundation
import PromiseKit

protocol GraphService: class {
func obtainGraph() -> Promise<KnowledgeGraphPlainObject>
/// Method is used to fetch KnowledgeGraphPlainObject object from API.
///
/// - Returns: Promise with a result of KnowledgeGraphPlainObject.
func fetchGraph() -> Promise<KnowledgeGraphPlainObject>
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ 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> {
func fetchGraph() -> Promise<KnowledgeGraphPlainObject> {
return Alamofire
.request(GraphServiceImpl.url)
.responseDecodable(KnowledgeGraphPlainObject.self)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// LessonsService.swift
// ExamEGERussian
//
// Created by Ivan Magda on 24/07/2018.
// Copyright © 2018 Alex Karpov. All rights reserved.
//

import Foundation
import PromiseKit

protocol LessonsService: class {
/// Method is used to fetch lessons from Stepik API.
///
/// - Parameter ids: Lessons ids.
/// - Returns: Promise with an array of LessonPlainObjects.
func fetchLessons(with ids: [Int]) -> Promise<[LessonPlainObject]>
/// Method is used to obtain lessons from cache.
///
/// - Parameter ids: Lessons ids.
/// - Returns: Promise with an array of cached LessonPlainObjects.
func obtainLessons(with ids: [Int]) -> Promise<[LessonPlainObject]>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// LessonsServiceImpl.swift
// ExamEGERussian
//
// Created by Ivan Magda on 24/07/2018.
// Copyright © 2018 Alex Karpov. All rights reserved.
//

import Foundation
import CoreData
import PromiseKit

final class LessonsServiceImpl: LessonsService {
private let lessonsAPI: LessonsAPI

init(lessonsAPI: LessonsAPI) {
self.lessonsAPI = lessonsAPI
}

func fetchLessons(with ids: [Int]) -> Promise<[LessonPlainObject]> {
return executeFetchRequest(ids: ids)
.then { self.lessonsAPI.retrieve(ids: ids, existing: $0) }
.mapValues { self.toPlainObject($0) }
}

func obtainLessons(with ids: [Int]) -> Promise<[LessonPlainObject]> {
return executeFetchRequest(ids: ids).mapValues { self.toPlainObject($0) }
}

// MARK: - Private API

private func executeFetchRequest(ids: [Int]) -> Promise<[Lesson]> {
let request = NSFetchRequest<NSFetchRequestResult>(entityName: String(describing: Lesson.self))
let descriptor = NSSortDescriptor(key: "managedId", ascending: true)

let idPredicates = ids.map { NSPredicate(format: "managedId == %@", $0 as NSNumber) }
let predicate = NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.or, subpredicates: idPredicates)
request.predicate = predicate
request.sortDescriptors = [descriptor]

return Promise<[Lesson]> { seal in
let asyncRequest = NSAsynchronousFetchRequest(fetchRequest: request, completionBlock: { results in
guard let lessons = results.finalResult as? [Lesson] else {
seal.fulfill([])
return
}
seal.fulfill(lessons)
})
_ = try? CoreDataHelper.instance.context.execute(asyncRequest)
}
}

private func toPlainObject(_ lesson: Lesson) -> LessonPlainObject {
return LessonPlainObject(id: lesson.id, steps: lesson.steps.map { $0.id }, title: lesson.title)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import Foundation

struct GoalPlainObject: Codable {
struct KnowledgeGraphGoalPlainObject: Codable {
let title: String
let id: String
let requiredTopics: [String]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import Foundation

struct LessonPlainObject: Codable {
struct KnowledgeGraphLessonPlainObject: Codable {
let id: Int
let type: String
let course: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,26 @@
import Foundation

struct KnowledgeGraphPlainObject: Codable {
let goals: [GoalPlainObject]
let topics: [TopicPlainObject]
let topicsMap: [TopicsMapPlainObject]
let goals: [KnowledgeGraphGoalPlainObject]
let topics: [KnowledgeGraphTopicPlainObject]
let topicsMap: [KnowledgeGraphTopicsMapPlainObject]

enum CodingKeys: String, CodingKey {
case goals
case topics
case topicsMap = "topics-map"
}

init(goals: [GoalPlainObject], topics: [TopicPlainObject], topicsMap: [TopicsMapPlainObject]) {
init(goals: [KnowledgeGraphGoalPlainObject], topics: [KnowledgeGraphTopicPlainObject], topicsMap: [KnowledgeGraphTopicsMapPlainObject]) {
self.goals = goals
self.topics = topics
self.topicsMap = topicsMap
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
goals = try container.decode([GoalPlainObject].self, forKey: .goals)
topics = try container.decode([TopicPlainObject].self, forKey: .topics)
topicsMap = try container.decode([TopicsMapPlainObject].self, forKey: .topicsMap)
goals = try container.decode([KnowledgeGraphGoalPlainObject].self, forKey: .goals)
topics = try container.decode([KnowledgeGraphTopicPlainObject].self, forKey: .topics)
topicsMap = try container.decode([KnowledgeGraphTopicsMapPlainObject].self, forKey: .topicsMap)
}
}
Loading