Cómo Volcar El Entorno SwiftUI De Manera Eficiente

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 @Environmentel 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 transformEnvironmentfunción en combinación con la dumpfunción de la biblioteca estándar de Swift.

Text("What is the magic color for this leaf node?")
  .transformEnvironment(\.magicColor) { dump($0) }

Dumpimprimirá 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 EnvironmentPropertyKeyes 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 descriptionde conforme EnvironmentValuesa .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 Fontno 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/ 

#swiftui 

What is GEEK

Buddha Community

Cómo Volcar El Entorno SwiftUI De Manera Eficiente

Cómo Volcar El Entorno SwiftUI De Manera Eficiente

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 @Environmentel 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 transformEnvironmentfunción en combinación con la dumpfunción de la biblioteca estándar de Swift.

Text("What is the magic color for this leaf node?")
  .transformEnvironment(\.magicColor) { dump($0) }

Dumpimprimirá 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 EnvironmentPropertyKeyes 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 descriptionde conforme EnvironmentValuesa .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 Fontno 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/ 

#swiftui 

joe biden

1617255938

¿Cómo migrar los buzones de correo de Exchange a la nube de Office 365?

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.

Razones para migrar Exchange Server a la nube de 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:

  1. Permite trabajar desde cualquier lugar y desde cualquier lugar.
  2. No se preocupe por el spam y el malware.
  3. La seguridad proporcionada por Office 365 es altamente confiable.
  4. Controla el costo total y brinda flexibilidad financiera.
  5. Todas las actualizaciones y mejoras son administradas por Microsoft.

¿Cómo migrar los buzones de correo de Exchange a 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:

  1. Descargue e instale el software en su sistema.
  2. Agregue el archivo EDB de Exchange con el botón Examinar.
  3. Seleccione exportar a buzones de correo de Office 365.
  4. Proporcione los detalles de inicio de sesión de la cuenta de Office 365.
  5. Seleccione la carpeta y presione el botón Finalizar.

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

Líneas finales

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

joe biden

1617257581

Software de restauración de Exchange para restaurar sin problemas PST en Exchange Server

¿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.

Conozca PST y 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.

Un método profesional para restaurar PST a Exchange Server

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.

Funciones principales ofrecidas por Exchange Restore Software

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.

Líneas finales

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

Herramienta de recuperación EDB para recuperar archivos EDB de Exchange corruptos

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.

¿Conoce el archivo EDB

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.

Razones detrás de la corrupción de archivos EDB

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.

Una solución inteligente para restaurar archivos EDB de Exchange

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.

  1. Descargue e inicie el Datavare software de recuperación EDB.
  2. Agregue el archivo EDB usando la opción Examinar.
  3. Seleccione la opción para guardar los datos después de la recuperación.
  4. Seleccione la ubicación del archivo de salida y haga clic en Siguiente.
  5. Guarde el informe de recuperación de archivos EDB y haga clic en Finalizar.
Líneas de cierre

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

Saul  Alaniz

Saul Alaniz

1661016600

Cómo Implementar Una Tabla De Clasificación De GameKit En SwiftUI

En este artículo, hablaremos sobre por qué y cómo implementar la tabla de clasificación de GameCenter dentro de su aplicación.

Por qué GameCenter está teniendo un gran renacimiento

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!

Requisitos previos:

Cómo implementar su tabla de clasificación de iOS en 6 pasos

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:

1. Cómo crear la tabla de clasificación de App Store Connect

imagen

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.

imagen-122

Detalles necesarios para crear una nueva tabla de clasificación

2. Cómo configurar la autenticación de GameCenter

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.

imagen

Un letrero que se muestra cuando un usuario ha iniciado sesión

3. Cómo mostrar los elementos de la tabla de clasificación en la interfaz de usuario

Para obtener los datos de las tablas de clasificación de GameCenter ViewController (GKLeaderboard), debe usar el archivo loadLeaderboards.

Puede cambiar la loadEntriesfunción de .globala .friendspara 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            
    }
}

imagen

Puede obtener datos de la tabla de clasificación en su aplicación

4. Cómo llamar a la funcionalidad en SwiftUI cuando aparece la vista/página

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()
        }
    }
}

5. Cómo cargar las partituras enviadas

Para cargar las puntuaciones, también debe enviarlas. La submitScorefunción puede ayudarte con eso.

  • La flightsClimbedvariable debe contener la puntuación que desea enviar.
  • GameKit se asegura de mostrar solo tu mejor puntaje durante la vida de la tabla de clasificación.
  • El leaderboardIdcontiene 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()
}

6. Cómo mostrar el portal GameCenter ViewController

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 ViewControllery 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.

Marco-3-3

Vista de clasificación de GameCenter

Cosas a tener en cuenta al usar las tablas de clasificación de GameCenter:

  • Depuración del simulador: por alguna razón, la autenticación en GameCenter es extremadamente lenta en un simulador, por lo que podría tener sentido incluso crear una simulación de datos al usar el simulador.
  • Desafíos: ya no puedes enviar desafíos de GameKit a tus amigos mediante programación debido a la obsolescencia . En su lugar, debe hacerlo manualmente dentro del panel de GameCenter del usuario contra GameKit Achievements. Además, no hay forma de ver los desafíos que ha enviado.
  • Logros: las tablas de clasificación son diferentes de los logros de GameKit, que se calculan y muestran de manera diferente, pero mucho más fácil . Esos también se pueden incorporar a la aplicación, como puede ver a continuación:

Marco-2-2

Desafíos y logros de GameKit

Terminando

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/

#swiftui