Replace uses of the Runtime library with stdlib (#370)

This should allow us to remove the Runtime dependency eventually, which seems to be unstable, especially across different platforms and Swift versions.

Seems to resolve in one instance https://github.com/TokamakUI/Tokamak/issues/367. There are a few other places where `typeInfo` is still used, I'll clean that up in a follow-up PR.

* Replace uses of the Runtime library with stdlib

* Remove irrelevant Runtime library imports

* Add TokamakCoreBenchmark target
This commit is contained in:
Max Desiatov 2021-01-24 15:26:51 +00:00 committed by GitHub
parent 9549282e53
commit e04b7934fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 66 additions and 46 deletions

View File

@ -119,6 +119,13 @@ let package = Package(
"TokamakCore",
]
),
.target(
name: "TokamakCoreBenchmark",
dependencies: [
"Benchmark",
"TokamakCore",
]
),
.target(
name: "TokamakStaticHTMLBenchmark",
dependencies: [

View File

@ -1,4 +1,4 @@
// Copyright 2020 Tokamak contributors
// Copyright 2020-2021 Tokamak contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -16,7 +16,6 @@
//
import CombineShim
import Runtime
/// Provides the ability to set the title of the Scene.
public protocol _TitledApp {

View File

@ -1,4 +1,4 @@
// Copyright 2020 Tokamak contributors
// Copyright 2020-2021 Tokamak contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -15,8 +15,6 @@
// Created by Carson Katri on 7/19/20.
//
import Runtime
public struct _AnyScene: Scene {
/** The result type of `bodyClosure` allowing to disambiguate between scenes that
produce other scenes or scenes that only produce containing views.
@ -63,13 +61,8 @@ public struct _AnyScene: Scene {
// swiftlint:disable:next force_cast
bodyClosure = { .scene(_AnyScene(($0 as! S).body)) }
}
// FIXME: no idea if using `mangledName` is reliable, but seems to be the only way to get
// a name of a type constructor in runtime. Should definitely check if these are different
// across modules, otherwise can cause problems with scenes with same names in different
// modules.
// swiftlint:disable:next force_try
typeConstructorName = try! typeInfo(of: type).mangledName
typeConstructorName = TokamakCore.typeConstructorName(type)
}
}

View File

@ -1,4 +1,4 @@
// Copyright 2020 Tokamak contributors
// Copyright 2020-2021 Tokamak contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -15,8 +15,6 @@
// Created by Carson Katri on 7/17/20.
//
import Runtime
public protocol DynamicProperty {
mutating func update()
}

View File

@ -1,4 +1,4 @@
// Copyright 2018-2020 Tokamak contributors
// Copyright 2018-2021 Tokamak contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -16,7 +16,6 @@
//
import CombineShim
import Runtime
// This is very similar to `MountedCompositeView`. However, the `mountedBody`
// is the computed content of the specified `Scene`, instead of having child

View File

@ -1,4 +1,4 @@
// Copyright 2018-2020 Tokamak contributors
// Copyright 2018-2021 Tokamak contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -16,7 +16,6 @@
//
import CombineShim
import Runtime
final class MountedCompositeView<R: Renderer>: MountedCompositeElement<R> {
override func mount(

View File

@ -1,4 +1,4 @@
// Copyright 2018-2020 Tokamak contributors
// Copyright 2018-2021 Tokamak contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -18,7 +18,7 @@
import Runtime
/// The container for any of the possible `MountedElement` types
enum MountedElementKind {
private enum MountedElementKind {
case app(_AnyApp)
case scene(_AnyScene)
case view(AnyView)

View File

@ -1,4 +1,4 @@
// Copyright 2018-2020 Tokamak contributors
// Copyright 2018-2021 Tokamak contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -15,8 +15,6 @@
// Created by Max Desiatov on 03/12/2018.
//
import Runtime
/* A representation of a `View`, which has a `body` of type `Never`, stored in the tree of mounted
views by `StackReconciler`.
*/

View File

@ -1,4 +1,4 @@
// Copyright 2020 Tokamak contributors
// Copyright 2020-2021 Tokamak contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import Runtime
final class MountedScene<R: Renderer>: MountedCompositeElement<R> {
let title: String?

View File

@ -0,0 +1,26 @@
// Copyright 2021 Tokamak contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/** Returns name of a given unapplied generic type. `Button<Text>` and
`Button<Image>` types are different, but when reconciling the tree of mounted views
they are treated the same, thus the `Button` part of the type (the type constructor)
is returned.
*/
public func typeConstructorName(_ type: Any.Type) -> String {
// FIXME: no idea if this calculation is reliable, but seems to be the only way to get
// a name of a type constructor in runtime. Should definitely check if these are different
// across modules, otherwise can cause problems with views with same names in different
// modules.
String(String(describing: type).prefix { $0 != "<" })
}

View File

@ -203,7 +203,7 @@ public final class StackReconciler<R: Renderer> {
}.store(in: &mountedApp.persistentSubscriptions)
}
func render<T>(
private func render<T>(
compositeElement: MountedCompositeElement<R>,
body bodyKeypath: ReferenceWritableKeyPath<MountedCompositeElement<R>, Any>,
result: KeyPath<MountedCompositeElement<R>, (Any) -> T>
@ -265,14 +265,8 @@ public final class StackReconciler<R: Renderer> {
case let (mountedChild?, childBody):
let childBodyType = getElementType(childBody)
// FIXME: no idea if using `mangledName` is reliable, but seems to be the only way to get
// a name of a type constructor in runtime. Should definitely check if these are different
// across modules, otherwise can cause problems with views with same names in different
// modules.
// new child has the same type as existing child
// swiftlint:disable:next force_try
if try! mountedChild.typeConstructorName == typeInfo(of: childBodyType).mangledName {
if mountedChild.typeConstructorName == typeConstructorName(childBodyType) {
updateChild(mountedChild)
mountedChild.update(with: self)
} else {

View File

@ -1,4 +1,4 @@
// Copyright 2020 Tokamak contributors
// Copyright 2020-2021 Tokamak contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -15,8 +15,6 @@
// Created by Max Desiatov on 08/04/2020.
//
import Runtime
/// A type-erased view.
public struct AnyView: View {
/// The type of the underlying `view`.
@ -48,13 +46,7 @@ public struct AnyView: View {
} else {
type = V.self
// FIXME: no idea if using `mangledName` is reliable, but seems to be the only way to get
// a name of a type constructor in runtime. Should definitely check if these are different
// across modules, otherwise can cause problems with views with same names in different
// modules.
// swiftlint:disable:next force_try
typeConstructorName = try! typeInfo(of: type).mangledName
typeConstructorName = TokamakCore.typeConstructorName(type)
bodyType = V.Body.self
self.view = view

View File

@ -0,0 +1,16 @@
import Benchmark
import Runtime
import TokamakCore
private let bigType = NavigationView<HStack<VStack<Button<Text>>>>.self
benchmark("mangledName Runtime") {
// swiftlint:disable:next force_try
_ = try! typeInfo(of: bigType).mangledName
}
benchmark("typeConstructorName TokamakCore") {
_ = typeConstructorName(bigType)
}
Benchmark.main()

View File

@ -1,4 +1,4 @@
// Copyright 2020 Tokamak contributors
// Copyright 2020-2021 Tokamak contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import Runtime
import TokamakCore
private protocol AnyModifiedContent {

View File

@ -2,5 +2,7 @@
set -eux
swift build -c release --product TokamakCoreBenchmark
./.build/release/TokamakCoreBenchmark
swift build -c release --product TokamakStaticHTMLBenchmark
./.build/release/TokamakStaticHTMLBenchmark