Last active
March 11, 2024 20:04
-
-
Save drewolbrich/b058b65c6d3e71f7ac611d9a505902e5 to your computer and use it in GitHub Desktop.
A wrapper around iOS Entity/loadAsync(named:in:) and visionOS Entity(named:in:) async that works on both platforms
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Entity+LoadFileAsync.swift | |
// | |
// Created by Drew Olbrich on 8/4/23. | |
// | |
import Foundation | |
import RealityKit | |
import Combine | |
extension Entity { | |
// The RealityKit visionOS `Entity(named:in:) async` initializer isn't available | |
// in iOS 17, so we implement our own replacement on iOS and wrap both implementations | |
// in `Entity/loadFileAsync(named:in:)`. | |
// | |
// Although visionOS supports `Entity/loadAsync(named:in:)`, it is deprecated and | |
// generates a compiler warning. | |
// | |
// Maybe in the future year 2026 we won't need this, assuming iOS 18 adds support for | |
// `Entity(named:in:) async` and we've decided to drop support for iOS 17. | |
#if os(visionOS) | |
/// Loads an entity from a file in a bundle asynchronously. | |
/// | |
/// Unlike `Entity(named:in:) async` or `Entity.loadAsync(named:in:)`, this method | |
/// works on both iOS and visionOS. | |
/// | |
/// If `flattened` is true, the entity hierarchy in the file will be flattened into | |
/// a single `ModelEntity`. | |
static func loadFileAsync(named name: String, in bundle: Bundle? = nil, flattened: Bool) async throws -> Entity { | |
if flattened { | |
return try await ModelEntity(named: name, in: bundle) | |
} else { | |
return try await Entity(named: name, in: bundle) | |
} | |
} | |
#else // !os(visionOS) | |
/// Loads an entity from a file in a bundle asynchronously. | |
/// | |
/// Unlike `Entity(named:in:) async` or `Entity.loadAsync(named:in:)`, this method | |
/// works on both iOS and visionOS. | |
/// | |
/// If `flattened` is true, the entity hierarchy in the file will be flattened into | |
/// a single `ModelEntity`. | |
static func loadFileAsync(named name: String, in bundle: Bundle? = nil, flattened: Bool) async throws -> Entity { | |
return try await withCheckedThrowingContinuation { continuation in | |
func loadAsync<T: Entity>(with loadRequest: LoadRequest<T>) { | |
var cancellable: AnyCancellable? | |
cancellable = loadRequest.sink(receiveCompletion: { completion in | |
switch completion { | |
case .finished: | |
break | |
case .failure(let error): | |
continuation.resume(throwing: error) | |
} | |
cancellable?.cancel() | |
}, receiveValue: { entity in | |
continuation.resume(returning: entity) | |
cancellable?.cancel() | |
}) | |
} | |
if flattened { | |
loadAsync(with: Entity.loadModelAsync(named: name, in: bundle)) | |
} else { | |
loadAsync(with: Entity.loadAsync(named: name, in: bundle)) | |
} | |
} | |
} | |
#endif // !os(visionOS) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment