1653649754
Esta publicación de blog explicará cómo imprimir valores de entorno de manera eficiente con fines de depuración. Por lo tanto, puede inspeccionar qué valor real se usa en un nivel de jerarquía de vista específico.
Un resumen rápido sobre el entorno de SwiftUI
SwiftUI usa el entorno para pasar valores para tipos predefinidos a la jerarquía de vistas.
Al usar @Environment
el contenedor de propiedades, podemos leer y suscribirnos a los cambios del valor seleccionado.
struct ContentView: View {
@Environment(\.sizeCategory) var sizeCategory
@Environment(\.horizontalSizeClass) var horizontalSizeClass
@Environment(\.scenePhase) var scenePhase
var body: some View { ... }
}
También podemos crear sus valores de entorno personalizados .
private struct MagicColorKey: EnvironmentKey {
static let defaultValue = Color(.secondarySystemBackground)
}
extension EnvironmentValues {
var magicColor: Color {
get { self[MagicColorKey.self] }
set { self[MagicColorKey.self] = newValue }
}
}
Estamos utilizando el modificador de vista de entorno para inyectar nuestro valor de entorno personalizado en el entorno de SwiftUI. Los valores se pueden anular en un nivel de jerarquía de vista inferior. Aquí hay un ejemplo:
struct ContentView: View {
var body: some View {
VStack {
HStack {
Text("What is the magic color for this leaf node?")
}
.environment(\.magicColor, .blue)
}
.environment(\.magicColor, .red)
}
}
Volcar un valor simple
La opción de depuración discreta es usar la transformEnvironment
función en combinación con la dump
función de la biblioteca estándar de Swift.
Text("What is the magic color for this leaf node?")
.transformEnvironment(\.magicColor) { dump($0) }
Dump
imprimirá el contenido del objeto dado usando su espejo en la salida estándar. El resultado impreso en la consola de depuración de Xcode muestra que, en este nivel de jerarquía de vista, el color mágico es blue
(como se esperaba).
▿ blue
▿ provider: SwiftUI.(unknown context at $7fff5dc005b0).ColorBox<SwiftUI.SystemColorType> #0
- super: SwiftUI.AnyColorBox
- super: SwiftUI.AnyShapeStyleBox
- base: SwiftUI.SystemColorType.blue
Volcar todos los valores
Chris Eidhoff comparte un fragmento de código en su artículo SwiftUI: configuración de los valores del entorno para inspeccionar el entorno actual en busca de una vista utilizando la siguiente vista contenedora de utilidades:
struct DumpingEnvironment<V: View>: View {
@Environment(\.self) var env
let content: V
var body: some View {
dump(env)
return content
}
}
Por ejemplo, podríamos volcar el entorno de un nodo hoja de la vista anterior:
struct ContentView: View {
var body: some View {
VStack {
HStack {
DumpingEnvironment(content: Text("What is the magic color for this leaf node?"))
}
.environment(\.magicColor, .blue)
}
.environment(\.magicColor, .red)
}
}
Esta solución funciona bien y se puede copiar y usar fácilmente en otros proyectos. Pero cuando ejecutamos el código, imprime un árbol colosal de una lista de propiedades serializadas.
▿ _plist: [EnvironmentPropertyKey<MagicColorKey> = blue, EnvironmentPropertyKey<MagicColorKey> = red, EnvironmentPropertyKey<RedactionReasonsKey> = RedactionReasons(rawValue: 2), EnvironmentPropertyKey<SceneStorageValuesKey> = Optional(SwiftUI.WeakBox<SwiftUI.SceneStorageValues>(base: Optional(SwiftUI.SceneStorageValues))), EnvironmentPropertyKey<StoreKey<SceneBridge>> = Optional(SceneBridge: rootViewController = Optional(<_TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x7f915800c530>)), EnvironmentPropertyKey<AppNavigationAuthorityKey> = Optional(SwiftUI.WeakBox<SwiftUI.AppNavigationAuthority>(base: Optional(SwiftUI.AppNavigationAuthority))), EnvironmentPropertyKey<EditModeKey> = Optional(SwiftUI.Binding<SwiftUI.EditMode>(transaction: SwiftUI.Transaction(plist: []), location: SwiftUI.StoredLocation<SwiftUI.EditMode>, _value: SwiftUI.EditMode.inactive)), EnvironmentPropertyKey<InputAccessoryKey> = WeakBox<InputAccessoryGenerator<SwiftUIToolbar>>(base: nil), EnvironmentPropertyKey<CanTakeFocusKey> = false, EnvironmentPropertyKey<IsPlatformFocusSystemEnabled> = false, EnvironmentPropertyKey<IsFocusedKey> = false, EnvironmentPropertyKey<FocusBridgeKey> = WeakBox<FocusBridge>(base: Optional(SwiftUI.FocusBridge)), EnvironmentPropertyKey<AllowedBehaviorsKey> = HostingControllerAllowedBehaviors(rawValue: 16), EnvironmentPropertyKey<ActiveContextMenuKey> = ViewIdentity(seed: 0), EnvironmentPropertyKey<Key> = Optional(SwiftUI.NavigationAuthority(host: Optional(<_TtGC7SwiftUI14_UIHostingViewGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x7f91581066f0; frame = (0 0; 390 844); autoresize = W+H; gestureRecognizers = <NSArray: 0x600003b52fa0>; layer = <CALayer: 0x6000035073c0>>))), EnvironmentPropertyKey<PresentationModeKey> = Binding<PresentationMode>(transaction: SwiftUI.Transaction(plist: []), location: SwiftUI.LocationBox<SwiftUI.FunctionalLocation<SwiftUI.PresentationMode>>, _value: SwiftUI.PresentationMode(isPresented: false)), EnvironmentPropertyKey<AccessibilityRequestFocusKey> = AccessibilityRequestFocusAction(onAccessibilityFocus: nil), EnvironmentPropertyKey<AccentColorKey> = Optional(#007AFFFF), EnvironmentPropertyKey<HostingViewOpenURLActionKey> = Optional(SwiftUI.OpenURLAction(handler: SwiftUI.OpenURLAction.Handler.system((Function)), isDefault: false)), EnvironmentPropertyKey<UndoManagerKey> = Optional(<NSUndoManager: 0x60000161a620>), EnvironmentPropertyKey<SceneSessionKey> = Optional(SwiftUI.WeakBox<__C.UISceneSession>(base: Optional(<UISceneSession: 0x600002066140; scene = <UIWindowScene: 0x7f9158008d60; sceneIdentifier: "sceneID:us.eidinger.DateExample-F8369E9A-67DA-48FC-BAB6-EC1A5D8D56CC">; role = UIWindowSceneSessionRoleApplication; sceneConfiguration = <UISceneConfiguration: 0x600002066240>; persistentIdentifier = F8369E9A-67DA-48FC-BAB6-EC1A5D8D56CC; userInfo = <(null): 0x0>))), EnvironmentPropertyKey<SystemColorSchemeKey> = light, EnvironmentPropertyKey<ExplicitPreferredColorSchemeKey> = nil, EnvironmentPropertyKey<AccessibilityLargeContentViewerKey> = false, EnvironmentPropertyKey<EnabledTechnologiesKey> = AccessibilityTechnologies(technologySet: SwiftUI.(unknown context at $7fff5dc3e438).AccessibilityTechnologySet(rawValue: 0)), EnvironmentPropertyKey<AccessibilityButtonShapesKey> = false, EnvironmentPropertyKey<AccessibilityPrefersCrossFadeTransitionsKey> = false, EnvironmentPropertyKey<AccessibilityInvertColorsKey> = false, EnvironmentPropertyKey<AccessibilityReduceMotionKey> = false, EnvironmentPropertyKey<AccessibilityReduceTransparencyKey> = false, EnvironmentPropertyKey<AccessibilityDifferentiateWithoutColorKey> = false, EnvironmentPropertyKey<BackgroundInfoKey> = BackgroundInfo(layer: 0, groupCount: 0), EnvironmentPropertyKey<VerticalUserInterfaceSizeClassKey> = Optional(SwiftUI.UserInterfaceSizeClass.regular), EnvironmentPropertyKey<HorizontalUserInterfaceSizeClassKey> = Optional(SwiftUI.UserInterfaceSizeClass.compact), EnvironmentPropertyKey<DisplayScaleKey> = 3.0, EnvironmentPropertyKey<ColorSchemeKey> = light, EnvironmentPropertyKey<ColorSchemeContrastKey> = standard, EnvironmentPropertyKey<DisplayGamutKey> = displayP3, EnvironmentPropertyKey<LegibilityWeightKey> = Optional(SwiftUI.LegibilityWeight.regular), EnvironmentPropertyKey<DynamicTypeSizeKey> = large, EnvironmentPropertyKey<LayoutDirectionKey> = leftToRight, EnvironmentPropertyKey<ScenePhaseKey> = inactive, EnvironmentPropertyKey<TimeZoneKey> = America/Los_Angeles (fixed (equal to current)), EnvironmentPropertyKey<CalendarKey> = gregorian (current), EnvironmentPropertyKey<LocaleKey> = en_US (current)]
▿ elements: Optional(EnvironmentPropertyKey<MagicColorKey> = blue)
▿ some: EnvironmentPropertyKey<MagicColorKey> = blue #0
▿ super: SwiftUI.PropertyList.Element
- keyType: SwiftUI.(unknown context at $7fff5dca9ff0).EnvironmentPropertyKey<DateExample.(unknown context at $1011eaf88).MagicColorKey> #1
- before: nil
▿ after: Optional(EnvironmentPropertyKey<MagicColorKey> = red)
▿ some: EnvironmentPropertyKey<MagicColorKey> = red #2
▿ super: SwiftUI.PropertyList.Element
- keyType: SwiftUI.(unknown context at $7fff5dca9ff0).EnvironmentPropertyKey<DateExample.(unknown context at $1011eaf88).MagicColorKey> #1
- before: nil
▿ after: Optional(EnvironmentPropertyKey<RedactionReasonsKey> = RedactionReasons(rawValue: 2))
▿ some: EnvironmentPropertyKey<RedactionReasonsKey> = RedactionReasons(rawValue: 2) #3
▿ ...
¡Demasiado grande!
Se vuelca el entorno completo con todos sus valores en todos los niveles de jerarquía.
Necesitamos leer la salida de arriba a abajo e ignorar las entradas posteriores para la misma EnvironmentPropertyKey
(ya que el valor de nivel superior para a EnvironmentPropertyKey
es el que se usa)
En la mayoría de los casos, una forma más eficaz es utilizar una función personalizada para imprimir los valores del entorno.
Mi enfoque inicial fue split(separator: ",")
el description
de conforme EnvironmentValues
a .EnvironmentValuesCustomStringConvertible
Sin embargo, esto no funciona correctamente para las descripciones de valores que contienen una coma y, además, la copia impresa todavía contiene varias entradas para la misma clave.
Terminé con la siguiente implementación "suficientemente buena"
struct DumpingEnvironment<Content>: View where Content: View {
@Environment(\.self) var environmentValues
var optimizePrint = true
let content: Content
init(optimized: Bool = true, content: Content) {
self.optimizePrint = optimized
self.content = content
}
var body: some View {
if optimizePrint {
printing(environmentValues)
} else {
dump(environmentValues)
}
return content
}
func printing(_ environmentValues: EnvironmentValues) {
print("--- Environment Values - BEGIN ---")
let keyValuePair = environmentValues.asTextualRepresentationWithNonReaptingKeys
keyValuePair.forEach { print($0) }
print("--- Environment Values - END ---")
}
}
extension EnvironmentValues {
var asTextualRepresentationWithNonReaptingKeys: [String] {
// split description into lines starting with EnvironmentPropertyKey
let lineIndices = description.indices(of: "EnvironmentPropertyKey")
var entries: [String] = []
for (idx, beginIndexValue) in lineIndices.enumerated() {
let next: Int = idx + 1
if idx == lineIndices.count - 1 {
continue
}
let nextIndexValue = lineIndices[next]
let beginIndex = description.index(description.startIndex, offsetBy: beginIndexValue)
let endIndex = description.index(description.startIndex, offsetBy: nextIndexValue)
guard let line = String(description[beginIndex ... endIndex]).components(separatedBy: ", E").first else { continue }
entries.append(line)
}
// filter out lines with repeating EnvironmentPropertyKey's because only the top level value is relevant
var processedKeys: [String] = []
entries = entries.filter { line in
var isIncluded = false
guard let key = line.components(separatedBy: "=").first else {
return isIncluded
}
if !processedKeys.contains(key) {
isIncluded = true
}
processedKeys.append(key)
return isIncluded
}
return entries
}
}
// Credit: https://gist.github.com/BetterProgramming/ac4f639c915ef0560fcca5208d9456f9#file-firstoccur-swift
extension String {
func indices(of occurrence: String) -> [Int] {
var indices = [Int]()
var position = startIndex
while let range = range(of: occurrence, range: position ..< endIndex) {
let i = distance(from: startIndex,
to: range.lowerBound)
indices.append(i)
let offset = occurrence.distance(from: occurrence.startIndex,
to: occurrence.endIndex) - 1
guard let after = index(range.lowerBound,
offsetBy: offset,
limitedBy: endIndex)
else {
break
}
position = index(after: after)
}
return indices
}
}
Ahora se imprimen ~ 50 líneas (en lugar de ~ 1160 líneas !!!).
Ejemplo:
--- Environment Values - BEGIN ---
EnvironmentPropertyKey<MagicColorKey> = blue
EnvironmentPropertyKey<SceneStorageValuesKey> = Optional(SwiftUI.WeakBox<SwiftUI.SceneStorageValues>(base: Optional(SwiftUI.SceneStorageValues)))
EnvironmentPropertyKey<StoreKey<SceneBridge>> = Optional(SceneBridge: rootViewController = Optional(<_TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x7f97bcb0aef0>))
EnvironmentPropertyKey<AppNavigationAuthorityKey> = Optional(SwiftUI.WeakBox<SwiftUI.AppNavigationAuthority>(base: Optional(SwiftUI.AppNavigationAuthority)))
EnvironmentPropertyKey<EditModeKey> = Optional(SwiftUI.Binding<SwiftUI.EditMode>(transaction: SwiftUI.Transaction(plist: []), location: SwiftUI.StoredLocation<SwiftUI.EditMode>, _value: SwiftUI.EditMode.inactive))
EnvironmentPropertyKey<InputAccessoryKey> = WeakBox<InputAccessoryGenerator<SwiftUIToolbar>>(base: nil)
EnvironmentPropertyKey<CanTakeFocusKey> = true
EnvironmentPropertyKey<IsPlatformFocusSystemEnabled> = false
EnvironmentPropertyKey<IsFocusedKey> = false
EnvironmentPropertyKey<FocusBridgeKey> = WeakBox<FocusBridge>(base: Optional(SwiftUI.FocusBridge))
EnvironmentPropertyKey<AllowedBehaviorsKey> = HostingControllerAllowedBehaviors(rawValue: 16)
EnvironmentPropertyKey<ActiveContextMenuKey> = ViewIdentity(seed: 0)
EnvironmentPropertyKey<Key> = Optional(SwiftUI.NavigationAuthority(host: Optional(<_TtGC7SwiftUI14_UIHostingViewGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x7f97bcb0b670; frame = (0 0; 390 844); autoresize = W+H; gestureRecognizers = <NSArray: 0x600000d47cf0>; layer = <CALayer: 0x60000032e980>>)))
EnvironmentPropertyKey<PresentationModeKey> = Binding<PresentationMode>(transaction: SwiftUI.Transaction(plist: []), location: SwiftUI.LocationBox<SwiftUI.FunctionalLocation<SwiftUI.PresentationMode>>, _value: SwiftUI.PresentationMode(isPresented: false))
EnvironmentPropertyKey<AccessibilityRequestFocusKey> = AccessibilityRequestFocusAction(onAccessibilityFocus: nil)
EnvironmentPropertyKey<AccentColorKey> = Optional(#007AFFFF)
EnvironmentPropertyKey<HostingViewOpenURLActionKey> = Optional(SwiftUI.OpenURLAction(handler: SwiftUI.OpenURLAction.Handler.system((Function)), isDefault: false))
EnvironmentPropertyKey<UndoManagerKey> = Optional(<NSUndoManager: 0x600002039270>)
EnvironmentPropertyKey<SceneSessionKey> = Optional(SwiftUI.WeakBox<__C.UISceneSession>(base: Optional(<UISceneSession: 0x600001641200; scene = <UIWindowScene: 0x7f97bcb0a040; sceneIdentifier: "sceneID:us.eidinger.DateExample-F8369E9A-67DA-48FC-BAB6-EC1A5D8D56CC">; role = UIWindowSceneSessionRoleApplication; sceneConfiguration = <UISceneConfiguration: 0x600001641300>; persistentIdentifier = F8369E9A-67DA-48FC-BAB6-EC1A5D8D56CC; userInfo = <(null): 0x0>)))
EnvironmentPropertyKey<SystemColorSchemeKey> = light
EnvironmentPropertyKey<ExplicitPreferredColorSchemeKey> = nil
EnvironmentPropertyKey<AccessibilityLargeContentViewerKey> = false
EnvironmentPropertyKey<EnabledTechnologiesKey> = AccessibilityTechnologies(technologySet: SwiftUI.(unknown context at $7fff5dc3e438).AccessibilityTechnologySet(rawValue: 0))
EnvironmentPropertyKey<AccessibilityButtonShapesKey> = false
EnvironmentPropertyKey<AccessibilityPrefersCrossFadeTransitionsKey> = false
EnvironmentPropertyKey<AccessibilityInvertColorsKey> = false
EnvironmentPropertyKey<AccessibilityReduceMotionKey> = false
EnvironmentPropertyKey<AccessibilityReduceTransparencyKey> = false
EnvironmentPropertyKey<AccessibilityDifferentiateWithoutColorKey> = false
EnvironmentPropertyKey<BackgroundInfoKey> = BackgroundInfo(layer: 0, groupCount: 0)
EnvironmentPropertyKey<VerticalUserInterfaceSizeClassKey> = Optional(SwiftUI.UserInterfaceSizeClass.regular)
EnvironmentPropertyKey<HorizontalUserInterfaceSizeClassKey> = Optional(SwiftUI.UserInterfaceSizeClass.compact)
EnvironmentPropertyKey<DisplayScaleKey> = 3.0
EnvironmentPropertyKey<ColorSchemeKey> = light
EnvironmentPropertyKey<ColorSchemeContrastKey> = standard
EnvironmentPropertyKey<DisplayGamutKey> = displayP3
EnvironmentPropertyKey<LegibilityWeightKey> = Optional(SwiftUI.LegibilityWeight.regular)
EnvironmentPropertyKey<DynamicTypeSizeKey> = large
EnvironmentPropertyKey<LayoutDirectionKey> = leftToRight
EnvironmentPropertyKey<ScenePhaseKey> = active
EnvironmentPropertyKey<TimeZoneKey> = America/Los_Angeles (fixed (equal to current))
EnvironmentPropertyKey<CalendarKey> = gregorian (current)
--- Environment Values - END ---
Los trabajos internos del manejo del entorno SwiftUI están ocultos. Esto está bien para la mayoría de las situaciones. Un inconveniente notable es que el resultado impreso Font
no es útil ya que su descripción no contiene el nombre de fuente/información de estilo.
Fuente del artículo original en https://betterprogramming.pub/
1653649754
Esta publicación de blog explicará cómo imprimir valores de entorno de manera eficiente con fines de depuración. Por lo tanto, puede inspeccionar qué valor real se usa en un nivel de jerarquía de vista específico.
Un resumen rápido sobre el entorno de SwiftUI
SwiftUI usa el entorno para pasar valores para tipos predefinidos a la jerarquía de vistas.
Al usar @Environment
el contenedor de propiedades, podemos leer y suscribirnos a los cambios del valor seleccionado.
struct ContentView: View {
@Environment(\.sizeCategory) var sizeCategory
@Environment(\.horizontalSizeClass) var horizontalSizeClass
@Environment(\.scenePhase) var scenePhase
var body: some View { ... }
}
También podemos crear sus valores de entorno personalizados .
private struct MagicColorKey: EnvironmentKey {
static let defaultValue = Color(.secondarySystemBackground)
}
extension EnvironmentValues {
var magicColor: Color {
get { self[MagicColorKey.self] }
set { self[MagicColorKey.self] = newValue }
}
}
Estamos utilizando el modificador de vista de entorno para inyectar nuestro valor de entorno personalizado en el entorno de SwiftUI. Los valores se pueden anular en un nivel de jerarquía de vista inferior. Aquí hay un ejemplo:
struct ContentView: View {
var body: some View {
VStack {
HStack {
Text("What is the magic color for this leaf node?")
}
.environment(\.magicColor, .blue)
}
.environment(\.magicColor, .red)
}
}
Volcar un valor simple
La opción de depuración discreta es usar la transformEnvironment
función en combinación con la dump
función de la biblioteca estándar de Swift.
Text("What is the magic color for this leaf node?")
.transformEnvironment(\.magicColor) { dump($0) }
Dump
imprimirá el contenido del objeto dado usando su espejo en la salida estándar. El resultado impreso en la consola de depuración de Xcode muestra que, en este nivel de jerarquía de vista, el color mágico es blue
(como se esperaba).
▿ blue
▿ provider: SwiftUI.(unknown context at $7fff5dc005b0).ColorBox<SwiftUI.SystemColorType> #0
- super: SwiftUI.AnyColorBox
- super: SwiftUI.AnyShapeStyleBox
- base: SwiftUI.SystemColorType.blue
Volcar todos los valores
Chris Eidhoff comparte un fragmento de código en su artículo SwiftUI: configuración de los valores del entorno para inspeccionar el entorno actual en busca de una vista utilizando la siguiente vista contenedora de utilidades:
struct DumpingEnvironment<V: View>: View {
@Environment(\.self) var env
let content: V
var body: some View {
dump(env)
return content
}
}
Por ejemplo, podríamos volcar el entorno de un nodo hoja de la vista anterior:
struct ContentView: View {
var body: some View {
VStack {
HStack {
DumpingEnvironment(content: Text("What is the magic color for this leaf node?"))
}
.environment(\.magicColor, .blue)
}
.environment(\.magicColor, .red)
}
}
Esta solución funciona bien y se puede copiar y usar fácilmente en otros proyectos. Pero cuando ejecutamos el código, imprime un árbol colosal de una lista de propiedades serializadas.
▿ _plist: [EnvironmentPropertyKey<MagicColorKey> = blue, EnvironmentPropertyKey<MagicColorKey> = red, EnvironmentPropertyKey<RedactionReasonsKey> = RedactionReasons(rawValue: 2), EnvironmentPropertyKey<SceneStorageValuesKey> = Optional(SwiftUI.WeakBox<SwiftUI.SceneStorageValues>(base: Optional(SwiftUI.SceneStorageValues))), EnvironmentPropertyKey<StoreKey<SceneBridge>> = Optional(SceneBridge: rootViewController = Optional(<_TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x7f915800c530>)), EnvironmentPropertyKey<AppNavigationAuthorityKey> = Optional(SwiftUI.WeakBox<SwiftUI.AppNavigationAuthority>(base: Optional(SwiftUI.AppNavigationAuthority))), EnvironmentPropertyKey<EditModeKey> = Optional(SwiftUI.Binding<SwiftUI.EditMode>(transaction: SwiftUI.Transaction(plist: []), location: SwiftUI.StoredLocation<SwiftUI.EditMode>, _value: SwiftUI.EditMode.inactive)), EnvironmentPropertyKey<InputAccessoryKey> = WeakBox<InputAccessoryGenerator<SwiftUIToolbar>>(base: nil), EnvironmentPropertyKey<CanTakeFocusKey> = false, EnvironmentPropertyKey<IsPlatformFocusSystemEnabled> = false, EnvironmentPropertyKey<IsFocusedKey> = false, EnvironmentPropertyKey<FocusBridgeKey> = WeakBox<FocusBridge>(base: Optional(SwiftUI.FocusBridge)), EnvironmentPropertyKey<AllowedBehaviorsKey> = HostingControllerAllowedBehaviors(rawValue: 16), EnvironmentPropertyKey<ActiveContextMenuKey> = ViewIdentity(seed: 0), EnvironmentPropertyKey<Key> = Optional(SwiftUI.NavigationAuthority(host: Optional(<_TtGC7SwiftUI14_UIHostingViewGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x7f91581066f0; frame = (0 0; 390 844); autoresize = W+H; gestureRecognizers = <NSArray: 0x600003b52fa0>; layer = <CALayer: 0x6000035073c0>>))), EnvironmentPropertyKey<PresentationModeKey> = Binding<PresentationMode>(transaction: SwiftUI.Transaction(plist: []), location: SwiftUI.LocationBox<SwiftUI.FunctionalLocation<SwiftUI.PresentationMode>>, _value: SwiftUI.PresentationMode(isPresented: false)), EnvironmentPropertyKey<AccessibilityRequestFocusKey> = AccessibilityRequestFocusAction(onAccessibilityFocus: nil), EnvironmentPropertyKey<AccentColorKey> = Optional(#007AFFFF), EnvironmentPropertyKey<HostingViewOpenURLActionKey> = Optional(SwiftUI.OpenURLAction(handler: SwiftUI.OpenURLAction.Handler.system((Function)), isDefault: false)), EnvironmentPropertyKey<UndoManagerKey> = Optional(<NSUndoManager: 0x60000161a620>), EnvironmentPropertyKey<SceneSessionKey> = Optional(SwiftUI.WeakBox<__C.UISceneSession>(base: Optional(<UISceneSession: 0x600002066140; scene = <UIWindowScene: 0x7f9158008d60; sceneIdentifier: "sceneID:us.eidinger.DateExample-F8369E9A-67DA-48FC-BAB6-EC1A5D8D56CC">; role = UIWindowSceneSessionRoleApplication; sceneConfiguration = <UISceneConfiguration: 0x600002066240>; persistentIdentifier = F8369E9A-67DA-48FC-BAB6-EC1A5D8D56CC; userInfo = <(null): 0x0>))), EnvironmentPropertyKey<SystemColorSchemeKey> = light, EnvironmentPropertyKey<ExplicitPreferredColorSchemeKey> = nil, EnvironmentPropertyKey<AccessibilityLargeContentViewerKey> = false, EnvironmentPropertyKey<EnabledTechnologiesKey> = AccessibilityTechnologies(technologySet: SwiftUI.(unknown context at $7fff5dc3e438).AccessibilityTechnologySet(rawValue: 0)), EnvironmentPropertyKey<AccessibilityButtonShapesKey> = false, EnvironmentPropertyKey<AccessibilityPrefersCrossFadeTransitionsKey> = false, EnvironmentPropertyKey<AccessibilityInvertColorsKey> = false, EnvironmentPropertyKey<AccessibilityReduceMotionKey> = false, EnvironmentPropertyKey<AccessibilityReduceTransparencyKey> = false, EnvironmentPropertyKey<AccessibilityDifferentiateWithoutColorKey> = false, EnvironmentPropertyKey<BackgroundInfoKey> = BackgroundInfo(layer: 0, groupCount: 0), EnvironmentPropertyKey<VerticalUserInterfaceSizeClassKey> = Optional(SwiftUI.UserInterfaceSizeClass.regular), EnvironmentPropertyKey<HorizontalUserInterfaceSizeClassKey> = Optional(SwiftUI.UserInterfaceSizeClass.compact), EnvironmentPropertyKey<DisplayScaleKey> = 3.0, EnvironmentPropertyKey<ColorSchemeKey> = light, EnvironmentPropertyKey<ColorSchemeContrastKey> = standard, EnvironmentPropertyKey<DisplayGamutKey> = displayP3, EnvironmentPropertyKey<LegibilityWeightKey> = Optional(SwiftUI.LegibilityWeight.regular), EnvironmentPropertyKey<DynamicTypeSizeKey> = large, EnvironmentPropertyKey<LayoutDirectionKey> = leftToRight, EnvironmentPropertyKey<ScenePhaseKey> = inactive, EnvironmentPropertyKey<TimeZoneKey> = America/Los_Angeles (fixed (equal to current)), EnvironmentPropertyKey<CalendarKey> = gregorian (current), EnvironmentPropertyKey<LocaleKey> = en_US (current)]
▿ elements: Optional(EnvironmentPropertyKey<MagicColorKey> = blue)
▿ some: EnvironmentPropertyKey<MagicColorKey> = blue #0
▿ super: SwiftUI.PropertyList.Element
- keyType: SwiftUI.(unknown context at $7fff5dca9ff0).EnvironmentPropertyKey<DateExample.(unknown context at $1011eaf88).MagicColorKey> #1
- before: nil
▿ after: Optional(EnvironmentPropertyKey<MagicColorKey> = red)
▿ some: EnvironmentPropertyKey<MagicColorKey> = red #2
▿ super: SwiftUI.PropertyList.Element
- keyType: SwiftUI.(unknown context at $7fff5dca9ff0).EnvironmentPropertyKey<DateExample.(unknown context at $1011eaf88).MagicColorKey> #1
- before: nil
▿ after: Optional(EnvironmentPropertyKey<RedactionReasonsKey> = RedactionReasons(rawValue: 2))
▿ some: EnvironmentPropertyKey<RedactionReasonsKey> = RedactionReasons(rawValue: 2) #3
▿ ...
¡Demasiado grande!
Se vuelca el entorno completo con todos sus valores en todos los niveles de jerarquía.
Necesitamos leer la salida de arriba a abajo e ignorar las entradas posteriores para la misma EnvironmentPropertyKey
(ya que el valor de nivel superior para a EnvironmentPropertyKey
es el que se usa)
En la mayoría de los casos, una forma más eficaz es utilizar una función personalizada para imprimir los valores del entorno.
Mi enfoque inicial fue split(separator: ",")
el description
de conforme EnvironmentValues
a .EnvironmentValuesCustomStringConvertible
Sin embargo, esto no funciona correctamente para las descripciones de valores que contienen una coma y, además, la copia impresa todavía contiene varias entradas para la misma clave.
Terminé con la siguiente implementación "suficientemente buena"
struct DumpingEnvironment<Content>: View where Content: View {
@Environment(\.self) var environmentValues
var optimizePrint = true
let content: Content
init(optimized: Bool = true, content: Content) {
self.optimizePrint = optimized
self.content = content
}
var body: some View {
if optimizePrint {
printing(environmentValues)
} else {
dump(environmentValues)
}
return content
}
func printing(_ environmentValues: EnvironmentValues) {
print("--- Environment Values - BEGIN ---")
let keyValuePair = environmentValues.asTextualRepresentationWithNonReaptingKeys
keyValuePair.forEach { print($0) }
print("--- Environment Values - END ---")
}
}
extension EnvironmentValues {
var asTextualRepresentationWithNonReaptingKeys: [String] {
// split description into lines starting with EnvironmentPropertyKey
let lineIndices = description.indices(of: "EnvironmentPropertyKey")
var entries: [String] = []
for (idx, beginIndexValue) in lineIndices.enumerated() {
let next: Int = idx + 1
if idx == lineIndices.count - 1 {
continue
}
let nextIndexValue = lineIndices[next]
let beginIndex = description.index(description.startIndex, offsetBy: beginIndexValue)
let endIndex = description.index(description.startIndex, offsetBy: nextIndexValue)
guard let line = String(description[beginIndex ... endIndex]).components(separatedBy: ", E").first else { continue }
entries.append(line)
}
// filter out lines with repeating EnvironmentPropertyKey's because only the top level value is relevant
var processedKeys: [String] = []
entries = entries.filter { line in
var isIncluded = false
guard let key = line.components(separatedBy: "=").first else {
return isIncluded
}
if !processedKeys.contains(key) {
isIncluded = true
}
processedKeys.append(key)
return isIncluded
}
return entries
}
}
// Credit: https://gist.github.com/BetterProgramming/ac4f639c915ef0560fcca5208d9456f9#file-firstoccur-swift
extension String {
func indices(of occurrence: String) -> [Int] {
var indices = [Int]()
var position = startIndex
while let range = range(of: occurrence, range: position ..< endIndex) {
let i = distance(from: startIndex,
to: range.lowerBound)
indices.append(i)
let offset = occurrence.distance(from: occurrence.startIndex,
to: occurrence.endIndex) - 1
guard let after = index(range.lowerBound,
offsetBy: offset,
limitedBy: endIndex)
else {
break
}
position = index(after: after)
}
return indices
}
}
Ahora se imprimen ~ 50 líneas (en lugar de ~ 1160 líneas !!!).
Ejemplo:
--- Environment Values - BEGIN ---
EnvironmentPropertyKey<MagicColorKey> = blue
EnvironmentPropertyKey<SceneStorageValuesKey> = Optional(SwiftUI.WeakBox<SwiftUI.SceneStorageValues>(base: Optional(SwiftUI.SceneStorageValues)))
EnvironmentPropertyKey<StoreKey<SceneBridge>> = Optional(SceneBridge: rootViewController = Optional(<_TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x7f97bcb0aef0>))
EnvironmentPropertyKey<AppNavigationAuthorityKey> = Optional(SwiftUI.WeakBox<SwiftUI.AppNavigationAuthority>(base: Optional(SwiftUI.AppNavigationAuthority)))
EnvironmentPropertyKey<EditModeKey> = Optional(SwiftUI.Binding<SwiftUI.EditMode>(transaction: SwiftUI.Transaction(plist: []), location: SwiftUI.StoredLocation<SwiftUI.EditMode>, _value: SwiftUI.EditMode.inactive))
EnvironmentPropertyKey<InputAccessoryKey> = WeakBox<InputAccessoryGenerator<SwiftUIToolbar>>(base: nil)
EnvironmentPropertyKey<CanTakeFocusKey> = true
EnvironmentPropertyKey<IsPlatformFocusSystemEnabled> = false
EnvironmentPropertyKey<IsFocusedKey> = false
EnvironmentPropertyKey<FocusBridgeKey> = WeakBox<FocusBridge>(base: Optional(SwiftUI.FocusBridge))
EnvironmentPropertyKey<AllowedBehaviorsKey> = HostingControllerAllowedBehaviors(rawValue: 16)
EnvironmentPropertyKey<ActiveContextMenuKey> = ViewIdentity(seed: 0)
EnvironmentPropertyKey<Key> = Optional(SwiftUI.NavigationAuthority(host: Optional(<_TtGC7SwiftUI14_UIHostingViewGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x7f97bcb0b670; frame = (0 0; 390 844); autoresize = W+H; gestureRecognizers = <NSArray: 0x600000d47cf0>; layer = <CALayer: 0x60000032e980>>)))
EnvironmentPropertyKey<PresentationModeKey> = Binding<PresentationMode>(transaction: SwiftUI.Transaction(plist: []), location: SwiftUI.LocationBox<SwiftUI.FunctionalLocation<SwiftUI.PresentationMode>>, _value: SwiftUI.PresentationMode(isPresented: false))
EnvironmentPropertyKey<AccessibilityRequestFocusKey> = AccessibilityRequestFocusAction(onAccessibilityFocus: nil)
EnvironmentPropertyKey<AccentColorKey> = Optional(#007AFFFF)
EnvironmentPropertyKey<HostingViewOpenURLActionKey> = Optional(SwiftUI.OpenURLAction(handler: SwiftUI.OpenURLAction.Handler.system((Function)), isDefault: false))
EnvironmentPropertyKey<UndoManagerKey> = Optional(<NSUndoManager: 0x600002039270>)
EnvironmentPropertyKey<SceneSessionKey> = Optional(SwiftUI.WeakBox<__C.UISceneSession>(base: Optional(<UISceneSession: 0x600001641200; scene = <UIWindowScene: 0x7f97bcb0a040; sceneIdentifier: "sceneID:us.eidinger.DateExample-F8369E9A-67DA-48FC-BAB6-EC1A5D8D56CC">; role = UIWindowSceneSessionRoleApplication; sceneConfiguration = <UISceneConfiguration: 0x600001641300>; persistentIdentifier = F8369E9A-67DA-48FC-BAB6-EC1A5D8D56CC; userInfo = <(null): 0x0>)))
EnvironmentPropertyKey<SystemColorSchemeKey> = light
EnvironmentPropertyKey<ExplicitPreferredColorSchemeKey> = nil
EnvironmentPropertyKey<AccessibilityLargeContentViewerKey> = false
EnvironmentPropertyKey<EnabledTechnologiesKey> = AccessibilityTechnologies(technologySet: SwiftUI.(unknown context at $7fff5dc3e438).AccessibilityTechnologySet(rawValue: 0))
EnvironmentPropertyKey<AccessibilityButtonShapesKey> = false
EnvironmentPropertyKey<AccessibilityPrefersCrossFadeTransitionsKey> = false
EnvironmentPropertyKey<AccessibilityInvertColorsKey> = false
EnvironmentPropertyKey<AccessibilityReduceMotionKey> = false
EnvironmentPropertyKey<AccessibilityReduceTransparencyKey> = false
EnvironmentPropertyKey<AccessibilityDifferentiateWithoutColorKey> = false
EnvironmentPropertyKey<BackgroundInfoKey> = BackgroundInfo(layer: 0, groupCount: 0)
EnvironmentPropertyKey<VerticalUserInterfaceSizeClassKey> = Optional(SwiftUI.UserInterfaceSizeClass.regular)
EnvironmentPropertyKey<HorizontalUserInterfaceSizeClassKey> = Optional(SwiftUI.UserInterfaceSizeClass.compact)
EnvironmentPropertyKey<DisplayScaleKey> = 3.0
EnvironmentPropertyKey<ColorSchemeKey> = light
EnvironmentPropertyKey<ColorSchemeContrastKey> = standard
EnvironmentPropertyKey<DisplayGamutKey> = displayP3
EnvironmentPropertyKey<LegibilityWeightKey> = Optional(SwiftUI.LegibilityWeight.regular)
EnvironmentPropertyKey<DynamicTypeSizeKey> = large
EnvironmentPropertyKey<LayoutDirectionKey> = leftToRight
EnvironmentPropertyKey<ScenePhaseKey> = active
EnvironmentPropertyKey<TimeZoneKey> = America/Los_Angeles (fixed (equal to current))
EnvironmentPropertyKey<CalendarKey> = gregorian (current)
--- Environment Values - END ---
Los trabajos internos del manejo del entorno SwiftUI están ocultos. Esto está bien para la mayoría de las situaciones. Un inconveniente notable es que el resultado impreso Font
no es útil ya que su descripción no contiene el nombre de fuente/información de estilo.
Fuente del artículo original en https://betterprogramming.pub/
1617255938
Si tiene problemas para migrar los buzones de correo de Exchange a Office 365, debe leer este artículo para saber cómo migrar los buzones de correo de Exchange EDB a Office 365. Al migrar a Office 365, los usuarios pueden acceder a sus buzones de correo desde cualquier lugar y desde cualquier dispositivo.
En esta publicación, explicaremos las razones detrás de esta migración y una solución profesional para migrar de Exchange a Office 365.
Office 365 apareció por primera vez en 2011 y, dado que se considera la mejor plataforma para aquellas organizaciones que desean administrar todo su sistema de correo electrónico en la nube. Estas son las características clave de Office 365:
Hay varias formas manuales de migrar los buzones de correo de Exchange EDB a Office 365, pero para evitar estos complicados y prolongados procedimientos, presentamos una solución de terceros, es decir, la herramienta de migración de Exchange, que es automatizada y directa para la migración de Exchange a Office 365. La herramienta funciona rápidamente y migra todos los elementos del buzón de Exchange Server a Office 365.
La herramienta de migración de Datavare Exchange es demasiado fácil de usar y ofrece pasos sencillos para migrar EDB a Office 365:
Por lo tanto, todos sus buzones de correo de Exchange EDB ahora se migran a Office 365.
Nota: puede usar filtros para migrar los elementos de datos deseados de la cuenta de Exchange a la de Office 365
Este blog le indica una solución profesional para la migración de buzones de correo de Exchange a la cuenta de Office 365. Dado que las soluciones manuales son complicadas, sugerimos la herramienta de migración de Exchange, que es demasiado simple de usar. Los usuarios no se enfrentan a problemas al operar el programa. La mejor parte de este software es que no necesita habilidades técnicas para realizar la migración. Se puede comprender el funcionamiento del software descargando la versión de demostración que permite la migración de los primeros 50 elementos por carpeta.
Más información:- https://www.datavare.com/software/edb-migration.html
#herramienta de migración de intercambio #migración de intercambio #migrar buzones de correo de exchange
1617257581
¿Quiere restaurar los buzones de correo de PST a Exchange Server? Entonces, estás en la página correcta. Aquí, lo guiaremos sobre cómo puede restaurar fácilmente mensajes y otros elementos de PST a MS Exchange Server.
Muchas veces, los usuarios necesitan restaurar los elementos de datos de PST en Exchange Server, pero debido a la falta de disponibilidad de una solución confiable, los usuarios no pueden obtener la solución. Háganos saber primero sobre el archivo PST y MS Exchange Server.
PST es un formato de archivo utilizado por MS Outlook, un cliente de correo electrónico de Windows y muy popular entre los usuarios domésticos y comerciales.
Por otro lado, Exchange Server es un poderoso servidor de correo electrónico donde todos los datos se almacenan en un archivo EDB. Los usuarios generalmente guardan la copia de seguridad de los buzones de correo de Exchange en el archivo PST, pero muchas veces, los usuarios deben restaurar los datos del archivo PST en Exchange. Para resolver este problema, estamos aquí con una solución profesional que discutiremos en la siguiente sección de esta publicación.
No le recomendamos que elija una solución al azar para restaurar los datos de PST en Exchange Server. Por lo tanto, al realizar varias investigaciones, estamos aquí con una solución inteligente y conveniente, es decir, Exchange Restore Software. Es demasiado fácil de manejar por todos los usuarios y restaurar cómodamente todos los datos del archivo PST a Exchange Server.
El software es demasiado simple de usar y se puede instalar fácilmente en todas las versiones de Windows. Con unos pocos clics, la herramienta puede restaurar los elementos del buzón de Exchange.
No es necesario que MS Outlook restaure los datos PST en Exchange. Todos los correos electrónicos, contactos, notas, calendarios, etc. se restauran desde el archivo PST a Exchange Server.
Todas las versiones de Outlook son compatibles con la herramienta, como Outlook 2019, 2016, 2013, 2010, 2007, etc. La herramienta proporciona varios filtros mediante los cuales se pueden restaurar los datos deseados desde un archivo PST a Exchange Server. El programa se puede instalar en todas las versiones de Windows como Windows 10, 8.1, 8, 7, XP, Vista, etc.
Descargue la versión de demostración del software de restauración de Exchange y analice el funcionamiento del software restaurando los primeros 50 elementos por carpeta.
No existe una solución manual para restaurar los buzones de correo de Exchange desde el archivo PST. Por lo tanto, hemos explicado una solución fácil e inteligente para restaurar datos de archivos PST en Exchange Server. Simplemente puede usar este software y restaurar todos los datos de PST a Exchange Server.
Más información:- https://www.datavare.com/software/exchange-restore.html
#intercambio de software de restauración #intercambio de restauración #buzón del servidor de intercambio #herramienta de restauración de intercambio
1614150601
Todos los datos de Exchange Server, como correos electrónicos, contactos, notas, eventos, calendario, etc. se almacenan en el archivo Exchange EDB. A veces, el archivo EDB se vuelve inaccesible debido a la corrupción o daños en el archivo EDB. La corrupción del archivo EDB puede deberse a varias razones, como: B. grandes cantidades de datos, ataques de virus, problemas de red, etc. En este caso, los usuarios no pueden acceder a sus datos a través de archivos EDB dañados. Debido a esto, es necesario restaurar los elementos del buzón de un archivo EDB dañado. Pero, ¿cómo recuperar elementos de buzón dañados para archivos EDB? La respuesta a esa pregunta se encuentra en este blog. Así que sigamos con esta publicación para obtener una solución inmediata para la recuperación de elementos del buzón de Exchange.
El archivo Exchange EDB almacena datos en forma de archivos de base de datos:
Priv1.edb y Pub1.edb
La corrupción puede ocurrir en cualquier archivo EDB. Aquí le ofrecemos una solución para la recuperación de datos de estos dos archivos EDB.
Antes de eso, aclaremos las razones detrás de la corrupción en el archivo EDB, que discutiremos en la siguiente sección de este blog.
Estas son las principales razones detrás de la corrupción de archivos EDB.
• Un problema de sincronización con Exchange Server puede dañar el archivo EDB.
• El antivirus instalado en el servidor ha eliminado el archivo de registro.
• Almacenamiento excesivo en Exchange Server.
• Error al proporcionar datos del archivo EDB por Exchange Server.
• Los problemas de hardware o de red pueden dañar el archivo EDB.
Hay varias formas manuales, como la utilidad Eseutil e Isinteg de Exchange Server, para restaurar la base de datos de Exchange Server. Sin embargo, estos métodos son complicados y es más probable que fallen. En esta situación, se recomienda utilizar otra solución inteligente y eficaz como la herramienta de terceros EDB Recovery. Esta es una de las soluciones más fáciles para restaurar todos los elementos como correo electrónico, contactos, notas, calendario, etc. desde el buzón de Exchange. El software soluciona rápidamente los problemas del archivo EDB y ayuda a los usuarios a obtener todos los elementos del buzón del archivo EDB dañado. Se necesitan unos minutos para reparar los elementos del archivo EDB y le permite exportar los datos recuperados a varios formatos de archivo como PST, EML, MSG, HTML, etc.
Siga los pasos para usar el software de recuperación de EDB para recuperar elementos de buzón de correo corruptos para archivos EDB.
Mit dieser Software, d. H. Der oben beschriebenen EDB-Wiederherstellung, können Benutzer alle beschädigten EDB-Dateien reibungslos reparieren und wiederherstellen. Benutzer haben keine Probleme beim Bedienen der Software. Benutzer können Exchange EDB-Dateien mithilfe der Software in PST und verschiedenen anderen Formaten wiederherstellen und speichern. Durch Herunterladen der Testversion können Benutzer die Funktionen und die Arbeitsgeschwindigkeit der Software überprüfen, indem sie die ersten 50 Elemente pro Ordner wiederherstellen. Wenden Sie sich bei Unklarheiten rund um die Uhr an unser technisches Personal.
Más información:- https://www.datavare.com/software/edb-recovery.html
#herramienta de recuperación de edb #recuperación de adobe #recuperar archivos edb de intercambio corruptos #recuperar archivos edb
1661016600
En este artículo, hablaremos sobre por qué y cómo implementar la tabla de clasificación de GameCenter dentro de su aplicación.
Puedes crear juegos para iPhone sin un marcador, pero los marcadores pueden ayudar a que el juego se sienta más competitivo, como si las personas estuvieran compitiendo entre sí en todo el mundo.
En lugar de crear y administrar su propio backend, GameCenter Leaderboard le permite escalar infinitamente con el tráfico, omitir una página de inicio de sesión completa para obtener autorización, obtener la imagen, el nombre y los amigos que juegan el mismo juego, todo sin que sus usuarios tengan que ingresar nada.
Especialmente con iOS 16, Apple está invirtiendo más en mejorarlo e impulsando un mayor uso de la aplicación, como a través de notificaciones automáticas cuando tu amigo supera tu puntuación en el juego.
En mi viaje de aprendizaje de SwiftUI, he estado creando y publicando aplicaciones porque, en mi opinión, esa es la mejor manera de aprender.
No había mucha documentación actualizada sobre cómo hacer mucho de esto, especialmente ninguno con SwiftUI ni con la llegada de async y await en Swift . Así que lo consolidé y lo simplifiqué para que todos puedan crear aplicaciones increíbles. ¡Así que siéntete libre de invitarme a probar tus aplicaciones también!
La mayor parte de la lógica del código para la tabla de clasificación se encuentra en este archivo si desea pasar a continuación . Aquí están los pasos de la siguiente manera:
Captura de pantalla del portal Apple iTunes Connect
Una vez que haya creado correctamente la aplicación en el portal App Store Connect, vaya a la pestaña Servicios de la aplicación -> y asegúrese de estar en la página de GameCenter.
Luego agregue una nueva tabla de clasificación usando el signo "+", que puede ser "Clásica" (las puntuaciones nunca se restablecen) o "Recurrente" (las puntuaciones se restablecen según su configuración de frecuencia).
La mayoría de los juegos prefieren una tabla de clasificación recurrente para que la tabla de clasificación no esté abarrotada de puntuaciones más altas imposibles de alcanzar.
El LeaderboardID que ingresa allí es el que debe usar en todos los lugares del código que lo solicitan.
Detalles necesarios para crear una nueva tabla de clasificación
Primero, deberá autenticar a los usuarios en GameCenter para que funcione cualquiera de estas funciones.
Así que usaremos este código para hacer eso, lo que básicamente asegura que usted (GKLocalPlayer.local) esté autenticado o imprima un error si hay uno:
func authenticateUser() {
GKLocalPlayer.local.authenticateHandler = { vc, error in
guard error == nil else {
print(error?.localizedDescription ?? "")
return
}
}
}
Si el usuario está autenticado, verá una pequeña ventana emergente en la interfaz de usuario. De lo contrario, se llevará al usuario a una página para iniciar sesión en su cuenta de GameCenter.
Un letrero que se muestra cuando un usuario ha iniciado sesión
Para obtener los datos de las tablas de clasificación de GameCenter ViewController (GKLeaderboard), debe usar el archivo loadLeaderboards
.
Puede cambiar la loadEntries
función de .global
a .friends
para atraer solo a sus amigos.
También puede recuperar la imagen de cada jugador iterando sobre cada jugador y realizando un loadPhoto
.
Con NSRang(1...5)
, puede elegir cuántos jugadores mostrar. Esto extrae los usuarios con los 5 puntajes más altos de la tabla de clasificación y no devuelve ninguno si no hay usuarios, como en el caso de que el ciclo se actualice para una tabla de clasificación recurrente.
Así es como podría verse la extracción de datos de una tabla de clasificación si aprovecha la espera asíncrona:
func loadLeaderboard() async {
playersList.removeAll()
Task{
var playersListTemp : [Player] = []
let leaderboards = try await GKLeaderboard.loadLeaderboards(IDs: [leaderboardIdentifier])
if let leaderboard = leaderboards.filter ({ $0.baseLeaderboardID == self.leaderboardIdentifier }).first {
let allPlayers = try await leaderboard.loadEntries(for: .global, timeScope: .allTime, range: NSRange(1...5))
if allPlayers.1.count > 0 {
try await allPlayers.1.asyncForEach { leaderboardEntry in
var image = try await leaderboardEntry.player.loadPhoto(for: .small)
playersListTemp.append(Player(name: leaderboardEntry.player.displayName, score:leaderboardEntry.formattedScore, image: image))
print(playersListTemp)
playersListTemp.sort{
$0.score < $1.score
}
}
}
}
playersList = playersListTemp
}
}
Puede obtener datos de la tabla de clasificación en su aplicación
Puede aprovechar la onAppear
función de ciclo de vida de la vista para realizar las llamadas para autenticar y cargar, pero también puede hacerlo con solo tocar un botón si lo prefiere:
.onAppear(){
if !GKLocalPlayer.local.isAuthenticated {
authenticateUser()
} else if playersList.count == 0 {
Task{
await loadLeaderboard()
}
}
}
Para cargar las puntuaciones, también debe enviarlas. La submitScore
función puede ayudarte con eso.
flightsClimbed
variable debe contener la puntuación que desea enviar.leaderboardId
contiene el valor que ingresa manualmente en su cuenta de App Store Connect:func leaderboard() async{
Task{
try await GKLeaderboard.submitScore(
flightsClimbed,
context: 0,
player: GKLocalPlayer.local,
leaderboardIDs: ["com.tfp.stairsteppermaster.flights"]
)
}
calculateAchievements()
}
Cuando inicia sesión en GameCenter, aparece un pequeño icono molesto en la parte superior derecha de la pantalla. Cuando lo toca, se le lleva al GameCenter ViewController. Afortunadamente, puedes ocultarlo si no es parte de tu diseño, usando GKAccessPoint.shared.isActive = false
.
Dado que la interfaz de usuario de GameCenter es un UIKit ViewController
y no una simple SwiftUI View
, primero debe crear este UIViewControllerRepresentable (como puede ver aquí ), para iniciar GameCenter usando un botón diferente,
Una vez que agregue ese archivo a su proyecto, puede mostrar el portal de GameCenter simplemente usando esto: GameCenterView(format: gameCenterViewControllerState)
donde gameCenterViewControllerState puede ayudarlo a ir a una página de detalles en GameCenter.
Vista de clasificación de GameCenter
Desafíos y logros de GameKit
Puede probar la aplicación gratuita de código abierto Stair Master Climber iPhone Health & Fitness que compartí anteriormente. Me encantaría saber lo que piensas para que podamos aprender juntos.
Fuente: https://www.freecodecamp.org/news/how-to-implement-a-leaderboard-in-swiftui/