Share files with SharePlay
Description: Discover how to work with files and attachments in a SharePlay activity. We'll explain how to use the GroupSessionJournal API to sync large amounts of data faster and show you how to adopt it in a demo of the sample app DrawTogether.
Limitations of GroupSessionManager
Previously, if you were creating a group drawing app and wanted to drop a photo onto the canvas, this wasn't possible due to the size limitations of GroupSessionManager
.
New in iOS17, file attachment transfers are not only possible but are very fast and end to end encrypted.
GroupSessionJournal
New to iOS17. GroupSessionJournal
an object that stays consistent for everyone across the GroupSession
. Actions you take on the journal affect everyone and properties on the journal are similarly synced for everyone.
public final class GroupSessionJournal {
public lazy var attachments: Attachments
public func add‹ItemType: Transferable>(_ item: ItemType) async throws → Attachment
public func remove(attachment: Attachment) async throws
}
You can upload any of your custom attachments with the "add" function. Just be sure your type conforms to the Transferable
protocol.
When calling add
orremove
everyone in the GroupSession will observe their "attachments" AsyncSequence being updated with the event.
- Attachments are limited to 100MB.
- The lifecycle of the attachment is as long as members of the
GroupSession
are connected- If the person who uploaded the attachment leaves the
GroupSession
the attachment remains until everyone disconnects
- If the person who uploaded the attachment leaves the
GroupSessionJournal: Late Joiners
Another advantage of GroupSessionJournal
over GroupSessionMessenger
is how it handles late joiners.
In GroupSessionMessenger
, late joiners are "caught up" on the session by having each session member re-upload their interactions behind the scenes. Obviously, having each member re-upload 100MB file attachments is not efficient so GroupSessionJournal
ensures that late joiners receive attachments without any re-uploads.
Code Example: Syncing an image and it's location across devices
- Create a struct for the image data and add it to your Canvas model
struct CanvasImage: Identifiable {
var id: UUID
let location: CGPoint
let imageData: Data
}
class Canvas: ObservableObject {
@Published var images = [CanvasImage]()
//...
}
- In your Canvas model, setup your
GroupSessionJournal
and listen for changes to its attachments
func configureGroupSession(_ groupSession: GroupSession<DrawTogether>) {
self.groupSession = groupSession
let messenger = GroupSessionMessenger(session: groupSession)
self.messenger = messenger
let journal = GroupSessionJournal(session: groupSession)
self.journal = journal
//set up groupSession, handle messenger events...
task = Task {
for await images in journal.attachments {
await handle(images)
}
}
tasks.insert(task)
//...
}
- In your Canvas model, create the image handler to convert journal attachments to
CanvasImage
func handle(_ attachments: GroupSessionJournal.Attachments.Element) async {
// Now make sure that our canvas always has all the images from this sequence.
self.images = await withTaskGroup(of: CanvasImage?.self) { group in
var images = [CanvasImage]()
attachments.forEach { attachment in
group.addTask {
do {
let metadata = try await attachment.loadMetadata(
of: ImageMetadataMessage.self
)
let imageData = try await attachment.load(
Data.self
)
return .init(
id: attachment.id,
location: metadata.location,
imageData: imageData
)
} catch { return nil }
}
for await image in group {
if let image {
images.append(image)
}
}
return images
}
}
}
- At this stage, simply update your UI to reflect Canvas.images
