Разработка мобильного приложения для HR и рекрутинга
HR-приложение обслуживает два принципиально разных сценария одновременно: рекрутер ведёт воронку кандидатов и расписывает интервью, а новый сотрудник проходит онбординг и заполняет документы. Оба сценария требуют разных UX-паттернов, разных прав доступа и разных push-уведомлений. На этом стыке и возникает большинство архитектурных решений.
Ролевая модель и архитектура
Минимум три роли: HR-менеджер/рекрутер, кандидат, сотрудник (после оффера). У каждой — свой навигационный флоу и набор экранов.
// iOS — NavigationRouter на основе роли
enum AppRole {
case recruiter
case candidate
case employee
}
func buildRootNavigation(for role: AppRole) -> UIViewController {
switch role {
case .recruiter: return RecruiterTabBarController()
case .candidate: return CandidateOnboardingFlow()
case .employee: return EmployeePortalController()
}
}
Роль пользователя приходит в JWT-токене после логина. При изменении роли (кандидат стал сотрудником) — перестраиваем навигацию без перезапуска приложения.
Воронка кандидатов: Kanban для рекрутера
Воронка рекрутинга — это Kanban: Новый → Телефонный скрининг → Техническое интервью → Оффер → Отказ. На мобиле — горизонтально прокручиваемые колонки.
// Android — состояние воронки через ViewModel
data class RecruitingFunnelState(
val stages: List<FunnelStage>,
val selectedStageIndex: Int = 0,
val candidates: Map<String, List<Candidate>> // stageId → candidates
)
class FunnelViewModel(private val repo: CandidateRepository) : ViewModel() {
val state = MutableStateFlow(RecruitingFunnelState(stages = emptyList()))
fun moveCandidateToStage(candidateId: String, targetStageId: String) {
viewModelScope.launch {
repo.updateCandidateStage(candidateId, targetStageId)
// Оптимистичное обновление UI
state.update { current ->
val updated = current.candidates.toMutableMap()
// переносим кандидата между списками
current.copy(candidates = updated)
}
}
}
}
Drag-and-drop между колонками — через ItemTouchHelper с кастомным ViewHolder.onDragOver. Проще — tap-to-move через bottom sheet с выбором стадии.
Планирование интервью
Интервью требует: выбор слота, приглашение интервьюера, отправка ссылки на встречу, push-напоминание за 15 минут.
Интеграция с Google Calendar через Google Calendar API или CalDAV, с Microsoft через Graph API. Мобильный клиент запрашивает доступные слоты:
func fetchAvailableSlots(interviewerId: String, date: Date) async throws -> [TimeSlot] {
// Запрос к бэкенду, который агрегирует Google Calendar + внутренний календарь
return try await api.getAvailableSlots(interviewerId: interviewerId, date: date)
}
Push-напоминание планируется при создании интервью — scheduled notification через FCM или OneSignal с deliver_after.
Онбординг нового сотрудника
Онбординг — это последовательный флоу: заполнение персональных данных, загрузка документов (паспорт, ИНН, трудовая), ознакомление с политиками компании, выполнение задач первого дня.
Прогресс онбординга — checklist с состоянием каждого пункта. На сервере хранится JSON-схема онбординга (набор шагов), клиент рендерит её динамически:
data class OnboardingStep(
val id: String,
val type: StepType, // FORM, DOCUMENT_UPLOAD, QUIZ, VIDEO, TASK
val title: String,
val isRequired: Boolean,
val completedAt: Long?
)
enum class StepType { FORM, DOCUMENT_UPLOAD, QUIZ, VIDEO, TASK }
Загрузка документов — multipart form data с прогрессом загрузки:
func uploadDocument(_ data: Data, type: DocumentType) async throws -> DocumentId {
return try await api.upload(
endpoint: "/employee/documents",
file: data,
filename: "\(type.rawValue)_\(Date().timestamp).pdf",
mimeType: "application/pdf"
) { progress in
self.uploadProgress = progress
}
}
Push-уведомления в HR-контексте
| Событие | Получатель | Приоритет |
|---|---|---|
| Новый отклик на вакансию | HR-менеджер | Высокий |
| Назначено интервью | Кандидат + интервьюер | Высокий |
| Напоминание об интервью (за 15 мин) | Все участники | Критический |
| Кандидату выслан оффер | Кандидат | Высокий |
| Шаг онбординга требует действия | Новый сотрудник | Средний |
| Истекает срок пробационного периода | HR-менеджер | Средний |
Корпоративная коммуникация
Внутренний чат или интеграция с существующими инструментами (Slack API, Microsoft Teams webhooks). Для MVP — внутренний чат через WebSocket:
class HRChatSocket(private val token: String) {
private val client = OkHttpClient()
private var ws: WebSocket? = null
fun connect(roomId: String, onMessage: (ChatMessage) -> Unit) {
val request = Request.Builder()
.url("wss://api.yourhr.app/ws/chat/$roomId")
.header("Authorization", "Bearer $token")
.build()
ws = client.newWebSocket(request, object : WebSocketListener() {
override fun onMessage(webSocket: WebSocket, text: String) {
onMessage(json.decodeFromString(text))
}
})
}
}
Сроки
MVP HR-приложения (воронка кандидатов, карточки кандидатов, базовый онбординг, push-уведомления) — 10–14 недель. Полный функционал с планировщиком интервью, интеграцией с календарями, корпоративным чатом и аналитикой — 20–28 недель.







