diff --git a/Stepic.xcodeproj/project.pbxproj b/Stepic.xcodeproj/project.pbxproj
index e5b39bfa01..08d517d03f 100644
--- a/Stepic.xcodeproj/project.pbxproj
+++ b/Stepic.xcodeproj/project.pbxproj
@@ -964,6 +964,7 @@
 		2C0AF18C203C67EC000EA3B6 /* MigrationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C0AF18B203C67EC000EA3B6 /* MigrationExtensions.swift */; };
 		2C1000802029BA7F00E55597 /* BaseCardsStepsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C5D51582024653B00B9D932 /* BaseCardsStepsViewController.swift */; };
 		2C104B682069064D0026FEB9 /* autocomplete_suggestions.plist in Resources */ = {isa = PBXBuildFile; fileRef = 2C104B672069064D0026FEB9 /* autocomplete_suggestions.plist */; };
+		2C10C477221AB8CC00FA3E13 /* UnitNavigationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C10C476221AB8CC00FA3E13 /* UnitNavigationService.swift */; };
 		2C1219901F9655AB00A43E98 /* NotificationsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C12198F1F9655AB00A43E98 /* NotificationsSection.swift */; };
 		2C130E892125A3F50022E998 /* AssemblyFactoryMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C130E882125A3F50022E998 /* AssemblyFactoryMock.swift */; };
 		2C130E8B2125A4410022E998 /* ApplicationAssemblyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C130E8A2125A4410022E998 /* ApplicationAssemblyMock.swift */; };
@@ -2630,9 +2631,9 @@
 		2C8A0F2220FC7CD80009F67E /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 08A1257B1BDEBCC90066B2B2 /* Localizable.strings */; };
 		2C8A0F3320FCCE230009F67E /* VKSocialSDKProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088E58C31DE34ED20009B9CE /* VKSocialSDKProvider.swift */; };
 		2C8A0F3620FCCE650009F67E /* SocialSDKProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088E58C01DE34E2F0009B9CE /* SocialSDKProvider.swift */; };
-		2C8AD7662208646100C9C089 /* DownloadsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0889FEAE1BFB771200C6417E /* DownloadsViewController.swift */; };
 		2C8AD758220311F000C9C089 /* DataBackUpdateService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8AD757220311F000C9C089 /* DataBackUpdateService.swift */; };
 		2C8AD7622204932300C9C089 /* CourseSubscriptionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8AD7612204932200C9C089 /* CourseSubscriptionManager.swift */; };
+		2C8AD7662208646100C9C089 /* DownloadsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0889FEAE1BFB771200C6417E /* DownloadsViewController.swift */; };
 		2C8C96EA211DE34700A9FB99 /* RateAppViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 089056101E98021000B8FE6A /* RateAppViewController.xib */; };
 		2C8CB0E31FB48F39008CB1AC /* EnrollmentsAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8CB0E21FB48F39008CB1AC /* EnrollmentsAPI.swift */; };
 		2C905230212E9C9300354DFB /* CoursePlainObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C90522F212E9C9300354DFB /* CoursePlainObjectTests.swift */; };
@@ -5570,6 +5571,7 @@
 		2C09313621D00B8D002B605D /* VideoDownloadingService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoDownloadingService.swift; sourceTree = "<group>"; };
 		2C0AF18B203C67EC000EA3B6 /* MigrationExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrationExtensions.swift; sourceTree = "<group>"; };
 		2C104B672069064D0026FEB9 /* autocomplete_suggestions.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = autocomplete_suggestions.plist; sourceTree = "<group>"; };
+		2C10C476221AB8CC00FA3E13 /* UnitNavigationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitNavigationService.swift; sourceTree = "<group>"; };
 		2C12198F1F9655AB00A43E98 /* NotificationsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsSection.swift; sourceTree = "<group>"; };
 		2C130E882125A3F50022E998 /* AssemblyFactoryMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssemblyFactoryMock.swift; sourceTree = "<group>"; };
 		2C130E8A2125A4410022E998 /* ApplicationAssemblyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationAssemblyMock.swift; sourceTree = "<group>"; };
@@ -9914,6 +9916,7 @@
 				089877B121404CF00065DFA2 /* StringStorageServiceProtocol.swift */,
 				2C23C12621AD8907001DCF89 /* NextLessonService.swift */,
 				2C8AD757220311F000C9C089 /* DataBackUpdateService.swift */,
+				2C10C476221AB8CC00FA3E13 /* UnitNavigationService.swift */,
 			);
 			path = Services;
 			sourceTree = "<group>";
@@ -15459,6 +15462,7 @@
 				73DB05040F3345F6568DCCBB /* FullscreenCourseListView.swift in Sources */,
 				62E983594B255BB6BE92AD8A /* UniqueIdentifierType.swift in Sources */,
 				62E9850F9C459CC122A416F3 /* ContentLanguageSwitchAvailabilityService.swift in Sources */,
+				2C10C477221AB8CC00FA3E13 /* UnitNavigationService.swift in Sources */,
 				62E9870883D5C6A1DCB15E4B /* TagsOutputProtocol.swift in Sources */,
 				62E9893ECE0FA9E7AEC2B6DD /* CourseListCollectionOutputProtocol.swift in Sources */,
 				62E98236342BF7C367FB6F36 /* Array+reordering.swift in Sources */,
diff --git a/Stepic/LessonPresenter.swift b/Stepic/LessonPresenter.swift
index 1b42ccaa46..88ac498136 100644
--- a/Stepic/LessonPresenter.swift
+++ b/Stepic/LessonPresenter.swift
@@ -8,6 +8,7 @@
 
 import Foundation
 import UIKit
+import SVProgressHUD
 
 enum LessonViewState {
     case displayingSteps, placeholder, refreshing
@@ -28,6 +29,8 @@ class LessonPresenter {
     static let stepUpdatedNotification = "StepUpdatedNotification"
     fileprivate var tabViewsForStepId = [Int: UIView]()
 
+    private var controllerForIndex: [Int: UIViewController] = [:]
+
     fileprivate var lesson: Lesson?
     fileprivate var startStepId: Int = 0
     fileprivate var stepId: Int?
@@ -37,9 +40,6 @@ class LessonPresenter {
     var stepsAPI: StepsAPI
     var lessonsAPI: LessonsAPI
 
-    var shouldNavigateToPrev: Bool = false
-    var shouldNavigateToNext: Bool = false
-
     fileprivate var didInitSteps: Bool = false
     fileprivate var didSelectTab: Bool = false
 
@@ -55,6 +55,24 @@ class LessonPresenter {
         return service
     }()
 
+    private lazy var unitNavigationService: UnitNavigationServiceProtocol = {
+        let service = UnitNavigationService(
+            sectionsPersistenceService: SectionsPersistenceService(),
+            sectionsNetworkService: SectionsNetworkService(sectionsAPI: SectionsAPI()),
+            unitsPersistenceService: UnitsPersistenceService(),
+            unitsNetworkService: UnitsNetworkService(unitsAPI: UnitsAPI()),
+            coursesPersistenceService: CoursesPersistenceService(),
+            coursesNetworkService: CoursesNetworkService(coursesAPI: CoursesAPI())
+        )
+        return service
+    }()
+
+    private var didNextUnitLoad = false
+    private var nextUnit: Unit?
+
+    private var didPreviousUnitLoad = false
+    private var previousUnit: Unit?
+
     init(objects: LessonInitObjects?, ids: LessonInitIds?, stepsAPI: StepsAPI, lessonsAPI: LessonsAPI) {
         if let objects = objects {
             self.lesson = objects.lesson
@@ -76,6 +94,56 @@ class LessonPresenter {
             name: .stepDone,
             object: nil
         )
+
+        if let unitID = self.unitId {
+            DispatchQueue.global().async { [weak self] in
+                guard let strongSelf = self else {
+                    return
+                }
+
+                strongSelf.unitNavigationService.findUnitForNavigation(
+                    from: unitID,
+                    direction: .next
+                ).done { unit in
+                    print("next unit loaded, unit = \(unit?.id)")
+                    guard let unit = unit else {
+                        return
+                    }
+
+                    if let stepsCount = strongSelf.lesson?.stepsArray.count {
+                        (strongSelf.controllerForIndex[stepsCount - 1] as? VideoStepViewController)?.nextLessonHandler = {
+                            strongSelf.navigateToNextOrPreviousUnit(direction: .next)
+                        }
+                        (strongSelf.controllerForIndex[stepsCount - 1] as? WebStepViewController)?.nextLessonHandler = {
+                            strongSelf.navigateToNextOrPreviousUnit(direction: .next)
+                        }
+                    }
+
+                    strongSelf.didNextUnitLoad = true
+                    strongSelf.nextUnit = unit
+                }.cauterize()
+
+                strongSelf.unitNavigationService.findUnitForNavigation(
+                    from: unitID,
+                    direction: .previous
+                ).done { unit in
+                    print("previous unit loaded, unit = \(unit?.id)")
+                    guard let unit = unit else {
+                        return
+                    }
+
+                    (strongSelf.controllerForIndex[0] as? VideoStepViewController)?.prevLessonHandler = {
+                        strongSelf.navigateToNextOrPreviousUnit(direction: .previous)
+                    }
+                    (strongSelf.controllerForIndex[0] as? WebStepViewController)?.prevLessonHandler = {
+                        strongSelf.navigateToNextOrPreviousUnit(direction: .previous)
+                    }
+
+                    strongSelf.didPreviousUnitLoad = true
+                    strongSelf.previousUnit = unit
+                }.cauterize()
+            }
+        }
     }
 
     deinit {
@@ -165,14 +233,6 @@ class LessonPresenter {
             return
         }
 
-        if let section = lesson?.unit?.section,
-            let unitId = unitId {
-            if let index = section.unitsArray.index(of: unitId) {
-                shouldNavigateToPrev = shouldNavigateToPrev || (index != 0)
-                shouldNavigateToNext = shouldNavigateToNext || (index < section.unitsArray.count - 1)
-            }
-        }
-
         view?.updateTitle(title: lesson?.title ?? NSLocalizedString("Lesson", comment: ""))
 
         if let stepId = stepId {
@@ -298,26 +358,25 @@ class LessonPresenter {
                 self?.canSendViews ?? false
             }
 
-            if context == .unit {
-                if index == 0 && shouldNavigateToPrev {
-                    stepController.prevLessonHandler = {
-                        [weak self] in
-                        if let unitID = self?.unitId {
-                            self?.sectionNavigationDelegate?.didRequestPreviousUnitPresentationForLessonInUnit(unitID: unitID)
-                        }
+            if context == .unit && index == 0 && didPreviousUnitLoad {
+                stepController.prevLessonHandler = { [weak self] in
+                    guard let strongSelf = self else {
+                        return
                     }
+                    strongSelf.navigateToNextOrPreviousUnit(direction: .previous)
                 }
+            }
 
-                if index == lesson.steps.count - 1 && shouldNavigateToNext {
-                    stepController.nextLessonHandler = {
-                        [weak self] in
-                        if let unitID = self?.unitId {
-                            self?.sectionNavigationDelegate?.didRequestNextUnitPresentationForLessonInUnit(unitID: unitID)
-                        }
+            if context == .unit && index == lesson.stepsArray.count - 1 && didNextUnitLoad {
+                stepController.nextLessonHandler = { [weak self] in
+                    guard let strongSelf = self else {
+                        return
                     }
+                    strongSelf.navigateToNextOrPreviousUnit(direction: .next)
                 }
             }
 
+            controllerForIndex[index] = stepController
             return stepController
         } else {
             let stepController = ControllerHelper.instantiateViewController(identifier: "WebStepViewController") as! WebStepViewController
@@ -345,30 +404,83 @@ class LessonPresenter {
                 self?.canSendViews ?? false
             }
             stepController.lessonSlug = lesson.slug
-            if context == .unit {
-                if index == 0 && shouldNavigateToPrev {
-                    stepController.prevLessonHandler = {
-                        [weak self] in
-                        if let unitID = self?.unitId {
-                            self?.sectionNavigationDelegate?.didRequestPreviousUnitPresentationForLessonInUnit(unitID: unitID)
-                        }
+
+            if context == .unit && index == 0 && didPreviousUnitLoad {
+                stepController.prevLessonHandler = { [weak self] in
+                    guard let strongSelf = self else {
+                        return
                     }
+                    strongSelf.navigateToNextOrPreviousUnit(direction: .previous)
                 }
+            }
 
-                if index == lesson.steps.count - 1 && shouldNavigateToNext {
-                    stepController.nextLessonHandler = {
-                        [weak self] in
-                        if let unitID = self?.unitId {
-                            self?.sectionNavigationDelegate?.didRequestNextUnitPresentationForLessonInUnit(unitID: unitID)
-                        }
+            if context == .unit && index == lesson.stepsArray.count - 1 && didNextUnitLoad {
+                stepController.nextLessonHandler = { [weak self] in
+                    guard let strongSelf = self else {
+                        return
                     }
+                    strongSelf.navigateToNextOrPreviousUnit(direction: .next)
                 }
             }
 
+            controllerForIndex[index] = stepController
             return stepController
         }
     }
 
+    private func navigateToNextOrPreviousUnit(direction: UnitNavigationDirection) {
+        guard let targetUnit = direction == .next ? self.nextUnit : self.previousUnit else {
+            return
+        }
+
+        SVProgressHUD.show()
+        let cachedLesson = Lesson.fetch([targetUnit.lessonId]).first
+        if let lesson = cachedLesson {
+            self.replaceByNewLesson(
+                lesson: lesson,
+                unit: targetUnit,
+                stepArrayFunction: { direction == .next ? $0.first : $0.last }
+            )
+        } else {
+            self.lessonsAPI.retrieve(ids: [targetUnit.lessonId]).done { lessons in
+                guard let lesson = lessons.first else {
+                    SVProgressHUD.showError(withStatus: nil)
+                    return
+                }
+
+                self.replaceByNewLesson(
+                    lesson: lesson,
+                    unit: targetUnit,
+                    stepArrayFunction: { direction == .next ? $0.first : $0.last }
+                )
+                SVProgressHUD.dismiss()
+            }.catch { _ in
+                print("error while fetching lesson for next/prev unit")
+                SVProgressHUD.showError(withStatus: nil)
+            }
+        }
+    }
+
+    private func replaceByNewLesson(lesson: Lesson, unit: Unit, stepArrayFunction: ([Int]) -> Int?) {
+        guard let viewControllers = self.view?.nController?.viewControllers,
+              let presentingViewController = self.view?.nController?.viewControllers[safe: viewControllers.count - 2] else {
+            return
+        }
+
+        guard let stepID = stepArrayFunction(lesson.stepsArray) else {
+            SVProgressHUD.showError(withStatus: nil)
+            return
+        }
+
+        let newLessonController = LessonLegacyAssembly(
+            initObjects: nil,
+            initIDs: (stepId: stepID, unitId: unit.id)
+        ).makeModule()
+
+        SVProgressHUD.dismiss()
+        presentingViewController.replace(by: newLessonController)
+    }
+
     func tabView(index: Int) -> UIView {
         guard lesson != nil else {
             return UIView()
diff --git a/Stepic/LessonViewController.swift b/Stepic/LessonViewController.swift
index 3058f659a2..10009b1159 100644
--- a/Stepic/LessonViewController.swift
+++ b/Stepic/LessonViewController.swift
@@ -13,19 +13,13 @@ import SnapKit
 final class LessonLegacyAssembly: Assembly {
     private let initObjects: LessonInitObjects?
     private let initIDs: LessonInitIds?
-    private let navigationRules: LessonNavigationRules
-    private let navigationDelegate: SectionNavigationDelegate
 
     init(
         initObjects: LessonInitObjects?,
-        initIDs: LessonInitIds?,
-        navigationRules: LessonNavigationRules,
-        navigationDelegate: SectionNavigationDelegate
+        initIDs: LessonInitIds?
     ) {
         self.initObjects = initObjects
         self.initIDs = initIDs
-        self.navigationRules = navigationRules
-        self.navigationDelegate = navigationDelegate
     }
 
     func makeModule() -> UIViewController {
@@ -37,9 +31,6 @@ final class LessonLegacyAssembly: Assembly {
         lessonVC.initObjects = self.initObjects
         lessonVC.initIds = self.initIDs
 
-        lessonVC.navigationRules = self.navigationRules
-        lessonVC.sectionNavigationDelegate = self.navigationDelegate
-
         return lessonVC
     }
 }
@@ -50,10 +41,6 @@ class LessonViewController: PagerController, ShareableController, LessonView {
 
     var parentShareBlock: ((UIActivityViewController) -> Void)?
 
-    weak var sectionNavigationDelegate: SectionNavigationDelegate?
-
-    var navigationRules : LessonNavigationRules?
-
     fileprivate var presenter: LessonPresenter?
 
     lazy var activityView: UIView = self.initActivityView()
@@ -145,11 +132,6 @@ class LessonViewController: PagerController, ShareableController, LessonView {
 
         presenter = LessonPresenter(objects: initObjects, ids: initIds, stepsAPI: ApiDataDownloader.steps, lessonsAPI: ApiDataDownloader.lessons)
         presenter?.view = self
-        presenter?.sectionNavigationDelegate = sectionNavigationDelegate
-        if let rules = navigationRules {
-            presenter?.shouldNavigateToPrev = rules.prev
-            presenter?.shouldNavigateToNext = rules.next
-        }
         presenter?.refreshSteps()
     }
 
diff --git a/Stepic/Modules/CourseInfo/CourseInfoDataFlow.swift b/Stepic/Modules/CourseInfo/CourseInfoDataFlow.swift
index 2bcb1c49ab..624fde1d1f 100644
--- a/Stepic/Modules/CourseInfo/CourseInfoDataFlow.swift
+++ b/Stepic/Modules/CourseInfo/CourseInfoDataFlow.swift
@@ -48,16 +48,12 @@ enum CourseInfo {
         struct Response {
             let lesson: Lesson
             let unitID: Unit.IdType
-            let navigationRules: LessonNavigationRules
-            let navigationDelegate: SectionNavigationDelegate
         }
 
         @available(*, deprecated, message: "Old ugly Lesson controller initialization")
         struct ViewModel {
             let initObjects: LessonInitObjects
             let initIDs: LessonInitIds
-            let navigationRules: LessonNavigationRules
-            let navigationDelegate: SectionNavigationDelegate
         }
     }
 
diff --git a/Stepic/Modules/CourseInfo/CourseInfoInteractor.swift b/Stepic/Modules/CourseInfo/CourseInfoInteractor.swift
index c886f5ea05..d51ba24745 100644
--- a/Stepic/Modules/CourseInfo/CourseInfoInteractor.swift
+++ b/Stepic/Modules/CourseInfo/CourseInfoInteractor.swift
@@ -261,23 +261,12 @@ final class CourseInfoInteractor: CourseInfoInteractorProtocol {
 }
 
 extension CourseInfoInteractor: CourseInfoTabSyllabusOutputProtocol {
-    func presentLesson(
-        in unit: Unit,
-        navigationDelegate: SectionNavigationDelegate,
-        navigationRules: LessonNavigationRules
-    ) {
+    func presentLesson(in unit: Unit) {
         guard let lesson = unit.lesson else {
             return
         }
 
-        self.presenter.presentLesson(
-            response: .init(
-                lesson: lesson,
-                unitID: unit.id,
-                navigationRules: navigationRules,
-                navigationDelegate: navigationDelegate
-            )
-        )
+        self.presenter.presentLesson(response: .init(lesson: lesson, unitID: unit.id))
     }
 
     func presentPersonalDeadlinesCreation(for course: Course) {
diff --git a/Stepic/Modules/CourseInfo/CourseInfoPresenter.swift b/Stepic/Modules/CourseInfo/CourseInfoPresenter.swift
index 280866f5f9..9d99b99645 100644
--- a/Stepic/Modules/CourseInfo/CourseInfoPresenter.swift
+++ b/Stepic/Modules/CourseInfo/CourseInfoPresenter.swift
@@ -50,9 +50,7 @@ final class CourseInfoPresenter: CourseInfoPresenterProtocol {
 
         let viewModel = CourseInfo.ShowLesson.ViewModel(
             initObjects: initObjects,
-            initIDs: initIDs,
-            navigationRules: response.navigationRules,
-            navigationDelegate: response.navigationDelegate
+            initIDs: initIDs
         )
 
         self.viewController?.displayLesson(viewModel: viewModel)
diff --git a/Stepic/Modules/CourseInfo/CourseInfoViewController.swift b/Stepic/Modules/CourseInfo/CourseInfoViewController.swift
index 3fb43e816e..2fc3ae4742 100644
--- a/Stepic/Modules/CourseInfo/CourseInfoViewController.swift
+++ b/Stepic/Modules/CourseInfo/CourseInfoViewController.swift
@@ -351,18 +351,10 @@ extension CourseInfoViewController: CourseInfoViewControllerProtocol {
     func displayLesson(viewModel: CourseInfo.ShowLesson.ViewModel) {
         let assembly = LessonLegacyAssembly(
             initObjects: viewModel.initObjects,
-            initIDs: viewModel.initIDs,
-            navigationRules: viewModel.navigationRules,
-            navigationDelegate: viewModel.navigationDelegate
+            initIDs: viewModel.initIDs
         )
 
-        // If already present lesson then replace top controller
-        let shouldReplaceLesson = self.navigationController?.topViewController !== self
-        if shouldReplaceLesson {
-            self.replace(by: assembly.makeModule())
-        } else {
-            self.push(module: assembly.makeModule())
-        }
+        self.push(module: assembly.makeModule())
     }
 
     func displayPersonalDeadlinesSettings(viewModel: CourseInfo.PersonalDeadlinesSettings.ViewModel) {
diff --git a/Stepic/Modules/CourseInfoTabInfo/Provider/SectionsNetworkService.swift b/Stepic/Modules/CourseInfoTabInfo/Provider/SectionsNetworkService.swift
index 1f0e543dfa..de036a7994 100644
--- a/Stepic/Modules/CourseInfoTabInfo/Provider/SectionsNetworkService.swift
+++ b/Stepic/Modules/CourseInfoTabInfo/Provider/SectionsNetworkService.swift
@@ -11,6 +11,7 @@ import PromiseKit
 
 protocol SectionsNetworkServiceProtocol: class {
     func fetch(ids: [Section.IdType]) -> Promise<[Section]>
+    func fetch(id: Section.IdType) -> Promise<Section?>
 }
 
 final class SectionsNetworkService: SectionsNetworkServiceProtocol {
@@ -31,6 +32,12 @@ final class SectionsNetworkService: SectionsNetworkServiceProtocol {
         }
     }
 
+    func fetch(id: Section.IdType) -> Promise<Section?> {
+        return self.fetch(ids: [id]).then { result -> Promise<Section?> in
+            Promise.value(result.first)
+        }
+    }
+
     enum Error: Swift.Error {
         case fetchFailed
     }
diff --git a/Stepic/Modules/CourseInfoTabInfo/Provider/SectionsPersistenceService.swift b/Stepic/Modules/CourseInfoTabInfo/Provider/SectionsPersistenceService.swift
index 791be22c76..39c34fae95 100644
--- a/Stepic/Modules/CourseInfoTabInfo/Provider/SectionsPersistenceService.swift
+++ b/Stepic/Modules/CourseInfoTabInfo/Provider/SectionsPersistenceService.swift
@@ -11,6 +11,7 @@ import PromiseKit
 
 protocol SectionsPersistenceServiceProtocol: class {
     func fetch(ids: [Section.IdType]) -> Promise<[Section]>
+    func fetch(id: Section.IdType) -> Promise<Section?>
 }
 
 final class SectionsPersistenceService: SectionsPersistenceServiceProtocol {
@@ -25,6 +26,16 @@ final class SectionsPersistenceService: SectionsPersistenceServiceProtocol {
         }
     }
 
+    func fetch(id: Section.IdType) -> Promise<Section?> {
+        return Promise { seal in
+            Section.fetchAsync(ids: [id]).done { sections in
+                seal.fulfill(sections.first)
+            }.catch { _ in
+                seal.reject(Error.fetchFailed)
+            }
+        }
+    }
+
     enum Error: Swift.Error {
         case fetchFailed
     }
diff --git a/Stepic/Modules/CourseInfoTabInfo/Provider/UnitsNetworkService.swift b/Stepic/Modules/CourseInfoTabInfo/Provider/UnitsNetworkService.swift
index 1ffcaedd63..5cea29aed3 100644
--- a/Stepic/Modules/CourseInfoTabInfo/Provider/UnitsNetworkService.swift
+++ b/Stepic/Modules/CourseInfoTabInfo/Provider/UnitsNetworkService.swift
@@ -11,6 +11,7 @@ import PromiseKit
 
 protocol UnitsNetworkServiceProtocol: class {
     func fetch(ids: [Unit.IdType]) -> Promise<[Unit]>
+    func fetch(id: Unit.IdType) -> Promise<Unit?>
 }
 
 final class UnitsNetworkService: UnitsNetworkServiceProtocol {
@@ -31,6 +32,12 @@ final class UnitsNetworkService: UnitsNetworkServiceProtocol {
         }
     }
 
+    func fetch(id: Unit.IdType) -> Promise<Unit?> {
+        return self.fetch(ids: [id]).then { result -> Promise<Unit?> in
+            Promise.value(result.first)
+        }
+    }
+
     enum Error: Swift.Error {
         case fetchFailed
     }
diff --git a/Stepic/Modules/CourseInfoTabInfo/Provider/UnitsPersistenceService.swift b/Stepic/Modules/CourseInfoTabInfo/Provider/UnitsPersistenceService.swift
index 1456d5f883..bd8b61e237 100644
--- a/Stepic/Modules/CourseInfoTabInfo/Provider/UnitsPersistenceService.swift
+++ b/Stepic/Modules/CourseInfoTabInfo/Provider/UnitsPersistenceService.swift
@@ -10,7 +10,8 @@ import Foundation
 import PromiseKit
 
 protocol UnitsPersistenceServiceProtocol: class {
-    func fetch(ids: [Unit.IdType])-> Promise<[Unit]>
+    func fetch(ids: [Unit.IdType]) -> Promise<[Unit]>
+    func fetch(id: Unit.IdType) -> Promise<Unit?>
 }
 
 final class UnitsPersistenceService: UnitsPersistenceServiceProtocol {
@@ -25,6 +26,16 @@ final class UnitsPersistenceService: UnitsPersistenceServiceProtocol {
         }
     }
 
+    func fetch(id: Unit.IdType) -> Promise<Unit?> {
+        return Promise { seal in
+            Unit.fetchAsync(ids: [id]).done { units in
+                seal.fulfill(units.first)
+            }.catch { _ in
+                seal.reject(Error.fetchFailed)
+            }
+        }
+    }
+
     enum Error: Swift.Error {
         case fetchFailed
     }
diff --git a/Stepic/Modules/CourseInfoTabSyllabus/CourseInfoTabSyllabusInteractor.swift b/Stepic/Modules/CourseInfoTabSyllabus/CourseInfoTabSyllabusInteractor.swift
index 622b8ec68c..f32b2c4e4b 100644
--- a/Stepic/Modules/CourseInfoTabSyllabus/CourseInfoTabSyllabusInteractor.swift
+++ b/Stepic/Modules/CourseInfoTabSyllabus/CourseInfoTabSyllabusInteractor.swift
@@ -427,14 +427,7 @@ final class CourseInfoTabSyllabusInteractor: CourseInfoTabSyllabusInteractorProt
             return
         }
 
-        self.moduleOutput?.presentLesson(
-            in: unit,
-            navigationDelegate: self,
-            navigationRules: (
-                prev: self.nextLessonService.findPreviousUnit(for: unit) != nil,
-                next: self.nextLessonService.findNextUnit(for: unit) != nil
-            )
-        )
+        self.moduleOutput?.presentLesson(in: unit)
     }
 
     enum Error: Swift.Error {
diff --git a/Stepic/Modules/CourseInfoTabSyllabus/CourseInfoTabSyllabusOutputProtocol.swift b/Stepic/Modules/CourseInfoTabSyllabus/CourseInfoTabSyllabusOutputProtocol.swift
index 40a0941842..6c6b27d4c4 100644
--- a/Stepic/Modules/CourseInfoTabSyllabus/CourseInfoTabSyllabusOutputProtocol.swift
+++ b/Stepic/Modules/CourseInfoTabSyllabus/CourseInfoTabSyllabusOutputProtocol.swift
@@ -9,11 +9,7 @@
 import Foundation
 
 protocol CourseInfoTabSyllabusOutputProtocol: class {
-    func presentLesson(
-        in unit: Unit,
-        navigationDelegate: SectionNavigationDelegate,
-        navigationRules: LessonNavigationRules
-    )
+    func presentLesson(in unit: Unit)
     func presentExamLesson()
     func presentPersonalDeadlinesCreation(for course: Course)
     func presentPersonalDeadlinesSettings(for course: Course)
diff --git a/Stepic/Services/UnitNavigationService.swift b/Stepic/Services/UnitNavigationService.swift
new file mode 100644
index 0000000000..2129cf4756
--- /dev/null
+++ b/Stepic/Services/UnitNavigationService.swift
@@ -0,0 +1,232 @@
+//
+//  UnitNavigationService.swift
+//  Stepic
+//
+//  Created by Vladislav Kiryukhin on 18/02/2019.
+//  Copyright © 2019 Alex Karpov. All rights reserved.
+//
+
+import Foundation
+import PromiseKit
+
+enum UnitNavigationDirection {
+    case next
+    case previous
+}
+
+protocol UnitNavigationServiceProtocol: class {
+    func findUnitForNavigation(
+        from unit: Unit.IdType,
+        direction: UnitNavigationDirection
+    ) -> Promise<Unit?>
+}
+
+final class UnitNavigationService: UnitNavigationServiceProtocol {
+    private let sectionsPersistenceService: SectionsPersistenceServiceProtocol
+    private let sectionsNetworkService: SectionsNetworkServiceProtocol
+    private let unitsPersistenceService: UnitsPersistenceServiceProtocol
+    private let unitsNetworkService: UnitsNetworkServiceProtocol
+    private let coursesPersistenceService: CoursesPersistenceServiceProtocol
+    private let coursesNetworkService: CoursesNetworkServiceProtocol
+
+    init(
+        sectionsPersistenceService: SectionsPersistenceServiceProtocol,
+        sectionsNetworkService: SectionsNetworkServiceProtocol,
+        unitsPersistenceService: UnitsPersistenceServiceProtocol,
+        unitsNetworkService: UnitsNetworkServiceProtocol,
+        coursesPersistenceService: CoursesPersistenceServiceProtocol,
+        coursesNetworkService: CoursesNetworkServiceProtocol
+    ) {
+        self.sectionsPersistenceService = sectionsPersistenceService
+        self.sectionsNetworkService = sectionsNetworkService
+        self.unitsPersistenceService = unitsPersistenceService
+        self.unitsNetworkService = unitsNetworkService
+        self.coursesPersistenceService = coursesPersistenceService
+        self.coursesNetworkService = coursesNetworkService
+    }
+
+    func findUnitForNavigation(
+        from unit: Unit.IdType,
+        direction: UnitNavigationDirection
+    ) -> Promise<Unit?> {
+        return self.getUnitFromCacheOrNetwork(id: unit).then { unit -> Promise<(Unit?, Section?)> in
+            guard let unit = unit else {
+                return Promise.value((nil, nil))
+            }
+
+            return self.getSectionFromCacheOrNetwork(id: unit.sectionId).map { (unit, $0) }
+        }.then { unit, section -> Promise<Unit?> in
+            guard let section = section, let unit = unit else {
+                return Promise.value(nil)
+            }
+
+            unit.section = section
+            CoreDataHelper.instance.save()
+
+            // Cause unit & section have 1-indexed position in API
+            let unitPosition = unit.position - 1
+            let sectionPosition = section.position - 1
+
+            let shouldLookUpInPreviousSection = unitPosition == 0
+                && direction == .previous
+            let shouldLookUpInNextSection = unitPosition == (section.unitsArray.count - 1)
+                && direction == .next
+            if shouldLookUpInPreviousSection || shouldLookUpInNextSection {
+                return self.findUnitInAnotherSections(
+                    courseID: section.courseId,
+                    sectionPosition: sectionPosition,
+                    direction: direction
+                )
+            } else {
+                return self.findUnitInCurrentSection(
+                    section,
+                    unitPosition: unitPosition,
+                    direction: direction
+                )
+            }
+        }
+    }
+
+    private func findUnitInCurrentSection(
+        _ section: Section,
+        unitPosition: Int,
+        direction: UnitNavigationDirection
+    ) -> Promise<Unit?> {
+        let unitID: Unit.IdType? = {
+            switch direction {
+            case .next:
+                return section.unitsArray[safe: unitPosition + 1]
+            case .previous:
+                return section.unitsArray[safe: unitPosition - 1]
+            }
+        }()
+
+        guard let targetUnitID = unitID else {
+            return Promise.value(nil)
+        }
+
+        return self.getUnitFromCacheOrNetwork(id: targetUnitID)
+    }
+
+    private func findUnitInAnotherSections(
+        courseID: Course.IdType,
+        sectionPosition: Int,
+        direction: UnitNavigationDirection
+    ) -> Promise<Unit?> {
+        return self.getSlicedSections(
+            courseID: courseID,
+            sectionPosition: sectionPosition,
+            direction: direction
+        ).then { sections -> Promise<Section?> in
+            let sections = direction == .previous ? sections.reversed() : sections
+            for section in sections {
+                if section.isReachable,
+                   !section.isExam,
+                   section.unitsArray.count > 0 {
+                    return Promise.value(section)
+                }
+            }
+
+            return Promise.value(nil)
+        }.then { targetSection -> Promise<Unit?> in
+            guard let section = targetSection else {
+                return Promise.value(nil)
+            }
+
+            guard let targetUnitID = direction == .previous
+                ? section.unitsArray.last
+                : section.unitsArray.first else {
+                return Promise.value(nil)
+            }
+
+            // Load all units to make next findUnitInCurrentSection calls faster
+            let allUnitsFromCacheOrNetwork = section.unitsArray.map { unitID in
+                self.getUnitFromCacheOrNetwork(id: unitID)
+            }
+
+            return Promise { seal in
+                when(fulfilled: allUnitsFromCacheOrNetwork).done { units in
+                    let targetUnit = units.compactMap { $0 }
+                        .filter { $0.id == targetUnitID }
+                        .first
+                    seal.fulfill(targetUnit)
+                }.catch { error in
+                    seal.reject(error)
+                }
+            }
+        }
+    }
+
+    /// Return array of sections after or before section with given position
+    private func getSlicedSections(
+        courseID: Course.IdType,
+        sectionPosition: Int,
+        direction: UnitNavigationDirection
+    ) -> Promise<[Section]> {
+        return Promise { seal in
+            self.getCourseFromCacheOrNetwork(id: courseID).then { course -> Promise<[Section]> in
+                guard let course = course else {
+                    throw Error.unknownCourse
+                }
+
+                let sectionIDs: [Section.IdType] = {
+                    switch direction {
+                    case .next:
+                        return sectionPosition == course.sectionsArray.count - 1
+                            ? []
+                            : Array(course.sectionsArray[(sectionPosition + 1)...])
+                    case .previous:
+                        return sectionPosition == 0
+                            ? []
+                            : Array(course.sectionsArray[...(sectionPosition - 1)])
+                    }
+                }()
+
+                return self.sectionsNetworkService.fetch(ids: sectionIDs)
+            }.done { sections in
+                seal.fulfill(sections)
+            }.catch { error in
+                seal.reject(error)
+            }
+        }
+    }
+
+    // MARK: Helpers
+    // Remove after network layer & services refactoring
+
+    private func getUnitFromCacheOrNetwork(id: Unit.IdType) -> Promise<Unit?> {
+        return self.unitsPersistenceService.fetch(id: id).then { unit -> Promise<Unit?> in
+            if let unit = unit {
+                return Promise.value(unit)
+            } else {
+                return self.unitsNetworkService.fetch(id: id)
+            }
+        }
+    }
+
+    private func getCourseFromCacheOrNetwork(id: Course.IdType) -> Promise<Course?> {
+        return self.coursesPersistenceService.fetch(id: id).then { course -> Promise<Course?> in
+            if let course = course {
+                return Promise.value(course)
+            } else {
+                return self.coursesNetworkService.fetch(id: id)
+            }
+        }
+    }
+
+    private func getSectionFromCacheOrNetwork(id: Section.IdType) -> Promise<Section?> {
+        return self.sectionsPersistenceService.fetch(id: id).then { section -> Promise<Section?> in
+            if let section = section {
+                return Promise.value(section)
+            } else {
+                return self.sectionsNetworkService.fetch(id: id)
+            }
+        }
+    }
+
+    // MARK: Enums
+
+    enum Error: Swift.Error {
+        case unknownCourse
+    }
+}
diff --git a/Stepic/VideoStepViewController.swift b/Stepic/VideoStepViewController.swift
index e772fda932..c08ba2e797 100644
--- a/Stepic/VideoStepViewController.swift
+++ b/Stepic/VideoStepViewController.swift
@@ -24,8 +24,16 @@ class VideoStepViewController: UIViewController {
 
     var assignment: Assignment?
 
-    var nextLessonHandler: (() -> Void)?
-    var prevLessonHandler: (() -> Void)?
+    var nextLessonHandler: (() -> Void)? {
+        didSet {
+            refreshNextPrevButtons()
+        }
+    }
+    var prevLessonHandler: (() -> Void)? {
+        didSet {
+            refreshNextPrevButtons()
+        }
+    }
 
     var nController: UINavigationController?
     var nItem: UINavigationItem!
@@ -63,6 +71,7 @@ class VideoStepViewController: UIViewController {
         prevLessonButton.setTitle("  \(NSLocalizedString("PrevLesson", comment: ""))  ", for: UIControlState())
 
         initialize()
+        refreshNextPrevButtons()
         navigationController?.navigationBar.sizeToFit()
     }
 
@@ -96,22 +105,16 @@ class VideoStepViewController: UIViewController {
             discussionCountViewHeight.constant = 0
         }
 
-        if nextLessonHandler == nil {
-            nextLessonButton.isHidden = true
-        } else {
-            nextLessonButton.setStepicWhiteStyle()
-        }
+        nextLessonButton.setStepicWhiteStyle()
+        prevLessonButton.setStepicWhiteStyle()
+    }
 
-        if prevLessonHandler == nil {
-            prevLessonButton.isHidden = true
-        } else {
-            prevLessonButton.setStepicWhiteStyle()
+    func refreshNextPrevButtons() {
+        if nextLessonButton != nil {
+            nextLessonButton.isHidden = nextLessonHandler == nil
         }
-
-        if nextLessonHandler == nil && prevLessonHandler == nil {
-            nextLessonButtonHeight.constant = 0
-            prevLessonButtonHeight.constant = 0
-            prevNextLessonButtonsContainerViewHeight.constant = 0
+        if prevLessonButton != nil {
+            prevLessonButton.isHidden = prevLessonHandler == nil
         }
     }
 
diff --git a/Stepic/WebStepViewController.swift b/Stepic/WebStepViewController.swift
index a572aaabd5..48c820a805 100644
--- a/Stepic/WebStepViewController.swift
+++ b/Stepic/WebStepViewController.swift
@@ -32,8 +32,16 @@ class WebStepViewController: UIViewController {
     @IBOutlet weak var prevToBottomDistance: NSLayoutConstraint!
     @IBOutlet weak var nextToBottomDistance: NSLayoutConstraint!
 
-    var nextLessonHandler: (() -> Void)?
-    var prevLessonHandler: (() -> Void)?
+    var nextLessonHandler: (() -> Void)? {
+        didSet {
+            refreshNextPrevButtons()
+        }
+    }
+    var prevLessonHandler: (() -> Void)? {
+        didSet {
+            refreshNextPrevButtons()
+        }
+    }
 
     weak var lessonView: LessonView?
 
@@ -78,6 +86,7 @@ class WebStepViewController: UIViewController {
         prevLessonButton.setTitle("  \(NSLocalizedString("PrevLesson", comment: ""))  ", for: UIControlState())
 
         initialize()
+        refreshNextPrevButtons()
     }
 
     @objc func sharePressed(_ item: UIBarButtonItem) {
@@ -109,25 +118,16 @@ class WebStepViewController: UIViewController {
             discussionCountViewHeight.constant = 0
         }
 
-        if nextLessonHandler == nil {
-            nextLessonButton.isHidden = true
-        } else {
-            nextLessonButton.setStepicWhiteStyle()
-        }
+        nextLessonButton.setStepicWhiteStyle()
+        prevLessonButton.setStepicWhiteStyle()
+    }
 
-        if prevLessonHandler == nil {
-            prevLessonButton.isHidden = true
-        } else {
-            prevLessonButton.setStepicWhiteStyle()
+    func refreshNextPrevButtons() {
+        if nextLessonButton != nil {
+            nextLessonButton.isHidden = nextLessonHandler == nil
         }
-
-        if nextLessonHandler == nil && prevLessonHandler == nil {
-            nextLessonButtonHeight.constant = 0
-            prevLessonButtonHeight.constant = 0
-            discussionToNextDistance.constant = 0
-            discussionToPrevDistance.constant = 0
-            prevToBottomDistance.constant = 0
-            nextToBottomDistance.constant = 0
+        if prevLessonButton != nil {
+            prevLessonButton.isHidden = prevLessonHandler == nil
         }
     }