Drag various backgrounds and emojis on to the canvas to create artworks. Available on three platforms: iPad, iPhone and Mac.
- Swift
- SwiftUI
- MVVM
- SwiftUI's integration with UIKit
- Multiplatform: iPhone, iPad, Mac
- Gestures
- Drag&Drop
- UndoManager
- URLSession and Combine
- GCD
class EmojiArtDocument: ReferenceFileDocument
{
static var readableContentTypes = [UTType.emojiart]
static var writeableContentTypes = [UTType.emojiart]
required init(configuration: ReadConfiguration) throws {
if let data = configuration.file.regularFileContents {
emojiArt = try EmojiArtModel(json: data)
fetchBackgroundImageDataIfNecessary()
} else {
throw CocoaError(.fileReadCorruptFile)
}
}
func snapshot(contentType: UTType) throws -> Data {
return try emojiArt.json()
}
func fileWrapper(snapshot: Data, configuration: WriteConfiguration) throws -> FileWrapper {
FileWrapper(regularFileWithContents: snapshot)
}
@Published private(set) var emojiArt: EmojiArtModel {
didSet {
if emojiArt.background != oldValue.background {
fetchBackgroundImageDataIfNecessary()
}
}
}
init() {
emojiArt = EmojiArtModel()
}
var emojis: [EmojiArtModel.Emoji] { emojiArt.emojis }
var background: EmojiArtModel.Background { emojiArt.background }
@Published var backgroundImage: UIImage?
@Published var backgroundImageFetchStatus = BackgroundImageFetchStatus.idle
enum BackgroundImageFetchStatus: Equatable {
case idle
case fetching
case failed(URL)
}
private var backgroundImageFetchCancellable: AnyCancellable?
private func fetchBackgroundImageDataIfNecessary() {
backgroundImage = nil
switch emojiArt.background {
case .url(let url):
// fetch the url
backgroundImageFetchStatus = .fetching
backgroundImageFetchCancellable?.cancel()
let session = URLSession.shared
let publisher = session.dataTaskPublisher(for: url)
.map { (data, urlResponse) in UIImage(data: data) }
.replaceError(with: nil)
.receive(on: DispatchQueue.main)
backgroundImageFetchCancellable = publisher
.sink { [weak self] image in
self?.backgroundImage = image
self?.backgroundImageFetchStatus = (image != nil) ? .idle : .failed(url)
}
case .imageData(let data):
backgroundImage = UIImage(data: data)
case .blank:
break
}
}
}