Connect with us

Разработка

Добавляем SharePlay в iOS-приложение

Я решил написать исчерпывающее руководство по настройке сеанса SharePlay, в котором собраны все мои находки.

Фото аватара

Опубликовано

/

     
     

Недавно я работал над функцией NowPlaying, которая использует API SharePlay в iOS, позволяя пользователям присоединяться к сеансам прослушивания и открывать новую музыку вместе со своими друзьями.

Мне пришлось немало потрудиться, чтобы заставить все работать, и я обнаружил, что документация Apple по настройке сессий SharePlay скудная и немного запутанная. По этой причине я решил написать исчерпывающее руководство по настройке сеанса SharePlay, в котором собраны все мои находки.

Добавление возможностей SharePlay

Первый шаг к созданию опыта SharePlay — это добавление необходимых возможностей в ваше приложение. Для этого нужно открыть проект в Xcode, выбрать файл проекта и затем выбрать цель.

На вкладке «Signing & Capabilities» нажмите кнопку «+», найдите «Group Activities» и добавьте эту возможность.

Добавляем SharePlay в iOS-приложение

Создание Group Activity

Опыт SharePlay построен на концепции GroupActivitys (групповых действиях или активностей), которые являются типами, представляющими опыт приложения, которым можно поделиться.

Создать такой тип просто: определите его (или используйте существующий) и заставьте его соответствовать протоколу GroupActivity.

import GroupActivities

struct DemoAppActivity: GroupActivity {
    static let activityIdentifier = "dev.polpiella.demoapp.DemoAppActivity"

    var metadata: GroupActivityMetadata {
        var metadata = GroupActivityMetadata()
        metadata.title = "Demo Activity"
        metadata.subtitle = "Share an experience together using SharePlay"
        metadata.previewImage = UIImage(named: "preview-image")?.cgImage
        metadata.type = .generic
        return metadata
    }
}

Как видно из приведенного примера, протокол GroupActivity требует определения свойства metadata, которое используется для предоставления информации об активности системе, и уникального идентификатора activityIdentifier, используемого для идентификации активности.

Запуск сеанса SharePlay

Теперь, когда вы определили свою GroupActivity, вы готовы начать сеанс SharePlay. Это можно сделать несколькими способами, в зависимости от состояния вашего приложения:

  • Если пользователь уже находится на вызове FaceTime, вы можете начать сеанс SharePlay напрямую.
  • Если пользователь не находится в режиме FaceTime, вы можете позволить ему поделиться сеансом со своими друзьями с помощью share sheet, который вы реализуете в ближайшее время.
import Foundation
import GroupActivities
import Combine

enum SharePlayActivationOutcome {
    case local
    case sharePlay
    case needsDialog
}

@Observable final class GroupActivityManager {
    private let groupStateObserver = GroupStateObserver()

    func startSharing() async -> SharePlayActivationOutcome {
        // 1
        if groupStateObserver.isEligibleForGroupSession {
            // 2
            let activity = DemoAppActivity()
            let result = await activity.prepareForActivation()

            switch result {
            case .activationPreferred:
                // 3
                _ = try? await activity.activate()
                return .sharePlay
            case .activationDisabled:
                // 4
                return .local
            default: return .local
            }
        } else {
            // 5
            return .needsDialog
        }
    }
}

Давайте разберем приведенный выше код шаг за шагом:

  1. Проверьте, находится ли пользователь в разговоре по FaceTime и, следовательно, имеет ли он право на сеанс SharePlay.
  2. Создайте новый экземпляр GroupActivity и вызовите метод prepareForActivation, чтобы проверить, выбрал ли пользователь сеанс SharePlay или при появлении запроса он решил начать локальный сеанс.
  3. Если пользователь решил начать сеанс SharePlay, вызовите метод activate, чтобы начать сеанс.
  4. Если пользователь решил не начинать сеанс SharePlay, верните значение .local.
  5. Если пользователь не находится на вызове FaceTime, верните .needsDialog, чтобы предложить пользователю поделиться активностью со своими друзьями.

Запуск сеанса SharePlay из представления

Если вы хотите начать сеанс SharePlay из представления, вы можете просто вызвать метод startSharing из GroupActivityManager и обработать результат соответствующим образом.

Если пользователь имеет право на участие в групповых сеансах и подтверждает, что хочет начать совместный опыт, используя системное оповещение, сеанс SharePlay начнется немедленно.

Если нет, вам нужно будет обработать результат и поделиться активностью с друзьями с помощью GroupActivitySharingController:

import SwiftUI
import UIKit

struct GroupActivityShareSheet<Activity: GroupActivity>: UIViewControllerRepresentable {
    let preparationHandler: () async throws -> Activity

    func makeUIViewController(context: Context) -> UIViewController {
        GroupActivitySharingController(preparationHandler: preparationHandler)
    }

    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
}

В коде представления вы можете просто вызвать метод startSharing и представить GroupActivityShareSheet, если результатом будет .needsDialog:

import SwiftUI

struct ContentView: View {
    @State private var showDialog = false
    private let activityManager = GroupActivityManager()

    var body: some View {
        VStack {
            Button(action: {
                Task {
                    let outcome = await activityManager.startSharing()
                    if outcome == .needsDialog {
                        showDialog = true
                    }
                }
            }, label: {
                Label(title: {
                    Text("Start SharePlay")
                }, icon: {
                    Image(systemName: "shareplay")
                })
            })
            .buttonStyle(.borderedProminent)
        }
        .sheet(isPresented: $showDialog, content: {
            GroupActivityShareSheet {
                DemoAppActivity()
            }
        })
        .padding()
    }
}

Управление сеансом SharePlay

До сих пор вы только запускали сеанс SharePlay, но не отслеживали его и не управляли им.

Чтобы получить экземпляр текущей сессии SharePlay, вам нужно использовать статическое свойство sessions() для вашего типа GroupActivity.

Поскольку это свойство возвращает AsyncStream, вы можете использовать его для подписки и прослушивания новых сессий и отслеживания текущей активной сессии:

@Observable final class GroupActivityManager {
    var session: GroupSession<DemoAppActivity>?
    var messenger: GroupSessionMessenger<DemoAppActivity>?

    init() {
        Task.detached {
            for await session in DemoAppActivity.sessions() {
                self.session = session
                self.sessionJoined(session)
            }
        }
    }

    private func sessionJoined(_ session: GroupSession<DemoAppActivity>) {
        if session.state != .joined { session.join() }
        messenger = GroupSessionMessenger(session: session)
        listenToMessages()
    }

    private func listenToMessages() {
        guard let messenger else { return }
        Task.detached {
            for await message in messenger.messages(of: String.self) {
                print("Received message: \(message.0)")
            }
        }
    }
}

Как видно из приведенного выше фрагмента, как только новая сессия становится доступной, вы можете позволить своему пользователю присоединиться к ней, вызвав метод join, а затем начать прослушивать сообщения, используя только что созданный экземпляр GroupSessionMessenger. Далее в статье вы также будете использовать его для отправки сообщений другим участникам.

Экземпляры GroupSessionMessenger имеют свойство messages. Это свойство представляет собой AsyncStream, который можно использовать для прослушивания новых сообщений и их соответствующей обработки.

Отправка сообщений

Как только у вас есть активная сессия, вы можете отправлять сообщения другим участникам с помощью метода send в GroupSessionMessenger:

@Observable final class GroupActivityManager {
    func send(_ message: String) async throws {
        try await messenger?.send(message)
    }
}

Обратите внимание, что вы можете отправить в сообщении любой тип Codable, но вы должны знать, что существует ограничение на размер полезной нагрузки в сообщениях, которые вы отправляете в сеансе SharePlay. Если вы отправите сообщение, превышающее лимит, вы получите ошибку.

Подсчет участников

Допустим, вы хотите показать количество участников в сеансе SharePlay. Вы можете сделать это, прослушивая свойство $activeParticipants в GroupSession:

@Observable final class GroupActivityManager {
    var participantCount = 0
    private var cancellables = Set<AnyCancellable>()

    private func keepParticipantCount() {
        guard let session else { return }
        session
            .$activeParticipants
            .sink { self.participantCount = $0.count }
            .store(in: &cancellables)
    }

    private func sessionJoined(_ session: GroupSession<DemoAppActivity>) {
        if session.state != .joined { session.join() }
        messenger = GroupSessionMessenger(session: session)
        listenToMessages()
        keepParticipantCount()
    }
}

Завершение сеанса SharePlay

С этой конкретной функцией я столкнулся в процессе внедрения и практически не смог найти документации по нему.

Пользователь может завершить сеанс SharePlay двумя способами:

  1. Пользователь нажимает на кнопку в приложении, чтобы завершить сеанс.
  2. Пользователь покидает вызов FaceTime или решает завершить сеанс SharePlay из системного меню.

Давайте рассмотрим первый случай и позволим пользователям завершать сеанс из приложения:

@Observable final class GroupActivityManager {
    func stop() {
        guard let session else { return }
        switch session.state {
        case .invalidated: break
        default: isSessionActive = false; session.end()
        }
    }
}

Как видно из приведенного выше фрагмента, завершение сеанса — это просто вызов метода end для экземпляра GroupSession, только если сеанс еще не признан недействительным.

Стоит отметить, что для других участников существует метод, позволяющий просто покинуть сессию, не завершая ее, который вы можете использовать в зависимости от вашего случая.

Теперь, когда сессия будет признана недействительной, вам нужно будет обработать изменение состояния и очистить все ресурсы для всех участников, а не только для того, кто завершил сессию. Это позволит справиться с ситуацией, когда пользователь решает завершить вызов FaceTime или сеанс SharePlay из системного меню.

Вы можете реагировать на инвалидацию сессий, прослушивая свойство $state в GroupSession:

@Observable final class GroupActivityManager {
    private func sessionJoined(_ session: GroupSession<DemoAppActivity>) {
        if session.state != .joined { session.join() }
        messenger = GroupSessionMessenger(session: session)
        listenToMessages()
        monitorSessionState()
    }

    private func monitorSessionState() {
        session?.$state.sink { state in
            switch state {
            case .invalidated:
                // Perform any cleanup here
                break
            case .joined:
                // Handle a re-join to the same session
                self.session.map(self.sessionJoined)
            default: break
            }
        }
        .store(in: &cancellables)
    }
}

Источник

Если вы нашли опечатку - выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.
Advertisement

Наши партнеры:

LEGALBET

Мобильные приложения для ставок на спорт
Telegram

Популярное

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: