LeadKit/docs/tideeplink/deeplinks.md

181 lines
6.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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<Void, Never> { [weak self] completion in
self?.openOfficesScreen {
self?.openOfficesScreen(forId: id)
completion(.success(()))
}
return Cancellables.nonCancellable()
}
}
}
}
```
**Опционально** 4. Создать сабкласс `TIDeeplinksService` для удобного использования по проекту
```swift
final class ProjectDeeplinksService: TIDeeplinksService<ProjectDeeplink> {
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<ProjectDeeplink>(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()
```