Javier Cicchelli 9bcdaa697b [Setup] Basic project structure (#1)
This PR contains all the work related to setting up this project as required to implement the [Assignment](https://repo.rock-n-code.com/rock-n-code/deep-linking-assignment/wiki/Assignment) on top, as intended.

To summarise this work:
- [x] created a new **Xcode** project;
- [x] cloned the `Wikipedia` app and inserted it into the **Xcode** project;
- [x] created the `Locations` app and also, its `Libraries` package;
- [x] created the `Shared` package to share dependencies between the apps;
- [x] added a `Makefile` file and implemented some **environment** and **help** commands.

Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Reviewed-on: rock-n-code/deep-linking-assignment#1
2023-04-08 18:37:13 +00:00

1009 lines
30 KiB
Swift

import Foundation
import SystemConfiguration
import Components
public extension UIColor {
@objc(initWithHexInteger:alpha:)
convenience init(_ hex: Int, alpha: CGFloat) {
let r = CGFloat((hex & 0xFF0000) >> 16) / 255.0
let g = CGFloat((hex & 0xFF00) >> 8) / 255.0
let b = CGFloat(hex & 0xFF) / 255.0
self.init(red: r, green: g, blue: b, alpha: alpha)
}
@objc(initWithHexInteger:)
convenience init(_ hex: Int) {
self.init(hex, alpha: 1)
}
@objc class func wmf_colorWithHex(_ hex: Int) -> UIColor {
return UIColor(hex)
}
// `initWithHexString:alpha:` should almost never be used. `initWithHexInteger:alpha:` is preferred.
@objc(initWithHexString:alpha:)
convenience init(_ hexString: String, alpha: CGFloat = 1.0) {
let hex = hexString.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
guard hex.count == 6,
let int = Scanner(string: hex).scanInt32(representation: .hexadecimal),
int != UINT32_MAX else {
assertionFailure("Unexpected issue scanning hex string: \(hexString)")
self.init(white: 0, alpha: alpha)
return
}
self.init(Int(int), alpha: alpha)
}
// Make colors accessible to @objc
@objc static var wmf_blue_700: UIColor {
return .blue700
}
@objc static var wmf_blue_300: UIColor {
return .blue300
}
@objc static var wmf_blue_600: UIColor {
return .blue600
}
@objc static var wmf_yellow_600: UIColor {
return .yellow600
}
@objc static var wmf_red_600: UIColor {
return .red600
}
@objc static var wmf_gray_400: UIColor {
return .gray400
}
@objc static var wmf_green_600: UIColor {
return .green600
}
@objc static var wmf_purple: UIColor {
return .purple600
}
@objc static var wmf_orange: UIColor {
return .orange600
}
@objc func wmf_hexStringIncludingAlpha(_ includeAlpha: Bool) -> String {
var r: CGFloat = 0
var g: CGFloat = 0
var b: CGFloat = 0
var a: CGFloat = 0
getRed(&r, green: &g, blue: &b, alpha: &a)
var hexString = String(format: "%02X%02X%02X", Int(255.0 * r), Int(255.0 * g), Int(255.0 * b))
if includeAlpha {
hexString = hexString.appendingFormat("%02X", Int(255.0 * a))
}
return hexString
}
@objc var wmf_hexString: String {
return wmf_hexStringIncludingAlpha(false)
}
}
@objc(WMFColors)
public class Colors: NSObject {
fileprivate static let light = Colors(
identifier: .light)
fileprivate static let sepia = Colors(identifier: .sepia)
fileprivate static let dark = Colors(identifier: .dark)
fileprivate static let black = Colors(identifier: .black)
fileprivate static let widgetLight = Colors(identifier: .widgetLight)
fileprivate static let widgetDark = Colors(identifier: .widgetDark)
public let identifier: Identifier
@objc public var baseBackground: UIColor {
switch identifier {
case .light, .widgetLight:
return .gray200
case .sepia:
return .beige400
case .dark, .black, .widgetDark:
return .gray800
}
}
@objc public var midBackground: UIColor {
switch identifier {
case .light, .widgetLight:
return .gray100
case .sepia:
return .beige300
case .dark, .black, .widgetDark:
return .gray700
}
}
@objc public var subCellBackground: UIColor {
switch identifier {
case .light:
return .white
case .sepia:
return .beige300
case .dark, .black:
return .gray700
case .widgetLight, .widgetDark:
return .clear
}
}
@objc public var paperBackground: UIColor {
switch identifier {
case .light, .widgetLight:
return .white
case .sepia:
return .beige100
case .dark:
return .gray675
case .black, .widgetDark:
return .black
}
}
@objc public var popoverBackground: UIColor {
switch identifier {
case .light, .sepia:
return .white
case .dark:
return .gray800
case .black:
return .gray700
case .widgetLight, .widgetDark:
return .clear
}
}
@objc public var chromeBackground: UIColor {
switch identifier {
case .light:
return .white
case .sepia:
return .beige100
case .dark:
return .gray700
case .black:
return .gray700
case .widgetLight, .widgetDark:
return .clear
}
}
@objc public var chromeShadow: UIColor {
switch identifier {
case .light, .widgetLight:
return .gray400
case .sepia:
return .taupe200
case .dark, .widgetDark:
return .gray650
case .black:
return .gray675
}
}
@objc public var overlayBackground: UIColor {
switch identifier {
case .light, .widgetLight:
return .black.withAlphaComponent(0.5)
case .sepia:
return .taupe600.withAlphaComponent(0.6)
default:
return .black.withAlphaComponent(0.75)
}
}
@objc public var batchSelectionBackground: UIColor {
switch identifier {
case .light, .sepia, .widgetLight:
return .blue100
case .dark, .black, .widgetDark:
return .blue700
}
}
@objc public var referenceHighlightBackground: UIColor {
switch identifier {
case .light, .sepia, .dark, .widgetLight:
return .clear
case .black, .widgetDark:
return .white.withAlphaComponent(0.2)
}
}
@objc public var hintBackground: UIColor {
switch identifier {
case .light, .sepia, .widgetLight:
return .blue100
case .dark, .widgetDark:
return .gray800
case .black:
return .gray650
}
}
@objc public var hintWarningText: UIColor {
switch identifier {
case .light, .sepia, .widgetLight:
return .gray700
case .dark, .black, .widgetDark:
return .yellow600
}
}
@objc public var hintWarningBackground: UIColor {
switch identifier {
case .light, .sepia:
return .orange600
case .dark, .black:
return .gray700
default:
return .clear
}
}
@objc public var animationBackground: UIColor {
switch identifier {
case .light, .sepia, .widgetLight:
return .gray150
case .dark, .black, .widgetDark:
return .gray700
}
}
@objc public var overlayText: UIColor {
return .gray600
}
@objc public var primaryText: UIColor {
switch identifier {
case .light, .sepia, .widgetLight:
return .gray700
case .dark, .black, .widgetDark:
return .gray100
}
}
@objc public var secondaryText: UIColor {
switch identifier {
case .light, .widgetLight:
return .gray500
case .sepia:
return .taupe600
default:
return .gray300
}
}
@objc public var tertiaryText: UIColor {
switch identifier {
case .light:
return .gray500
case .sepia:
return .taupe600
default:
return .gray300
}
}
@objc public var disabledText: UIColor {
switch identifier {
case .light:
return .gray500
case .sepia:
return .taupe600
default:
return .gray300
}
}
@objc public var disabledLink: UIColor {
switch identifier {
case .light, .widgetLight:
return .gray600
case .sepia:
return .gray500
case .dark, .black, .widgetDark:
return .gray400
}
}
@objc public var chromeText: UIColor {
switch identifier {
case .light, .sepia, .widgetLight:
return .gray700
case .dark, .black, .widgetDark:
return .gray100
}
}
@objc public var link: UIColor {
switch identifier {
case .light, .sepia, .widgetLight:
return .blue600
case .dark, .black, .widgetDark:
return .blue300
}
}
@objc public var accent: UIColor {
switch identifier {
default:
return .green600
}
}
@objc public var secondaryAction: UIColor {
return .blue700
}
@objc public var destructive: UIColor {
switch identifier {
case .sepia:
return .red700
default:
return .red600
}
}
@objc public var warning: UIColor {
switch identifier {
case .light, .sepia, .widgetLight:
return .orange600
case .dark, .black, .widgetDark:
return .yellow600
}
}
@objc public var error: UIColor {
switch identifier {
case .sepia:
return .red700
default:
return .red600
}
}
@objc public var unselected: UIColor {
switch identifier {
case .light, .widgetLight:
return .gray400
case .sepia:
return .taupe600
case .dark, .black, .widgetDark:
return .gray300
}
}
@objc public var border: UIColor {
switch identifier {
case .light, .widgetLight:
return .gray400
case .sepia:
return .taupe200
case .dark, .widgetDark:
return .gray650
case .black:
return .gray675
}
}
@objc public var shadow: UIColor {
switch identifier {
case .light, .widgetLight:
return .gray200
case .sepia:
return .taupe200
case .dark, .widgetDark:
return .gray800
case .black:
return .gray700
}
}
public var cardBackground: UIColor {
switch identifier {
case .light:
return .white
case .sepia:
return .beige300
case .dark, .black:
return .gray700
case .widgetLight, .widgetDark:
return .clear
}
}
public var selectedCardBackground: UIColor {
switch identifier {
case .light, .widgetLight:
return .gray100
case .sepia:
return .beige400
case .dark, .widgetDark:
return .gray700
case .black:
return .gray675
}
}
@objc public var cardBorder: UIColor {
switch identifier {
case .light, .widgetLight:
return .gray100
case .sepia:
return .taupe200
case .dark, .widgetDark:
return .gray650
case .black:
return .gray675
}
}
@objc public var cardShadow: UIColor {
switch identifier {
case .light:
return .gray700
default:
return .clear
}
}
@objc public var cardButtonBackground: UIColor {
switch identifier {
case .light, .widgetLight:
return .gray100
case .sepia:
return .beige300
case .dark, .black, .widgetDark:
return .gray650
}
}
@objc public var cardButtonSelectedBackground: UIColor {
switch identifier {
case .light, .widgetLight:
return .gray100
case .sepia:
return .taupe200
case .dark, .widgetDark:
return .gray650
case .black:
return .gray675
}
}
@objc public var icon: UIColor? {
switch identifier {
case .sepia:
return .taupe600
case .dark, .black:
return .gray300
default:
return nil
}
}
@objc public var iconBackground: UIColor? {
switch identifier {
case .sepia:
return .beige400
case .dark, .black:
return .gray675
default:
return nil
}
}
@objc public var searchFieldBackground: UIColor {
return .darkSearchFieldBackground
}
@objc public var keyboardBarSearchFieldBackground: UIColor {
switch identifier {
case .light, .sepia, .widgetLight:
return .gray200
case .dark, .black, .widgetDark:
return .gray650
}
}
@objc public var rankGradientStart: UIColor {
switch identifier {
case .light, .sepia, .widgetLight:
return .blue600
case .dark, .black, .widgetDark:
return .blue300
}
}
@objc public var rankGradientEnd: UIColor {
return .green600
}
@objc public var rankGradient: Gradient {
return Gradient(startColor: rankGradientStart, endColor: rankGradientEnd)
}
@objc public var blurEffectStyle: UIBlurEffect.Style {
switch identifier {
case .light, .sepia, .widgetLight:
return .extraLight
case .dark, .black, .widgetDark:
return .dark
}
}
@objc public var blurEffectBackground: UIColor {
switch identifier {
case .black, .dark, .widgetDark:
return .gray300.withAlphaComponent(0.55)
default:
return .clear
}
}
@objc public var tagText: UIColor {
switch identifier {
case .light:
return .blue600
default:
return .white
}
}
@objc public var tagBackground: UIColor {
switch identifier {
case .light:
return .blue600.withAlphaComponent(0.1)
default:
return .blue300
}
}
@objc public var tagSelectedBackground: UIColor {
switch identifier {
case .light:
return .blue600.withAlphaComponent(0.25)
default:
return .blue600
}
}
@objc public var distanceBorder: UIColor {
switch identifier {
case .light, .widgetLight:
return .gray400
case .sepia:
return .taupe600
case .dark, .black, .widgetDark:
return .gray300
}
}
@objc public var descriptionBackground: UIColor {
switch identifier {
case .light:
return .yellow600
case .sepia, .widgetLight:
return .orange600
case .dark, .black, .widgetDark:
return .blue300
}
}
@objc public var descriptionWarning: UIColor {
switch identifier {
case .light, .sepia, .widgetLight:
return .orange600
case .dark, .black, .widgetDark:
return .yellow600
}
}
@objc public var pageIndicator: UIColor {
return .blue100
}
@objc public var pageIndicatorCurrent: UIColor {
switch identifier {
case .light, .sepia, .widgetLight:
return .blue600
case .dark, .black, .widgetDark:
return .blue300
}
}
@objc public var unreadIndicator: UIColor {
return .green600
}
@objc public var refreshControlTint: UIColor {
return secondaryText
}
@objc public var inputAccessoryBackground: UIColor {
switch identifier {
case .light:
return .white
case .sepia:
return .beige300
case .dark, .black:
return .gray700
case .widgetLight, .widgetDark:
return .clear
}
}
@objc public var inputAccessoryButtonTint: UIColor {
switch identifier {
case .light, .sepia, .widgetLight:
return .gray600
case .dark, .black, .widgetDark:
return .gray100
}
}
@objc public var inputAccessoryButtonSelectedTint: UIColor {
return primaryText
}
@objc public var inputAccessoryButtonSelectedBackgroundColor: UIColor {
return baseBackground
}
public var diffTextAdd: UIColor {
switch identifier {
case .light:
return .gray700
default:
return .green600
}
}
public var diffTextDelete: UIColor {
switch identifier {
case .light:
return .gray700
case .sepia:
return.red700
case .dark, .black:
return .red600
default:
return .clear
}
}
public var diffHighlightAdd: UIColor? {
switch identifier {
case .light:
return .green100
default:
return nil
}
}
public var diffHighlightDelete: UIColor? {
switch identifier {
case .light:
return .red100
default:
return nil
}
}
public var diffStrikethroughColor: UIColor {
switch identifier {
case .light:
return .gray700
case .sepia:
return .red700
case .dark, .black:
return .red600
default:
return .clear
}
}
public var diffContextItemBackground: UIColor {
switch identifier {
case .light, .widgetLight:
return .gray100
case .sepia:
return .beige300
case .dark, .black, .widgetDark:
return .gray700
}
}
public var diffContextItemBorder: UIColor {
switch identifier {
case .light, .widgetLight:
return .gray400
case .sepia:
return .taupe200
case .dark, .widgetDark:
return .gray650
case .black:
return .gray675
}
}
public var diffMoveParagraphBackground: UIColor {
switch identifier {
case .light, .widgetLight:
return .gray100
case .sepia:
return .beige300
case .dark, .black, .widgetDark:
return .gray700
}
}
public var diffCompareAccent: UIColor {
return .orange600.withAlphaComponent(0.3)
}
public var diffCompareChangeHeading: UIColor {
switch identifier {
case .light:
return .white
case .sepia:
return .beige100
case .black, .dark:
return .black
default:
return .clear
}
}
public var talkPageCoffeRollBackground: UIColor {
switch identifier {
case .light:
return .beige100
case .sepia:
return .beige400
case .dark, .black:
return .gray800
default:
return .clear
}
}
init(identifier: Identifier) {
self.identifier = identifier
}
public enum Identifier {
case light
case sepia
case dark
case black
case widgetLight
case widgetDark
}
}
@objc(WMFTheme)
public class Theme: NSObject {
@objc public static let standard = Theme.light
@objc public let colors: Colors
@objc public let isDark: Bool
@objc public let hasInputAccessoryShadow: Bool
@objc public var preferredStatusBarStyle: UIStatusBarStyle {
return isDark ? .lightContent : .default
}
@objc public var scrollIndicatorStyle: UIScrollView.IndicatorStyle {
return isDark ? .white : .black
}
@objc public var blurEffectStyle: UIBlurEffect.Style {
return isDark ? .dark : .light
}
@objc public var keyboardAppearance: UIKeyboardAppearance {
return isDark ? .dark : .light
}
@objc public lazy var navigationBarBackgroundImage: UIImage = {
return UIImage.wmf_image(from: colors.paperBackground)
}()
@objc public lazy var sheetNavigationBarBackgroundImage: UIImage = {
return UIImage.wmf_image(from: colors.chromeBackground)
}()
@objc public lazy var editorNavigationBarBackgroundImage: UIImage = {
return UIImage.wmf_image(from: colors.inputAccessoryBackground)
}()
@objc public var navigationBarShadowImage: UIImage {
return clearImage
}
@objc public lazy var clearImage: UIImage = {
return #imageLiteral(resourceName: "transparent-pixel")
}()
static let tabBarItemBadgeParagraphStyle: NSParagraphStyle = {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.firstLineHeadIndent = 0.4
return paragraphStyle
}()
static let tabBarItemFont: UIFont = {
return UIFont.systemFont(ofSize: 12)
}()
public lazy var tabBarItemBadgeTextAttributes: [NSAttributedString.Key: Any] = {
return [NSAttributedString.Key.foregroundColor: colors.chromeBackground, NSAttributedString.Key.paragraphStyle: Theme.tabBarItemBadgeParagraphStyle]
}()
public lazy var tabBarTitleTextAttributes: [NSAttributedString.Key: Any] = {
return [.foregroundColor: colors.secondaryText, .font: Theme.tabBarItemFont]
}()
public lazy var tabBarSelectedTitleTextAttributes: [NSAttributedString.Key: Any] = {
return [.foregroundColor: colors.link, .font: Theme.tabBarItemFont]
}()
public static let exploreCardCornerRadius: CGFloat = 10
static func roundedRectImage(with color: UIColor, cornerRadius: CGFloat, width: CGFloat? = nil, height: CGFloat? = nil) -> UIImage? {
let minDimension = 2 * cornerRadius + 1
let rect = CGRect(x: 0, y: 0, width: width ?? minDimension, height: height ?? minDimension)
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(rect.size, false, scale)
guard let context = UIGraphicsGetCurrentContext() else {
return nil
}
context.setFillColor(color.cgColor)
let path = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius)
path.fill()
let capInsets = UIEdgeInsets(top: cornerRadius, left: cornerRadius, bottom: cornerRadius, right: cornerRadius)
let image = UIGraphicsGetImageFromCurrentImageContext()?.resizableImage(withCapInsets: capInsets)
UIGraphicsEndImageContext()
return image
}
@objc public lazy var searchFieldBackgroundImage: UIImage? = {
return Theme.roundedRectImage(with: colors.searchFieldBackground, cornerRadius: 10, height: 36)
}()
@objc public lazy var navigationBarTitleTextAttributes: [NSAttributedString.Key: Any] = {
return [NSAttributedString.Key.foregroundColor: colors.chromeText]
}()
public static let dimmedImageOpacity: CGFloat = 0.65
@objc public let imageOpacity: CGFloat
@objc public let cardBorderWidthInPixels: Int
@objc public let cardShadowOpacity: Float
@objc public let name: String
@objc public let displayName: String
public let analyticsName: String
public let webName: String
@objc public let multiSelectIndicatorImage: UIImage?
fileprivate static let lightMultiSelectIndicator = UIImage(named: "selected", in: Bundle.main, compatibleWith:nil)
fileprivate static let darkMultiSelectIndicator = UIImage(named: "selected-dark", in: Bundle.main, compatibleWith:nil)
private static let defaultCardBorderWidthInPixels: Int = 1
private static let lightCardBorderWidthInPixels: Int = {
return DeviceInfo.shared.isOlderDevice ? 4 : defaultCardBorderWidthInPixels
}()
private static let defaultCardShadowOpacity: Float = {
return DeviceInfo.shared.isOlderDevice ? 0 : 0.13
}()
@objc public static let defaultThemeName = "standard"
@objc public static let defaultAnalyticsThemeName = "default"
private static let darkThemePrefix = "dark"
private static let blackThemePrefix = "black"
@objc public static func isDefaultThemeName(_ name: String?) -> Bool {
guard let name = name else {
return true
}
return name == defaultThemeName
}
@objc public static func isDarkThemeName(_ name: String?) -> Bool {
guard let name = name else {
return false
}
return name.hasPrefix(darkThemePrefix) || name.hasPrefix(blackThemePrefix)
}
@objc public static let light = Theme(colors: .light, imageOpacity: 1, cardBorderWidthInPixels: Theme.lightCardBorderWidthInPixels, cardShadowOpacity: defaultCardShadowOpacity, multiSelectIndicatorImage: Theme.lightMultiSelectIndicator, isDark: false, hasInputAccessoryShadow: true, name: "light", displayName: WMFLocalizedString("theme-light-display-name", value: "Light", comment: "Light theme name presented to the user"), analyticsName: "default", webName: "light")
@objc public static let sepia = Theme(colors: .sepia, imageOpacity: 1, cardBorderWidthInPixels: Theme.defaultCardBorderWidthInPixels, cardShadowOpacity: 0, multiSelectIndicatorImage: Theme.lightMultiSelectIndicator, isDark: false, hasInputAccessoryShadow: false, name: "sepia", displayName: WMFLocalizedString("theme-sepia-display-name", value: "Sepia", comment: "Sepia theme name presented to the user"), analyticsName: "sepia", webName: "sepia")
@objc public static let dark = Theme(colors: .dark, imageOpacity: 1, cardBorderWidthInPixels: Theme.defaultCardBorderWidthInPixels, cardShadowOpacity: 0, multiSelectIndicatorImage: Theme.darkMultiSelectIndicator, isDark: true, hasInputAccessoryShadow: false, name: darkThemePrefix, displayName: WMFLocalizedString("theme-dark-display-name", value: "Dark", comment: "Dark theme name presented to the user"), analyticsName: "dark", webName: "dark")
@objc public static let darkDimmed = Theme(colors: .dark, imageOpacity: Theme.dimmedImageOpacity, cardBorderWidthInPixels: Theme.defaultCardBorderWidthInPixels, cardShadowOpacity: 0, multiSelectIndicatorImage: Theme.darkMultiSelectIndicator, isDark: true, hasInputAccessoryShadow: false, name: "\(darkThemePrefix)-dimmed", displayName: Theme.dark.displayName, analyticsName: "dark", webName: "dark")
@objc public static let black = Theme(colors: .black, imageOpacity: 1, cardBorderWidthInPixels: Theme.defaultCardBorderWidthInPixels, cardShadowOpacity: 0, multiSelectIndicatorImage: Theme.darkMultiSelectIndicator, isDark: true, hasInputAccessoryShadow: false, name: blackThemePrefix, displayName: WMFLocalizedString("theme-black-display-name", value: "Black", comment: "Black theme name presented to the user"), analyticsName: "black", webName: "black")
@objc public static let blackDimmed = Theme(colors: .black, imageOpacity: Theme.dimmedImageOpacity, cardBorderWidthInPixels: Theme.defaultCardBorderWidthInPixels, cardShadowOpacity: 0, multiSelectIndicatorImage: Theme.darkMultiSelectIndicator, isDark: true, hasInputAccessoryShadow: false, name: "\(blackThemePrefix)-dimmed", displayName: Theme.black.displayName, analyticsName: "black", webName: "black")
@objc public static let widgetLight = Theme(colors: .widgetLight, imageOpacity: 1, cardBorderWidthInPixels: Theme.defaultCardBorderWidthInPixels, cardShadowOpacity: 0, multiSelectIndicatorImage: nil, isDark: false, hasInputAccessoryShadow: false, name: "widget-light", displayName: "", analyticsName: "", webName: "light")
@objc public static let widgetDark = Theme(colors: .widgetDark, imageOpacity: 1, cardBorderWidthInPixels: Theme.defaultCardBorderWidthInPixels, cardShadowOpacity: 0, multiSelectIndicatorImage: nil, isDark: false, hasInputAccessoryShadow: false, name: "widget-dark", displayName: "", analyticsName: "", webName: "black")
public class func widgetThemeCompatible(with traitCollection: UITraitCollection) -> Theme {
return traitCollection.userInterfaceStyle == .dark ? Theme.widgetDark : Theme.widgetLight
}
init(colors: Colors, imageOpacity: CGFloat, cardBorderWidthInPixels: Int, cardShadowOpacity: Float, multiSelectIndicatorImage: UIImage?, isDark: Bool, hasInputAccessoryShadow: Bool, name: String, displayName: String, analyticsName: String, webName: String) {
self.colors = colors
self.imageOpacity = imageOpacity
self.name = name
self.displayName = displayName
self.multiSelectIndicatorImage = multiSelectIndicatorImage
self.isDark = isDark
self.hasInputAccessoryShadow = hasInputAccessoryShadow
self.cardBorderWidthInPixels = cardBorderWidthInPixels
self.cardShadowOpacity = cardShadowOpacity
self.analyticsName = analyticsName
self.webName = webName
}
fileprivate static let themesByName = [Theme.light.name: Theme.light, Theme.dark.name: Theme.dark, Theme.sepia.name: Theme.sepia, Theme.darkDimmed.name: Theme.darkDimmed, Theme.black.name: Theme.black, Theme.blackDimmed.name: Theme.blackDimmed]
@objc(withName:)
public class func withName(_ name: String?) -> Theme? {
guard let name = name else {
return nil
}
return themesByName[name]
}
@objc public func withDimmingEnabled(_ isDimmingEnabled: Bool) -> Theme {
guard let baseName = name.components(separatedBy: "-").first else {
return self
}
let adjustedName = isDimmingEnabled ? "\(baseName)-dimmed" : baseName
return Theme.withName(adjustedName) ?? self
}
}
@objc(WMFThemeable)
public protocol Themeable: AnyObject {
@objc(applyTheme:)
func apply(theme: Theme) // this might be better as a var theme: Theme { get set } - common VC superclasses could check for viewIfLoaded and call an update method in the setter. This would elminate the need for the viewIfLoaded logic in every applyTheme:
}
// Use for SwiftUI environment objects
public final class ObservableTheme: ObservableObject {
@Published public var theme: Theme
public init(theme: Theme) {
self.theme = theme
}
}