# Deeplink API _TIDeeplink_ добавляет сервис `TIDeeplinksService` для обработки диплинков ## Как настроить 1. Создать объект, представляющий диплинк. Такой объект должен соответствовать протоколу `Hashable` ```swift import Foundation import TIDeeplink import TIFoundationUtils import TISwiftUtils import UIKit enum ProjectDeeplink: Hashable { case editProfile case office(id: String) } ``` 2. Создать объект, соответствующий протоколу `DeeplinkMapper`. Его задачей будет - преобразование _URL_ в диплинк ```swift final class ProjectDeeplinkMapper: DeeplinkMapper { func map(url: URL) -> ProjectDeeplink? { guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return nil } switch components.host { case "office": if let id = components.path.split(separator: "/").last { return .office(id: String(id)) } return nil case "editProfile": return .editProfile default: return nil } } } ``` 3. Реализовать у объекта отвечающего за навигацию протокол`DeeplinkHandler`. В необходимом для реализации методе `handle(deeplink:)` нужно обработать передаваемый диплинк и вернуть соответствующее действие ```swift class MainCoordinator: DeeplinkHandler { func openEditProfileScreen() { print("Presenting edit profile view controller") } func openOfficesScreen(completion: VoidClosure?) { print("Presenting offices view controller") completion?() } func openOfficesScreen(forId id: String) { print("Presenting offices view controller by id: \(id)") } // MARK: DeeplinkHandler func handle(deeplink: ProjectDeeplink) -> Operation? { switch deeplink { case .editProfile: return BlockOperation { [weak self] in self?.openEditProfileScreen() } case let .office(id): return ClosureAsyncOperation { [weak self] completion in self?.openOfficesScreen { self?.openOfficesScreen(forId: id) completion(.success(())) } return Cancellables.nonCancellable() } } } } ``` **Опционально** 4. Создать сабкласс `TIDeeplinksService` для удобного использования по проекту ```swift final class ProjectDeeplinksService: TIDeeplinksService { static let shared = ProjectDeeplinksService() convenience init(mapper: ProjectDeeplinkMapper = .init(), handler: MainCoordinator = .init()) { self.init(mapper: mapper, handler: handler, operationQueue: .main) } } ``` Создаем и передаем в сервис Mapper и Handler. > Так же можно воспользоваться инициализатором `init(mapper:handler:operationQueue:)`. Если Mapper и Handler передаются не в инициализаторе, то их самостоятельно необходимо привести к виду `AnyDeeplinkHandler` и `AnyDeeplinkMapper` с момощью методов: `eraseToAnyDeeplinkHandler()` и `eraseToAnyDeeplinkMapper()` ```swift let coordinator = MainCoordinator() let mapper = ProjectDeeplinkMapper() ProjectDeeplinksService.shared.deeplinkMapper = mapper.eraseToAnyDeeplinkMapper() ProjectDeeplinksService.shared.deeplinkHandler = coordinator.eraseToAnyDeeplinkHandler() ``` В `AppDelegate` использвуем методы `deferredHandle(url:)` и `handlePendingDeeplinks()` для обработки диплинков. - `deferredHandle(url:)`: пытается создать из _URL_ диплинк и добавить его в очередь на обработку - `handlePendingDeeplinks()`: обрабатывает первый в череди диплинк ```swift func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { ProjectDeeplinksService.shared.deferredHandle(url: url) ProjectDeeplinksService.shared.handlePendingDeeplinks() return true } guard let url = URL(string: "app://office/123") else { fatalError() } application(.shared, open: url) ``` В качестве `DeeplinkHandler` можно использовать базовую реализацию `BaseNavigationStackDeeplinkHandler`, которая позволяет искать handler в иерархии view, способный обработать диплинк. ```swift protocol ProjectDeeplinkHandler: DeeplinkHandler where Deeplink == ProjectDeeplink { } class ViewController: UIViewController, ProjectDeeplinkHandler { func handle(deeplink: ProjectDeeplink) -> Operation? { switch deeplink { case .editProfile: return BlockOperation { print("Presenting edit profile view controller") } case let .office(id): return BlockOperation { print("Presenting offices view controller by id: \(id)") } } } } ``` Создание Handler. Для настройки передается rootViewController, который должен наследоваться от `UIViewController` С этого контроллера будет начинаться поиск обработчика ```swift let viewController = ViewController() let navigationController = UINavigationController(rootViewController: viewController) let handler = BaseNavigationStackDeeplinkHandler(rootViewController: navigationController) { guard let projectHandler = $0 as? (any ProjectDeeplinkHandler) else { return nil } return projectHandler.eraseToAnyDeeplinkHandler() } ``` Далее handler может передаваться для использования в `TIDeeplinksService` ```swift let mapper = ProjectDeeplinkMapper() let service = TIDeeplinksService(mapper: mapper, handler: handler) service.deferredHandle(url: url) service.handlePendingDeeplinks() RunLoop.main.run() ```