Does anyone know the runtime performance I can expect from the anchor(for:) and node(for:) methods on ARSCNView? I don't see this documented. Would it be safe for me to assume this is a constant-time lookup? Would I need to assume this is a linear-time operation that scales up with the number of nodes and anchors active?
Is it my responsibility to do my own bookkeeping if I want to look these relationships up in constant-time?
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hi! I am seeing some unexpected behavior when attempting to create a Model instance with a variable named updated. I start with a simple Model:
@Model final public class Item {
var timestamp: Int
var updated: Int
public init(timestamp: Int = 1, updated: Int = 1) {
self.timestamp = timestamp
self.updated = updated
}
}
I then attempt to create an item instance:
func main() throws {
let schema = Schema([Item.self])
let configuration = ModelConfiguration(isStoredInMemoryOnly: true)
let container = try ModelContainer(
for: schema,
configurations: configuration
)
let modelContext = ModelContext(container)
let item = Item()
print(item.timestamp)
print(item.updated)
}
try main()
The value of item.timestamp is printing as 1 and the value of item.updated is printing as 0.
I have no idea what could be causing that to happen… why would both those values not be printing as 1? Is there some private API that is somehow colliding with the (public) updated variable and causing the item instance to report back with a value of 0? Is there documentation warning engineers that a variable named updated is off-limits and results in undefined behavior?
I can fix that by renaming the variable:
@Model final public class Item {
var timestamp: Int
var updatedTimestamp: Int
public init() {
self.timestamp = 1
self.updatedTimestamp = 1
}
}
I am unblocked on this (because renaming the variable seems to work fine)… but is there any insight on why this might be happening in the first place? I am building from Xcode_16_beta_5. Thanks!
Hi! I'm running into a warning from a SwiftUI.DynamicProperty on a 6.0 development build (swift-6.0-DEVELOPMENT-SNAPSHOT-2024-03-26-a).
I am attempting to build a type (conforming to DynamicProperty) that should also be MainActor. This type with also need a custom update function. Here is a simple custom wrapper (handwaving over the orthogonal missing pieces) that shows the warning:
import SwiftUI
@MainActor struct MainProperty: DynamicProperty {
// Main actor-isolated instance method 'update()' cannot be used to satisfy nonisolated protocol requirement; this is an error in the Swift 6 language mode
@MainActor func update() {
}
}
Is there anything I can do about that warning? Does the warning correctly imply that this will be a legit compiler error when 6.0 ships?
I can find (at least) two examples of types adopting DynamicProperty from Apple that are also MainActor: FetchRequest and SectionedFetchRequest. What is confusing is that both FetchRequest^1 and SectionedFetchRequest^2 explicitly declare their update method to be MainActor. Is there anything missing from my Wrapper declaration that can get me what I'm looking for? Any more advice about that? Thanks!
https://gist.github.com/vanvoorden/37ff2b2f9a2a0d0657a3cc5624cc9139
Hi! I'm experimenting with the Entry macro in a SwiftUI app. I'm a little confused about how to stored a defaultValue to prevent extra work from creating this more than once.
A "legacy" approach to defining an Environment variable looks something like this:
struct StoredValue {
var value: String {
"Hello, world!"
}
init() {
print("StoredValue.init()")
}
}
extension EnvironmentValues {
var storedValue: StoredValue {
get {
self[StoredValueKey.self]
}
set {
self[StoredValueKey.self] = newValue
}
}
struct StoredValueKey: EnvironmentKey {
static let defaultValue = StoredValue()
}
}
The defaultValue is a static stored property.
Here is a "modern" approach using the Entry macro:
struct ComputedValue {
var value: String {
"Hello, world!"
}
init() {
print("ComputedValue.init()")
}
}
extension EnvironmentValues {
@Entry var computedValue: ComputedValue = ComputedValue()
}
From the perspective of the product engineer, it looks like I am defining another stored defaultValue property… but this actually expands to a computed property:
extension EnvironmentValues {
var computedValue: ComputedValue {
get {
self[__Key_computedValue.self]
}
set {
self[__Key_computedValue.self] = newValue
}
}
private struct __Key_computedValue: SwiftUICore.EnvironmentKey {
static var defaultValue: ComputedValue {
get {
ComputedValue()
}
}
}
}
If I tried to use both of these Environment properties in a SwiftUI component, it looks like I can confirm the computedValue is computing its defaultValue several times:
@main
struct EnvironmentDemoApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
@Environment(\.computedValue) var computedValue
@Environment(\.storedValue) var storedValue
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
}
.padding()
}
}
And then when I run the app:
ComputedValue.init()
StoredValue.init()
ComputedValue.init()
ComputedValue.init()
ComputedValue.init()
ComputedValue.init()
ComputedValue.init()
ComputedValue.init()
ComputedValue.init()
Is there any way to use the Entry macro in a way that we store the defaultValue instead of computing it on-demand every time?
Hi! Any ETA for a KDK for 23A344? The most recent available KDK I see is 22G91. Thanks!
Hi! I'm seeing some confusing behavior with a propertyWrapper that tries to constrain its wrappedValue to MainActor. I'm using this in a SwiftUI.View… but I'm seeing some confusing behavior when I try to add that component to my graph. There seems to be some specific problem when body is defined in an extension.
I start with a simple property wrapper:
@propertyWrapper struct Wrapper<T> {
@MainActor var wrappedValue: T
}
I then try a simple App with a View that uses a Wrapper:
@main struct MainActorDemoApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
@Wrapper var value = "Hello, world!"
var body: some View {
Text(self.value)
}
}
This code compiles with no problems for me.
For style… I might choose to define the body property of my MainActorDemoApp with an extension:
@main struct MainActorDemoApp: App {
// var body: some Scene {
// WindowGroup {
// ContentView()
// }
// }
}
extension MainActorDemoApp {
var body: some Scene {
WindowGroup {
ContentView() // Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context
}
}
}
struct ContentView: View {
@Wrapper var value = "Hello, world!"
var body: some View {
Text(self.value)
}
}
Explicitly marking my body as a MainActor fixes the compiler error:
@main struct MainActorDemoApp: App {
// var body: some Scene {
// WindowGroup {
// ContentView()
// }
// }
}
extension MainActorDemoApp {
@MainActor var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
@Wrapper var value = "Hello, world!"
var body: some View {
Text(self.value)
}
}
So I guess the question is… why? Why would code that breaks when my body is in an extension not break when my body is in my original struct definition? Is this intended behavior?
I'm on Xcode Version 15.2 (15C500b) and Swift 5.9.2 (swiftlang-5.9.2.2.56 clang-1500.1.0.2.5).
It's unclear to me what is "wrong" about the code that broke… any ideas?
Hi! I'm experimenting with Xcode performance testing. I'm specifically focusing on XCTMemoryMetric. I have a demo project with two performance tests that do some work wrapped with XCTestCase.measure. I can navigate to my Report navigator in Xcode and see the memory footprints of these two tests… but I only see how to view them individually:
From this screen it looks like I have the ability to Export this benchmark to CSV.
My question is what my options are for viewing all my XCTestCase.measure tests together. I see the Duration for both tests… but I don't see any way to view the memory benchmarks for all my tests in just one place:
Ideally… I would also like to run these tests and produce one CSV file (or XML or JSON) report. I'm not opposed to using command line for this (if the option exists there) but Xcode also works for me. I'm also flexible whether or not these tests live in a Swift Package or directly in an Xcode project if that makes any difference.
Any more advice about how to set up these performance tests? Thanks!
Hi! I'm running into some confusing behavior when attempting to delete all instance of one model type from a ModelContext. My problem is specifically using the delete(model:where:includeSubclasses:)^1 function (and passing in a model type). I seem to be running into situations where this function fails silently without throwing an error (no models are deleted).
I am seeing this same behavior from Xcode_15.4.0 and Xcode_16_beta_4.
I start with a model:
@Model final public class Item {
var timestamp: Date
public init(timestamp: Date = .now) {
self.timestamp = timestamp
}
}
Here is an example of a Store class that wraps a ModelContext:
final public class Store {
public let modelContext: ModelContext
public init(modelContainer: SwiftData.ModelContainer) {
self.modelContext = ModelContext(modelContainer)
}
}
extension Store {
private convenience init(
schema: Schema,
configuration: ModelConfiguration
) throws {
let container = try ModelContainer(
for: schema,
configurations: configuration
)
self.init(modelContainer: container)
}
}
extension Store {
public convenience init(url: URL) throws {
let schema = Schema(Self.models)
let configuration = ModelConfiguration(url: url)
try self.init(
schema: schema,
configuration: configuration
)
}
}
extension Store {
public convenience init(isStoredInMemoryOnly: Bool = false) throws {
let schema = Schema(Self.models)
let configuration = ModelConfiguration(isStoredInMemoryOnly: isStoredInMemoryOnly)
try self.init(
schema: schema,
configuration: configuration
)
}
}
extension Store {
public func fetch<T>(_ type: T.Type) throws -> Array<T> where T : PersistentModel {
try self.modelContext.fetch(
FetchDescriptor<T>()
)
}
}
extension Store {
public func fetchCount<T>(_ type: T.Type) throws -> Int where T : PersistentModel {
try self.modelContext.fetchCount(
FetchDescriptor<T>()
)
}
}
extension Store {
public func insert<T>(_ model: T) where T : PersistentModel {
self.modelContext.insert(model)
}
}
extension Store {
public func delete<T>(model: T.Type) throws where T : PersistentModel {
try self.modelContext.delete(model: model)
}
}
extension Store {
public func deleteWithIteration<T>(model: T.Type) throws where T : PersistentModel {
for model in try self.fetch(model) {
self.modelContext.delete(model)
}
}
}
extension Store {
private static var models: Array<any PersistentModel.Type> {
[Item.self]
}
}
That should be pretty simple… I can use this Store to read and write Item instances to a ModelContext.
Here is an example of an executable that shows off the unexpected behavior:
func main() async throws {
do {
let store = try Store(isStoredInMemoryOnly: true)
store.insert(Item())
print(try store.fetchCount(Item.self) == 1)
try store.delete(model: Item.self)
print(try store.fetchCount(Item.self) == 0)
}
do {
let store = try Store(isStoredInMemoryOnly: true)
store.insert(Item())
print(try store.fetchCount(Item.self) == 1)
try store.deleteWithIteration(model: Item.self)
print(try store.fetchCount(Item.self) == 0)
}
do {
let store = try StoreActor(isStoredInMemoryOnly: true)
await store.insert(Item())
print(try await store.fetchCount(Item.self) == 1)
try await store.delete(model: Item.self)
print(try await store.fetchCount(Item.self) == 0)
}
do {
let store = try StoreActor(isStoredInMemoryOnly: true)
await store.insert(Item())
print(try await store.fetchCount(Item.self) == 1)
try await store.deleteWithIteration(model: Item.self)
print(try await store.fetchCount(Item.self) == 0)
}
}
try await main()
My first step is to set up an executable with an info.plist to support SwiftData.^2
My expectation is all these print statements should be true. What actually happens is that the calls to delete(model:where:includeSubclasses:) seem to not be deleting any models (and also seem to not be throwing errors).
I also have the option to test this behavior with XCTest. I see the same unexpected behavior:
import XCTest
final class StoreXCTests : XCTestCase {
func testDelete() throws {
let store = try Store(isStoredInMemoryOnly: true)
store.insert(Item())
XCTAssert(try store.fetchCount(Item.self) == 1)
try store.delete(model: Item.self)
XCTAssert(try store.fetchCount(Item.self) == 0)
}
func testDeleteWithIteration() throws {
let store = try Store(isStoredInMemoryOnly: true)
store.insert(Item())
XCTAssert(try store.fetchCount(Item.self) == 1)
try store.deleteWithIteration(model: Item.self)
XCTAssert(try store.fetchCount(Item.self) == 0)
}
}
Those tests fail… implying that the delete(model:where:includeSubclasses:) is not actually deleting any models.
FWIW… I see the same behavior (from command-line and XCTest) when my Store conforms to ModelActor.^3 ^4
This does not seem to be the behavior I am seeing from using the delete(model:where:includeSubclasses:) in a SwiftUI app.^5 Calling the delete(model:where:includeSubclasses:) function from SwiftUI does delete all the model instances.
The SwiftUI app uses a ModelContext directly (without a Store type). I can trying writing unit tests directly against ModelContext and I see the same behavior as before (no model instances are being deleted).^6
Any ideas about that? Is this a known issue with SwiftData that is being tracked? Is the delete(model:where:includeSubclasses:) known to be "flaky" when called from outside SwiftUI? Is there anything about the way these ModelContext instance are being created that we think is leading to this unexpected behavior?
Ok… I'm baffled here… this is very strange.
Here is a SwiftUI app:
import SwiftUI
@main struct StepperDemoApp: App {
func onIncrement() {
print(#function)
}
func onDecrement() {
print(#function)
}
var body: some Scene {
WindowGroup {
Stepper {
Text("Stepper")
} onIncrement: {
self.onIncrement()
} onDecrement: {
self.onDecrement()
}
}
}
}
When I run in the app in macOS (Xcode 16.0 (16A242) and macOS 14.6.1 (23G93)), I see some weird behavior from these buttons. My experiment is tapping + + + - - -. Here is what I see printed:
onIncrement()
onIncrement()
onIncrement()
onIncrement()
onDecrement()
What I expected was:
onIncrement()
onIncrement()
onIncrement()
onDecrement()
onDecrement()
onDecrement()
Why is an extra onIncrement being called? And why is one onDecrement dropping on the floor?
Deploying the app to iPhone Simulator does not repro this behavior (I see the six "correct" logs from iPhone Simulator).
Hi! I have a stateful object that should be created in my app entry point and delivered through my component graph:
@main
@MainActor struct MyApp : App {
@State private var myAppState = MyAppState()
var body: some Scene {
...
}
}
The MyApp is a struct value type… in a SwiftUI world that seems to imply it could "go away" and be recreated as the system sees appropriate. We see this with view components all the time (hence the State wrapper to help preserve our instance across the lifecycle of our identity)… but I'm wondering if this State wrapper is even necessary with an App entry point. Could this struct ever go away? Would there be any legit reasons that this struct should go away and recreate over one SwiftUI app lifecycle (other than terminating the app and starting a whole new process of course)?
And what lifecycle is the SwiftUI.State tied to in this example? In a view component our SwiftUI.State is tied to our component identity. In this example… we are tied to app component identity? Is there ever going to be multiple legit app component identities live in the same process?
I'm thinking I could just go ahead and keep using State as a best practice… but is this just overkill or is there a real best practice lurking under here? Any more ideas about that? Thanks!
Hi! I believe there might be a small bug in the SwiftData Quakes Sample App.^1 The Quakes app requests a JSON feed from USGS.^2 What seems to be breaking is that apparently earthquake entities from USGS can return with null magnitudes. That is throwing errors from the decoder:
struct GeoFeatureCollection: Decodable {
let features: [Feature]
struct Feature: Decodable {
let properties: Properties
let geometry: Geometry
struct Properties: Decodable {
let mag: Double
let place: String
let time: Date
let code: String
}
struct Geometry: Decodable {
let coordinates: [Double]
}
}
}
which is expecting mag to not be nil.
Here is my workaround:
struct GeoFeatureCollection: Decodable {
let features: [Feature]
struct Feature: Decodable {
let properties: Properties
let geometry: Geometry
struct Properties: Decodable {
let mag: Double?
let place: String
let time: Date
let code: String
}
struct Geometry: Decodable {
let coordinates: [Double]
}
}
}
And then:
extension Quake {
/// Creates a new quake instance from a decoded feature.
convenience init(from feature: GeoFeatureCollection.Feature) {
self.init(
code: feature.properties.code,
magnitude: feature.properties.mag ?? 0.0,
time: feature.properties.time,
name: feature.properties.place,
longitude: feature.geometry.coordinates[0],
latitude: feature.geometry.coordinates[1]
)
}
}
https://github.com/apple/sample-food-truck
Hi! I'm following along with the sample-food-truck application from WWDC 2022. I'm seeing some weird navigation issues when building the app for iPadOS.
The Table component displays a Select Button for selecting elements and a Done Button disabling that state. These buttons seem to be breaking something about navigation on iOS 18.4.1. It's a nondeterministic issue… but tapping the buttons seems to lead to some corrupt state where the app transitions out of OrdersView and back to TruckView.
This code from FoodTruckModel seems to be making a difference:
Task(priority: .background) {
var generator = OrderGenerator.SeededRandomGenerator(seed: 5)
for _ in 0..<20 {
try? await Task.sleep(nanoseconds: .secondsToNanoseconds(.random(in: 3 ... 8, using: &generator)))
Task { @MainActor in
withAnimation(.spring(response: 0.4, dampingFraction: 1)) {
self.orders.append(orderGenerator.generateOrder(number: orders.count + 1, date: .now, generator: &generator))
}
}
}
}
Commenting out that code and disabling the new Order values coming in seems to fix the issue.
Is there any public documentation for me to learn about the Select and Done buttons? I don't see anywhere for me to learn about how these work and what my ability is to customize their behavior.
Any ideas? I can repro from device and simulator.
https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/working_with_core_foundation_types
If we define a C function that returns a CFType, Swift can (sometimes) bridge the memory management semantics. If Swift cannot bridge the memory management semantics, we can end up with an Unmanaged type:
CFStringRef StringByAddingTwoStrings(CFStringRef s1, CFStringRef s2);
bridges to:
public func StringByAddingTwoStrings(_ s1: CFString!, _ s2: CFString!) -> Unmanaged<CFString>!
We can improve the bridging with the CF_RETURNS_RETAINED annotation:
CFStringRef StringByAddingTwoStrings(CFStringRef s1, CFStringRef s2) CF_RETURNS_RETAINED;
now bridges to:
public func StringByAddingTwoStrings(_ s1: CFString!, _ s2: CFString!) -> CFString!
What does not seem to be documented is how to improve bridging when function pointer return values should be annotated. For example:
typedef CFStringRef (*StringByAddingTwoStrings)(CFStringRef, CFStringRef);
bridges to:
public typealias StringByAddingTwoStrings = @convention(c) (CFString?, CFString?) -> Unmanaged<CFString>?
Is there any way to annotate the function pointer to properly communicate the memory management semantics? Adding the CF_RETURNS_RETAINED to the function pointer does not seem to help.
Does anyone have some resources to recommend for learning more about measuring and optimizing SwiftUI performance and rendering? I haven't seen a ton from Apple that looks like it's going to help me more.
I did find two sessions from WWDC 2023. The "Demystify SwiftUI performance" session[1] does tell us the _printChanges function can help tell us when component bodies are computed. I see how I can attach this to a component that I built… I'm not (yet) sure if I have the ability to run this on arbitrary system components in my component graph.
The "Analyze hangs with Instruments" session[2] does walk us through using the SwiftUI Instruments to measure body computations.
Does anyone have any more resources along these lines for learning more? SwiftUI is closed source… so we don't really have easy visibility inside to see for ourselves. Bummer.
Are there any additional debug APIs (like _printChanges) that can help for local development?
I'm also interested to learn more about view equality[3]… I found this essay but it's four years old and I still can't find more information directly from Apple about EquatableView…
I would prefer to find any more documentation or resources directly from Apple… but I would be happy with free third-party resources as long as they have some legit data to back up their code. Thanks!
[1] https://developer.apple.com/videos/play/wwdc2023/10160/
[2] https://developer.apple.com/videos/play/wwdc2023/10248/
[3] https://swiftui-lab.com/equatableview/
https://gist.github.com/vanvoorden/1c7c6ed08898de7f4b8619147537c0eb
Hi! Has anyone here seen weird performance problems when passing values from a Collections.OrderedDictionary to a SwiftUI.List inside SwiftUI.NavigationStack? I see some performance problems that go away when switching back to SwiftUI.NavigationView. At this point, I'm not sure if it's a problem in the OrderedDictionary implementation or not. Anyone want to take a look and see if you can help track this down?
For a simple backing store to drive a simple SwiftUI app displaying a List of data, the OrderedDictionary (from swift-collections) has the advantage of giving us the option to access (by an arbitrary key) in constant time. The OrderedDictionary exposes a values property that conforms to RandomAccessCollection. Since we can pass a RandomAccessCollection as data to generate a List, we should hope to be able to pass the OrderedDictionary.values the same way we would pass an Array.
This is leading to some strange performance problems that seem to happen when our List is contained in a NavigationStack (and we push and pop a detail view on the stack). Here is a simple example:
import Collections
import SwiftUI
let NavigationDemoRange = 0...999999
extension Int: Identifiable {
public var id: Self {
return self
}
}
@main struct NavigationDemoApp: App {
let dictionary = OrderedDictionary(
uniqueKeys: NavigationDemoRange,
values: NavigationDemoRange
)
}
extension NavigationDemoApp {
var body: some Scene {
WindowGroup {
NavigationStack {
List(
self.dictionary.values,
rowContent: { element in
NavigationLink(
String(element),
value: element
)
}
).navigationDestination(
for: Int.self,
destination: { element in
Text(
String(element)
)
}
)
}
}
}
}
In this example, we start by defining a NavigationDemoRange that we use to generate 1MM data elements. Our data element is a simple Int, which we mark as Identifiable to make it easier to pass to a List. We then construct a simple OrderedDictionary instance, where every key simply maps to itself (and the values property preserves the intended ordering).
We then create (and present) a NavigationStack that wraps a List that displays every element from our OrderedDictionary.values. We add a simple NavigationLink (and pair with a navigationDestination) to display a "detail view" (which is just a Text element to display the Int).
When we launch this app (on simulator or device), we see a significant hang when tapping an item from the list (pushing a detail view). Popping back appears not to hang, but then pushing another item hangs again.
When we inspect instruments to troubleshoot this hang, we see that our app is spending a lot of time testing our OrderedDictionary.values for equality:
static OrderedDictionary.Values<>.== infix(_:_:)
Adding a breakpoint here and running our app shows that this equality check happens 7 times when the app is launched, 5 times when the detail is pushed, and 5 times when the detail is popped. That is 17 total equality checks to display a list, push a detail, and pop back all driven from data that we defined as immutable (a let constant).
If we try and implement our app on the alternative NavigationLink API (before the introduction of navigationDestination), we see different results. Now, our app launches and we see a significant hang on both pushing and popping a detail view. Instruments confirms we are still spending a lot of the on equality checks. Adding a breakpoint on the check shows us this equality check happens 1 time when the app launches, 24 times when the detail view is pushed, and 12 times when the app is popped. That is 37 total equality checks against data that is (still) an immutable let constant.
We set our NavigationDemoRange to 1MM elements to exaggerate performance problems, but for any amount of data we still see the equality checks occurring the same amount of times.
The OrderedDictionary.values type exposes an elements property that wraps the values with an Array. When we pass OrderedDictionary.values.elements to our List, our performance problems go away:
extension NavigationDemoApp {
var body: some Scene {
WindowGroup {
NavigationStack {
List(
self.dictionary.values.elements,
rowContent: { element in
NavigationLink(
String(element)
) {
Text(
String(element)
)
}
}
)
}
}
}
}
FWIW, we see the exact same performance problems (number of equality checks) when passing in Collections.OrderedSet (and the problems go away when we pass the OrderedSet.elements property).
That was NavigationStack. What happens if we try this same test with NavigationView?
extension NavigationDemoApp {
var body: some Scene {
WindowGroup {
NavigationView {
List(
self.dictionary.values,
rowContent: { element in
NavigationLink(
String(element)
) {
Text(
String(element)
)
}
}
)
}
}
}
}
This looks much faster with no noticeable performance problems. Setting a breakpoint confirms that equality is checked once on app launch. Pushing and popping perform no equality checks.
Here is what we know (so far):
OrderedDictionary.Values performs poorly when List is paired with NavigationStack.
OrderedDictionary.Values performs well when List is paired with NavigationView.
Array performs well when List is paired with NavigationStack or NavigationView.
What could be causing these performance problems? It's not clear. Most of the sample code from Apple passes an Array when constructing a List, but the List constructor just takes an arbitrary RandomAccessCollection without telling us there are performance implications from rolling our own RandomAccessCollection outside the system Swift types.
Is there some kind of bug in the implementation of OrderedDictionary.Values (and OrderedSet) that would lead to this performance problem? If that was true, why would that performance problem only seem to show up in NavigationStack (and not NavigationView)?
If it's not safe to pass arbitrary RandomAccessCollection types to List, should we always wrap any collection in an Array before constructing a List?
Any more thoughts about this? Any ideas where the performance problem could be caused from?
These have all been tested on 17.1 iPhone simulator and device. The swift-collections version is 1.0.5
Thanks!