LeadKit/docs/tideeplink/deeplinks.md

6.4 KiB
Raw Blame History

Deeplink API

TIDeeplink добавляет сервис TIDeeplinksService для обработки диплинков

Как настроить

  1. Создать объект, представляющий диплинк. Такой объект должен соответствовать протоколу Hashable
import Foundation
import TIDeeplink
import TIFoundationUtils
import TISwiftUtils
import UIKit

enum ProjectDeeplink: Hashable {
    case editProfile
    case office(id: String)
}
  1. Создать объект, соответствующий протоколу DeeplinkMapper. Его задачей будет - преобразование URL в диплинк
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
        }
    }
}
  1. Реализовать у объекта отвечающего за навигацию протоколDeeplinkHandler. В необходимом для реализации методе handle(deeplink:) нужно обработать передаваемый диплинк и вернуть соответствующее действие
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 для удобного использования по проекту

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()

let coordinator = MainCoordinator()
let mapper = ProjectDeeplinkMapper()
ProjectDeeplinksService.shared.deeplinkMapper = mapper.eraseToAnyDeeplinkMapper()
ProjectDeeplinksService.shared.deeplinkHandler = coordinator.eraseToAnyDeeplinkHandler()

В AppDelegate использвуем методы deferredHandle(url:) и handlePendingDeeplinks() для обработки диплинков.

  • deferredHandle(url:): пытается создать из URL диплинк и добавить его в очередь на обработку
  • handlePendingDeeplinks(): обрабатывает первый в череди диплинк
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, способный обработать диплинк.

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 С этого контроллера будет начинаться поиск обработчика

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

let mapper = ProjectDeeplinkMapper()
let service = TIDeeplinksService(mapper: mapper, handler: handler)

service.deferredHandle(url: url)
service.handlePendingDeeplinks()

RunLoop.main.run()