diff --git a/Buildsystem/Testing/Unit/PreferenceSettingTests.swift b/Buildsystem/Testing/Unit/PreferenceSettingTests.swift
new file mode 100644
index 0000000000000000000000000000000000000000..8d4c20859e321aace72ec55a1df318795c5ebed2
--- /dev/null
+++ b/Buildsystem/Testing/Unit/PreferenceSettingTests.swift
@@ -0,0 +1,75 @@
+/*****************************************************************************
+ * PreferenceSettingTests.swift
+ * VLC for iOS
+ *****************************************************************************
+ * Copyright (c) 2025 VideoLAN. All rights reserved.
+ *
+ * Authors: Craig Reyenga <craig.reyenga # gmail.com>
+ *
+ * Refer to the COPYING file of the official project for license.
+ *****************************************************************************/
+
+import XCTest
+@testable import VLC
+
+final class PreferenceSettingTests: XCTestCase {
+
+    func testDecodeAll() {
+        let fileURL = Bundle(for: type(of: self)).url(forResource: "PreferenceSettingTestsSuccess",
+                                                      withExtension: "plist")!
+        let data = try! Data(contentsOf: fileURL)
+
+        let decoder = PropertyListDecoder()
+        let decoded = try! decoder.decode(PreferenceSettingRoot.self, from: data)
+
+        // Sample data is intentionally meant to have nothing to do with VLC.
+        // This way, global string searches won't bring up this test as a result unnecessarily.
+
+        let group = PreferenceSetting.Group(title: "First Section", footerText: "First")
+
+        let titleChoices = PreferenceSetting.Choices
+            .number([.init(title: "Zero", value: .from(integer: 0)),
+                     .init(title: "One", value: .from(integer: 1)),
+                     .init(title: "Two", value: .from(integer: 2)),
+                     .init(title: "Three", value: .from(integer: 3))],
+                    defaultValue: .zero)
+        let title = PreferenceSetting.Title(key: "numberOfParkingTickets",
+                                            title: "Number of Parking Tickets",
+                                            choices: titleChoices)
+
+        let multiChoices = PreferenceSetting.Choices
+            .string([.init(title: "Zero", value: "Zero"),
+                     .init(title: "One", value: "One"),
+                     .init(title: "Two", value: "Two"),
+                     .init(title: "Three", value: "Three")],
+                    defaultValue: "Zero")
+        let multi = PreferenceSetting.MultiValue(key: "numberOfSpeedingTickets",
+                                                 title: "Number of Speeding Tickets",
+                                                 choices: multiChoices)
+
+        let textField = PreferenceSetting.TextField(key: "name",
+                                                    title: "Name",
+                                                    defaultValue: "")
+
+        let toggle = PreferenceSetting.Toggle(key: "WashHandsBeforeEating",
+                                              title: "Wash Hands Before Eating",
+                                              defaultValue: true)
+
+        let custom = PreferenceSetting.Custom.helloWorld(.init(title: "Greetings Earth",
+                                                               population: 8_200_000_000))
+
+        let expected: [PreferenceSetting] = [
+            .groupSpecifier(group),
+            .titleSpecifier(title),
+            .multiValueSpecifier(multi),
+            .textFieldSpecifier(textField),
+            .toggleSwitchSpecifier(toggle),
+            .custom(custom)
+        ]
+
+        XCTAssertTrue(decoded.specifiers == expected)
+    }
+
+    // TODO: create tests for failure cases.
+
+}
diff --git a/Buildsystem/Testing/Unit/PreferenceSettingTestsSuccess.plist b/Buildsystem/Testing/Unit/PreferenceSettingTestsSuccess.plist
new file mode 100644
index 0000000000000000000000000000000000000000..598563423480713e7ed513f0daceabb4cb136532
--- /dev/null
+++ b/Buildsystem/Testing/Unit/PreferenceSettingTestsSuccess.plist
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+    <key>PreferenceSpecifiers</key>
+    <array>
+        <dict>
+            <key>Type</key>
+            <string>PSGroupSpecifier</string>
+            <key>Title</key>
+            <string>First Section</string>
+            <key>FooterText</key>
+            <string>First</string>
+        </dict>
+        <dict>
+            <key>Type</key>
+            <string>PSTitleValueSpecifier</string>
+            <key>DefaultValue</key>
+            <integer>0</integer>
+            <key>Key</key>
+            <string>numberOfParkingTickets</string>
+            <key>Title</key>
+            <string>Number of Parking Tickets</string>
+            <key>Titles</key>
+            <array>
+                <string>Zero</string>
+                <string>One</string>
+                <string>Two</string>
+                <string>Three</string>
+            </array>
+            <key>Values</key>
+            <array>
+                <integer>0</integer>
+                <integer>1</integer>
+                <integer>2</integer>
+                <integer>3</integer>
+            </array>
+        </dict>
+        <dict>
+            <key>Type</key>
+            <string>PSMultiValueSpecifier</string>
+            <key>DefaultValue</key>
+            <string>Zero</string>
+            <key>Key</key>
+            <string>numberOfSpeedingTickets</string>
+            <key>Title</key>
+            <string>Number of Speeding Tickets</string>
+            <key>Titles</key>
+            <array>
+                <string>Zero</string>
+                <string>One</string>
+                <string>Two</string>
+                <string>Three</string>
+            </array>
+            <key>Values</key>
+            <array>
+                <string>Zero</string>
+                <string>One</string>
+                <string>Two</string>
+                <string>Three</string>
+            </array>
+        </dict>
+        <dict>
+            <key>Type</key>
+            <string>PSTextFieldSpecifier</string>
+            <key>Title</key>
+            <string>Name</string>
+            <key>FooterText</key>
+            <string>Enter your name</string>
+            <key>Key</key>
+            <string>name</string>
+        </dict>
+        <dict>
+            <key>Type</key>
+            <string>PSToggleSwitchSpecifier</string>
+            <key>DefaultValue</key>
+            <true/>
+            <key>Key</key>
+            <string>WashHandsBeforeEating</string>
+            <key>Title</key>
+            <string>Wash Hands Before Eating</string>
+        </dict>
+        <dict>
+            <key>Type</key>
+            <string>VLCCustomSpecifier</string>
+            <key>Subtype</key>
+            <string>HelloWorld</string>
+            <key>Title</key>
+            <string>Greetings Earth</string>
+            <key>Population</key>
+            <integer>8200000000</integer>
+        </dict>
+    </array>
+    <key>StringsTable</key>
+    <string>Root</string>
+</dict>
+</plist>
diff --git a/Sources/Helpers/PreferenceSetting.swift b/Sources/Helpers/PreferenceSetting.swift
new file mode 100644
index 0000000000000000000000000000000000000000..4aaae2e2e63b72606976ff5122e3c2f116cc9634
--- /dev/null
+++ b/Sources/Helpers/PreferenceSetting.swift
@@ -0,0 +1,459 @@
+/*****************************************************************************
+ * PreferenceSetting.swift
+ * VLC for iOS
+ *****************************************************************************
+ * Copyright (c) 2025 VideoLAN. All rights reserved.
+ *
+ * Authors: Craig Reyenga <craig.reyenga # gmail.com>
+ *
+ * Refer to the COPYING file of the official project for license.
+ *****************************************************************************/
+
+/// A preference from Settings.app.
+///
+/// These structures are decodable from plists. Apple's schema is followed
+/// closely, however, the decoder does not support everything in full.
+///
+/// https://developer.apple.com/library/archive/documentation/PreferenceSettings/Conceptual/SettingsApplicationSchemaReference/
+enum PreferenceSetting: Equatable {
+    // Apple:
+    case groupSpecifier(Group)
+    case titleSpecifier(Title)
+    case multiValueSpecifier(MultiValue)
+    case textFieldSpecifier(TextField)
+    case toggleSwitchSpecifier(Toggle)
+    // not implemented yet, because we don't use them:
+    //    case sliderSpecifier(Slider)
+    //    case radioGroupSpecifier(RadioGroup)
+    //    case childPaneSpecifier(ChildPane)
+
+    // Us:
+    case custom(Custom)
+
+    // Nobody:
+    case unsupported(String)
+}
+
+// - MARK: PreferenceSettingRoot
+
+/// Container for preference specifiers
+struct PreferenceSettingRoot {
+    let specifiers: [PreferenceSetting]
+}
+
+// - MARK: Types of Preferences
+
+extension PreferenceSetting {
+    struct Group: Equatable {
+        let title: String
+        let footerText: String?
+    }
+}
+
+extension PreferenceSetting {
+    struct Title: Equatable {
+        let key: String
+        let title: String
+        let choices: Choices
+    }
+}
+
+extension PreferenceSetting {
+    struct MultiValue: Equatable {
+        let key: String
+        let title: String
+        let choices: Choices
+    }
+}
+
+extension PreferenceSetting {
+    struct TextField: Equatable {
+        let key: String
+        let title: String
+        let defaultValue: String
+    }
+}
+
+extension PreferenceSetting {
+    struct Toggle: Equatable {
+        let key: String
+        let title: String
+        let defaultValue: Bool
+    }
+}
+
+// - MARK: Custom Preferences
+
+extension PreferenceSetting {
+    enum Custom: Equatable {
+        case helloWorld(HelloWorld)
+    }
+}
+
+extension PreferenceSetting.Custom {
+    /// An example of a custom preference. Don't use it.
+    struct HelloWorld: Equatable {
+        let title: String
+        let population: Int
+    }
+}
+
+// - MARK: Number
+
+extension PreferenceSetting {
+    /// PropertyListDecoder has a bug where values that are explicitly declared
+    /// as being integer or float are actually decodable as either. There is no
+    /// way to distinguish them during decoding. To get around this, we use a
+    /// structure that provides both types of values and place the burden of
+    /// choice between the two on the consumers of this data.
+    struct Number: Equatable {
+        let float: Float
+        let integer: Int
+
+        static var zero: Number {
+            .init(float: 0, integer: 0)
+        }
+
+        static func from(integer: Int) -> Self {
+            .init(float: Float(integer), integer: integer)
+        }
+    }
+}
+
+// - MARK: Choices
+
+extension PreferenceSetting {
+    /// Choices are pairings of titles and values; values can be boolean, numeric, or string.
+    enum Choices: Equatable {
+        case bool([BoolChoice], defaultValue: Bool)
+        case number([NumberChoice], defaultValue: Number)
+        case string([StringChoice], defaultValue: String)
+    }
+
+    struct BoolChoice: Equatable {
+        let title: String
+        let value: Bool
+    }
+
+    struct NumberChoice: Equatable {
+        let title: String
+        let value: Number
+    }
+
+    struct StringChoice: Equatable {
+        let title: String
+        let value: String
+    }
+}
+
+// - MARK: Decodable
+
+extension PreferenceSettingRoot: Decodable {
+    enum CodingKeys: String, CodingKey {
+        case specifiers = "PreferenceSpecifiers"
+    }
+}
+
+extension PreferenceSetting: Decodable {
+    init(from decoder: any Decoder) throws {
+        let container = try decoder.container(keyedBy: CodingKeys.self)
+
+        let typeString = try container.decode(String.self, forKey: .type)
+        
+        switch typeString {
+        case SettingType.psGroupSpecifier.rawValue:
+            let pref = try Group(from: decoder)
+            self = .groupSpecifier(pref)
+
+        case SettingType.psTitleValueSpecifier.rawValue:
+            let pref = try Title(from: decoder)
+            self = .titleSpecifier(pref)
+
+        case SettingType.psMultiValueSpecifier.rawValue:
+            let pref = try MultiValue(from: decoder)
+            self = .multiValueSpecifier(pref)
+
+        case SettingType.psTextFieldSpecifier.rawValue:
+            let pref = try TextField(from: decoder)
+            self = .textFieldSpecifier(pref)
+
+        case SettingType.psToggleSwitchSpecifier.rawValue:
+            let pref = try Toggle(from: decoder)
+            self = .toggleSwitchSpecifier(pref)
+
+        case SettingType.vlcCustomSpecifier.rawValue:
+            let pref = try Custom(from: decoder)
+            self = .custom(pref)
+
+        case SettingType.psSliderSpecifier.rawValue,
+            SettingType.psRadioGroupSpecifier.rawValue,
+            SettingType.psChildPaneSpecifier.rawValue:
+            fallthrough
+
+        default:
+            self = .unsupported(typeString)
+
+        }
+    }
+
+    enum CodingKeys: String, CodingKey {
+        case type = "Type"
+    }
+}
+
+extension PreferenceSetting.Group: Decodable {
+    enum CodingKeys: String, CodingKey {
+        case title = "Title"
+        case footerText = "FooterText"
+    }
+}
+
+extension PreferenceSetting.Title: Decodable {
+    init(from decoder: any Decoder) throws {
+        let container = try decoder.container(keyedBy: CodingKeys.self)
+        self.key = try container.decode(String.self, forKey: .key)
+        self.title = try container.decode(String.self, forKey: .title)
+        self.choices = try .init(from: decoder)
+    }
+
+    enum CodingKeys: String, CodingKey {
+        case key = "Key"
+        case title = "Title"
+    }
+}
+
+extension PreferenceSetting.MultiValue: Decodable {
+    init(from decoder: any Decoder) throws {
+        let container = try decoder.container(keyedBy: CodingKeys.self)
+        self.key = try container.decode(String.self, forKey: .key)
+        self.title = try container.decode(String.self, forKey: .title)
+        self.choices = try .init(from: decoder)
+    }
+
+    enum CodingKeys: String, CodingKey {
+        case key = "Key"
+        case title = "Title"
+    }
+}
+
+extension PreferenceSetting.TextField: Decodable {
+    init(from decoder: any Decoder) throws {
+        let container = try decoder.container(keyedBy: CodingKeys.self)
+        self.key = try container.decode(String.self, forKey: .key)
+        self.title = try container.decode(String.self, forKey: .title)
+        self.defaultValue = try container.decodeIfPresent(String.self, forKey: .defaultValue) ?? ""
+    }
+
+    enum CodingKeys: String, CodingKey {
+        case key = "Key"
+        case title = "Title"
+        case defaultValue = "DefaultValue"
+    }
+}
+
+extension PreferenceSetting.Toggle: Decodable {
+    init(from decoder: any Decoder) throws {
+        let container = try decoder.container(keyedBy: CodingKeys.self)
+        self.key = try container.decode(String.self, forKey: .key)
+        self.title = try container.decode(String.self, forKey: .title)
+        self.defaultValue = try container.decode(Bool.self, forKey: .defaultValue)
+    }
+
+    enum CodingKeys: String, CodingKey {
+        case key = "Key"
+        case title = "Title"
+        case defaultValue = "DefaultValue"
+    }
+}
+
+extension PreferenceSetting.Custom: Decodable {
+    init(from decoder: any Decoder) throws {
+        let container = try decoder.container(keyedBy: CodingKeys.self)
+
+        let subtype = try container.decode(String.self, forKey: .subtype)
+
+        switch subtype {
+        case PreferenceSetting.CustomSettingSubType.helloWorld.rawValue:
+            self = .helloWorld(try HelloWorld(from: decoder))
+
+        default:
+            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: container.codingPath, debugDescription: "Unsupported custom setting subtype: \(subtype)"))
+        }
+    }
+
+    enum CodingKeys: String, CodingKey {
+        case subtype = "Subtype"
+    }
+}
+
+extension PreferenceSetting.Custom.HelloWorld: Decodable {
+    enum CodingKeys: String, CodingKey {
+        case title = "Title"
+        case population = "Population"
+    }
+}
+
+extension PreferenceSetting.Choices {
+    /// A value type used only for the purposes of decoding.
+    fileprivate enum IntermediateValue: Decodable {
+        case bool(Bool)
+        case string(String)
+        case number(PreferenceSetting.Number)
+
+        var boolValue: Bool? {
+            switch self {
+            case let .bool(value):
+                return value
+            default:
+                return nil
+            }
+        }
+
+        var stringValue: String? {
+            switch self {
+            case let .string(value):
+                return value
+            default:
+                return nil
+            }
+        }
+
+        var numberValue: PreferenceSetting.Number? {
+            switch self {
+            case let .number(value):
+                return value
+            default:
+                return nil
+            }
+        }
+
+        var floatValue: Float? {
+            switch self {
+            case let .number(value):
+                return value.float
+            default:
+                return nil
+            }
+        }
+
+        var integerValue: Int? {
+            switch self {
+            case let .number(value):
+                return value.integer
+            default:
+                return nil
+            }
+        }
+
+        init(from decoder: any Decoder) throws {
+            let container = try decoder.singleValueContainer()
+
+            if let bool = try? container.decode(Bool.self) {
+                self = .bool(bool)
+                return
+            }
+
+            if let str = try? container.decode(String.self) {
+                self = .string(str)
+                return
+            }
+
+            let float = try container.decode(Float.self)
+            let integer = try container.decode(Int.self)
+
+            self = .number(PreferenceSetting.Number(float: float, integer: integer))
+        }
+    }
+
+    enum CodingKeys: String, CodingKey {
+        case titles = "Titles"
+        case values = "Values"
+        case defaultValue = "DefaultValue"
+    }
+
+    init(from decoder: any Decoder) throws {
+        let container = try decoder.container(keyedBy: CodingKeys.self)
+        let titles = try container.decode([String].self, forKey: .titles)
+        let values = try container.decode([IntermediateValue].self, forKey: .values)
+        let defaultValue = try container.decode(IntermediateValue.self, forKey: .defaultValue)
+
+        guard !titles.isEmpty else {
+            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: container.codingPath, debugDescription: "empty titles array"))
+        }
+
+        guard titles.count == values.count else {
+            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: container.codingPath, debugDescription: "mismatch between titles and values"))
+        }
+
+        switch defaultValue {
+        case let .bool(boolValue):
+            let vals = values.compactMap(\.boolValue)
+            let choices = zip(titles, vals).map { t, v in
+                PreferenceSetting.BoolChoice(title: t, value: v)
+            }
+
+            guard vals.count == values.count else {
+                throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: container.codingPath, debugDescription: "values did not all decode to the same type (bool)"))
+            }
+
+            guard vals.contains(boolValue) else {
+                throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: container.codingPath, debugDescription: "Default value \(boolValue) not found in values"))
+            }
+
+            self = .bool(choices, defaultValue: boolValue)
+
+        case let .number(numberValue):
+            let vals = values.compactMap(\.numberValue)
+            let choices = zip(titles, vals).map { t, v in
+                PreferenceSetting.NumberChoice(title: t, value: v)
+            }
+
+            guard vals.count == values.count else {
+                throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: container.codingPath, debugDescription: "values did not all decode to the same type (integer)"))
+            }
+
+            guard vals.contains(numberValue) else {
+                throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: container.codingPath, debugDescription: "Default value \(numberValue) not found in values"))
+            }
+
+            self = .number(choices, defaultValue: numberValue)
+
+        case let .string(stringValue):
+            let vals = values.compactMap(\.stringValue)
+            let choices = zip(titles, vals).map { t, v in
+                PreferenceSetting.StringChoice(title: t, value: v)
+            }
+
+            guard vals.count == values.count else {
+                throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: container.codingPath, debugDescription: "values did not all decode to the same type (string)"))
+            }
+
+            guard vals.contains(stringValue) else {
+                throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: container.codingPath, debugDescription: "Default value \(stringValue) not found in values"))
+            }
+
+            self = .string(choices, defaultValue: stringValue)
+
+        }
+    }
+}
+
+// - MARK: SettingType
+
+fileprivate extension PreferenceSetting {
+    enum SettingType: String {
+        case psGroupSpecifier = "PSGroupSpecifier"
+        case psTitleValueSpecifier = "PSTitleValueSpecifier"
+        case psMultiValueSpecifier = "PSMultiValueSpecifier"
+        case psTextFieldSpecifier = "PSTextFieldSpecifier"
+        case psToggleSwitchSpecifier = "PSToggleSwitchSpecifier"
+        case psSliderSpecifier = "PSSliderSpecifier"
+        case psRadioGroupSpecifier = "PSRadioGroupSpecifier"
+        case psChildPaneSpecifier = "PSChildPaneSpecifier"
+        case vlcCustomSpecifier = "VLCCustomSpecifier"
+    }
+
+    enum CustomSettingSubType: String {
+        case helloWorld = "HelloWorld"
+    }
+}
diff --git a/VLC.xcodeproj/project.pbxproj b/VLC.xcodeproj/project.pbxproj
index 51e6a0e32019aa92c68f67624532a9c73ae27c23..94af9dff110d5fd8076986b88433030a90a8ffc9 100644
--- a/VLC.xcodeproj/project.pbxproj
+++ b/VLC.xcodeproj/project.pbxproj
@@ -95,6 +95,10 @@
 		4342C3C327474CA000E52334 /* SortedMediaFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4342C3C227474CA000E52334 /* SortedMediaFiles.swift */; };
 		444E5BFA24C6081B0003B69C /* PasscodeLockController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 444E5BF924C6081A0003B69C /* PasscodeLockController.swift */; };
 		444E5C0024C719480003B69C /* AboutController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 444E5BFF24C719480003B69C /* AboutController.swift */; };
+		448BA8482D83116D008CCDFF /* PreferenceSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 448BA8472D83115D008CCDFF /* PreferenceSetting.swift */; };
+		448BA8492D83116D008CCDFF /* PreferenceSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 448BA8472D83115D008CCDFF /* PreferenceSetting.swift */; };
+		448BA84B2D832D30008CCDFF /* PreferenceSettingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 448BA84A2D832D30008CCDFF /* PreferenceSettingTests.swift */; };
+		448BA84D2D84E1ED008CCDFF /* PreferenceSettingTestsSuccess.plist in Resources */ = {isa = PBXBuildFile; fileRef = 448BA84C2D84E1DE008CCDFF /* PreferenceSettingTestsSuccess.plist */; };
 		44B5822024E434FD001A2583 /* MediaGridCollectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44B5821F24E434FD001A2583 /* MediaGridCollectionCell.swift */; };
 		44C8BBA324AF2B5C003F8940 /* FeedbackGenerators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44C8BBA224AF2B5C003F8940 /* FeedbackGenerators.swift */; };
 		44C8BBAE24AF36F4003F8940 /* SettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44C8BBA624AF36F4003F8940 /* SettingsController.swift */; };
@@ -1014,6 +1018,9 @@
 		4342C3C227474CA000E52334 /* SortedMediaFiles.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SortedMediaFiles.swift; sourceTree = "<group>"; };
 		444E5BF924C6081A0003B69C /* PasscodeLockController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasscodeLockController.swift; sourceTree = "<group>"; };
 		444E5BFF24C719480003B69C /* AboutController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutController.swift; sourceTree = "<group>"; };
+		448BA8472D83115D008CCDFF /* PreferenceSetting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceSetting.swift; sourceTree = "<group>"; };
+		448BA84A2D832D30008CCDFF /* PreferenceSettingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceSettingTests.swift; sourceTree = "<group>"; };
+		448BA84C2D84E1DE008CCDFF /* PreferenceSettingTestsSuccess.plist */ = {isa = PBXFileReference; explicitFileType = text.plist.xml; path = PreferenceSettingTestsSuccess.plist; sourceTree = "<group>"; };
 		44B5821F24E434FD001A2583 /* MediaGridCollectionCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaGridCollectionCell.swift; sourceTree = "<group>"; };
 		44C8BBA224AF2B5C003F8940 /* FeedbackGenerators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedbackGenerators.swift; sourceTree = "<group>"; };
 		44C8BBA624AF36F4003F8940 /* SettingsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsController.swift; sourceTree = "<group>"; };
@@ -1867,6 +1874,8 @@
 		41533C92211338D500EC3ABA /* Unit Testing */ = {
 			isa = PBXGroup;
 			children = (
+				448BA84C2D84E1DE008CCDFF /* PreferenceSettingTestsSuccess.plist */,
+				448BA84A2D832D30008CCDFF /* PreferenceSettingTests.swift */,
 				41533CA1211343D100EC3ABA /* Info.plist */,
 				41533C9D2113392F00EC3ABA /* URLHandlerTests.swift */,
 			);
@@ -1901,6 +1910,7 @@
 		44C8BBA124AF2B5C003F8940 /* Helpers */ = {
 			isa = PBXGroup;
 			children = (
+				448BA8472D83115D008CCDFF /* PreferenceSetting.swift */,
 				419A2C651F37A4B70069D224 /* VLCStringsForLocalization.m */,
 				4152F1611FEF19BD00F1908B /* KeychainCoordinator.swift */,
 				7D0C209A28C89F5400CCFFEF /* Network */,
@@ -3658,6 +3668,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				448BA84D2D84E1ED008CCDFF /* PreferenceSettingTestsSuccess.plist in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -4134,6 +4145,7 @@
 				1B39FA112C73BBA900F6F960 /* NSURLSession+sharedMPTCPSession.m in Sources */,
 				1B39FA0B2C73BBA300F6F960 /* NSURLSessionConfiguration+default.m in Sources */,
 				41533C9E2113392F00EC3ABA /* URLHandlerTests.swift in Sources */,
+				448BA84B2D832D30008CCDFF /* PreferenceSettingTests.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -4340,6 +4352,7 @@
 				7D50C6202BBD20DF00B9F1A0 /* SettingsController.swift in Sources */,
 				7D50C6212BBD20DF00B9F1A0 /* VLCNetworkImageView.m in Sources */,
 				7D50C6222BBD20DF00B9F1A0 /* EqualizerView.swift in Sources */,
+				448BA8492D83116D008CCDFF /* PreferenceSetting.swift in Sources */,
 				7D50C6232BBD20DF00B9F1A0 /* VLCConfettiView.swift in Sources */,
 				7D50C6252BBD20DF00B9F1A0 /* VLCAppCoordinator.m in Sources */,
 				7D50C6272BBD20DF00B9F1A0 /* PlayerController.swift in Sources */,
@@ -4662,6 +4675,7 @@
 				7D3784AE183A9906009EE944 /* VLCDropboxTableViewController.m in Sources */,
 				DD3EFF3B1BDEBCE500B68579 /* VLCNetworkServerBrowserVLCMedia.m in Sources */,
 				8DB0D71924CED15C00915506 /* VideoPlayerControls.swift in Sources */,
+				448BA8482D83116D008CCDFF /* PreferenceSetting.swift in Sources */,
 				41B93C051A53835300102E8B /* VLCCloudServiceCell.m in Sources */,
 				1B39FA082C73BBA200F6F960 /* NSURLSessionConfiguration+default.m in Sources */,
 				8DE1887421089B3A00A091D2 /* MediaLibraryBaseModel.swift in Sources */,
@@ -5129,7 +5143,7 @@
 				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				INFOPLIST_FILE = Buildsystem/Testing/Unit/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 11.4;
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"@executable_path/Frameworks",
@@ -5172,7 +5186,7 @@
 				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				INFOPLIST_FILE = Buildsystem/Testing/Unit/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 11.4;
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"@executable_path/Frameworks",
@@ -5214,7 +5228,7 @@
 				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				INFOPLIST_FILE = Buildsystem/Testing/Unit/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 11.4;
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"@executable_path/Frameworks",