diff --git a/Resources/iOS/Settings.bundle/Root.inApp.plist b/Resources/iOS/Settings.bundle/Root.inApp.plist
index 2172d720bdc9e792760ec9c5eef1515bdd12c97e..614adcb10f39b525764739219eb10f5722defdc8 100644
--- a/Resources/iOS/Settings.bundle/Root.inApp.plist
+++ b/Resources/iOS/Settings.bundle/Root.inApp.plist
@@ -49,7 +49,7 @@
 				<integer>0</integer>
 				<integer>1</integer>
 				<integer>2</integer>
-				<string>3</string>
+				<integer>3</integer>
 			</array>
 		</dict>
 		<dict>
@@ -163,6 +163,8 @@
 			<string>SETTINGS_NETWORK_PLAY_ALL</string>
 			<key>Type</key>
 			<string>PSMultiValueSpecifier</string>
+			<key>DefaultValue</key>
+				<false/>
 			<key>Titles</key>
 			<array>
 				<string>SETTINGS_PLAY_ALL</string>
@@ -170,8 +172,8 @@
 			</array>
 			<key>Values</key>
 			<array>
-				<integer>0</integer>
-				<integer>1</integer>
+				<false/>
+				<true/>
 			</array>
 		</dict>
 		<dict>
@@ -522,7 +524,7 @@
 			<key>Key</key>
 			<string>avcodec-skiploopfilter</string>
 			<key>DefaultValue</key>
-			<string>1</string>
+			<integer>1</integer>
 			<key>Titles</key>
 			<array>
 				<string>SETTINGS_SKIP_LOOP_FILTER_NONE</string>
@@ -557,7 +559,7 @@
 			<array>
 				<integer>1</integer>
 				<integer>0</integer>
-				<string>-1</string>
+				<integer>-1</integer>
 			</array>
 		</dict>
 		<dict>
diff --git a/Sources/About/AboutController.swift b/Sources/About/AboutController.swift
index 3072c947a70891d2b15edb843657bfd5cccbac9f..8175dcec6399fba71dd1e381f30b2d86e58dbf37 100644
--- a/Sources/About/AboutController.swift
+++ b/Sources/About/AboutController.swift
@@ -181,9 +181,26 @@ class AboutController: UIViewController, MFMailComposeViewControllerDelegate, UI
     func generateFeedbackEmailPrefill() -> String {
         let bundleShortVersionString = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String
         let device = UIDevice.current
-        let defaults = UserDefaults.standard
         let locale = NSLocale.autoupdatingCurrent
-        let prefilledFeedback = String(format: "\n\n\n----------------------------------------\n%@\nDevice: %@\nOS: %@ - %@\nLocale: %@ (%@)\nVLC app version: %@\nlibvlc version: %@\nhardware decoding: %i\nnetwork caching level: %i\nskip loop filter: %i\nRTSP over TCP: %i\nAudio time stretching: %i",
+        let defaults = VLCDefaults.shared
+        let messageFormat = """
+            
+
+
+        ----------------------------------------
+        %@
+        Device: %@
+        OS: %@ - %@
+        Locale: %@ (%@)
+        VLC app version: %@
+        libvlc version: %@
+        hardware decoding: %@
+        network caching level: %i
+        skip loop filter: %i
+        RTSP over TCP: %i
+        Audio time stretching: %i
+        """
+        let prefilledFeedback = String(format: messageFormat,
                                        NSLocalizedString("FEEDBACK_EMAIL_BODY", comment: ""),
                                        generateDeviceIdentifier(),
                                        device.systemName,
@@ -192,11 +209,11 @@ class AboutController: UIViewController, MFMailComposeViewControllerDelegate, UI
                                        locale.regionCode!,
                                        bundleShortVersionString,
                                        VLCLibrary.shared().changeset,
-                                       defaults.integer(forKey: kVLCSettingHardwareDecoding),
-                                       defaults.integer(forKey: kVLCSettingNetworkCaching),
-                                       defaults.integer(forKey: kVLCSettingSkipLoopFilter),
-                                       defaults.integer(forKey: kVLCSettingNetworkRTSPTCP),
-                                       defaults.integer(forKey: kVLCSettingStretchAudio))
+                                       defaults.hardwareDecoding.description,
+                                       defaults.networkCaching.rawValue,
+                                       defaults.skipLoopFilter.rawValue,
+                                       defaults.networkRTSPTCP ? 1 : 0,
+                                       defaults.stretchAudio ? 1 : 0)
         return prefilledFeedback
     }
 
diff --git a/Sources/App/iOS/TabBarCoordinator.swift b/Sources/App/iOS/TabBarCoordinator.swift
index 83047c4df765d094a473d3e4f6603b08151473fe..b956f1bc0167fd3049bd59a59a14ce155e96010d 100644
--- a/Sources/App/iOS/TabBarCoordinator.swift
+++ b/Sources/App/iOS/TabBarCoordinator.swift
@@ -50,7 +50,7 @@ class TabBarCoordinator: NSObject {
         ]
 
         tabBarController.viewControllers = controllers.map { UINavigationController(rootViewController: $0) }
-        tabBarController.selectedIndex = UserDefaults.standard.integer(forKey: kVLCTabBarIndex)
+        tabBarController.selectedIndex = VLCDefaults.shared.tabBarIndex
     }
 
     func setupEditToolbar() {
@@ -162,6 +162,6 @@ class TabBarCoordinator: NSObject {
 extension TabBarCoordinator: UITabBarControllerDelegate {
     func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
         let viewControllerIndex: Int = tabBarController.viewControllers?.firstIndex(of: viewController) ?? 0
-        UserDefaults.standard.set(viewControllerIndex, forKey: kVLCTabBarIndex)
+        VLCDefaults.shared.tabBarIndex = viewControllerIndex
     }
 }
diff --git a/Sources/App/iOS/VLCAppDelegate.m b/Sources/App/iOS/VLCAppDelegate.m
index d054b695fd72d1b4ac34b8c41ffefe250e623e0b..5d359783e7783168c7c3ad6a7f88bea0a56c155b 100644
--- a/Sources/App/iOS/VLCAppDelegate.m
+++ b/Sources/App/iOS/VLCAppDelegate.m
@@ -35,75 +35,7 @@
 
 + (void)initialize
 {
-    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
-    NSUInteger appThemeIndex = kVLCSettingAppThemeBright;
-    if (@available(iOS 13.0, *)) {
-        appThemeIndex = kVLCSettingAppThemeSystem;
-    }
-
-    NSDictionary *appDefaults = @{kVLCSettingAppTheme : @(appThemeIndex),
-                                  kVLCSettingPasscodeEnableBiometricAuth : @(1),
-                                  kVLCSettingContinueAudioInBackgroundKey : @(YES),
-                                  kVLCSettingStretchAudio : @(YES),
-                                  kVLCSettingDefaultPreampLevel : @(6),
-                                  kVLCSettingTextEncoding : kVLCSettingTextEncodingDefaultValue,
-                                  kVLCSettingSkipLoopFilter : kVLCSettingSkipLoopFilterNonRef,
-                                  kVLCSettingSubtitlesFont : kVLCSettingSubtitlesFontDefaultValue,
-                                  kVLCSettingSubtitlesFontColor : kVLCSettingSubtitlesFontColorDefaultValue,
-                                  kVLCSettingSubtitlesFontSize : kVLCSettingSubtitlesFontSizeDefaultValue,
-                                  kVLCSettingSubtitlesBoldFont: kVLCSettingSubtitlesBoldFontDefaultValue,
-                                  kVLCSettingDeinterlace : kVLCSettingDeinterlaceDefaultValue,
-                                  kVLCSettingHardwareDecoding : kVLCSettingHardwareDecodingDefault,
-                                  kVLCSettingNetworkCaching : kVLCSettingNetworkCachingDefaultValue,
-                                  kVLCSettingVolumeGesture : @(YES),
-                                  kVLCSettingPlayPauseGesture : @(YES),
-                                  kVLCSettingBrightnessGesture : @(YES),
-                                  kVLCSettingSeekGesture : @(YES),
-                                  kVLCSettingCloseGesture : @(YES),
-                                  kVLCSettingPlaybackLongTouchSpeedUp : @(YES),
-                                  kVLCSettingVideoFullscreenPlayback : @(YES),
-                                  kVLCSettingContinuePlayback : @(1),
-                                  kVLCSettingContinueAudioPlayback : @(1),
-                                  kVLCSettingWiFiSharingIPv6 : kVLCSettingWiFiSharingIPv6DefaultValue,
-                                  kVLCSettingNetworkRTSPTCP : @(NO),
-                                  kVLCSettingNetworkSatIPChannelListUrl : @"",
-                                  kVLCSettingEqualizerProfile : kVLCSettingEqualizerProfileDefaultValue,
-                                  kVLCSettingEqualizerProfileDisabled : @(YES),
-                                  kVLCSettingPlaybackForwardBackwardEqual: @(YES),
-                                  kVLCSettingPlaybackTapSwipeEqual:  @(YES),
-                                  kVLCSettingPlaybackForwardSkipLength : kVLCSettingPlaybackForwardSkipLengthDefaultValue,
-                                  kVLCSettingPlaybackBackwardSkipLength : kVLCSettingPlaybackBackwardSkipLengthDefaultValue,
-                                  kVLCSettingPlaybackForwardSkipLengthSwipe : kVLCSettingPlaybackForwardSkipLengthSwipeDefaultValue,
-                                  kVLCSettingPlaybackBackwardSkipLengthSwipe : kVLCSettingPlaybackBackwardSkipLengthSwipeDefaultValue,
-                                  kVLCSettingPlaybackLockscreenSkip : @(NO),
-                                  kVLCSettingPlaybackRemoteControlSkip : @(NO),
-                                  kVLCSettingOpenAppForPlayback : kVLCSettingOpenAppForPlaybackDefaultValue,
-                                  kVLCAutomaticallyPlayNextItem : @(YES),
-                                  kVLCPlaylistPlayNextItem: @(YES),
-                                  kVLCSettingEnableMediaCellTextScrolling : @(NO),
-                                  kVLCSettingShowThumbnails : kVLCSettingShowThumbnailsDefaultValue,
-                                  kVLCSettingShowArtworks : kVLCSettingShowArtworksDefaultValue,
-                                  kVLCSettingBackupMediaLibrary : kVLCSettingBackupMediaLibraryDefaultValue,
-                                  kVLCSettingCastingAudioPassthrough : @(NO),
-                                  kVLCSettingCastingConversionQuality : @(2),
-                                  kVLCForceSMBV1 : @(YES),
-                                  @"kVLCAudioLibraryGridLayoutALBUMS" : @(YES),
-                                  @"kVLCAudioLibraryGridLayoutARTISTS" : @(YES),
-                                  @"kVLCAudioLibraryGridLayoutGENRES" : @(YES),
-                                  @"kVLCVideoLibraryGridLayoutALL_VIDEOS" : @(YES),
-                                  @"kVLCVideoLibraryGridLayoutVIDEO_GROUPS" : @(YES),
-                                  @"kVLCVideoLibraryGridLayoutVLCMLMediaGroupCollections" : @(YES),
-                                  kVLCPlayerShouldRememberState: @(YES),
-                                  kVLCPlayerIsShuffleEnabled: kVLCPlayerIsShuffleEnabledDefaultValue,
-                                  kVLCPlayerIsRepeatEnabled: kVLCPlayerIsRepeatEnabledDefaultValue,
-                                  kVLCSettingPlaybackSpeedDefaultValue: @(1.0),
-                                  kVLCPlayerShowPlaybackSpeedShortcut: @(NO),
-                                  kVLCSettingAlwaysPlayURLs: @(NO),
-                                  kVLCRestoreLastPlayedMedia: @(YES),
-                                  kVLCSettingPlayerControlDuration: kVLCSettingPlayerControlDurationDefaultValue,
-                                  kVLCSettingPauseWhenShowingControls: @(NO)
-    };
-    [defaults registerDefaults:appDefaults];
+    [VLCDefaults.shared registerDefaults];
 }
 
 - (void)setupTabBarAppearance
@@ -163,8 +95,7 @@
 
     [self configureShortCutItemsWithApplication:application];
 
-    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
-    [defaults setInteger:([defaults integerForKey:kVLCNumberOfLaunches] + 1) forKey:kVLCNumberOfLaunches];
+    [VLCDefaults.shared incrementNumberOfLaunches];
 
     return YES;
 }
@@ -277,7 +208,7 @@
 {
     if ([[VLCKeychainCoordinator passcodeService] hasSecret]) {
         //TODO: Dismiss playback
-        BOOL allowBiometricAuthentication = [[NSUserDefaults standardUserDefaults] boolForKey:kVLCSettingPasscodeEnableBiometricAuth];
+        BOOL allowBiometricAuthentication = VLCDefaults.shared.passcodeEnableBiometricAuth;
 
         [[VLCKeychainCoordinator passcodeService]
          validateSecretWithAllowBiometricAuthentication:allowBiometricAuthentication
@@ -332,7 +263,7 @@
 - (void)recoverLastPlayingMedia {
     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
 
-    if (![defaults boolForKey:kVLCRestoreLastPlayedMedia]) {
+    if (!VLCDefaults.shared.restoreLastPlayedMedia) {
         return;
     }
 
diff --git a/Sources/App/tvOS/AppleTVAppDelegate.m b/Sources/App/tvOS/AppleTVAppDelegate.m
index 4502514633e16a6eea701196e089fb459ff36b40..6aa2f8046059c23c7962042235d7f566418f5288 100644
--- a/Sources/App/tvOS/AppleTVAppDelegate.m
+++ b/Sources/App/tvOS/AppleTVAppDelegate.m
@@ -38,37 +38,7 @@
 
 + (void)initialize
 {
-    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
-
-    NSDictionary *appDefaults = @{kVLCSettingContinueAudioInBackgroundKey : @(YES),
-                                  kVLCSettingStretchAudio : @(YES),
-                                  kVLCSettingDefaultPreampLevel : @(6),
-                                  kVLCSettingTextEncoding : kVLCSettingTextEncodingDefaultValue,
-                                  kVLCSettingSkipLoopFilter : kVLCSettingSkipLoopFilterNonRef,
-                                  kVLCSettingSubtitlesFont : kVLCSettingSubtitlesFontDefaultValue,
-                                  kVLCSettingSubtitlesFontColor : kVLCSettingSubtitlesFontColorDefaultValue,
-                                  kVLCSettingSubtitlesFontSize : kVLCSettingSubtitlesFontSizeDefaultValue,
-                                  kVLCSettingSubtitlesBoldFont: kVLCSettingSubtitlesBoldFontDefaultValue,
-                                  kVLCSettingDeinterlace : kVLCSettingDeinterlaceDefaultValue,
-                                  kVLCSettingHardwareDecoding : kVLCSettingHardwareDecodingDefault,
-                                  kVLCSettingNetworkCaching : kVLCSettingNetworkCachingDefaultValue,
-                                  kVLCSettingNetworkRTSPTCP : @(NO),
-                                  kVLCSettingNetworkSatIPChannelListUrl : @"",
-                                  kVLCSettingEqualizerProfileDisabled : @(YES),
-                                  kVLCSettingEqualizerProfile : kVLCSettingEqualizerProfileDefaultValue,
-                                  kVLCSettingPlaybackForwardSkipLength : kVLCSettingPlaybackForwardSkipLengthDefaultValue,
-                                  kVLCSettingPlaybackBackwardSkipLength : kVLCSettingPlaybackBackwardSkipLengthDefaultValue,
-                                  kVLCSettingPlaybackLockscreenSkip : @(NO),
-                                  kVLCSettingPlaybackRemoteControlSkip : @(NO),
-                                  kVLCSettingWiFiSharingIPv6 : kVLCSettingWiFiSharingIPv6DefaultValue,
-                                  kVLCAutomaticallyPlayNextItem : @(YES),
-                                  kVLCPlayerShouldRememberState: @(YES),
-                                  kVLCPlayerUIShouldHide : @(NO),
-                                  kVLCSettingDownloadArtwork : @(YES),
-                                  kVLCForceSMBV1 : @(YES),
-                                  kVLCSettingBackupMediaLibrary : kVLCSettingBackupMediaLibraryDefaultValue,
-                                  kVLCSettingPlaybackSpeedDefaultValue: @(1.0)};
-    [defaults registerDefaults:appDefaults];
+    [VLCDefaults.shared registerDefaults];
 }
 
 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
diff --git a/Sources/CarPlay/VLCCarPlayArtistsController.m b/Sources/CarPlay/VLCCarPlayArtistsController.m
index 4a21decea2485f391af3b58cd262945c1624573d..191325aa39b0f4d2c2ff4216cb513997d35a2045 100644
--- a/Sources/CarPlay/VLCCarPlayArtistsController.m
+++ b/Sources/CarPlay/VLCCarPlayArtistsController.m
@@ -66,7 +66,7 @@
 
 - (NSArray *)listOfArtists
 {
-    BOOL hideFeatArtists = [[NSUserDefaults standardUserDefaults] boolForKey:kVLCAudioLibraryHideFeatArtists];
+    BOOL hideFeatArtists = VLCDefaults.shared.audioLibraryHideFeatArtists;
     NSArray *artists = [[VLCAppCoordinator sharedInstance].mediaLibraryService artistsWithSortingCriteria:VLCMLSortingCriteriaDefault
                                                                                                      desc:NO
                                                                                                   listAll:!hideFeatArtists];
diff --git a/Sources/Cloud/Services/OneDrive/VLCOneDriveTableViewController.m b/Sources/Cloud/Services/OneDrive/VLCOneDriveTableViewController.m
index 2f48bc65749cfeb1140fd048374eb48d8f39ef4e..66841cf2925bd478f485fd059a77cd23935ce64b 100644
--- a/Sources/Cloud/Services/OneDrive/VLCOneDriveTableViewController.m
+++ b/Sources/Cloud/Services/OneDrive/VLCOneDriveTableViewController.m
@@ -132,7 +132,7 @@
             NSInteger positionIndex = 0;
             VLCMedia *mediaToPlay = [VLCMedia mediaWithURL:url];
             mediaToPlay = [_oneDriveController setMediaNameMetadata:mediaToPlay withName:selectedItem.name];
-            if (![[NSUserDefaults standardUserDefaults] boolForKey:kVLCAutomaticallyPlayNextItem]) {
+            if (!VLCDefaults.shared.automaticallyPlayNextItem) {
                 mediaList = [[VLCMediaList alloc] initWithArray:@[mediaToPlay]];
                 subtitlePath = [_oneDriveController configureSubtitleWithFileName:selectedItem.name
                                                                       folderItems:items];
diff --git a/Sources/Donation/VLCStripeController.m b/Sources/Donation/VLCStripeController.m
index 1e8b3e7aec7c0b7e67a370b44d19c84ab3e4eb6f..ed0f13bff59b7379cc8c56f795bde079c89b9c79 100644
--- a/Sources/Donation/VLCStripeController.m
+++ b/Sources/Donation/VLCStripeController.m
@@ -20,6 +20,7 @@
 #import "VLCSubscription.h"
 #import "VLCDonationInvoicesViewController.h"
 #import "VLCDonationViewController.h"
+#import "VLC-Swift.h"
 
 const NSString *publishableStripeAPIKey = @"";
 const NSString *secretStripeAPIKey = @"";
@@ -708,12 +709,12 @@ NSString *callbackURLString = @"vlcpay://3ds";
         nextReminderMonth = currentMonth + 3;
     }
 
-    [[NSUserDefaults standardUserDefaults] setInteger:nextReminderMonth forKey:kVLCHasNaggedThisMonth];
+    VLCDefaults.shared.hasNaggedThisMonth = nextReminderMonth;
 }
 
 - (void)activeSubscription:(BOOL)bValue
 {
-    [[NSUserDefaults standardUserDefaults] setBool:bValue forKey:kVLCHasActiveSubscription];
+    VLCDefaults.shared.hasActiveSubscription = bValue;
 }
 
 @end
diff --git a/Sources/Headers/VLCConstants.h b/Sources/Headers/VLCConstants.h
index c5d22a5b154c56bff82141ad42060c657cdc2bda..442157ed1ffb3c1f2d894f051cae71bef4e6fb8a 100644
--- a/Sources/Headers/VLCConstants.h
+++ b/Sources/Headers/VLCConstants.h
@@ -11,130 +11,25 @@
  * Refer to the COPYING file of the official project for license.
  *****************************************************************************/
 
-#define kVLCSettingPasscodeOnKey @"PasscodeProtection"
-#define kVLCSettingPasscodeEnableBiometricAuth @"EnableBiometricAuth"
-#define kVLCSettingHideLibraryInFilesApp @"HideLibraryInFilesApp"
 #define kVLCThemeDidChangeNotification @"themeDidChangeNotfication"
-#define kVLCSettingAppTheme @"darkMode"
-#define kVLCSettingAppThemeBright 0
-#define kVLCSettingAppThemeDark 1
-#define kVLCSettingAppThemeSystem 2
-#define kVLCSettingAppThemeBlack @"blackTheme"
-#define kVLCOptimizeItemNamesForDisplay @"MLDecrapifyTitles"
-#define kVLCSettingAbout @"about"
-#define kVLCAutomaticallyPlayNextItem @"AutomaticallyPlayNextItem"
-#define kVLCPlaylistPlayNextItem @"PlaylistPlayNextItem"
-#define kVLCLastPlayedPlaylist @"LastPlayedPlaylist"
-#define kVLCIsCurrentlyPlayingPlaylist @"isPlaylistCurrentlyPlaying"
-#define kVLCCurrentPlaylistMediasQueue @"currentPlaylistMediasQueue"
-#define kVLCSettingEnableMediaCellTextScrolling @"EnableMediaCellTextScrolling"
-#define kVLCSettingContinueAudioInBackgroundKey @"BackgroundAudioPlayback"
-#define kVLCSettingStretchAudio @"audio-time-stretch"
-#define kVLCSettingDefaultPreampLevel @"pre-amp-level"
-#define kVLCSettingTextEncoding @"subsdec-encoding"
-#define kVLCSettingTextEncodingDefaultValue @"Windows-1252"
-#define kVLCSettingSkipLoopFilter @"avcodec-skiploopfilter"
-#define kVLCSettingSkipLoopFilterNone @(0)
-#define kVLCSettingSkipLoopFilterNonRef @(1)
 #define kVLCSettingSaveHTTPUploadServerStatus @"isHTTPServerOn"
-#define kVLCSettingSubtitlesFont @"quartztext-font"
-#define kVLCSettingSubtitlesFontDefaultValue @"HelveticaNeue"
-#define kVLCSettingSubtitlesFontSize @"quartztext-rel-fontsize"
-#define kVLCSettingSubtitlesFontSizeDefaultValue @"16"
-#define kVLCSettingSubtitlesBoldFont @"quartztext-bold"
-#define kVLCSettingSubtitlesBoldFontDefaultValue @NO
-#define kVLCSettingSubtitlesFontColor @"quartztext-color"
-#define kVLCSettingSubtitlesFontColorDefaultValue @"16777215"
 #define kVLCSettingSubtitlesFilePath @"sub-file"
 #define kVLCSubtitlesCacheFolderName @"cached-subtitles"
-#define kVLCSettingDeinterlace @"deinterlace"
-#define kVLCSettingDeinterlaceDefaultValue @(-1)
-#define kVLCSettingHardwareDecoding @"codec"
-#define kVLCSettingHardwareDecodingDefault @""
-#define kVLCSettingRotationLock @"kVLCSettingRotationLock"
-#define kVLCSettingNetworkCaching @"network-caching"
-#define kVLCSettingNetworkCachingDefaultValue @(999)
-#define kVLCSettingNetworkRTSPTCP @"rtsp-tcp"
-#define kVLCSaveDebugLogs @"kVLCSaveDebugLogs"
 #define kVLCSettingNetworkSatIPChannelList @"satip-channelist"
 #define kVLCSettingNetworkSatIPChannelListCustom @"CustomList"
 #define kVLCSettingNetworkSatIPChannelListUrl @"satip-channellist-url"
-#define kVLCSettingsDecrapifyTitles @"MLDecrapifyTitles"
-#define kVLCSettingVolumeGesture @"EnableVolumeGesture"
-#define kVLCSettingPlayPauseGesture @"EnablePlayPauseGesture"
-#define kVLCSettingBrightnessGesture @"EnableBrightnessGesture"
-#define kVLCSettingSeekGesture @"EnableSeekGesture"
-#define kVLCSettingCloseGesture @"EnableCloseGesture"
-#define kVLCSettingVideoFullscreenPlayback @"AlwaysUseFullscreenForVideo"
-#define kVLCSettingContinuePlayback @"ContinuePlayback"
-#define kVLCSettingContinueAudioPlayback @"ContinueAudioPlayback"
-#define kVLCSettingPlaybackSpeedDefaultValue @"playback-speed"
-#define kVLCSettingWiFiSharingIPv6 @"wifi-sharing-ipv6"
-#define kVLCSettingWiFiSharingIPv6DefaultValue @(NO)
-#define kVLCSettingEqualizerProfile @"EqualizerProfile"
-#define kVLCSettingEqualizerProfileDisabled @"EqualizerDisabled"
-#define kVLCSettingEqualizerProfileDefaultValue @(0)
-#define kVLCSettingPlaybackForwardBackwardEqual @"playback-forward-backward-equal"
-#define kVLCSettingPlaybackTapSwipeEqual @"playback-tap-swipe-equal"
-#define kVLCSettingPlaybackForwardSkipLength @"playback-forward-skip-length"
-#define kVLCSettingPlaybackForwardSkipLengthDefaultValue @(10)
-#define kVLCSettingPlaybackBackwardSkipLength @"playback-backward-skip-length"
-#define kVLCSettingPlaybackBackwardSkipLengthDefaultValue @(10)
-#define kVLCSettingPlaybackForwardSkipLengthSwipe @"playback-forward-skip-length-swipe"
-#define kVLCSettingPlaybackForwardSkipLengthSwipeDefaultValue @(10)
-#define kVLCSettingPlaybackBackwardSkipLengthSwipe @"playback-backward-skip-length-swipe"
-#define kVLCSettingPlaybackLongTouchSpeedUp @"LongTouchSpeedUp"
-#define kVLCSettingPlaybackBackwardSkipLengthSwipeDefaultValue @(10)
-#define kVLCSettingPlaybackLockscreenSkip @"playback-lockscreen-skip"
-#define kVLCSettingPlaybackRemoteControlSkip @"playback-remote-control-skip"
-#define kVLCSettingOpenAppForPlayback @"open-app-for-playback"
-#define kVLCSettingOpenAppForPlaybackDefaultValue @YES
-#define kVLCSettingShowThumbnails @"ShowThumbnails"
-#define kVLCSettingShowThumbnailsDefaultValue @YES
-#define kVLCSettingShowArtworks @"ShowArtworks"
-#define kVLCSettingShowArtworksDefaultValue @YES
-#define kVLCSettingsDisableGrouping @"MLDisableGrouping"
-#define kVLCkVLCSettingsDisableGroupingDefaultValue @NO
+#define kVLCSettingLastUsedSubtitlesSearchLanguage @"kVLCSettingLastUsedSubtitlesSearchLanguage"
 #define kVLCSettingCastingAudioPassthrough @"sout-chromecast-audio-passthrough"
 #define kVLCSettingCastingConversionQuality @"sout-chromecast-conversion-quality"
-#define kVLCSettingBackupMediaLibrary @"BackupMediaLibrary"
-#define kVLCSettingBackupMediaLibraryDefaultValue @NO
-#define kVLCSettingLastUsedSubtitlesSearchLanguage @"kVLCSettingLastUsedSubtitlesSearchLanguage"
-#define kVLCResetSettings @"kVLCResetSettings"
-#define kVLCSettingAlwaysPlayURLs @"kVLCSettingAlwaysPlayURLs"
-#define kVLCSettingDisableSubtitles @"kVLCSettingDisableSubtitles"
-#define kVLCSettingPlayerControlDuration @"kVLCSettingPlayerControlDuration"
-#define kVLCSettingPlayerControlDurationDefaultValue @(4)
-#define kVLCSettingPauseWhenShowingControls @"kVLCSettingPauseWhenShowingControls"
-
-#define kVLCForceSMBV1 @"smb-force-v1"
-
-#define kVLCShowRemainingTime @"show-remaining-time"
 #define kVLCRecentURLs @"recent-urls"
 #define kVLCRecentURLTitles @"recent-url-titles"
 #define kVLCPrivateWebStreaming @"private-streaming"
 #define kVLChttpScanSubtitle @"http-scan-subtitle"
 #define kVLCHTTPUploadDirectory @"Upload"
-#define kVLCAudioLibraryGridLayout @"kVLCAudioLibraryGridLayout"
-#define kVLCAudioLibraryHideFeatArtists @"kVLCAudioLibraryHideFeatArtists"
-#define kVLCAudioLibraryHideTrackNumbers @"kVLCAudioLibraryHideTrackNumbers"
-#define kVLCVideoLibraryGridLayout @"kVLCVideoLibraryGridLayout"
-
+#define kVLCSettingStretchAudio @"audio-time-stretch"
 #define kVLCLastPlayedMediaIdentifier @"LastPlayedMediaIdentifier"
-#define kVLCRestoreLastPlayedMedia @"RestoreLastPlayedMedia"
 
 #define kVLCPlayerOpenInMiniPlayer @"OpenInMiniPlayer"
-#define kVLCPlayerShouldRememberState @"PlayerShouldRememberState"
-#define kVLCPlayerShouldRememberBrightness @"PlayerShouldRememberBrightness"
-#define KVLCPlayerBrightness @"playerbrightness"
-#define kVLCPlayerIsShuffleEnabled @"PlayerIsShuffleEnabled"
-#define kVLCPlayerIsShuffleEnabledDefaultValue @NO
-#define kVLCPlayerIsRepeatEnabled @"PlayerIsRepeatEnabled"
-#define kVLCPlayerIsRepeatEnabledDefaultValue @(0)
-#define kVLCPlayerShowPlaybackSpeedShortcut @"kVLCPlayerShowPlaybackSpeedShortcut"
-
-#define kVLCCustomProfileEnabled @"kVLCCustomProfileEnabled"
-#define kVLCCustomEqualizerProfiles @"kVLCCustomEqualizerProfiles"
 
 #define kSupportedFileExtensions @"\\.(669|3g2|3gp|3gp2|3gpp|amv|asf|avi|bik|bin|crf|divx|drc|dv|evo|f4v|far|flv|gvi|gxf|hevc|iso|it|m1v|m2v|m2t|m2ts|m4v|mkv|mov|mp2|mp2v|mp4|mp4v|mpe|mpeg|mpeg1|mpeg2|mpeg4|mpg|mpv2|mtm|mts|mtv|mxf|mxg|nsv|nuv|ogg|ogm|ogv|ogx|ps|rec|rm|rmvb|rpl|s3m|thp|tod|ts|tts|txd|vlc|vob|vro|webm|wm|wmv|wtv|xesc|xm)$"
 #define kSupportedSubtitleFileExtensions @"\\.(cdg|idx|srt|sub|utf|ass|ssa|aqt|jss|psb|rt|smi|txt|smil|stl|usf|dks|pjs|mpl2|mks|vtt|ttml|dfxp)$"
@@ -170,19 +65,8 @@
 #define kVLCWifiAuthentificationFailure 1
 #define kVLCWifiAuthentificationBanned 2
 
-#define kVLCSortDefault @"SortDefault"
-#define kVLCSortDescendingDefault @"SortDescendingDefault"
-#define kVLCHasLaunchedBefore @"hasLaunchedBefore"
-#define kVLCHasNaggedThisMonth @"kVLCHasNaggedThisMonth"
-#define kVLCNumberOfLaunches @"kVLCNumberOfLaunches"
-#define kVLCHasActiveSubscription @"kVLCHasActiveSubscription"
-
-#define kVLCTabBarIndex @"TabBarIndex"
-
 #define kVLCGroupLayout @"kVLCGroupLayout"
 
-#define kVLCEqualizerSnapBands @"EqualizerSnapBands"
-
 #define kVLCDonationAnonymousCustomerID @"kVLCDonationAnonymousCustomerID"
 
 /* LEGACY KEYS, DO NOT USE IN NEW CODE */
diff --git a/Sources/Headers/VLCTVConstants.h b/Sources/Headers/VLCTVConstants.h
index 3d5e993b09d098c0f682f539619ac4c2575b5743..e2c9d0997d2efe9da4a03736600c947fd86ec9a9 100644
--- a/Sources/Headers/VLCTVConstants.h
+++ b/Sources/Headers/VLCTVConstants.h
@@ -25,71 +25,26 @@
 
 #define kSupportedProtocolSchemes @"(rtsp|mms|mmsh|udp|rtp|rtmp|sftp|ftp|smb)$"
 
-#define kVLCSettingPlaybackSpeedDefaultValue @"playback-speed"
-#define kVLCSettingNetworkCaching @"network-caching"
-#define kVLCSettingNetworkCachingDefaultValue @(999)
-#define kVLCSettingNetworkRTSPTCP @"rtsp-tcp"
-#define kVLCSaveDebugLogs @"kVLCSaveDebugLogs"
 #define kVLCSettingNetworkSatIPChannelList @"satip-channelist"
 #define kVLCSettingNetworkSatIPChannelListCustom @"CustomList"
+
 #define kVLCSettingNetworkSatIPChannelListUrl @"satip-channellist-url"
-#define kVLCSettingSkipLoopFilter @"avcodec-skiploopfilter"
-#define kVLCSettingSkipLoopFilterNone @(0)
-#define kVLCSettingSkipLoopFilterNonRef @(1)
-#define kVLCSettingSkipLoopFilterNonKey @(3)
-#define kVLCSettingDeinterlace @"deinterlace"
-#define kVLCSettingDeinterlaceDefaultValue @(-1)
-#define kVLCSettingHardwareDecoding @"codec"
-#define kVLCSettingHardwareDecodingDefault @""
-#define kVLCSettingSubtitlesFont @"quartztext-font"
-#define kVLCSettingSubtitlesFontDefaultValue @"HelveticaNeue"
-#define kVLCSettingSubtitlesFontSize @"quartztext-rel-fontsize"
-#define kVLCSettingSubtitlesFontSizeDefaultValue @"16"
-#define kVLCSettingSubtitlesBoldFont @"quartztext-bold"
-#define kVLCSettingSubtitlesBoldFontDefaultValue @NO
-#define kVLCSettingSubtitlesFontColor @"quartztext-color"
-#define kVLCSettingSubtitlesFontColorDefaultValue @"16777215"
+
 #define kVLCSubtitlesCacheFolderName @"cached-subtitles"
-#define kVLCSettingTextEncoding @"subsdec-encoding"
-#define kVLCSettingTextEncodingDefaultValue @"Windows-1252"
-#define kVLCSettingStretchAudio @"audio-time-stretch"
-#define kVLCSettingStretchAudioOnValue @"1"
-#define kVLCSettingStretchAudioOffValue @"0"
-#define kVLCSettingContinueAudioInBackgroundKey @"BackgroundAudioPlayback"
-#define kVLCSettingDefaultPreampLevel @"pre-amp-level"
 #define kVLCSettingSubtitlesFilePath @"sub-file"
-#define kVLCSettingEqualizerProfile @"EqualizerProfile"
-#define kVLCSettingEqualizerProfileDisabled @"EqualizerDisabled"
-#define kVLCSettingEqualizerProfileDefaultValue @(0)
-#define kVLCSettingPlaybackForwardSkipLength @"playback-forward-skip-length"
-#define kVLCSettingPlaybackForwardSkipLengthDefaultValue @(10)
-#define kVLCSettingPlaybackBackwardSkipLength @"playback-backward-skip-length"
-#define kVLCSettingPlaybackBackwardSkipLengthDefaultValue @(10)
-#define kVLCSettingPlaybackLockscreenSkip @"playback-lockscreen-skip"
-#define kVLCSettingPlaybackRemoteControlSkip @"playback-remote-control-skip"
 #define kVLCSettingSaveHTTPUploadServerStatus @"isHTTPServerOn"
-#define kVLCAutomaticallyPlayNextItem @"AutomaticallyPlayNextItem"
-#define kVLCPlayerUIShouldHide @"PlayerUIShouldHide"
-#define kVLCSettingDownloadArtwork @"download-artwork"
 #define kVLCSettingUseSPDIF @"kVLCSettingUseSPDIF"
-#define kVLCSettingBackupMediaLibrary @"BackupMediaLibrary"
-#define kVLCSettingBackupMediaLibraryDefaultValue @NO
-#define kVLCSettingDisableSubtitles @"kVLCSettingDisableSubtitles"
-#define kVLCSettingPlayerControlDuration @"kVLCSettingPlayerControlDuration"
-#define kVLCSettingPlayerControlDurationDefaultValue @(4)
+
+#define kVLCSettingStretchAudio @"audio-time-stretch"
 
 #define kVLCLastPlayedMediaIdentifier @"LastPlayedMediaIdentifier"
 
 #define kVLCPlayerOpenInMiniPlayer @"OpenInMiniPlayer"
-#define kVLCPlayerShouldRememberState @"PlayerShouldRememberState"
-#define kVLCPlayerIsShuffleEnabled @"PlayerIsShuffleEnabled"
-#define kVLCPlayerIsShuffleEnabledDefaultValue @NO
-#define kVLCPlayerIsRepeatEnabled @"PlayerIsRepeatEnabled"
-#define kVLCPlayerIsRepeatEnabledDefaultValue @(0)
 
 #define kVLCSettingLastUsedSubtitlesSearchLanguage @"kVLCSettingLastUsedSubtitlesSearchLanguage"
-#define kVLCSettingWiFiSharingIPv6 @"wifi-sharing-ipv6"
-#define kVLCSettingWiFiSharingIPv6DefaultValue @(NO)
+
+#define kVLCSettingCastingAudioPassthrough @"sout-chromecast-audio-passthrough"
+#define kVLCSettingCastingConversionQuality @"sout-chromecast-conversion-quality"
 
 #define kVLCfortvOSMovieDBKey @""
 
@@ -97,9 +52,4 @@
 
 #define kVLCHTTPUploadDirectory @"Upload"
 
-#define kVLCSettingCastingAudioPassthrough @"sout-chromecast-audio-passthrough"
-#define kVLCSettingCastingConversionQuality @"sout-chromecast-conversion-quality"
-
-#define kVLCForceSMBV1 @"smb-force-v1"
-
 #define kVLCSettingReset @"kVLCSettingReset"
diff --git a/Sources/Helpers/ColorThemeExtension.swift b/Sources/Helpers/ColorThemeExtension.swift
index b3f07ce243aec30a0194af746d72c93cdc7942d1..160384079cf9527eab23beddc090d09cc54f1091 100644
--- a/Sources/Helpers/ColorThemeExtension.swift
+++ b/Sources/Helpers/ColorThemeExtension.swift
@@ -17,7 +17,7 @@ extension PresentationTheme {
                 // there was a userInterfaceStyle change.
                 return
             }
-            guard UserDefaults.standard.integer(forKey: kVLCSettingAppTheme) == kVLCSettingAppThemeSystem else {
+            guard VLCDefaults.shared.appTheme == .system else {
                 // Theme is specificly set, do not follow systeme theme.
                 return
             }
diff --git a/Sources/Helpers/Network/URLHandler.swift b/Sources/Helpers/Network/URLHandler.swift
index 9ff0e7388a89172f4eecccc520f70d5e8b000e2a..d0a6e375ad21fcc7cb484995c76b9b398c202eec 100644
--- a/Sources/Helpers/Network/URLHandler.swift
+++ b/Sources/Helpers/Network/URLHandler.swift
@@ -173,7 +173,7 @@ extension VLCURLHandler {
         let alwaysPlayAction = UIAlertAction(title: NSLocalizedString("ALWAYS_STREAM_URL",
                                                                       comment: ""),
                                              style: .default) { _ in
-            UserDefaults.standard.set(true, forKey: kVLCSettingAlwaysPlayURLs)
+            VLCDefaults.shared.alwaysPlayURLs = true
             self.handlePlay()
         }
 
@@ -363,7 +363,7 @@ class XCallbackURLHandler: NSObject, VLCURLHandler {
             return true
         default:
 #if os(iOS)
-            if UserDefaults.standard.bool(forKey: kVLCSettingAlwaysPlayURLs) {
+            if VLCDefaults.shared.alwaysPlayURLs {
                 self.handlePlay()
             } else {
                 self.createAlert()
@@ -410,7 +410,7 @@ public class VLCCallbackURLHandler: NSObject, VLCURLHandler {
 #if os(iOS)
         let scheme = transformedURL.scheme
         if scheme == "http" || scheme == "https" || scheme == "ftp" {
-            if UserDefaults.standard.bool(forKey: kVLCSettingAlwaysPlayURLs) {
+            if VLCDefaults.shared.alwaysPlayURLs {
                 handlePlay()
             } else {
                 self.createAlert()
diff --git a/Sources/Helpers/VLCDefaults.swift b/Sources/Helpers/VLCDefaults.swift
new file mode 100644
index 0000000000000000000000000000000000000000..27b6fdbcc49cb2d83c059b0cebd4c721b13d6062
--- /dev/null
+++ b/Sources/Helpers/VLCDefaults.swift
@@ -0,0 +1,1230 @@
+/*****************************************************************************
+ * VLCDefaults.swift
+ * VLC for iOS
+ *****************************************************************************
+ * Copyright (c) 2025 VideoLAN. All rights reserved.
+ * $Id$
+ *
+ * Authors: Craig Reyenga <craig.reyenga # gmail.com>
+ *
+ * Refer to the COPYING file of the official project for license.
+ *****************************************************************************/
+
+extension Notification.Name {
+    static let VLCDefaultsDidUpdate = Notification.Name("VLCDefaultsDidUpdate")
+}
+
+@objc final class VLCDefaults: NSObject {
+    @objc static let shared = VLCDefaults()
+
+    private let userDefaults = UserDefaults.standard
+
+    private override init() {
+        super.init()
+        NotificationCenter.default.addObserver(self,
+                                               selector: #selector(defaultsDidChange),
+                                               name: UserDefaults.didChangeNotification,
+                                               object: nil)
+    }
+
+    @objc func registerDefaults() {
+        var dict: [String: Any] = [
+            // bools
+            Keys.alwaysPlayURLs: false,
+            Keys.appThemeBlack: false,
+            Keys.audioLibraryHideFeatArtists: false,
+            Keys.audioLibraryHideTrackNumbers: false,
+            Keys.automaticallyPlayNextItem: true,
+            Keys.backupMediaLibrary: false,
+            Keys.brightnessGesture: true,
+            Keys.castingAudioPassthrough: false,
+            Keys.closeGesture: true,
+            Keys.continueAudioInBackground: true,
+            Keys.currentlyPlayingPlaylist: false,
+            Keys.customEqualizerProfileEnabled: false,
+            Keys.optimizeTitles: false,
+            Keys.disableGrouping: false,
+            Keys.disableSubtitles: false,
+            Keys.downloadArtwork: true,
+            Keys.enableMediaCellTextScrolling: false,
+            Keys.equalizerProfileDisabled: true,
+            Keys.equalizerSnapBands: false,
+            Keys.forceSMBV1: true,
+            Keys.hasActiveSubscription: false,
+            Keys.hasLaunchedBefore: false,
+            Keys.hideLibraryInFilesApp: false,
+            Keys.lockscreenSkip: false,
+            Keys.mediaLibraryServiceDidForceRescan: false,
+            Keys.networkRTSPTCP: false,
+            Keys.passcodeEnableBiometricAuth: true,
+            Keys.passcodeOn: false,
+            Keys.pauseWhenShowingControls: false,
+            Keys.playbackForwardBackwardEqual: true,
+            Keys.playbackLongTouchSpeedUp: true,
+            Keys.playbackTapSwipeEqual: true,
+            Keys.playerIsShuffleEnabled: false,
+            Keys.playerShouldRememberBrightness: false,
+            Keys.playerShouldRememberState: true,
+            Keys.playerShowPlaybackSpeedShortcut: false,
+            Keys.playerUIShouldHide: false,
+            Keys.playlistPlayNextItem: true,
+            Keys.playPauseGesture: true,
+            Keys.remoteControlSkip: false,
+            Keys.restoreLastPlayedMedia: true,
+            Keys.rotationLock: false,
+            Keys.saveDebugLogs: false,
+            Keys.seekGesture: true,
+            Keys.showRemainingTime: false,
+            Keys.showThumbnails: true,
+            Keys.stretchAudio: true,
+            Keys.subtitlesBoldFont: false,
+            Keys.videoFullscreenPlayback: true,
+            Keys.volumeGesture: true,
+            Keys.wifiSharingIPv6: false,
+
+            // numbers
+            Keys.castingConversionQuality: DefaultValues.castingConversionQuality,
+            Keys.continueAudioPlayback: 1,
+            Keys.continuePlayback: 1,
+            Keys.defaultPreampLevel: Float(6),
+            Keys.deinterlace: DefaultValues.deinterlace,
+            Keys.equalizerProfile: DefaultValues.equalizerProfile,
+            Keys.hasNaggedThisMonth: 0,
+            Keys.numberOfLaunches: 0,
+            Keys.playbackBackwardSkipLength: DefaultValues.playbackBackwardSkipLength,
+            Keys.playbackBackwardSkipLengthSwipe: DefaultValues.playbackBackwardSkipLengthSwipe,
+            Keys.playbackForwardSkipLength: DefaultValues.playbackForwardSkipLength,
+            Keys.playbackForwardSkipLengthSwipe: DefaultValues.playbackForwardSkipLengthSwipe,
+            Keys.playbackSpeedDefaultValue: DefaultValues.playbackSpeedDefaultValue,
+            Keys.playerControlDuration: DefaultValues.playerControlDuration,
+            Keys.tabBarIndex: 0,
+
+            // other
+            Keys.appTheme: DefaultValues.appTheme.rawValue,
+            Keys.hardwareDecoding: HardwareDecoding.hardware.rawValue,
+            Keys.networkCaching: NetworkCaching.normal.rawValue,
+            Keys.networkSatIPChannelListUrl: DefaultValues.networkSatIPChannelListUrl,
+            Keys.playerIsRepeatEnabled: DefaultValues.playerRepeatMode.rawValue,
+            Keys.skipLoopFilter: DefaultValues.skipLoopFilter.rawValue,
+            Keys.subtitlesFontColor: DefaultValues.subtitlesFontColor,
+            Keys.subtitlesFontSize: DefaultValues.subtitlesFontSize,
+            Keys.textEncoding: DefaultValues.textEncoding,
+        ]
+
+        [
+            "ALBUMS",
+            "ARTISTS",
+            "GENRES",
+            "ALL_VIDEOS",
+            "VIDEO_GROUPS",
+            "VLCMLMediaGroupCollections"
+        ].forEach { s in
+            dict[Keys.videoLibraryGridLayout(name: s)] = true
+        }
+
+        userDefaults.register(defaults: dict)
+    }
+
+    func reset() {
+        let appDomain = Bundle.main.bundleIdentifier!
+        UserDefaults().removePersistentDomain(forName: appDomain)
+    }
+
+    @objc private func defaultsDidChange(_: Notification) {
+        NotificationCenter.default.post(name: .VLCDefaultsDidUpdate, object: self)
+    }
+
+    // These methods are not strictly necessary, however, they help prevent a
+    // programmer error whereby attempts to write invalid data types will get
+    // past the compiler, but will cause a crash at runtime.
+
+    fileprivate func set(bool b: Bool, forKey key: String) {
+        userDefaults.set(b, forKey: key)
+    }
+
+    fileprivate func set(float f: Float, forKey key: String) {
+        userDefaults.set(f, forKey: key)
+    }
+
+    fileprivate func set(integer i: Int, forKey key: String) {
+        userDefaults.set(i, forKey: key)
+    }
+
+    fileprivate func set(string s: String, forKey key: String) {
+        userDefaults.set(s, forKey: key)
+    }
+}
+
+// MARK: - Defaults
+
+extension VLCDefaults {
+
+    // Bools
+
+    @objc var alwaysPlayURLs: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.alwaysPlayURLs)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.alwaysPlayURLs)
+        }
+    }
+
+    @objc var appThemeBlack: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.appThemeBlack)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.appThemeBlack)
+        }
+    }
+
+    @objc var audioLibraryHideFeatArtists: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.audioLibraryHideFeatArtists)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.audioLibraryHideFeatArtists)
+        }
+    }
+
+    @objc var audioLibraryHideTrackNumbers: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.audioLibraryHideTrackNumbers)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.audioLibraryHideTrackNumbers)
+        }
+    }
+
+    @objc var automaticallyPlayNextItem: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.automaticallyPlayNextItem)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.automaticallyPlayNextItem)
+        }
+    }
+
+    @objc var backupMediaLibrary: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.backupMediaLibrary)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.backupMediaLibrary)
+        }
+    }
+
+    @objc var brightnessGesture: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.brightnessGesture)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.brightnessGesture)
+        }
+    }
+
+    @objc var castingAudioPassthrough: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.castingAudioPassthrough)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.castingAudioPassthrough)
+        }
+    }
+
+    @objc var closeGesture: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.closeGesture)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.closeGesture)
+        }
+    }
+
+    @objc var continueAudioInBackgroundKey: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.continueAudioInBackground)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.continueAudioInBackground)
+        }
+    }
+
+    @objc var currentlyPlayingPlaylist: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.currentlyPlayingPlaylist)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.currentlyPlayingPlaylist)
+        }
+    }
+
+    @objc var customEqualizerProfileEnabled: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.customEqualizerProfileEnabled)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.customEqualizerProfileEnabled)
+        }
+    }
+
+    @objc var optimizeTitles: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.optimizeTitles)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.optimizeTitles)
+        }
+    }
+
+    @objc var disableGrouping: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.disableGrouping)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.disableGrouping)
+        }
+    }
+
+    @objc var disableSubtitles: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.disableSubtitles)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.disableSubtitles)
+        }
+    }
+
+    @objc var downloadArtwork: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.downloadArtwork)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.downloadArtwork)
+        }
+    }
+
+    @objc var enableMediaCellTextScrolling: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.enableMediaCellTextScrolling)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.enableMediaCellTextScrolling)
+        }
+    }
+
+    @objc var equalizerProfileDisabled: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.equalizerProfileDisabled)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.equalizerProfileDisabled)
+        }
+    }
+
+    @objc var equalizerSnapBands: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.equalizerSnapBands)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.equalizerSnapBands)
+        }
+    }
+
+    @objc var forceSMBV1: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.forceSMBV1)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.forceSMBV1)
+        }
+    }
+
+    @objc var hasActiveSubscription: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.hasActiveSubscription)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.hasActiveSubscription)
+        }
+    }
+
+    var hasLaunchedBefore: Bool {
+        userDefaults.bool(forKey: Keys.hasLaunchedBefore)
+    }
+
+    func setHasLaunchedBeforeIfNeeded() {
+        if !hasLaunchedBefore {
+            userDefaults.set(true, forKey: Keys.hasLaunchedBefore)
+        }
+    }
+
+    @objc var hideLibraryInFilesApp: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.hideLibraryInFilesApp)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.hideLibraryInFilesApp)
+        }
+    }
+
+    @objc var lockscreenSkip: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.lockscreenSkip)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.lockscreenSkip)
+        }
+    }
+
+    @objc var mediaLibraryServiceDidForceRescan: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.mediaLibraryServiceDidForceRescan)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.mediaLibraryServiceDidForceRescan)
+        }
+    }
+
+    @objc var networkRTSPTCP: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.networkRTSPTCP)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.networkRTSPTCP)
+        }
+    }
+
+    @objc var pauseWhenShowingControls: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.pauseWhenShowingControls)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.pauseWhenShowingControls)
+        }
+    }
+
+    var playbackForwardBackwardEqual: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.playbackForwardBackwardEqual)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.playbackForwardBackwardEqual)
+        }
+    }
+
+    @objc var playbackLongTouchSpeedUp: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.playbackLongTouchSpeedUp)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.playbackLongTouchSpeedUp)
+        }
+    }
+
+    @objc var playbackTapSwipeEqual: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.playbackTapSwipeEqual)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.playbackTapSwipeEqual)
+        }
+    }
+
+    @objc var playerIsShuffleEnabled: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.playerIsShuffleEnabled)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.playerIsShuffleEnabled)
+        }
+    }
+
+    @objc var playerShouldRememberBrightness: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.playerShouldRememberBrightness)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.playerShouldRememberBrightness)
+        }
+    }
+
+    @objc var playerShouldRememberState: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.playerShouldRememberState)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.playerShouldRememberState)
+        }
+    }
+
+    @objc var passcodeEnableBiometricAuth: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.passcodeEnableBiometricAuth)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.passcodeEnableBiometricAuth)
+        }
+    }
+
+    @objc var passcodeOn: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.passcodeOn)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.passcodeOn)
+        }
+    }
+
+    @objc var playerShowPlaybackSpeedShortcut: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.playerShowPlaybackSpeedShortcut)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.playerShowPlaybackSpeedShortcut)
+        }
+    }
+
+    /// tvOS only
+    @objc var playerUIShouldHide: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.playerUIShouldHide)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.playerUIShouldHide)
+        }
+    }
+
+    @objc var playlistPlayNextItem: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.playlistPlayNextItem)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.playlistPlayNextItem)
+        }
+    }
+
+    @objc var playPauseGesture: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.playPauseGesture)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.playPauseGesture)
+        }
+    }
+
+    @objc var restoreLastPlayedMedia: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.restoreLastPlayedMedia)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.restoreLastPlayedMedia)
+        }
+    }
+
+    @objc var remoteControlSkip: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.remoteControlSkip)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.remoteControlSkip)
+        }
+    }
+
+    @objc var rotationLock: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.rotationLock)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.rotationLock)
+        }
+    }
+
+    @objc var saveDebugLogs: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.saveDebugLogs)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.saveDebugLogs)
+        }
+    }
+
+    @objc var seekGesture: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.seekGesture)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.seekGesture)
+        }
+    }
+
+    @objc var showArtworks: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.showArtworks)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.showArtworks)
+        }
+    }
+
+    @objc var showRemainingTime: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.showRemainingTime)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.showRemainingTime)
+        }
+    }
+
+    @objc var showThumbnails: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.showThumbnails)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.showThumbnails)
+        }
+    }
+
+    @objc var stretchAudio: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.stretchAudio)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.stretchAudio)
+        }
+    }
+
+    @objc var subtitlesBoldFont: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.subtitlesBoldFont)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.subtitlesBoldFont)
+        }
+    }
+
+    @objc var videoFullscreenPlayback: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.videoFullscreenPlayback)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.videoFullscreenPlayback)
+        }
+    }
+
+    @objc var volumeGesture: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.volumeGesture)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.volumeGesture)
+        }
+    }
+
+    @objc var wifiSharingIPv6: Bool {
+        get {
+            userDefaults.bool(forKey: Keys.wifiSharingIPv6)
+        }
+        set {
+            set(bool: newValue, forKey: Keys.wifiSharingIPv6)
+        }
+    }
+
+    // Numbers
+
+    @objc var castingConversionQuality: Int {
+        get {
+            userDefaults.integer(forKey: Keys.castingConversionQuality)
+        }
+        set {
+            set(integer: newValue, forKey: Keys.castingConversionQuality)
+        }
+    }
+
+    @objc var continueAudioPlayback: Int {
+        get {
+            userDefaults.integer(forKey: Keys.continueAudioPlayback)
+        }
+        set {
+            set(integer: newValue, forKey: Keys.continueAudioPlayback)
+        }
+    }
+
+    @objc var continuePlayback: Int {
+        get {
+            userDefaults.integer(forKey: Keys.continuePlayback)
+        }
+        set {
+            set(integer: newValue, forKey: Keys.continuePlayback)
+        }
+    }
+
+    @objc var defaultPreampLevel: Float {
+        get {
+            userDefaults.float(forKey: Keys.defaultPreampLevel)
+        }
+        set {
+            set(float: newValue, forKey: Keys.defaultPreampLevel)
+        }
+    }
+
+    @objc var deinterlace: Int {
+        get {
+            userDefaults.integer(forKey: Keys.deinterlace)
+        }
+        set {
+            set(integer: newValue, forKey: Keys.deinterlace)
+        }
+    }
+
+    @objc var equalizerProfile: Int {
+        get {
+            userDefaults.integer(forKey: Keys.equalizerProfile)
+        }
+        set {
+            set(integer: newValue, forKey: Keys.equalizerProfile)
+        }
+    }
+
+    @objc var hasNaggedThisMonth: Int {
+        get {
+            userDefaults.integer(forKey: Keys.hasNaggedThisMonth)
+        }
+        set {
+            set(integer: newValue, forKey: Keys.hasNaggedThisMonth)
+        }
+    }
+
+    @objc var numberOfLaunches: Int {
+        userDefaults.integer(forKey: Keys.numberOfLaunches)
+    }
+
+    @objc func incrementNumberOfLaunches() {
+        userDefaults.set(numberOfLaunches + 1, forKey: Keys.numberOfLaunches)
+    }
+
+    @objc func resetNumberOfLaunches() {
+        userDefaults.set(0, forKey: Keys.numberOfLaunches)
+    }
+
+    @objc var playbackBackwardSkipLength: Int {
+        get {
+            userDefaults.integer(forKey: Keys.playbackBackwardSkipLength)
+        }
+        set {
+            set(integer: newValue, forKey: Keys.playbackBackwardSkipLength)
+        }
+    }
+
+    @objc var playbackBackwardSkipLengthSwipe: Int {
+        get {
+            userDefaults.integer(forKey: Keys.playbackBackwardSkipLengthSwipe)
+        }
+        set {
+            set(integer: newValue, forKey: Keys.playbackBackwardSkipLengthSwipe)
+        }
+    }
+
+    @objc var playbackForwardSkipLength: Int {
+        get {
+            userDefaults.integer(forKey: Keys.playbackForwardSkipLength)
+        }
+        set {
+            set(integer: newValue, forKey: Keys.playbackForwardSkipLength)
+        }
+    }
+
+    @objc var playbackForwardSkipLengthSwipe: Int {
+        get {
+            userDefaults.integer(forKey: Keys.playbackForwardSkipLengthSwipe)
+        }
+        set {
+            set(integer: newValue, forKey: Keys.playbackForwardSkipLengthSwipe)
+        }
+    }
+
+    @objc var playbackSpeedDefaultValue: Float {
+        get {
+            userDefaults.float(forKey: Keys.playbackSpeedDefaultValue)
+        }
+        set {
+            set(float: newValue, forKey: Keys.playbackSpeedDefaultValue)
+        }
+    }
+
+    var playerBrightness: Float? {
+        get {
+            // Use data(forKey:) to determine if a value has been set at all
+            userDefaults.data(forKey: Keys.playerBrightness).flatMap { _ in
+                userDefaults.float(forKey: Keys.playerBrightness)
+            }
+        }
+        set {
+            if let newValue = newValue {
+                set(float: newValue, forKey: Keys.playerBrightness)
+            } else {
+                userDefaults.removeObject(forKey: Keys.playerBrightness)
+            }
+        }
+    }
+
+    @objc var playerControlDuration: Int {
+        get {
+            userDefaults.integer(forKey: Keys.playerControlDuration)
+        }
+        set {
+            set(integer: newValue, forKey: Keys.playerControlDuration)
+        }
+    }
+
+    @objc var tabBarIndex: Int {
+        get {
+            userDefaults.integer(forKey: Keys.tabBarIndex)
+        }
+        set {
+            set(integer: newValue, forKey: Keys.tabBarIndex)
+        }
+    }
+
+    // Other
+
+    var appTheme: AppTheme {
+        get {
+            let v = userDefaults.integer(forKey: Keys.appTheme)
+            return AppTheme(rawValue: v) ?? DefaultValues.appTheme
+        }
+        set {
+            set(integer: newValue.rawValue, forKey: Keys.appTheme)
+        }
+    }
+
+    @objc var appThemeIsSystem: Bool {
+        appTheme == .system
+    }
+
+#if os(iOS) || os(visionOS)
+    var customEqualizerProfiles: CustomEqualizerProfiles? {
+        get {
+            guard let encodedData = userDefaults.data(forKey: Keys.customEqualizerProfiles) else {
+                return nil
+            }
+
+            guard let decoded = try? NSKeyedUnarchiver(forReadingFrom: encodedData)
+                .decodeObject(forKey: "root") as? CustomEqualizerProfiles else {
+                return nil
+            }
+
+            return decoded
+        }
+        set {
+            guard let newValue = newValue else {
+                return
+            }
+
+            guard let encoded = try? NSKeyedArchiver
+                .archivedData(withRootObject: newValue, requiringSecureCoding: false) else {
+                return
+            }
+
+            userDefaults.setValue(encoded, forKey: Keys.customEqualizerProfiles)
+        }
+    }
+#endif
+
+    var hardwareDecoding: HardwareDecoding {
+        get {
+            guard let v = userDefaults.string(forKey: Keys.hardwareDecoding) else {
+                return HardwareDecoding.hardware
+            }
+
+            return HardwareDecoding(rawValue: v) ?? .hardware
+        }
+        set {
+            set(string: newValue.rawValue, forKey: Keys.hardwareDecoding)
+        }
+    }
+
+    @objc var hardwareDecodingObjC: String {
+        hardwareDecoding.rawValue
+    }
+
+#if os(iOS) || os(visionOS)
+    var lastPlayedPlaylist: LastPlayedPlaylistModel? {
+        get {
+            guard let encodedData = userDefaults.data(forKey: Keys.lastPlayedPlaylist) else {
+                return nil
+            }
+
+            guard let decoded = try? NSKeyedUnarchiver(forReadingFrom: encodedData)
+                .decodeObject(forKey: "root") as? LastPlayedPlaylistModel else {
+                return nil
+            }
+
+            return decoded
+        }
+        set {
+            guard let newValue = newValue else {
+                return
+            }
+
+            guard let encoded = try? NSKeyedArchiver
+                .archivedData(withRootObject: newValue, requiringSecureCoding: false) else {
+                return
+            }
+
+            userDefaults.setValue(encoded, forKey: Keys.lastPlayedPlaylist)
+        }
+    }
+#endif
+
+    var networkCaching: NetworkCaching {
+        get {
+            let v = userDefaults.integer(forKey: Keys.networkCaching)
+            return NetworkCaching(rawValue: v) ?? .normal
+        }
+        set {
+            set(integer: newValue.rawValue, forKey: Keys.networkCaching)
+        }
+    }
+
+    @objc var networkCachingObjC: Int {
+        networkCaching.rawValue
+    }
+
+    @objc var networkSatIPChannelListUrl: String {
+        get {
+            userDefaults.string(forKey: Keys.networkSatIPChannelListUrl) ?? DefaultValues.networkSatIPChannelListUrl
+        }
+        set {
+            set(string: newValue, forKey: Keys.networkSatIPChannelListUrl)
+        }
+    }
+
+    @objc var playerIsRepeatEnabled: VLCRepeatMode {
+        get {
+            let v = userDefaults.integer(forKey: Keys.playerIsRepeatEnabled)
+            return VLCRepeatMode(rawValue: v) ?? DefaultValues.playerRepeatMode
+        }
+        set {
+            set(integer: newValue.rawValue, forKey: Keys.playerIsRepeatEnabled)
+        }
+    }
+
+    @objc var textEncoding: String {
+        get {
+            userDefaults.string(forKey: Keys.textEncoding) ?? DefaultValues.textEncoding
+        }
+        set {
+            set(string: newValue, forKey: Keys.textEncoding)
+        }
+    }
+
+    var skipLoopFilter: SkipLoopFilter {
+        get {
+            let v = userDefaults.integer(forKey: Keys.skipLoopFilter)
+            return SkipLoopFilter(rawValue: v) ?? DefaultValues.skipLoopFilter
+        }
+        set {
+            set(integer: newValue.rawValue, forKey: Keys.skipLoopFilter)
+        }
+    }
+
+    @objc var skipLoopFilterObjC: Int {
+        get {
+            userDefaults.integer(forKey: Keys.skipLoopFilter)
+        }
+    }
+
+    @objc var subtitlesFont: String {
+        get {
+            userDefaults.string(forKey: Keys.subtitlesFont) ?? DefaultValues.subtitlesFont
+        }
+        set {
+            set(string: newValue, forKey: Keys.subtitlesFont)
+        }
+    }
+
+    @objc var subtitlesFontColor: String {
+        get {
+            userDefaults.string(forKey: Keys.subtitlesFontColor) ?? DefaultValues.subtitlesFontColor
+        }
+        set {
+            set(string: newValue, forKey: Keys.subtitlesFontColor)
+        }
+    }
+
+    @objc var subtitlesFontSize: String {
+        get {
+            userDefaults.string(forKey: Keys.subtitlesFontSize) ?? DefaultValues.subtitlesFontSize
+        }
+        set {
+            set(string: newValue, forKey: Keys.subtitlesFontSize)
+        }
+    }
+
+    func videoLibraryGridLayout(collectionModelName: String? = nil, name: String) -> Bool {
+        userDefaults.bool(forKey: Keys.videoLibraryGridLayout(collectionModelName: collectionModelName, name: name))
+    }
+
+    func setVideoLibraryGridLayout(collectionModelName: String? = nil, name: String, isGrid: Bool) {
+        userDefaults.set(isGrid, forKey: Keys.videoLibraryGridLayout(collectionModelName: collectionModelName, name: name))
+    }
+
+    func audioLibraryGridLayout(collectionModelName: String? = nil, name: String) -> Bool {
+        userDefaults.bool(forKey: Keys.audioLibraryGridLayout(collectionModelName: collectionModelName, name: name))
+    }
+
+    func setAudioLibraryGridLayout(collectionModelName: String? = nil, name: String, isGrid: Bool) {
+        userDefaults.set(isGrid, forKey: Keys.audioLibraryGridLayout(collectionModelName: collectionModelName, name: name))
+    }
+
+#if os(iOS) || os(visionOS)
+    func sortDefault(name: String) -> VLCMLSortingCriteria? {
+        let k = Keys.sortDefault(name: name)
+
+        // Use data(forKey:) to determine if a value has been set at all
+        return userDefaults.data(forKey: k).flatMap { _ in
+            let v = userDefaults.integer(forKey: k)
+            return VLCMLSortingCriteria(rawValue: UInt(v))
+        }
+    }
+
+    func setSortDefault(name: String, criteria: VLCMLSortingCriteria) {
+        let k = Keys.sortDefault(name: name)
+        userDefaults.set(criteria.rawValue, forKey: k)
+    }
+
+    func sortDescendingDefault(name: String) -> Bool {
+        let k = Keys.sortDescendingDefault(name: name)
+        return userDefaults.bool(forKey: k)
+    }
+
+    func setSortDescendingDefault(name: String, isDescending: Bool) {
+        let k = Keys.sortDescendingDefault(name: name)
+        userDefaults.set(isDescending, forKey: k)
+    }
+#endif
+}
+
+// MARK: - Compatibility
+
+extension VLCDefaults {
+    @available(*, deprecated, message: "avoid using keys to access defaults directly, instead use properties on VLCDefaults")
+    @objc(VLCDefaultsCompat)
+    final class Compat: NSObject {
+        static let appThemeKey: String = Keys.appTheme
+        static let automaticallyPlayNextItemKey: String = Keys.automaticallyPlayNextItem
+        static let backupMediaLibraryKey: String = Keys.backupMediaLibrary
+        static let castingConversionQualityKey: String = Keys.castingConversionQuality
+        static let continueAudioPlaybackKey: String = Keys.continueAudioPlayback
+        static let continuePlaybackKey: String = Keys.continuePlayback
+        static let defaultPreampLevelKey: String = Keys.defaultPreampLevel
+        static let deinterlaceKey: String = Keys.deinterlace
+        static let disableGroupingKey: String = Keys.disableGrouping
+        static let hardwareDecodingKey: String = Keys.hardwareDecoding
+        static let hideLibraryInFilesAppKey: String = Keys.hideLibraryInFilesApp
+        static let lockscreenSkipKey: String = Keys.lockscreenSkip
+        static let networkCachingKey: String = Keys.networkCaching
+        static let passcodeOnKey: String = Keys.passcodeOn
+        static let playbackBackwardSkipLengthKey: String = Keys.playbackBackwardSkipLength
+        static let playbackBackwardSkipLengthSwipeKey: String = Keys.playbackBackwardSkipLengthSwipe
+        static let playbackForwardSkipLengthKey: String = Keys.playbackForwardSkipLength
+        static let playbackForwardSkipLengthSwipeKey: String = Keys.playbackForwardSkipLengthSwipe
+        static let playbackSpeedDefaultValueKey: String = Keys.playbackSpeedDefaultValue
+        static let playerControlDurationKey: String = Keys.playerControlDuration
+        static let remoteControlSkipKey: String = Keys.remoteControlSkip
+        static let skipLoopFilterKey: String = Keys.skipLoopFilter
+        static let subtitlesFontColorKey: String = Keys.subtitlesFontColor
+        static let subtitlesFontKey: String = Keys.subtitlesFont
+        static let subtitlesFontSizeKey: String = Keys.subtitlesFontSize
+        static let textEncodingKey: String = Keys.textEncoding
+
+        override init() {
+            fatalError("compat struct not intended to be instantiated")
+        }
+    }
+}
+
+// MARK: - Value Types
+
+extension VLCDefaults {
+    enum AppTheme: Int {
+        case bright = 0
+        case dark = 1
+        case system = 2
+        case black = 3
+    }
+}
+
+extension VLCDefaults {
+    enum HardwareDecoding: String, CustomStringConvertible {
+        case software = "avcodec,all"
+        case hardware = ""
+
+        var description: String {
+            switch self {
+            case .software:
+                return "Software"
+            case .hardware:
+                return "Hardware"
+            }
+        }
+    }
+}
+
+extension VLCDefaults {
+    enum NetworkCaching: Int {
+        case lowest = 333
+        case low = 666
+        case normal = 999
+        case high = 1667
+        case highest = 3333
+    }
+}
+
+extension VLCDefaults {
+    enum SkipLoopFilter: Int {
+        case none = 0
+        case nonRef = 1
+        case nonKey = 3
+    }
+}
+
+// MARK: - Keys
+
+fileprivate enum Keys {
+    // Avoid ever changing these values. Some are used as parameters in functions.
+    // Changing a value also causes the locally stored value to become unreachable.
+    static let alwaysPlayURLs = "kVLCSettingAlwaysPlayURLs"
+    static let appTheme = "darkMode"
+    static let appThemeBlack = "blackTheme"
+    static let audioLibraryHideFeatArtists = "kVLCAudioLibraryHideFeatArtists"
+    static let audioLibraryHideTrackNumbers = "kVLCAudioLibraryHideTrackNumbers"
+    static let automaticallyPlayNextItem = "AutomaticallyPlayNextItem"
+    static let backupMediaLibrary = "BackupMediaLibrary"
+    static let brightnessGesture = "EnableBrightnessGesture"
+    static let castingAudioPassthrough = kVLCSettingCastingAudioPassthrough
+    static let castingConversionQuality = kVLCSettingCastingConversionQuality
+    static let closeGesture = "EnableCloseGesture"
+    static let continueAudioInBackground = "BackgroundAudioPlayback"
+    static let continueAudioPlayback = "ContinueAudioPlayback"
+    static let continuePlayback = "ContinuePlayback"
+    static let currentlyPlayingPlaylist = "isPlaylistCurrentlyPlaying"
+    static let customEqualizerProfileEnabled = "kVLCCustomProfileEnabled"
+    static let customEqualizerProfiles = "kVLCCustomEqualizerProfiles"
+    static let optimizeTitles = "MLDecrapifyTitles"
+    static let defaultPreampLevel = "pre-amp-level"
+    static let deinterlace = "deinterlace"
+    static let disableGrouping = "MLDisableGrouping"
+    static let disableSubtitles = "kVLCSettingDisableSubtitles"
+    static let downloadArtwork = "download-artwork"
+    static let enableMediaCellTextScrolling = "EnableMediaCellTextScrolling"
+    static let equalizerProfile = "EqualizerProfile"
+    static let equalizerProfileDisabled = "EqualizerDisabled"
+    static let equalizerSnapBands = "EqualizerSnapBands"
+    static let forceSMBV1 = "smb-force-v1"
+    static let hardwareDecoding = "codec"
+    static let hasActiveSubscription = "kVLCHasActiveSubscription"
+    static let hasLaunchedBefore = "hasLaunchedBefore"
+    static let hasNaggedThisMonth = "kVLCHasNaggedThisMonth"
+    static let hideLibraryInFilesApp = "HideLibraryInFilesApp"
+    static let lastPlayedPlaylist = "LastPlayedPlaylist"
+    static let lockscreenSkip = "playback-lockscreen-skip"
+    static let mediaLibraryServiceDidForceRescan = "MediaLibraryDidForceRescan"
+    static let networkCaching = "network-caching"
+    static let networkRTSPTCP = "rtsp-tcp"
+    static let networkSatIPChannelListUrl = kVLCSettingNetworkSatIPChannelListUrl
+    static let numberOfLaunches = "kVLCNumberOfLaunches"
+    static let passcodeEnableBiometricAuth = "EnableBiometricAuth"
+    static let passcodeOn = "PasscodeProtection"
+    static let pauseWhenShowingControls = "kVLCSettingPauseWhenShowingControls"
+    static let playbackBackwardSkipLength = "playback-backward-skip-length"
+    static let playbackBackwardSkipLengthSwipe = "playback-backward-skip-length-swipe"
+    static let playbackForwardBackwardEqual = "playback-forward-backward-equal"
+    static let playbackForwardSkipLength = "playback-forward-skip-length"
+    static let playbackForwardSkipLengthSwipe = "playback-forward-skip-length-swipe"
+    static let playbackLongTouchSpeedUp = "LongTouchSpeedUp"
+    static let playbackSpeedDefaultValue = "playback-speed"
+    static let playbackTapSwipeEqual = "playback-tap-swipe-equal"
+    static let playerBrightness = "playerbrightness"
+    static let playerControlDuration = "kVLCSettingPlayerControlDuration"
+    static let playerIsRepeatEnabled = "PlayerIsRepeatEnabled"
+    static let playerIsShuffleEnabled = "PlayerIsShuffleEnabled"
+    static let playerShouldRememberBrightness = "PlayerShouldRememberBrightness"
+    static let playerShouldRememberState = "PlayerShouldRememberState"
+    static let playerShowPlaybackSpeedShortcut = "kVLCPlayerShowPlaybackSpeedShortcut"
+    static let playerUIShouldHide = "PlayerUIShouldHide"
+    static let playlistPlayNextItem = "PlaylistPlayNextItem"
+    static let playPauseGesture = "EnablePlayPauseGesture"
+    static let remoteControlSkip = "playback-remote-control-skip"
+    static let restoreLastPlayedMedia = "RestoreLastPlayedMedia"
+    static let rotationLock = "kVLCSettingRotationLock"
+    static let saveDebugLogs = "kVLCSaveDebugLogs"
+    static let seekGesture = "EnableSeekGesture"
+    static let showArtworks = "ShowArtworks"
+    static let showRemainingTime = "show-remaining-time"
+    static let showThumbnails = "ShowThumbnails"
+    static let skipLoopFilter = "avcodec-skiploopfilter"
+    static let stretchAudio = kVLCSettingStretchAudio
+    static let subtitlesBoldFont = "quartztext-bold"
+    static let subtitlesFont = "quartztext-font"
+    static let subtitlesFontColor = "quartztext-color"
+    static let subtitlesFontSize = "quartztext-rel-fontsize"
+    static let tabBarIndex = "TabBarIndex"
+    static let textEncoding = "subsdec-encoding"
+    static let videoFullscreenPlayback = "AlwaysUseFullscreenForVideo"
+    static let volumeGesture = "EnableVolumeGesture"
+    static let wifiSharingIPv6 = "wifi-sharing-ipv6"
+
+    static func videoLibraryGridLayout(collectionModelName: String? = nil, name: String) -> String {
+        [
+            "kVLCVideoLibraryGridLayout", collectionModelName, name
+        ].compactMap { $0 }.joined()
+    }
+
+    static func audioLibraryGridLayout(collectionModelName: String? = nil, name: String) -> String {
+        [
+            "kVLCAudioLibraryGridLayout", collectionModelName, name
+        ].compactMap { $0 }.joined()
+    }
+
+    static func sortDefault(name: String) -> String {
+        [
+            "SortDefault", name
+        ].joined()
+    }
+
+    static func sortDescendingDefault(name: String) -> String {
+        [
+            "SortDescendingDefault", name
+        ].joined()
+    }
+}
+
+// MARK: - Default Values
+
+fileprivate enum DefaultValues {
+    static let appTheme: VLCDefaults.AppTheme = {
+        if #available(iOS 13.0, *) {
+            return .system
+        }
+        return .bright
+    }()
+    static let castingConversionQuality = 2
+    static let deinterlace = Int(-1)
+    static let equalizerProfile = Int(0)
+    static let textEncoding = "Windows-1252"
+    static let networkSatIPChannelListUrl = ""
+    static let playbackBackwardSkipLength = 10
+    static let playbackBackwardSkipLengthSwipe = 10
+    static let playbackForwardSkipLength = 10
+    static let playbackForwardSkipLengthSwipe = 10
+    static let playbackSpeedDefaultValue = Float(1)
+    static let playerControlDuration = 4
+    static let playerRepeatMode = VLCRepeatMode.doNotRepeat
+    static let skipLoopFilter = VLCDefaults.SkipLoopFilter.nonRef
+    static let subtitlesFont = "HelveticaNeue"
+    static let subtitlesFontColor = "16777215"
+    static let subtitlesFontSize = "16"
+}
diff --git a/Sources/Media Library/Discovery/VLCMediaFileDiscoverer.m b/Sources/Media Library/Discovery/VLCMediaFileDiscoverer.m
index ce034844be2e7c3d9e04bcf513b22a87fabb9396..abac807374ff97945e215a917f006a16016d24c7 100644
--- a/Sources/Media Library/Discovery/VLCMediaFileDiscoverer.m	
+++ b/Sources/Media Library/Discovery/VLCMediaFileDiscoverer.m	
@@ -200,7 +200,7 @@ const float MediaTimerInterval = 2.f;
                     }
                 }
             }
-            BOOL backupMediaLibrary = [NSUserDefaults.standardUserDefaults boolForKey:kVLCSettingBackupMediaLibrary];
+            BOOL backupMediaLibrary = VLCDefaults.shared.backupMediaLibrary;
             NSURL *fileURL = [NSURL fileURLWithPath:filePath];
             [fileURL setExcludedFromBackup:!backupMediaLibrary recursive:NO onlyFirstLevel:NO :nil];
 
@@ -226,7 +226,7 @@ const float MediaTimerInterval = 2.f;
 - (void)didAddMedia:(NSTimer*)timer
 {
 #if TARGET_OS_IOS
-    BOOL hideMediaLibrary = [NSUserDefaults.standardUserDefaults boolForKey:kVLCSettingHideLibraryInFilesApp];
+    BOOL hideMediaLibrary = VLCDefaults.shared.hideLibraryInFilesApp;
     [(NSURL*)[timer.userInfo valueForKey:@"fileURL"] setHidden:hideMediaLibrary recursive:NO onlyFirstLevel:NO :nil];
 #endif
     [NSFileManager.defaultManager removeItemAtPath:[NSString pathWithComponents:@[_directoryPath, NSLocalizedString(@"MEDIALIBRARY_ADDING_PLACEHOLDER", "")]] error:nil];
diff --git a/Sources/Media Library/MediaCategories/MediaCategoryViewController.swift b/Sources/Media Library/MediaCategories/MediaCategoryViewController.swift
index c221da347649b09f85b2e6a0c64177a52a15ceb1..0d4dfe56fa4bf9e72ceeab4d55c38e4347e47940 100644
--- a/Sources/Media Library/MediaCategories/MediaCategoryViewController.swift	
+++ b/Sources/Media Library/MediaCategories/MediaCategoryViewController.swift	
@@ -40,7 +40,6 @@ class MediaCategoryViewController: UICollectionViewController, UISearchBarDelega
     private var searchBarConstraint: NSLayoutConstraint?
     private var searchDataSource: LibrarySearchDataSource
     private let searchBarSize: CGFloat = 50.0
-    private let userDefaults = UserDefaults.standard
 #if os(iOS)
     private var rendererButton: UIButton
 #endif
@@ -79,7 +78,7 @@ class MediaCategoryViewController: UICollectionViewController, UISearchBarDelega
     private lazy var navItemTitle: VLCMarqueeLabel = VLCMarqueeLabel()
 
     private var hasLaunchedBefore: Bool {
-        return userDefaults.bool(forKey: kVLCHasLaunchedBefore)
+        return VLCDefaults.shared.hasLaunchedBefore
     }
 
     @objc private lazy var sortActionSheet: ActionSheet = {
@@ -202,17 +201,12 @@ class MediaCategoryViewController: UICollectionViewController, UISearchBarDelega
     }()
 
     private var lastPlaylist: LastPlayedPlaylistModel? {
-        let encodedLastPlaylist = userDefaults.data(forKey: kVLCLastPlayedPlaylist)
-        guard let encodedData = encodedLastPlaylist,
-              let lastPlayed = NSKeyedUnarchiver(forReadingWith: encodedData).decodeObject(forKey: "root") as? LastPlayedPlaylistModel else {
-            return nil
-        }
-        return lastPlayed
+        return VLCDefaults.shared.lastPlayedPlaylist
     }
 
     // Indicating that the current chosen collection to play is playlist, useful for handling Observer
     private var isPlaylistCurrentlyPlaying: Bool {
-        return userDefaults.bool(forKey: kVLCIsCurrentlyPlayingPlaylist)
+        return VLCDefaults.shared.currentlyPlayingPlaylist
     }
 
     // catch the selected index from collection view, helper for playbackDidStart
@@ -234,8 +228,8 @@ class MediaCategoryViewController: UICollectionViewController, UISearchBarDelega
         videoModel.secondName = model.name
 
         if model is MediaGroupViewModel {
-            self.model = userDefaults.bool(forKey: kVLCSettingsDisableGrouping) ? videoModel : model
-            self.secondModel = userDefaults.bool(forKey: kVLCSettingsDisableGrouping) ? model : videoModel
+            self.model = VLCDefaults.shared.disableGrouping ? videoModel : model
+            self.secondModel = VLCDefaults.shared.disableGrouping ? model : videoModel
         } else {
             self.model = model
             self.secondModel = videoModel
@@ -567,13 +561,8 @@ class MediaCategoryViewController: UICollectionViewController, UISearchBarDelega
     }
 
     func loadSort() {
-        let sortingCriteria: VLCMLSortingCriteria
-        if let sortingCriteriaDefault = UserDefaults.standard.value(forKey: "\(kVLCSortDefault)\(model.name)") as? UInt {
-            sortingCriteria = VLCMLSortingCriteria(rawValue: sortingCriteriaDefault) ?? model.sortModel.currentSort
-        } else {
-            sortingCriteria = model.sortModel.currentSort
-        }
-        let desc = UserDefaults.standard.bool(forKey: "\(kVLCSortDescendingDefault)\(model.name)")
+        let sortingCriteria = VLCDefaults.shared.sortDefault(name: model.name) ?? model.sortModel.currentSort
+        let desc = VLCDefaults.shared.sortDescendingDefault(name: model.name)
         self.model.sort(by: sortingCriteria, desc: desc)
     }
 
@@ -604,14 +593,14 @@ class MediaCategoryViewController: UICollectionViewController, UISearchBarDelega
             let navigationController = UINavigationController(rootViewController: firstStepController)
             navigationController.modalPresentationStyle = .formSheet
             self.present(navigationController, animated: true)
-            userDefaults.set(true, forKey: kVLCHasLaunchedBefore)
+            VLCDefaults.shared.setHasLaunchedBeforeIfNeeded()
         } else {
-            if userDefaults.bool(forKey: kVLCHasActiveSubscription) {
+            if VLCDefaults.shared.hasActiveSubscription {
                 return
             }
 
-            var lastNagMonth = userDefaults.integer(forKey: kVLCHasNaggedThisMonth)
-            let numberOfLaunches = userDefaults.integer(forKey: kVLCNumberOfLaunches)
+            var lastNagMonth = VLCDefaults.shared.hasNaggedThisMonth
+            let numberOfLaunches = VLCDefaults.shared.numberOfLaunches
             let currentMonth = NSCalendar.current.component(.month, from: Date())
 
             if lastNagMonth == 12 && currentMonth < 12 {
@@ -619,8 +608,8 @@ class MediaCategoryViewController: UICollectionViewController, UISearchBarDelega
             }
 
             if lastNagMonth < currentMonth && numberOfLaunches >= 5 {
-                userDefaults.setValue(currentMonth, forKey: kVLCHasNaggedThisMonth)
-                userDefaults.setValue(0, forKey: kVLCNumberOfLaunches)
+                VLCDefaults.shared.hasNaggedThisMonth = currentMonth
+                VLCDefaults.shared.resetNumberOfLaunches()
                 let donationVC = VLCDonationNagScreenViewController(nibName: "VLCDonationNagScreenViewController", bundle: nil)
                 let donationNC = UINavigationController(rootViewController: donationVC)
                 donationNC.navigationBar.isHidden = true
@@ -641,7 +630,7 @@ class MediaCategoryViewController: UICollectionViewController, UISearchBarDelega
             saveCurrentPlaylistInfo(with: playlist.identifier(), playlistTitle: playlist.title(), media: playlist.media?[selectedIndex.row])
             addPlaybackWillStopObserver()
             reloadData()
-            userDefaults.set(true, forKey: kVLCIsCurrentlyPlayingPlaylist)
+            VLCDefaults.shared.currentlyPlayingPlaylist = true
         } else if let playlists = currentDataSet as? [VLCMLPlaylist], let selectedIndex = collectionSelectedIndex {
             let selectedPlaylist = playlists[selectedIndex.row]
             guard let media = PlaybackService.sharedInstance().currentlyPlayingMedia,
@@ -650,7 +639,7 @@ class MediaCategoryViewController: UICollectionViewController, UISearchBarDelega
             saveCurrentPlaylistInfo(with: selectedPlaylist.identifier(), playlistTitle: selectedPlaylist.title(), media: mlMedia)
             addPlaybackWillStopObserver()
             reloadData()
-            userDefaults.set(true, forKey: kVLCIsCurrentlyPlayingPlaylist)
+            VLCDefaults.shared.currentlyPlayingPlaylist = true
         } else if isPlaylistCurrentlyPlaying {
             //if the playlist media is already being played and the current model is not Playlist or playlist collection media.
             //This will update the value of last played media, leading to right indication if the app is suddenly closed.
@@ -937,10 +926,8 @@ extension MediaCategoryViewController {
 
     @objc func executeSortAction(with sortingCriteria: VLCMLSortingCriteria, desc: Bool) {
         model.sort(by: sortingCriteria, desc: desc)
-        userDefaults.set(desc,
-                         forKey: "\(kVLCSortDescendingDefault)\(model.name)")
-        userDefaults.set(sortingCriteria.rawValue,
-                         forKey: "\(kVLCSortDefault)\(model.name)")
+        VLCDefaults.shared.setSortDescendingDefault(name: model.name, isDescending: desc)
+        VLCDefaults.shared.setSortDefault(name: model.name, criteria: sortingCriteria)
         sortActionSheet.removeActionSheet()
         reloadData()
     }
@@ -1525,8 +1512,6 @@ extension MediaCategoryViewController: ActionSheetSortSectionHeaderDelegate {
 
     func handleLayoutChange(gridLayout: Bool) {
         var prefix: String = ""
-        var suffix: String = ""
-
         var collectionModelName: String = ""
         var isVideoModel = false
         if let model = model as? CollectionModel {
@@ -1538,9 +1523,17 @@ extension MediaCategoryViewController: ActionSheetSortSectionHeaderDelegate {
             isVideoModel = true
         }
 
-        prefix = isVideoModel ? kVLCVideoLibraryGridLayout : kVLCAudioLibraryGridLayout
-        suffix = collectionModelName + model.name
-        userDefaults.set(gridLayout, forKey: "\(prefix)\(suffix)")
+        switch isVideoModel {
+        case true:
+            VLCDefaults.shared.setVideoLibraryGridLayout(collectionModelName: collectionModelName,
+                                                         name: model.name,
+                                                         isGrid: gridLayout)
+        case false:
+            VLCDefaults.shared.setAudioLibraryGridLayout(collectionModelName: collectionModelName,
+                                                         name: model.name,
+                                                         isGrid: gridLayout)
+        }
+
         setupCollectionView()
         cachedCellSize = .zero
         collectionView?.collectionViewLayout.invalidateLayout()
@@ -1548,7 +1541,7 @@ extension MediaCategoryViewController: ActionSheetSortSectionHeaderDelegate {
     }
 
     func actionSheetSortSectionHeaderShouldHideFeatArtists(onSwitchIsOnChange: Bool) {
-        userDefaults.set(onSwitchIsOnChange, forKey: "\(kVLCAudioLibraryHideFeatArtists)")
+        VLCDefaults.shared.audioLibraryHideFeatArtists = onSwitchIsOnChange
         setupCollectionView()
         cachedCellSize = .zero
         model.sort(by: model.sortModel.currentSort, desc: model.sortModel.desc)
@@ -1556,7 +1549,7 @@ extension MediaCategoryViewController: ActionSheetSortSectionHeaderDelegate {
     }
 
     func actionSheetSortSectionHeaderShouldHideTrackNumbers(onSwitchIsOnChange: Bool) {
-        userDefaults.set(onSwitchIsOnChange, forKey: "\(kVLCAudioLibraryHideTrackNumbers)")
+        VLCDefaults.shared.audioLibraryHideTrackNumbers = onSwitchIsOnChange
         setupCollectionView()
         cachedCellSize = .zero
         model.sort(by: model.sortModel.currentSort, desc: model.sortModel.desc)
@@ -1564,18 +1557,17 @@ extension MediaCategoryViewController: ActionSheetSortSectionHeaderDelegate {
     }
 
     func actionSheetSortSectionHeader(_ header: ActionSheetSortSectionHeader, onSwitchIsOnChange: Bool, type: ActionSheetSortHeaderOptions) {
-        var prefix: String = ""
-        var suffix: String = ""
-        if type == .descendingOrder {
+        switch type {
+        case .descendingOrder:
             model.sort(by: model.sortModel.currentSort, desc: onSwitchIsOnChange)
-            prefix = kVLCSortDescendingDefault
-            suffix = model is VideoModel ? secondModel.name : model.name
-            userDefaults.set(onSwitchIsOnChange, forKey: "\(prefix)\(suffix)")
+            let name = model is VideoModel ? secondModel.name : model.name
+            VLCDefaults.shared.setSortDescendingDefault(name: name, isDescending: onSwitchIsOnChange)
             setupCollectionView()
             cachedCellSize = .zero
             collectionView?.collectionViewLayout.invalidateLayout()
             reloadData()
-        } else if type == .layoutChange {
+
+        case .layoutChange:
             handleLayoutChange(gridLayout: onSwitchIsOnChange)
         }
     }
@@ -1779,13 +1771,13 @@ extension MediaCategoryViewController: MediaLibraryBaseModelObserver {
 extension MediaCategoryViewController {
     func play(media: VLCMLMedia, at indexPath: IndexPath) {
         let playbackController = PlaybackService.sharedInstance()
-        var autoPlayNextItem: Bool = userDefaults.bool(forKey: kVLCAutomaticallyPlayNextItem)
+        var autoPlayNextItem: Bool = VLCDefaults.shared.automaticallyPlayNextItem
 
         playbackController.fullscreenSessionRequested = media.type() != .audio
 
         if let model = model as? CollectionModel,
            model.mediaCollection is VLCMLPlaylist {
-            autoPlayNextItem = userDefaults.bool(forKey: kVLCPlaylistPlayNextItem)
+            autoPlayNextItem = VLCDefaults.shared.playlistPlayNextItem
         }
 
         if !autoPlayNextItem {
@@ -1836,7 +1828,7 @@ extension MediaCategoryViewController {
 
         let lastMedia = LastPlayed(identifier: media.identifier(), title: media.title)
         let playlistInfo = LastPlayedPlaylistModel(identifier: playlistId, title: playlistTitle, lastPlayedMedia: lastMedia)
-        userDefaults.setValue(NSKeyedArchiver.archivedData(withRootObject: playlistInfo), forKey: kVLCLastPlayedPlaylist)
+        VLCDefaults.shared.lastPlayedPlaylist = playlistInfo
     }
 
     private func addPlaybackWillStopObserver() {
@@ -1859,7 +1851,7 @@ extension MediaCategoryViewController {
 
         reloadData()
         removePlaybackWillStopObserver()
-        userDefaults.setValue(false, forKey: kVLCIsCurrentlyPlayingPlaylist)
+        VLCDefaults.shared.currentlyPlayingPlaylist = false
         playbackCache.clearQueuePlaylistInfo()
     }
 
diff --git a/Sources/Media Library/MediaCategoryCells/MediaCollectionViewCell.swift b/Sources/Media Library/MediaCategoryCells/MediaCollectionViewCell.swift
index ebf09f30f3e442e2225e6bc9f7433e9c122a8e3b..43d14a1f1df3d20d79acb2a2c5e68b08d936d031 100644
--- a/Sources/Media Library/MediaCategoryCells/MediaCollectionViewCell.swift	
+++ b/Sources/Media Library/MediaCategoryCells/MediaCollectionViewCell.swift	
@@ -106,7 +106,7 @@ class MediaCollectionViewCell: BaseCollectionViewCell, UIScrollViewDelegate {
     }
 
     private var enableMarquee: Bool {
-        return !UserDefaults.standard.bool(forKey: kVLCSettingEnableMediaCellTextScrolling)
+        return !VLCDefaults.shared.enableMediaCellTextScrolling
     }
 
     // MARK: - Init
@@ -260,7 +260,7 @@ class MediaCollectionViewCell: BaseCollectionViewCell, UIScrollViewDelegate {
             trackNumber = String(describing: media.trackNumber) + ". "
         }
 
-        let displayTrackNumber: Bool = !UserDefaults.standard.bool(forKey: kVLCAudioLibraryHideTrackNumbers)
+        let displayTrackNumber: Bool = !VLCDefaults.shared.audioLibraryHideTrackNumbers
         titleLabel.text = displayTrackNumber ? trackNumber + audiotrack.title() : audiotrack.title()
         accessibilityLabel = audiotrack.accessibilityText(editing: false)
         var descriptionText = audiotrack.albumTrackArtistName()
@@ -617,7 +617,7 @@ class MediaCollectionViewCell: BaseCollectionViewCell, UIScrollViewDelegate {
     // MARK: - Handle  New label Text
 
     func handleLastPlayed() {
-        let isCurrentlyPlayingPlaylist = UserDefaults.standard.bool(forKey: kVLCIsCurrentlyPlayingPlaylist)
+        let isCurrentlyPlayingPlaylist = VLCDefaults.shared.currentlyPlayingPlaylist
         let shouldDisplayLastPlayedLabel = (!playbackService.isPlaying && playbackService.currentlyPlayingMedia == nil) || !isCurrentlyPlayingPlaylist
         newLabel.isHidden = !shouldDisplayLastPlayedLabel
 
diff --git a/Sources/Media Library/MediaCategoryCells/MediaGridCollectionCell.swift b/Sources/Media Library/MediaCategoryCells/MediaGridCollectionCell.swift
index b77ae39e5d0d0266852d6365b367ab0a1b493c91..22f37e16588a6abece5c08530b8f1420ea4f989b 100644
--- a/Sources/Media Library/MediaCategoryCells/MediaGridCollectionCell.swift	
+++ b/Sources/Media Library/MediaCategoryCells/MediaGridCollectionCell.swift	
@@ -15,7 +15,6 @@ import UIKit
 class MediaGridCollectionCell: BaseCollectionViewCell {
 
     private let notificationCenter = NotificationCenter.default
-    private let userDefaults = UserDefaults.standard
     private let selectionOverlayColor = UIColor.orange.withAlphaComponent(0.4)
 
     private let checkboxImageView: UIImageView = {
@@ -146,7 +145,7 @@ class MediaGridCollectionCell: BaseCollectionViewCell {
     }
 
     private var enableMarquee: Bool {
-       return !userDefaults.bool(forKey: kVLCSettingEnableMediaCellTextScrolling)
+        return !VLCDefaults.shared.enableMediaCellTextScrolling
     }
 
     override init(frame: CGRect) {
diff --git a/Sources/Media Library/MediaCategoryCells/MovieCollectionViewCell.swift b/Sources/Media Library/MediaCategoryCells/MovieCollectionViewCell.swift
index f51f579c0251ef00bc808895a4e08a4e9cdb6e84..99500d1df53dcba37895cbcd0b7c9fbc0437e614 100644
--- a/Sources/Media Library/MediaCategoryCells/MovieCollectionViewCell.swift	
+++ b/Sources/Media Library/MediaCategoryCells/MovieCollectionViewCell.swift	
@@ -169,9 +169,7 @@ class MovieCollectionViewCell: BaseCollectionViewCell {
         descriptionLabel.text = movie.mediaDuration()
         thumbnailView.image = movie.thumbnailImage()
         let progress = movie.progress
-        guard let value = UserDefaults.standard.value(forKey: kVLCSettingContinuePlayback) as? Int else {
-            return
-        }
+        let value = VLCDefaults.shared.continuePlayback
         if value <= 0 {
             progressView.isHidden = true
         } else {
@@ -195,7 +193,7 @@ class MovieCollectionViewCell: BaseCollectionViewCell {
         let playbackService = PlaybackService.sharedInstance()
 
         if lastPlayed {
-            let isCurrentlyPlayingPlaylist = UserDefaults.standard.bool(forKey: kVLCIsCurrentlyPlayingPlaylist)
+            let isCurrentlyPlayingPlaylist = VLCDefaults.shared.currentlyPlayingPlaylist
             let shouldDisplayLastPlayedLabel = (!playbackService.isPlaying && playbackService.currentlyPlayingMedia == nil) || !isCurrentlyPlayingPlaylist
             groupLastPlayedLabel.isHidden = !shouldDisplayLastPlayedLabel
             groupLastPlayedLabel.text = NSLocalizedString("LAST_PLAYED_PLAYLIST_LABEL_TITLE", comment: "")
diff --git a/Sources/Media Library/MediaLibraryModel/AlbumModel.swift b/Sources/Media Library/MediaLibraryModel/AlbumModel.swift
index a2ccfa9f2a58cc0588d918d6cc7cafd3546a437f..ec14e4ca5ba78fea10399fdf2d87ab25fdc92b9f 100644
--- a/Sources/Media Library/MediaLibraryModel/AlbumModel.swift	
+++ b/Sources/Media Library/MediaLibraryModel/AlbumModel.swift	
@@ -22,7 +22,7 @@ class AlbumModel: AudioCollectionModel {
     private var artist: VLCMLArtist? = nil
 
     var cellType: BaseCollectionViewCell.Type {
-        return UserDefaults.standard.bool(forKey: "\(kVLCAudioLibraryGridLayout)\(name)") ? MediaGridCollectionCell.self : MediaCollectionViewCell.self
+        return VLCDefaults.shared.audioLibraryGridLayout(name: name) ? MediaGridCollectionCell.self : MediaCollectionViewCell.self
     }
 
     var medialibrary: MediaLibraryService
diff --git a/Sources/Media Library/MediaLibraryModel/ArtistModel.swift b/Sources/Media Library/MediaLibraryModel/ArtistModel.swift
index 4934fab6e74f0e9e862abe0e7fdafe38c1e4c2a9..6422ec0a37622aa77eea7f1b37bb51794e23f732 100644
--- a/Sources/Media Library/MediaLibraryModel/ArtistModel.swift	
+++ b/Sources/Media Library/MediaLibraryModel/ArtistModel.swift	
@@ -20,7 +20,7 @@ class ArtistModel: AudioCollectionModel {
     var fileArrayLock = NSRecursiveLock()
 
     var cellType: BaseCollectionViewCell.Type {
-        return UserDefaults.standard.bool(forKey: "\(kVLCAudioLibraryGridLayout)\(name)") ? MediaGridCollectionCell.self : MediaCollectionViewCell.self
+        return VLCDefaults.shared.audioLibraryGridLayout(name: name) ? MediaGridCollectionCell.self : MediaCollectionViewCell.self
     }
 
     var medialibrary: MediaLibraryService
@@ -30,7 +30,7 @@ class ArtistModel: AudioCollectionModel {
     var indicatorName: String = NSLocalizedString("ARTISTS", comment: "")
 
     var hideFeatArtists: Bool {
-        return UserDefaults.standard.bool(forKey: "\(kVLCAudioLibraryHideFeatArtists)")
+        return VLCDefaults.shared.audioLibraryHideFeatArtists
     }
 
     required init(medialibrary: MediaLibraryService) {
diff --git a/Sources/Media Library/MediaLibraryModel/CollectionModel.swift b/Sources/Media Library/MediaLibraryModel/CollectionModel.swift
index 9bd87f214be04a9b3cac52b6c72d9a0cbf5f9242..a0ac6ec3e88fd483335e7edafc0129f2b865feed 100644
--- a/Sources/Media Library/MediaLibraryModel/CollectionModel.swift	
+++ b/Sources/Media Library/MediaLibraryModel/CollectionModel.swift	
@@ -27,12 +27,16 @@ class CollectionModel: MLBaseModel {
     var files = [VLCMLMedia]()
 
     var cellType: BaseCollectionViewCell.Type {
+        let collectionModelName: String = String(describing: type(of: mediaCollection))
+
         if mediaCollection is VLCMLMediaGroup {
-            return UserDefaults.standard.bool(forKey: "\(kVLCVideoLibraryGridLayout)\(String(describing: type(of: mediaCollection)) + name)") ?
-                                              MovieCollectionViewCell.self : MediaCollectionViewCell.self
+            return VLCDefaults.shared
+                .videoLibraryGridLayout(collectionModelName: collectionModelName, name: name) ?
+            MovieCollectionViewCell.self : MediaCollectionViewCell.self
         } else {
-            return UserDefaults.standard.bool(forKey: "\(kVLCAudioLibraryGridLayout)\(String(describing: type(of: mediaCollection)) + name)") ?
-                                              MediaGridCollectionCell.self : MediaCollectionViewCell.self
+            return VLCDefaults.shared
+                .audioLibraryGridLayout(collectionModelName: collectionModelName, name: name) ?
+            MediaGridCollectionCell.self : MediaCollectionViewCell.self
         }
     }
 
diff --git a/Sources/Media Library/MediaLibraryModel/GenreModel.swift b/Sources/Media Library/MediaLibraryModel/GenreModel.swift
index 6824e031eea5f28fe3d8c7dcd15e9c8d352c524f..d0f7cbb18906eee84b37fc237071c65da3e29b22 100644
--- a/Sources/Media Library/MediaLibraryModel/GenreModel.swift	
+++ b/Sources/Media Library/MediaLibraryModel/GenreModel.swift	
@@ -20,7 +20,7 @@ class GenreModel: AudioCollectionModel {
     var files = [VLCMLGenre]()
 
     var cellType: BaseCollectionViewCell.Type {
-        return UserDefaults.standard.bool(forKey: "\(kVLCAudioLibraryGridLayout)\(name)") ? MediaGridCollectionCell.self : MediaCollectionViewCell.self
+        return VLCDefaults.shared.audioLibraryGridLayout(name: name) ? MediaGridCollectionCell.self : MediaCollectionViewCell.self
     }
 
     var medialibrary: MediaLibraryService
diff --git a/Sources/Media Library/MediaLibraryModel/MediaGroupViewModel.swift b/Sources/Media Library/MediaLibraryModel/MediaGroupViewModel.swift
index 43bdd193d32d2842cbb5edc4af7d546c7da2176f..07c9eb299e6f68e9bc316e8776b791db72473acb 100644
--- a/Sources/Media Library/MediaLibraryModel/MediaGroupViewModel.swift	
+++ b/Sources/Media Library/MediaLibraryModel/MediaGroupViewModel.swift	
@@ -21,7 +21,7 @@ class MediaGroupViewModel: MLBaseModel {
     var files: [VLCMLMediaGroup]
 
     var cellType: BaseCollectionViewCell.Type {
-        return UserDefaults.standard.bool(forKey: "\(kVLCVideoLibraryGridLayout)\(name)") ? MovieCollectionViewCell.self : MediaCollectionViewCell.self
+        return VLCDefaults.shared.videoLibraryGridLayout(name: name) ? MovieCollectionViewCell.self : MediaCollectionViewCell.self
     }
 
     var medialibrary: MediaLibraryService
diff --git a/Sources/Media Library/MediaLibraryModel/MediaLibraryBaseModel.swift b/Sources/Media Library/MediaLibraryModel/MediaLibraryBaseModel.swift
index 34360210d34ea347de2047d336e1ab25a65e6f44..c93e89dac2a66e17ae9e5edec2777d9a3ef4fa92 100644
--- a/Sources/Media Library/MediaLibraryModel/MediaLibraryBaseModel.swift	
+++ b/Sources/Media Library/MediaLibraryModel/MediaLibraryBaseModel.swift	
@@ -169,8 +169,8 @@ extension MediaCollectionModel {
             }
         }
         if image == nil
-            || (!UserDefaults.standard.bool(forKey: kVLCSettingShowThumbnails) && self is VLCMLMediaGroup)
-            || (!UserDefaults.standard.bool(forKey: kVLCSettingShowArtworks) && !(self is VLCMLMediaGroup)) {
+            || (!VLCDefaults.shared.showThumbnails && self is VLCMLMediaGroup)
+            || (!VLCDefaults.shared.showArtworks && !(self is VLCMLMediaGroup)) {
             let isDarktheme = PresentationTheme.current.isDark
             if self is VLCMLMediaGroup {
                 image = isDarktheme ? UIImage(named: "movie-placeholder-dark") : UIImage(named: "movie-placeholder-white")
diff --git a/Sources/Media Library/MediaLibraryModel/MediaModel.swift b/Sources/Media Library/MediaLibraryModel/MediaModel.swift
index 892bcdf8a94b741b36d7f09c3a01da8a44e5d753..18b4912af8a54a60d7c70bf42142d09ded16e9c5 100644
--- a/Sources/Media Library/MediaLibraryModel/MediaModel.swift	
+++ b/Sources/Media Library/MediaLibraryModel/MediaModel.swift	
@@ -58,8 +58,8 @@ extension VLCMLMedia {
     @objc func thumbnailImage() -> UIImage? {
         var image = VLCThumbnailsCache.thumbnail(for: thumbnail())
         if image == nil
-            || (!UserDefaults.standard.bool(forKey: kVLCSettingShowThumbnails) && subtype() != .albumTrack)
-            || (!UserDefaults.standard.bool(forKey: kVLCSettingShowArtworks) && subtype() == .albumTrack) {
+            || (!VLCDefaults.shared.showThumbnails && subtype() != .albumTrack)
+            || (!VLCDefaults.shared.showArtworks && subtype() == .albumTrack) {
             let isDarktheme = PresentationTheme.current.isDark
             if subtype() == .albumTrack {
                 image = isDarktheme ? UIImage(named: "song-placeholder-dark") : UIImage(named: "song-placeholder-white")
@@ -78,7 +78,7 @@ extension VLCMLMedia {
     }
 
     func title() -> String {
-        if UserDefaults.standard.bool(forKey: kVLCOptimizeItemNamesForDisplay) == true
+        if VLCDefaults.shared.optimizeTitles
             && ((subtype() == .albumTrack && title.isSupportedAudioMediaFormat())
                 || (subtype() != .albumTrack && title.isSupportedMediaFormat())) {
             return (title as NSString).deletingPathExtension
diff --git a/Sources/Media Library/MediaLibraryModel/PlaylistModel.swift b/Sources/Media Library/MediaLibraryModel/PlaylistModel.swift
index 25c1e973220ac424dfdb39742ded4d9b91322d0e..a7164ff001103d53b08a99f3632ead988e3d15b2 100644
--- a/Sources/Media Library/MediaLibraryModel/PlaylistModel.swift	
+++ b/Sources/Media Library/MediaLibraryModel/PlaylistModel.swift	
@@ -21,7 +21,7 @@ class PlaylistModel: MLBaseModel {
     var files = [VLCMLPlaylist]()
 
     var cellType: BaseCollectionViewCell.Type {
-        return UserDefaults.standard.bool(forKey: "\(kVLCAudioLibraryGridLayout)\(name)") ? MovieCollectionViewCell.self : MediaCollectionViewCell.self
+        return VLCDefaults.shared.audioLibraryGridLayout(name: name) ? MovieCollectionViewCell.self : MediaCollectionViewCell.self
     }
 
     var medialibrary: MediaLibraryService
diff --git a/Sources/Media Library/MediaLibraryModel/TrackModel.swift b/Sources/Media Library/MediaLibraryModel/TrackModel.swift
index f7cdb6b0c5f633eafd7d8e2c67ba2ea671f113d5..7623a4152a447eaec1c38c16e650aa4f3d8caf99 100644
--- a/Sources/Media Library/MediaLibraryModel/TrackModel.swift	
+++ b/Sources/Media Library/MediaLibraryModel/TrackModel.swift	
@@ -20,7 +20,7 @@ class TrackModel: MediaModel {
     var fileArrayLock = NSRecursiveLock()
 
     var cellType: BaseCollectionViewCell.Type {
-        return UserDefaults.standard.bool(forKey: "\(kVLCAudioLibraryGridLayout)\(name)") ? MediaGridCollectionCell.self : MediaCollectionViewCell.self
+        return VLCDefaults.shared.audioLibraryGridLayout(name: name) ? MediaGridCollectionCell.self : MediaCollectionViewCell.self
     }
 
     var medialibrary: MediaLibraryService
diff --git a/Sources/Media Library/MediaLibraryModel/VideoModel.swift b/Sources/Media Library/MediaLibraryModel/VideoModel.swift
index d438d50f4517211bb1a9a81458e8125c1c677af5..2ed299581cee3385170d297c320388740ba3bf2a 100644
--- a/Sources/Media Library/MediaLibraryModel/VideoModel.swift	
+++ b/Sources/Media Library/MediaLibraryModel/VideoModel.swift	
@@ -20,7 +20,7 @@ class VideoModel: MediaModel {
     var files = [VLCMLMedia]()
 
     var cellType: BaseCollectionViewCell.Type {
-        return UserDefaults.standard.bool(forKey: "\(kVLCVideoLibraryGridLayout)\(name)") ? MovieCollectionViewCell.self : MediaCollectionViewCell.self
+        return VLCDefaults.shared.videoLibraryGridLayout(name: name) ? MovieCollectionViewCell.self : MediaCollectionViewCell.self
     }
 
     var medialibrary: MediaLibraryService
diff --git a/Sources/Media Library/MediaLibraryService.swift b/Sources/Media Library/MediaLibraryService.swift
index 4a218f2e734cc7a1f75419bc1f6f498ec7dd3775..4d14094edd5c39dc72b2a8b6afa543cd36883c86 100644
--- a/Sources/Media Library/MediaLibraryService.swift	
+++ b/Sources/Media Library/MediaLibraryService.swift	
@@ -136,7 +136,6 @@ extension NSNotification {
 
 class MediaLibraryService: NSObject {
     private static let databaseName: String = "medialibrary.db"
-    private static let didForceRescan: String = "MediaLibraryDidForceRescan"
     private var triedToRecoverFromInitializationErrorOnce = false
 
     private var didFinishDiscovery = false
@@ -189,14 +188,14 @@ private extension MediaLibraryService {
     }
 
     private func startMediaLibrary(on path: String) {
-        let excludeMediaLibrary = !UserDefaults.standard.bool(forKey: kVLCSettingBackupMediaLibrary)
-        let hideML = UserDefaults.standard.bool(forKey: kVLCSettingHideLibraryInFilesApp)
+        let excludeMediaLibrary = !VLCDefaults.shared.backupMediaLibrary
+        let hideML = VLCDefaults.shared.hideLibraryInFilesApp
         excludeFromDeviceBackup(excludeMediaLibrary)
         hideMediaLibrary(hideML)
 
-        if UserDefaults.standard.bool(forKey: MediaLibraryService.didForceRescan) == false {
+        if !VLCDefaults.shared.mediaLibraryServiceDidForceRescan {
             medialib.forceRescan()
-            UserDefaults.standard.set(true, forKey: MediaLibraryService.didForceRescan)
+            VLCDefaults.shared.mediaLibraryServiceDidForceRescan = true
         }
 
         FileManager.default.createFile(atPath: "\(path)/\(NSLocalizedString("MEDIALIBRARY_FILES_PLACEHOLDER", comment: ""))", contents: nil, attributes: nil)
diff --git a/Sources/Media Library/MediaViewControllers/MediaViewController.swift b/Sources/Media Library/MediaViewControllers/MediaViewController.swift
index 3077251a6fb2dc5fca449c9e3efbe69288c3257e..ff00e5e31fad94cf8652efbc32c3dea61ae169f7 100644
--- a/Sources/Media Library/MediaViewControllers/MediaViewController.swift	
+++ b/Sources/Media Library/MediaViewControllers/MediaViewController.swift	
@@ -533,7 +533,7 @@ extension MediaViewController {
         var additionalMenuItems: [UIAction] = []
 
         if mediaCategoryViewController.model is ArtistModel {
-            let isIncludeAllArtistActive = UserDefaults.standard.bool(forKey: kVLCAudioLibraryHideFeatArtists)
+            let isIncludeAllArtistActive = VLCDefaults.shared.audioLibraryHideFeatArtists
             let includeAllArtist = UIAction(title: NSLocalizedString("HIDE_FEAT_ARTISTS", comment: ""),
                                             image: UIImage(systemName: "person.3"),
                                             state: isIncludeAllArtistActive ? .on : .off,
@@ -545,7 +545,7 @@ extension MediaViewController {
         } else if let model = mediaCategoryViewController.model as? CollectionModel,
                   let mediaCollection = model.mediaCollection as? VLCMLAlbum,
                   !mediaCollection.isUnknownAlbum() {
-            let hideTrackNumbers = UserDefaults.standard.bool(forKey: kVLCAudioLibraryHideTrackNumbers)
+            let hideTrackNumbers = VLCDefaults.shared.audioLibraryHideTrackNumbers
             let hideTrackNumbersAction = UIAction(title: NSLocalizedString("HIDE_TRACK_NUMBERS", comment: ""),
                                                      state: hideTrackNumbers ? .on : .off,
                                                      handler: { _ in
diff --git a/Sources/Media Library/tvOS/VLCMicroMediaLibraryService.m b/Sources/Media Library/tvOS/VLCMicroMediaLibraryService.m
index 3afeffb3cb715439f957cdf52a08030813f9ef10..fd6769ec952c142e6bfae241b2d4c4eec2e2c124 100644
--- a/Sources/Media Library/tvOS/VLCMicroMediaLibraryService.m	
+++ b/Sources/Media Library/tvOS/VLCMicroMediaLibraryService.m	
@@ -113,7 +113,7 @@
         ret = self.discoveredFiles.readonlycopy;
     }
 
-    if ([[NSUserDefaults standardUserDefaults] boolForKey:kVLCSaveDebugLogs]) {
+    if (VLCDefaults.shared.saveDebugLogs) {
         ret = [self injectLogsToMedia:ret];
     }
 
diff --git a/Sources/Network/Server Browsing/Data/Protocols/General/VLCLocalNetworkServiceBrowserMediaDiscoverer.m b/Sources/Network/Server Browsing/Data/Protocols/General/VLCLocalNetworkServiceBrowserMediaDiscoverer.m
index 20b779a470438ccf463c4498b0ead104e79387f0..94ccdc73b40836ac14175ae1b5dea5068d175fc1 100644
--- a/Sources/Network/Server Browsing/Data/Protocols/General/VLCLocalNetworkServiceBrowserMediaDiscoverer.m	
+++ b/Sources/Network/Server Browsing/Data/Protocols/General/VLCLocalNetworkServiceBrowserMediaDiscoverer.m	
@@ -16,6 +16,7 @@
 #import "VLCLocalNetworkServiceBrowserUPnP.h"
 #import "VLCAppCoordinator.h"
 #import "VLCHTTPUploaderController.h"
+#import "VLC-Swift.h"
 
 @interface VLCLocalNetworkServiceBrowserMediaDiscoverer () <VLCMediaListDelegate>
 {
@@ -42,8 +43,7 @@
          * so it should be only if explicitly demanded by the user */
         _isUPnPdiscoverer = [serviceName isEqualToString:@"upnp"];
         if (_isUPnPdiscoverer) {
-            NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
-            NSString *satipURLstring = [defaults stringForKey:kVLCSettingNetworkSatIPChannelListUrl];
+            NSString *satipURLstring = VLCDefaults.shared.networkSatIPChannelListUrl;
             NSMutableArray *libVLCOptions = [NSMutableArray array];
             if (satipURLstring.length > 0) {
                 [libVLCOptions addObject:[NSString stringWithFormat:@"--%@=%@", kVLCSettingNetworkSatIPChannelListUrl, satipURLstring]];
diff --git a/Sources/Network/Server Browsing/Data/Protocols/SMB/VLCLocalNetworkServiceBrowserDSM.m b/Sources/Network/Server Browsing/Data/Protocols/SMB/VLCLocalNetworkServiceBrowserDSM.m
index dd1e48b7e118f1bcd7de7bd38dd04fb8e9f22479..d584a502934c4e4bbba269e77f9a1c09da769e13 100644
--- a/Sources/Network/Server Browsing/Data/Protocols/SMB/VLCLocalNetworkServiceBrowserDSM.m	
+++ b/Sources/Network/Server Browsing/Data/Protocols/SMB/VLCLocalNetworkServiceBrowserDSM.m	
@@ -12,6 +12,7 @@
 
 #import "VLCLocalNetworkServiceBrowserDSM.h"
 #import "VLCNetworkServerLoginInformation.h"
+#import "VLC-Swift.h"
 
 @interface VLCLocalNetworkServiceDSM ()
 + (void)registerLoginInformation;
@@ -110,8 +111,8 @@ static NSString *const VLCLocalNetworkServiceDSMWorkgroupIdentifier = @"VLCLocal
         @"smb-pwd" : password ?: @"",
         @"smb-domain" : workgroup?: @"WORKGROUP",
     }.mutableCopy;
-    if ([[NSUserDefaults standardUserDefaults] boolForKey:kVLCForceSMBV1]) {
-        mediaOptions[kVLCForceSMBV1] = [NSNull null];
+    if (VLCDefaults.shared.forceSMBV1) {
+        mediaOptions[@"smb-force-v1"] = [NSNull null];
     }
 	[media addOptions:mediaOptions];
 	return [[self alloc] initWithMedia:media options:mediaOptions];
diff --git a/Sources/Network/Server Browsing/View Controllers/VLCNetworkServerBrowserViewController.m b/Sources/Network/Server Browsing/View Controllers/VLCNetworkServerBrowserViewController.m
index fa54c61caf1fcae7a6593cacbc5b963ea1c66d23..81e2354dc843401ae36dc5eaa15cfee0ad8a1a89 100644
--- a/Sources/Network/Server Browsing/View Controllers/VLCNetworkServerBrowserViewController.m	
+++ b/Sources/Network/Server Browsing/View Controllers/VLCNetworkServerBrowserViewController.m	
@@ -273,7 +273,7 @@
 {
     id<VLCNetworkServerBrowserItem> item;
     NSInteger row = indexPath.row;
-    BOOL singlePlayback = ![[NSUserDefaults standardUserDefaults] boolForKey:kVLCAutomaticallyPlayNextItem];
+    BOOL singlePlayback = !VLCDefaults.shared.automaticallyPlayNextItem;
     if (self.searchController.isActive) {
         if (row < _searchArray.count) {
             item = _searchArray[row];
diff --git a/Sources/Network/Server Browsing/View Controllers/VLCServerBrowsingTVViewController.m b/Sources/Network/Server Browsing/View Controllers/VLCServerBrowsingTVViewController.m
index d2f7355fd2747ecba5e97e7158c78da9ad411fc0..78fe65da8abeb7c9dc1c75b260c668efd538ed0e 100644
--- a/Sources/Network/Server Browsing/View Controllers/VLCServerBrowsingTVViewController.m	
+++ b/Sources/Network/Server Browsing/View Controllers/VLCServerBrowsingTVViewController.m	
@@ -18,6 +18,7 @@
 #import "GRKArrayDiff+UICollectionView.h"
 #import "VLCFavoriteService.h"
 #import "VLCAppCoordinator.h"
+#import "VLC-Swift.h"
 
 @interface VLCServerBrowsingTVViewController ()
 {
@@ -47,7 +48,7 @@
         
         self.title = serverBrowser.title;
 
-        self.downloadArtwork = [[NSUserDefaults standardUserDefaults] boolForKey:kVLCSettingDownloadArtwork];
+        self.downloadArtwork = VLCDefaults.shared.downloadArtwork;
     }
     return self;
 }
@@ -284,7 +285,7 @@
 
     // would make sence if item came from search which isn't
     // currently the case on the TV
-    const BOOL singlePlayback = ![[NSUserDefaults standardUserDefaults] boolForKey:kVLCAutomaticallyPlayNextItem];
+    const BOOL singlePlayback = !VLCDefaults.shared.automaticallyPlayNextItem;
     [self didSelectItem:item index:row singlePlayback:singlePlayback];
 }
 
diff --git a/Sources/Network/Server List/VLCServerListViewController.m b/Sources/Network/Server List/VLCServerListViewController.m
index d188892635c9685751c45d0dcd1ec0d6ae6c98ce..8dbc3aeb69743f64f8ca8cb063af839d8db592e4 100644
--- a/Sources/Network/Server List/VLCServerListViewController.m	
+++ b/Sources/Network/Server List/VLCServerListViewController.m	
@@ -82,7 +82,7 @@
             return;
         }
 
-        if ([[NSUserDefaults standardUserDefaults] integerForKey:kVLCSettingAppTheme] == kVLCSettingAppThemeSystem) {
+        if (VLCDefaults.shared.appThemeIsSystem) {
             [PresentationTheme themeDidUpdate];
         }
         [self themeDidChange];
diff --git a/Sources/Playback/Control/VLCPlaybackService.m b/Sources/Playback/Control/VLCPlaybackService.m
index 75663a087e95d917deeea1e83be741298173d960..85d8ffb85f2ad78440853bcd8e1c07b29a4de4e1 100644
--- a/Sources/Playback/Control/VLCPlaybackService.m
+++ b/Sources/Playback/Control/VLCPlaybackService.m
@@ -220,8 +220,6 @@ NSString *const VLCLastPlaylistPlayedMedia = @"LastPlaylistPlayedMedia";
         return;
     }
 
-    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
-
     if (!self.mediaList) {
         APLog(@"%s: no URL and no media list set, stopping playback", __PRETTY_FUNCTION__);
         [_playbackSessionManagementLock unlock];
@@ -245,12 +243,11 @@ NSString *const VLCLastPlaylistPlayedMedia = @"LastPlaylistPlayedMedia";
     /* the chromecast and audio options cannot be set per media, so we need to set it per
      * media player instance however, potentially initialising an additional library instance
      * for this is costly, so this should be done only if needed */
-    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
-    BOOL audioTimeStretch = [[userDefaults objectForKey:kVLCSettingStretchAudio] boolValue];
+    BOOL audioTimeStretch = VLCDefaults.shared.stretchAudio;
     NSMutableArray *libVLCOptions = [NSMutableArray array];
 #if TARGET_OS_IOS
-    BOOL chromecastPassthrough = [[userDefaults objectForKey:kVLCSettingCastingAudioPassthrough] boolValue];
-    int chromecastQuality = [[userDefaults objectForKey:kVLCSettingCastingConversionQuality] intValue];
+    BOOL chromecastPassthrough = VLCDefaults.shared.castingAudioPassthrough;
+    int chromecastQuality = (int)VLCDefaults.shared.castingConversionQuality;
     if (chromecastPassthrough) {
         [libVLCOptions addObject:[@"--" stringByAppendingString:kVLCSettingCastingAudioPassthrough]];
     }
@@ -275,7 +272,7 @@ NSString *const VLCLastPlaylistPlayedMedia = @"LastPlaylistPlayedMedia";
     consoleLogger.level = kVLCLogLevelDebug;
     [debugLoggers addObject:consoleLogger];
 #endif
-    BOOL saveDebugLogs = [userDefaults boolForKey:kVLCSaveDebugLogs];
+    BOOL saveDebugLogs = VLCDefaults.shared.saveDebugLogs;
     if (saveDebugLogs) {
         NSArray *searchPaths;
 #if TARGET_OS_TV
@@ -315,15 +312,15 @@ NSString *const VLCLastPlaylistPlayedMedia = @"LastPlaylistPlayedMedia";
 #endif
 
     [_mediaPlayer setDelegate:self];
-    CGFloat defaultPlaybackSpeed = [[defaults objectForKey:kVLCSettingPlaybackSpeedDefaultValue] floatValue];
+    CGFloat defaultPlaybackSpeed = VLCDefaults.shared.playbackSpeedDefaultValue;
     if (defaultPlaybackSpeed != 0.)
         [_mediaPlayer setRate: defaultPlaybackSpeed];
-    int deinterlace = [[defaults objectForKey:kVLCSettingDeinterlace] intValue];
+    int deinterlace = (int)VLCDefaults.shared.deinterlace;
     [_mediaPlayer setDeinterlace:deinterlace withFilter:@"blend"];
 
     [_listPlayer setMediaList:self.mediaList];
-    if ([defaults boolForKey:kVLCPlayerShouldRememberState]) {
-        VLCRepeatMode repeatMode = [defaults integerForKey:kVLCPlayerIsRepeatEnabled];
+    if (VLCDefaults.shared.playerShouldRememberState) {
+        VLCRepeatMode repeatMode = VLCDefaults.shared.playerIsRepeatEnabled;
         [_listPlayer setRepeatMode:repeatMode];
     }
 
@@ -340,17 +337,16 @@ NSString *const VLCLastPlaylistPlayedMedia = @"LastPlaylistPlayedMedia";
         return;
     }
 
-    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
-    BOOL equalizerEnabled = ![userDefaults boolForKey:kVLCSettingEqualizerProfileDisabled];
+    BOOL equalizerEnabled = !VLCDefaults.shared.equalizerProfileDisabled;
 
     VLCAudioEqualizer *equalizer;
 
     if (equalizerEnabled) {
         NSArray *presets = [VLCAudioEqualizer presets];
-        unsigned int profile = (unsigned int)[userDefaults integerForKey:kVLCSettingEqualizerProfile];
+        unsigned int profile = (unsigned int)VLCDefaults.shared.equalizerProfile;
         equalizer = [[VLCAudioEqualizer alloc] initWithPreset:presets[profile]];
     } else {
-        float preampValue = [userDefaults floatForKey:kVLCSettingDefaultPreampLevel];
+        float preampValue = VLCDefaults.shared.defaultPreampLevel;
         equalizer = [[VLCAudioEqualizer alloc] init];
         equalizer.preAmplification = preampValue;
     }
@@ -499,13 +495,13 @@ NSString *const VLCLastPlaylistPlayedMedia = @"LastPlaylistPlayedMedia";
             }
         }
 
-        if ([[NSUserDefaults standardUserDefaults] boolForKey:kVLCSettingDisableSubtitles]) {
+        if (VLCDefaults.shared.disableSubtitles) {
             _mediaPlayer.currentVideoSubTitleIndex = -1;
         } else {
             [self selectVideoSubtitleAtIndex:media.subtitleTrackIndex];
         }
 #else
-        BOOL disableSubtitles = [[NSUserDefaults standardUserDefaults] boolForKey:kVLCSettingDisableSubtitles];
+        BOOL disableSubtitles = VLCDefaults.shared.disableSubtitles;
 
         NSArray *audioTracks = _mediaPlayer.audioTracks;
         if (media.audioTrackIndex < audioTracks.count) {
@@ -580,9 +576,8 @@ NSString *const VLCLastPlaylistPlayedMedia = @"LastPlaylistPlayedMedia";
     }
     [[NSNotificationCenter defaultCenter] postNotificationName:VLCPlaybackServicePlaybackModeUpdated object:self];
 
-    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
-    if ([defaults boolForKey:kVLCPlayerShouldRememberState]) {
-        [defaults setInteger:repeatMode forKey:kVLCPlayerIsRepeatEnabled];
+    if (VLCDefaults.shared.playerShouldRememberState) {
+        VLCDefaults.shared.playerIsRepeatEnabled = repeatMode;
     }
 }
 
@@ -859,13 +854,12 @@ NSString *const VLCLastPlaylistPlayedMedia = @"LastPlaylistPlayedMedia";
             _mediaPlayer.media.delegate = self;
 
             /* on-the-fly values through hidden API */
-            NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wundeclared-selector"
-            [_mediaPlayer performSelector:@selector(setTextRendererFont:) withObject:[defaults objectForKey:kVLCSettingSubtitlesFont]];
-            [_mediaPlayer performSelector:@selector(setTextRendererFontSize:) withObject:[defaults objectForKey:kVLCSettingSubtitlesFontSize]];
-            [_mediaPlayer performSelector:@selector(setTextRendererFontColor:) withObject:[defaults objectForKey:kVLCSettingSubtitlesFontColor]];
-            [_mediaPlayer performSelector:@selector(setTextRendererFontForceBold:) withObject:[defaults objectForKey:kVLCSettingSubtitlesBoldFont]];
+            [_mediaPlayer performSelector:@selector(setTextRendererFont:) withObject:VLCDefaults.shared.subtitlesFontSize];
+            [_mediaPlayer performSelector:@selector(setTextRendererFontSize:) withObject:VLCDefaults.shared.subtitlesFontSize];
+            [_mediaPlayer performSelector:@selector(setTextRendererFontColor:) withObject:VLCDefaults.shared.subtitlesFontColor];
+            [_mediaPlayer performSelector:@selector(setTextRendererFontForceBold:) withObject:[NSNumber numberWithBool:VLCDefaults.shared.subtitlesBoldFont]];
 #pragma clang diagnostic pop
         } break;
 
@@ -1018,9 +1012,8 @@ NSString *const VLCLastPlaylistPlayedMedia = @"LastPlaylistPlayedMedia";
 
     [[NSNotificationCenter defaultCenter] postNotificationName:VLCPlaybackServiceShuffleModeUpdated object:self];
 
-    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
-    if ([[defaults valueForKey:kVLCPlayerShouldRememberState] boolValue]) {
-        [defaults setBool:shuffleMode forKey:kVLCPlayerIsShuffleEnabled];
+    if (VLCDefaults.shared.playerShouldRememberState) {
+        VLCDefaults.shared.playerIsShuffleEnabled = shuffleMode;
     }
 }
 
@@ -1080,7 +1073,7 @@ NSString *const VLCLastPlaylistPlayedMedia = @"LastPlaylistPlayedMedia";
 - (BOOL)next
 {
     if (_mediaList.count == 1) {
-        NSNumber *skipLength = [[NSUserDefaults standardUserDefaults] valueForKey:kVLCSettingPlaybackForwardSkipLength];
+        NSNumber *skipLength = [NSNumber numberWithInteger:VLCDefaults.shared.playbackForwardSkipLength];
         [_mediaPlayer jumpForward:skipLength.intValue];
         return YES;
     }
@@ -1133,7 +1126,7 @@ NSString *const VLCLastPlaylistPlayedMedia = @"LastPlaylistPlayedMedia";
             [_listPlayer playItemAtNumber:@(_currentIndex)];
         }
     } else {
-        NSNumber *skipLength = [[NSUserDefaults standardUserDefaults] valueForKey:kVLCSettingPlaybackBackwardSkipLength];
+        NSNumber *skipLength = [NSNumber numberWithInteger:VLCDefaults.shared.playbackBackwardSkipLength];
         [_mediaPlayer jumpBackward:skipLength.intValue];
     }
     return YES;
@@ -1400,12 +1393,11 @@ NSString *const VLCLastPlaylistPlayedMedia = @"LastPlaylistPlayedMedia";
 
 - (void)resetEqualizerFromProfile:(unsigned int)profile
 {
-    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
     if (profile == 0) {
         _mediaPlayer.equalizer = nil;
-        [userDefaults setBool:YES forKey:kVLCSettingEqualizerProfileDisabled];
+        VLCDefaults.shared.equalizerProfileDisabled = YES;
 
-        float preampValue = [userDefaults floatForKey:kVLCSettingDefaultPreampLevel];
+        float preampValue = VLCDefaults.shared.defaultPreampLevel;
         if (preampValue != 6.0) {
             APLog(@"Enforcing presumbly disabled equalizer due to custom preamp value of %f2.0", preampValue);
             VLCAudioEqualizer *eq = [[VLCAudioEqualizer alloc] init];
@@ -1415,10 +1407,10 @@ NSString *const VLCLastPlaylistPlayedMedia = @"LastPlaylistPlayedMedia";
         return;
     }
 
-    [userDefaults setBool:NO forKey:kVLCSettingEqualizerProfileDisabled];
+    VLCDefaults.shared.equalizerProfileDisabled = NO;
 
     unsigned int actualProfile = profile - 1;
-    [userDefaults setInteger:actualProfile forKey:kVLCSettingEqualizerProfile];
+    VLCDefaults.shared.equalizerProfile = actualProfile;
 
     NSArray *presets = [VLCAudioEqualizer presets];
     VLCAudioEqualizer *equalizer = [[VLCAudioEqualizer alloc] initWithPreset:presets[actualProfile]];
@@ -1442,7 +1434,7 @@ NSString *const VLCLastPlaylistPlayedMedia = @"LastPlaylistPlayedMedia";
         return equalizer.preAmplification;
     }
 
-    return [[NSUserDefaults standardUserDefaults] floatForKey:kVLCSettingDefaultPreampLevel];
+    return VLCDefaults.shared.defaultPreampLevel;
 }
 
 - (unsigned int)numberOfBands
@@ -1471,16 +1463,16 @@ NSString *const VLCLastPlaylistPlayedMedia = @"LastPlaylistPlayedMedia";
 {
     /* this is a bit complex, if the eq is off, we need to return 0
      * if it is on, we need to provide the profile + 1 as the UI fakes a "Off" profile in its list */
-    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
-    if ([userDefaults boolForKey:kVLCSettingEqualizerProfileDisabled]) {
+    if (VLCDefaults.shared.equalizerProfileDisabled) {
         return [NSIndexPath indexPathForRow:0 inSection:0];
     }
 
-    unsigned int actualProfile = (unsigned int)[userDefaults integerForKey:kVLCSettingEqualizerProfile];
-    if (![userDefaults boolForKey:kVLCCustomProfileEnabled]) {
-        return [NSIndexPath indexPathForRow:actualProfile + 1 inSection:0];
-    } else {
+    unsigned int actualProfile = (unsigned int)VLCDefaults.shared.equalizerProfile;
+
+    if (VLCDefaults.shared.customEqualizerProfileEnabled) {
         return [NSIndexPath indexPathForRow:actualProfile inSection:1];
+    } else {
+        return [NSIndexPath indexPathForRow:actualProfile + 1 inSection:0];
     }
 }
 #endif
@@ -1600,9 +1592,9 @@ NSString *const VLCLastPlaylistPlayedMedia = @"LastPlaylistPlayedMedia";
             if (!libraryMedia.isPodcast) {
                 return;
             }
-            continuePlayback = [[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingContinueAudioPlayback] integerValue];
+            continuePlayback = VLCDefaults.shared.continueAudioPlayback;
         } else {
-            continuePlayback = [[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingContinuePlayback] integerValue];
+            continuePlayback = VLCDefaults.shared.continuePlayback;
         }
 
         if (continuePlayback == 1) {
@@ -1691,7 +1683,7 @@ NSString *const VLCLastPlaylistPlayedMedia = @"LastPlaylistPlayedMedia";
 
 - (void)disableSubtitlesIfNeeded
 {
-    if ([[NSUserDefaults standardUserDefaults] boolForKey:kVLCSettingDisableSubtitles]) {
+    if (VLCDefaults.shared.disableSubtitles) {
         [_mediaPlayer deselectAllTextTracks];
     }
 }
@@ -1721,8 +1713,7 @@ NSString *const VLCLastPlaylistPlayedMedia = @"LastPlaylistPlayedMedia";
 #if !TARGET_OS_TV
     [self savePlaybackState];
 #endif
-    if (![self isPlayingOnExternalScreen]
-        && ![[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingContinueAudioInBackgroundKey] boolValue]) {
+    if (![self isPlayingOnExternalScreen] && !VLCDefaults.shared.continueAudioInBackgroundKey) {
         if ([_mediaPlayer isPlaying]) {
             [_mediaPlayer pause];
             _shouldResumePlaying = YES;
@@ -1777,12 +1768,11 @@ NSString *const VLCLastPlaylistPlayedMedia = @"LastPlaylistPlayedMedia";
 
 - (NSDictionary *)mediaOptionsDictionary
 {
-    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
-    return @{ kVLCSettingNetworkCaching : [defaults objectForKey:kVLCSettingNetworkCaching],
-              kVLCSettingTextEncoding : [defaults objectForKey:kVLCSettingTextEncoding],
-              kVLCSettingSkipLoopFilter : [defaults objectForKey:kVLCSettingSkipLoopFilter],
-              kVLCSettingHardwareDecoding : [defaults objectForKey:kVLCSettingHardwareDecoding],
-              kVLCSettingNetworkRTSPTCP : [defaults objectForKey:kVLCSettingNetworkRTSPTCP]
+    return @{ @"network-caching" : [NSNumber numberWithInteger:VLCDefaults.shared.networkCachingObjC],
+              @"subsdec-encoding" : VLCDefaults.shared.textEncoding,
+              @"avcodec-skiploopfilter" : [NSNumber numberWithInteger:VLCDefaults.shared.skipLoopFilterObjC],
+              @"codec" : VLCDefaults.shared.hardwareDecodingObjC,
+              @"rtsp-tcp" : [NSNumber numberWithBool:VLCDefaults.shared.networkRTSPTCP]
     };
 }
 
diff --git a/Sources/Playback/Control/VLCPlayerDisplayController.m b/Sources/Playback/Control/VLCPlayerDisplayController.m
index 0ce8275dcd119767c0c7bec7c3d1cea51d1246d2..8070810ac9cc8df89aa962b0992520fe00f0deb2 100644
--- a/Sources/Playback/Control/VLCPlayerDisplayController.m
+++ b/Sources/Playback/Control/VLCPlayerDisplayController.m
@@ -165,8 +165,7 @@ NSString *const VLCPlayerDisplayControllerHideMiniPlayer = @"VLCPlayerDisplayCon
 
 - (void)playbackDidStart:(NSNotification *)notification
 {
-    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
-    BOOL enforceFullscreen = [[defaults objectForKey:kVLCSettingVideoFullscreenPlayback] boolValue];
+    BOOL enforceFullscreen = VLCDefaults.shared.videoFullscreenPlayback;
 
     VLCMedia *currentMedia = _playbackController.currentlyPlayingMedia;
     VLCMLMedia *media = [VLCMLMedia mediaForPlayingMedia:currentMedia];
@@ -484,11 +483,9 @@ NSString *const VLCPlayerDisplayControllerHideMiniPlayer = @"VLCPlayerDisplayCon
         }
 
         // Properly set the shuffle and repeat mode
-        NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
-        if ([userDefaults boolForKey:kVLCPlayerShouldRememberState]) {
-            _playbackController.shuffleMode = [userDefaults boolForKey:kVLCPlayerIsShuffleEnabled];
-            NSInteger repeatMode = [userDefaults integerForKey:kVLCPlayerIsRepeatEnabled];
-            _playbackController.repeatMode = repeatMode;
+        if (VLCDefaults.shared.playerShouldRememberState) {
+            _playbackController.shuffleMode = VLCDefaults.shared.playerIsShuffleEnabled;
+            _playbackController.repeatMode = VLCDefaults.shared.playerIsRepeatEnabled;
         }
 
         [self addPlayqueueToMiniPlayer];
@@ -675,13 +672,13 @@ NSString *const VLCPlayerDisplayControllerHideMiniPlayer = @"VLCPlayerDisplayCon
 
 - (void)keyLeftArrow
 {
-    NSInteger seekBy = [[NSUserDefaults standardUserDefaults] integerForKey:kVLCSettingPlaybackBackwardSkipLength];
+    NSInteger seekBy = VLCDefaults.shared.playbackBackwardSkipLength;
     [_playbackController jumpBackward:(int)seekBy];
 }
 
 - (void)keyRightArrow
 {
-    NSInteger seekBy = [[NSUserDefaults standardUserDefaults] integerForKey:kVLCSettingPlaybackForwardSkipLength];
+    NSInteger seekBy = VLCDefaults.shared.playbackForwardSkipLength;
     [_playbackController jumpForward:(int)seekBy];
 }
 
diff --git a/Sources/Playback/OS Integration/VLCRemoteControlService.m b/Sources/Playback/OS Integration/VLCRemoteControlService.m
index 4ac418525fff7fbdc52adea8be55de602e8d1553..6399251040510f82d3a4d6e2b47dc846e343a3e1 100644
--- a/Sources/Playback/OS Integration/VLCRemoteControlService.m	
+++ b/Sources/Playback/OS Integration/VLCRemoteControlService.m	
@@ -13,6 +13,7 @@
 
 #import "VLCRemoteControlService.h"
 #import "VLCPlaybackService.h"
+#import "VLC-Swift.h"
 #import <MediaPlayer/MediaPlayer.h>
 
 @implementation VLCRemoteControlService
@@ -55,13 +56,12 @@ static inline NSArray * RemoteCommandCenterCommandsToHandle(void)
 - (void)playbackStarted:(NSNotification *)aNotification
 {
     MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
-    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
 
     /* Since the control center and lockscreen shows only either skipForward/Backward
      * or next/previousTrack buttons but prefers skip buttons,
      * we only enable skip buttons if we have no medialist
      */
-    BOOL alwaysEnableSkip = [defaults boolForKey:kVLCSettingPlaybackLockscreenSkip];
+    BOOL alwaysEnableSkip = VLCDefaults.shared.lockscreenSkip;
     BOOL enableSkip = alwaysEnableSkip || [VLCPlaybackService sharedInstance].mediaList.count <= 1;
     commandCenter.skipForwardCommand.enabled = enableSkip;
     commandCenter.skipBackwardCommand.enabled = enableSkip;
@@ -78,9 +78,9 @@ static inline NSArray * RemoteCommandCenterCommandsToHandle(void)
     commandCenter.seekForwardCommand.enabled = NO;
     commandCenter.seekBackwardCommand.enabled = NO;
 
-    NSNumber *forwardSkip = [defaults valueForKey:kVLCSettingPlaybackForwardSkipLength];
+    NSNumber *forwardSkip = [NSNumber numberWithInteger:VLCDefaults.shared.playbackForwardSkipLength];
     commandCenter.skipForwardCommand.preferredIntervals = @[forwardSkip];
-    NSNumber *backwardSkip = [defaults valueForKey:kVLCSettingPlaybackBackwardSkipLength];
+    NSNumber *backwardSkip = [NSNumber numberWithInteger:VLCDefaults.shared.playbackBackwardSkipLength];
     commandCenter.skipBackwardCommand.preferredIntervals = @[backwardSkip];
 
     commandCenter.changePlaybackRateCommand.supportedPlaybackRates = @[@(0.5),@(0.75),@(1.0),@(1.25),@(1.5),@(1.75),@(2.0)];
@@ -122,8 +122,8 @@ static inline NSArray * RemoteCommandCenterCommandsToHandle(void)
         return MPRemoteCommandHandlerStatusSuccess;
     }
     if (event.command == cc.nextTrackCommand) {
-        if ([defaults boolForKey:kVLCSettingPlaybackRemoteControlSkip]) {
-            NSInteger interval = [defaults integerForKey:kVLCSettingPlaybackForwardSkipLength];
+        if (VLCDefaults.shared.remoteControlSkip) {
+            NSInteger interval = VLCDefaults.shared.playbackForwardSkipLength;
             [vps jumpForward:(int)interval];
             return MPRemoteCommandHandlerStatusSuccess;
         } else {
@@ -132,8 +132,8 @@ static inline NSArray * RemoteCommandCenterCommandsToHandle(void)
         }
     }
     if (event.command == cc.previousTrackCommand) {
-        if ([defaults boolForKey:kVLCSettingPlaybackRemoteControlSkip]) {
-            NSInteger interval = [defaults integerForKey:kVLCSettingPlaybackBackwardSkipLength];
+        if (VLCDefaults.shared.remoteControlSkip) {
+            NSInteger interval = VLCDefaults.shared.playbackBackwardSkipLength;
             [vps jumpBackward:(int)interval];
             return MPRemoteCommandHandlerStatusSuccess;
 
diff --git a/Sources/Playback/Player/AudioPlayer/AudioPlayerView.swift b/Sources/Playback/Player/AudioPlayer/AudioPlayerView.swift
index 1b94c4be28f1894c6b8b6f6ff2dcb4cb810176c3..3d1e96d45f3902b4ba8fdfd545e8c1a989738fbb 100644
--- a/Sources/Playback/Player/AudioPlayer/AudioPlayerView.swift
+++ b/Sources/Playback/Player/AudioPlayer/AudioPlayerView.swift
@@ -576,7 +576,7 @@ class AudioPlayerView: UIView, UIGestureRecognizerDelegate {
         
         secondaryControlStackView.addArrangedSubview(playbackSpeedButton)
 
-        let displaySecondaryStackView: Bool = UserDefaults.standard.bool(forKey: kVLCPlayerShowPlaybackSpeedShortcut)
+        let displaySecondaryStackView: Bool = VLCDefaults.shared.playerShowPlaybackSpeedShortcut
         secondaryControlStackView.isHidden = !displaySecondaryStackView
     }
 
diff --git a/Sources/Playback/Player/AudioPlayer/AudioPlayerViewController.swift b/Sources/Playback/Player/AudioPlayer/AudioPlayerViewController.swift
index 189fcee327eb13635b6ae4146f3daeb0e8f1c067..522f61680d8d73845962874d4893df2bdf173f7f 100644
--- a/Sources/Playback/Player/AudioPlayer/AudioPlayerViewController.swift
+++ b/Sources/Playback/Player/AudioPlayer/AudioPlayerViewController.swift
@@ -126,7 +126,7 @@ class AudioPlayerViewController: PlayerViewController {
         mediaScrubProgressBar.shouldHideScrubLabels = false
 #endif
 
-        let displayShortcutView: Bool = UserDefaults.standard.bool(forKey: kVLCPlayerShowPlaybackSpeedShortcut)
+        let displayShortcutView: Bool = VLCDefaults.shared.playerShowPlaybackSpeedShortcut
         audioPlayerView.shouldDisplaySecondaryStackView(displayShortcutView)
     }
 
diff --git a/Sources/Playback/Player/MiniPlayer-iOS/AudioMiniPlayer.swift b/Sources/Playback/Player/MiniPlayer-iOS/AudioMiniPlayer.swift
index 961431cc9d456efdc633ad7b0947205a0c8b285a..5eb0f534fc64efb03a6fbf9ecf2295888ac599f3 100644
--- a/Sources/Playback/Player/MiniPlayer-iOS/AudioMiniPlayer.swift
+++ b/Sources/Playback/Player/MiniPlayer-iOS/AudioMiniPlayer.swift
@@ -165,16 +165,13 @@ private extension AudioMiniPlayer {
     }
 
     private func applyCustomEqualizerProfileIfNeeded() {
-        let userDefaults = UserDefaults.standard
-        guard userDefaults.bool(forKey: kVLCCustomProfileEnabled) else {
+        guard VLCDefaults.shared.customEqualizerProfileEnabled else {
             return
         }
 
-        let profileIndex = userDefaults.integer(forKey: kVLCSettingEqualizerProfile)
-        let encodedData = userDefaults.data(forKey: kVLCCustomEqualizerProfiles)
+        let profileIndex = VLCDefaults.shared.equalizerProfile
 
-        guard let encodedData = encodedData,
-              let customProfiles = NSKeyedUnarchiver(forReadingWith: encodedData).decodeObject(forKey: "root") as? CustomEqualizerProfiles,
+        guard let customProfiles = VLCDefaults.shared.customEqualizerProfiles,
               profileIndex < customProfiles.profiles.count else {
             return
         }
diff --git a/Sources/Playback/Player/PlayerViewController.swift b/Sources/Playback/Player/PlayerViewController.swift
index 092f048a3afc87f92fd0dff4464deaf7f319a018..8d98b0b5758e6ca5c9a4a3af2fdf62437575f6c4 100644
--- a/Sources/Playback/Player/PlayerViewController.swift
+++ b/Sources/Playback/Player/PlayerViewController.swift
@@ -314,8 +314,6 @@ class PlayerViewController: UIViewController {
 
     private let notificationCenter = NotificationCenter.default
 
-    private let userDefaults = UserDefaults.standard
-
     // MARK: - Gestures
 
     lazy var panRecognizer: UIPanGestureRecognizer = {
@@ -436,8 +434,8 @@ class PlayerViewController: UIViewController {
         super.viewDidAppear(animated)
 
         if playerController.isRememberBrightnessEnabled && self is VideoPlayerViewController {
-            if let brightness = userDefaults.value(forKey: KVLCPlayerBrightness) as? CGFloat {
-                animateBrightness(to: brightness)
+            if let brightness = VLCDefaults.shared.playerBrightness {
+                animateBrightness(to: CGFloat(brightness))
                 self.brightnessControl.value = Float(brightness)
             }
         }
@@ -458,9 +456,9 @@ class PlayerViewController: UIViewController {
         super.viewDidDisappear(animated)
 
         if playerController.isRememberBrightnessEnabled && self is VideoPlayerViewController {
-            let currentBrightness = UIScreen.main.brightness
-            self.brightnessControl.value = Float(currentBrightness) // helper in indicating change in the system brightness
-            userDefaults.set(currentBrightness, forKey: KVLCPlayerBrightness)
+            let currentBrightness = Float(UIScreen.main.brightness)
+            self.brightnessControl.value = currentBrightness // helper in indicating change in the system brightness
+            VLCDefaults.shared.playerBrightness = currentBrightness
         }
         //set the value of system brightness after closing the app
         //even if the Player Should Remember Brightness option is disabled
@@ -736,13 +734,11 @@ class PlayerViewController: UIViewController {
     }
 
     private func setupSeekDurations() {
-        let defaults = UserDefaults.standard
-
-        tapSwipeEqual = defaults.bool(forKey: kVLCSettingPlaybackTapSwipeEqual)
-        forwardBackwardEqual = defaults.bool(forKey: kVLCSettingPlaybackForwardBackwardEqual)
-        seekForwardBy = defaults.integer(forKey: kVLCSettingPlaybackForwardSkipLength)
-        seekBackwardBy = forwardBackwardEqual ? seekForwardBy : defaults.integer(forKey: kVLCSettingPlaybackBackwardSkipLength)
-        seekForwardBySwipe = tapSwipeEqual ? seekForwardBy : defaults.integer(forKey: kVLCSettingPlaybackForwardSkipLengthSwipe)
+        tapSwipeEqual = VLCDefaults.shared.playbackTapSwipeEqual
+        forwardBackwardEqual = VLCDefaults.shared.playbackForwardBackwardEqual
+        seekForwardBy = VLCDefaults.shared.playbackForwardSkipLength
+        seekBackwardBy = forwardBackwardEqual ? seekForwardBy : VLCDefaults.shared.playbackBackwardSkipLength
+        seekForwardBySwipe = tapSwipeEqual ? seekForwardBy : VLCDefaults.shared.playbackForwardSkipLengthSwipe
 
         if tapSwipeEqual, forwardBackwardEqual {
             // if tap = swipe, and backward = forward, then backward swipe = forward tap
@@ -755,21 +751,18 @@ class PlayerViewController: UIViewController {
             seekBackwardBySwipe = seekForwardBySwipe
         } else {
             // otherwise backward swipe = backward swipe
-            seekBackwardBySwipe = defaults.integer(forKey: kVLCSettingPlaybackBackwardSkipLengthSwipe)
+            seekBackwardBySwipe = VLCDefaults.shared.playbackBackwardSkipLengthSwipe
         }
     }
 
     private func applyCustomEqualizerProfileIfNeeded() {
-        let userDefaults = UserDefaults.standard
-        guard userDefaults.bool(forKey: kVLCCustomProfileEnabled) else {
+        guard VLCDefaults.shared.customEqualizerProfileEnabled else {
             return
         }
 
-        let profileIndex = userDefaults.integer(forKey: kVLCSettingEqualizerProfile)
-        let encodedData = userDefaults.data(forKey: kVLCCustomEqualizerProfiles)
+        let profileIndex = VLCDefaults.shared.equalizerProfile
 
-        guard let encodedData = encodedData,
-              let customProfiles = NSKeyedUnarchiver(forReadingWith: encodedData).decodeObject(forKey: "root") as? CustomEqualizerProfiles,
+        guard let customProfiles = VLCDefaults.shared.customEqualizerProfiles,
               profileIndex < customProfiles.profiles.count else {
             return
         }
diff --git a/Sources/Playback/Player/VideoPlayer-iOS/MediaScrubProgressBar.swift b/Sources/Playback/Player/VideoPlayer-iOS/MediaScrubProgressBar.swift
index 46e0832491e97094b70352de0669aaef73b3b047..ecf7f0dd1cd976dbde1aa9c4e16f694f2d3a440a 100644
--- a/Sources/Playback/Player/VideoPlayer-iOS/MediaScrubProgressBar.swift
+++ b/Sources/Playback/Player/VideoPlayer-iOS/MediaScrubProgressBar.swift
@@ -144,14 +144,12 @@ class MediaScrubProgressBar: UIStackView {
     }
 
     @objc private func handleAccessibilityForward() -> Bool {
-        let defaults = UserDefaults.standard
-        playbackService.jumpForward(Int32(defaults.integer(forKey: kVLCSettingPlaybackForwardSkipLength)))
+        playbackService.jumpForward(Int32(VLCDefaults.shared.playbackForwardSkipLength))
         return true
     }
 
     @objc private func handleAccessibilityBackward() -> Bool {
-        let defaults = UserDefaults.standard
-        playbackService.jumpBackward(Int32(defaults.integer(forKey: kVLCSettingPlaybackBackwardSkipLength)))
+        playbackService.jumpBackward(Int32(VLCDefaults.shared.playbackBackwardSkipLength))
         return true
     }
 
@@ -417,8 +415,7 @@ fileprivate enum RemainingTimeMode {
     case remaining
 
     static var current: RemainingTimeMode {
-        let userDefault = UserDefaults.standard
-        let currentSetting = userDefault.bool(forKey: kVLCShowRemainingTime)
+        let currentSetting = VLCDefaults.shared.showRemainingTime
 
         switch currentSetting {
             case true: return .remaining
@@ -428,8 +425,7 @@ fileprivate enum RemainingTimeMode {
 
     @discardableResult
     static func toggle() -> RemainingTimeMode {
-        let userDefault = UserDefaults.standard
-        userDefault.set(!userDefault.bool(forKey: kVLCShowRemainingTime), forKey: kVLCShowRemainingTime)
+        VLCDefaults.shared.showRemainingTime.toggle()
         return current
     }
 }
diff --git a/Sources/Playback/Player/VideoPlayer-iOS/PlayerController.swift b/Sources/Playback/Player/VideoPlayer-iOS/PlayerController.swift
index 07217a3b4143fdabf3de4928861f82f0081ed154..978c7dc506ebcb2d5837afc6b573787a1873d7fd 100644
--- a/Sources/Playback/Player/VideoPlayer-iOS/PlayerController.swift
+++ b/Sources/Playback/Player/VideoPlayer-iOS/PlayerController.swift
@@ -38,52 +38,50 @@ class PlayerController: NSObject {
 
     var isTapSeeking: Bool = false
 
-    // MARK: - UserDefaults computed properties getters
+    // MARK: - Defaults computed properties getters
 
     var displayRemainingTime: Bool {
-        return UserDefaults.standard.bool(forKey: kVLCShowRemainingTime)
+        return VLCDefaults.shared.showRemainingTime
     }
 
     var isVolumeGestureEnabled: Bool {
-        return UserDefaults.standard.bool(forKey: kVLCSettingVolumeGesture)
+        return VLCDefaults.shared.volumeGesture
     }
 
     var isPlayPauseGestureEnabled: Bool {
-        return UserDefaults.standard.bool(forKey: kVLCSettingPlayPauseGesture)
+        return VLCDefaults.shared.playPauseGesture
     }
 
     var isBrightnessGestureEnabled: Bool {
-        return UserDefaults.standard.bool(forKey: kVLCSettingBrightnessGesture)
+        return VLCDefaults.shared.brightnessGesture
     }
 
     var isSwipeSeekGestureEnabled: Bool {
-        return UserDefaults.standard.bool(forKey: kVLCSettingSeekGesture)
+        return VLCDefaults.shared.seekGesture
     }
 
     var isCloseGestureEnabled: Bool {
-        return UserDefaults.standard.bool(forKey: kVLCSettingCloseGesture)
+        return VLCDefaults.shared.closeGesture
     }
 
     var isSpeedUpGestureEnabled: Bool {
-        return UserDefaults.standard.bool(forKey: kVLCSettingPlaybackLongTouchSpeedUp)
+        return VLCDefaults.shared.playbackLongTouchSpeedUp
     }
 
     var isShuffleEnabled: Bool {
-        return UserDefaults.standard.bool(forKey: kVLCPlayerIsShuffleEnabled)
+        return VLCDefaults.shared.playerIsShuffleEnabled
     }
 
     var isRepeatEnabled: VLCRepeatMode {
-        let storedValue = UserDefaults.standard.integer(forKey: kVLCPlayerIsRepeatEnabled)
-
-        return VLCRepeatMode(rawValue: storedValue) ?? .doNotRepeat
+        return VLCDefaults.shared.playerIsRepeatEnabled
     }
 
     var isRememberStateEnabled: Bool {
-        return UserDefaults.standard.bool(forKey: kVLCPlayerShouldRememberState)
+        return VLCDefaults.shared.playerShouldRememberState
     }
 
     var isRememberBrightnessEnabled: Bool {
-        return UserDefaults.standard.bool(forKey: kVLCPlayerShouldRememberBrightness)
+        return VLCDefaults.shared.playerShouldRememberBrightness
     }
 
     @objc override init() {
@@ -91,10 +89,6 @@ class PlayerController: NSObject {
         setupObservers()
     }
 
-    func updateUserDefaults() {
-
-    }
-
     private func setupObservers() {
         let notificationCenter = NotificationCenter.default
 
diff --git a/Sources/Playback/Player/VideoPlayer-iOS/Subviews/CustomEqualizerProfiles.swift b/Sources/Playback/Player/VideoPlayer-iOS/Subviews/CustomEqualizerProfiles.swift
index 1ee4d69a20596083f34ec97751cc350f5189df68..657428ff19a3347089ef1c11e8fbf73dfbb8939b 100644
--- a/Sources/Playback/Player/VideoPlayer-iOS/Subviews/CustomEqualizerProfiles.swift
+++ b/Sources/Playback/Player/VideoPlayer-iOS/Subviews/CustomEqualizerProfiles.swift
@@ -87,14 +87,13 @@ class CustomEqualizerProfiles: NSObject, NSCoding {
 
         profiles.swapAt(index, index - 1)
 
-        let userDefaults = UserDefaults.standard
-        if userDefaults.bool(forKey: kVLCCustomProfileEnabled) {
-            let currentProfileIndex = userDefaults.integer(forKey: kVLCSettingEqualizerProfile)
+        if VLCDefaults.shared.customEqualizerProfileEnabled {
+            let currentProfileIndex = VLCDefaults.shared.equalizerProfile
 
             if currentProfileIndex == index {
-                userDefaults.setValue(index - 1, forKeyPath: kVLCSettingEqualizerProfile)
+                VLCDefaults.shared.equalizerProfile = index - 1
             } else if currentProfileIndex == index - 1 {
-                userDefaults.setValue(index, forKey: kVLCSettingEqualizerProfile)
+                VLCDefaults.shared.equalizerProfile = index
             }
         }
     }
@@ -106,14 +105,13 @@ class CustomEqualizerProfiles: NSObject, NSCoding {
 
         profiles.swapAt(index, index + 1)
 
-        let userDefaults = UserDefaults.standard
-        if userDefaults.bool(forKey: kVLCCustomProfileEnabled) {
-            let currentProfileIndex = userDefaults.integer(forKey: kVLCSettingEqualizerProfile)
+        if VLCDefaults.shared.customEqualizerProfileEnabled {
+            let currentProfileIndex = VLCDefaults.shared.equalizerProfile
 
             if currentProfileIndex == index {
-                userDefaults.setValue(index + 1, forKeyPath: kVLCSettingEqualizerProfile)
+                VLCDefaults.shared.equalizerProfile = index + 1
             } else if currentProfileIndex == index + 1 {
-                userDefaults.setValue(index, forKey: kVLCSettingEqualizerProfile)
+                VLCDefaults.shared.equalizerProfile = index
             }
         }
     }
diff --git a/Sources/Playback/Player/VideoPlayer-iOS/Subviews/EqualizerPresetSelector.swift b/Sources/Playback/Player/VideoPlayer-iOS/Subviews/EqualizerPresetSelector.swift
index f26875a6be87daed31e2c7f34856127a49981bc7..d1165f808a3754834a3426546f966f6d265b69b8 100644
--- a/Sources/Playback/Player/VideoPlayer-iOS/Subviews/EqualizerPresetSelector.swift
+++ b/Sources/Playback/Player/VideoPlayer-iOS/Subviews/EqualizerPresetSelector.swift
@@ -102,9 +102,7 @@ class EqualizerPresetSelector: SpoilerButton, UITableViewDataSource, UITableView
 
     // MARK: - table view data source
     func numberOfSections(in tableView: UITableView) -> Int {
-        let profilesData = UserDefaults.standard.data(forKey: kVLCCustomEqualizerProfiles)
-        guard let profilesData = profilesData,
-              let customProfiles = NSKeyedUnarchiver(forReadingWith: profilesData).decodeObject(forKey: "root") as? CustomEqualizerProfiles else {
+        guard let customProfiles = VLCDefaults.shared.customEqualizerProfiles else {
             return 1
         }
 
@@ -117,9 +115,7 @@ class EqualizerPresetSelector: SpoilerButton, UITableViewDataSource, UITableView
             return profiles.count + 1
         }
 
-        let profilesData = UserDefaults.standard.data(forKey: kVLCCustomEqualizerProfiles)
-        guard let profilesData = profilesData,
-              let customProfiles = NSKeyedUnarchiver(forReadingWith: profilesData).decodeObject(forKey: "root") as? CustomEqualizerProfiles else {
+        guard let customProfiles = VLCDefaults.shared.customEqualizerProfiles else {
             return 0
         }
 
@@ -137,9 +133,7 @@ class EqualizerPresetSelector: SpoilerButton, UITableViewDataSource, UITableView
                 cell.textLabel?.text = profiles[indexPath.row - 1].name
             }
         } else {
-            let profilesData = UserDefaults.standard.data(forKey: kVLCCustomEqualizerProfiles)
-            guard let profilesData = profilesData,
-                  let customProfiles = NSKeyedUnarchiver(forReadingWith: profilesData).decodeObject(forKey: "root") as? CustomEqualizerProfiles else {
+            guard let customProfiles = VLCDefaults.shared.customEqualizerProfiles else {
                 return cell
             }
 
@@ -179,7 +173,7 @@ class EqualizerPresetSelector: SpoilerButton, UITableViewDataSource, UITableView
     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
         tableView.deselectRow(at: indexPath, animated: false)
         let isCustomProfile: Bool = indexPath.section == 0 ? false : true
-        UserDefaults.standard.setValue(isCustomProfile, forKey: kVLCCustomProfileEnabled)
+        VLCDefaults.shared.customEqualizerProfileEnabled = isCustomProfile
         delegate?.equalizerPresetSelector(self, didSelectPreset: indexPath.row, isCustom: isCustomProfile)
         presetsTableView.reloadData()
         toggleHiddenView()
@@ -232,10 +226,7 @@ class EqualizerPresetSelector: SpoilerButton, UITableViewDataSource, UITableView
     }
 
     func moveProfile(_ moveIdentifier: MoveEventIdentifier, at index: IndexPath) {
-        let userDefaults = UserDefaults.standard
-        let profilesData = userDefaults.data(forKey: kVLCCustomEqualizerProfiles)
-        guard let profilesData = profilesData,
-              let customProfiles = NSKeyedUnarchiver(forReadingWith: profilesData).decodeObject(forKey: "root") as? CustomEqualizerProfiles else {
+        guard let customProfiles = VLCDefaults.shared.customEqualizerProfiles else {
             return
         }
 
@@ -245,7 +236,7 @@ class EqualizerPresetSelector: SpoilerButton, UITableViewDataSource, UITableView
             customProfiles.moveDown(index: index.row)
         }
 
-        userDefaults.setValue(NSKeyedArchiver.archivedData(withRootObject: customProfiles), forKey: kVLCCustomEqualizerProfiles)
+        VLCDefaults.shared.customEqualizerProfiles = customProfiles
     }
 }
 
diff --git a/Sources/Playback/Player/VideoPlayer-iOS/Subviews/EqualizerView.swift b/Sources/Playback/Player/VideoPlayer-iOS/Subviews/EqualizerView.swift
index 21eed831aa28e6020e07a551ba75f67e2e8747a0..3d39b2732679efa5a4417e0711161399f7589349 100644
--- a/Sources/Playback/Player/VideoPlayer-iOS/Subviews/EqualizerView.swift
+++ b/Sources/Playback/Player/VideoPlayer-iOS/Subviews/EqualizerView.swift
@@ -214,7 +214,7 @@ import UIKit
         snapBandsLabel.text = NSLocalizedString("SNAP_BANDS", comment: "")
         snapBandsLabel.textAlignment = .right
         snapBandsLabel.setContentHuggingPriority(.required, for: .vertical)
-        snapBandsSwitch.isOn = UserDefaults.standard.bool(forKey: kVLCEqualizerSnapBands)
+        snapBandsSwitch.isOn = VLCDefaults.shared.equalizerSnapBands
         snapBandsSwitch.addTarget(self, action: #selector(snapBandsSwitchDidChangeValue), for: .valueChanged)
         snapBandsSwitch.setContentHuggingPriority(.required, for: .vertical)
         snapBandsStackView.addArrangedSubview(snapBandsLabel)
@@ -419,7 +419,7 @@ extension EqualizerView {
 
 extension EqualizerView {
     @objc func snapBandsSwitchDidChangeValue(sender: UISwitch) {
-        UserDefaults.standard.setValue(sender.isOn, forKey: kVLCEqualizerSnapBands)
+        VLCDefaults.shared.equalizerSnapBands = sender.isOn
     }
 }
 
@@ -447,11 +447,9 @@ extension EqualizerView {
             let preAmplification = self.playbackService.preAmplification
 
             let customProfile = CustomEqualizerProfile(name: name, preAmpLevel: Float(preAmplification), frequencies: frequencies)
-            let encodedProfiles = UserDefaults.standard.data(forKey: kVLCCustomEqualizerProfiles)
-            var customProfiles: CustomEqualizerProfiles
+            let customProfiles: CustomEqualizerProfiles
 
-            if let encodedProfiles = encodedProfiles,
-               let profiles = NSKeyedUnarchiver(forReadingWith: encodedProfiles).decodeObject(forKey: "root") as? CustomEqualizerProfiles {
+            if let profiles = VLCDefaults.shared.customEqualizerProfiles {
                 profiles.profiles.append(customProfile)
                 customProfiles = profiles
             } else {
@@ -459,11 +457,10 @@ extension EqualizerView {
             }
 
             let index = customProfiles.profiles.count - 1
-            let userDefaults = UserDefaults.standard
-            userDefaults.setValue(NSKeyedArchiver.archivedData(withRootObject: customProfiles), forKey: kVLCCustomEqualizerProfiles)
-            userDefaults.setValue(true, forKey: kVLCCustomProfileEnabled)
-            userDefaults.setValue(false, forKey: kVLCSettingEqualizerProfileDisabled)
-            userDefaults.setValue(index, forKey: kVLCSettingEqualizerProfile)
+            VLCDefaults.shared.customEqualizerProfiles = customProfiles
+            VLCDefaults.shared.customEqualizerProfileEnabled = true
+            VLCDefaults.shared.equalizerProfileDisabled = false
+            VLCDefaults.shared.equalizerProfile = index
 
             self.presetSelectorView?.presetsTableView.reloadData()
             self.shouldDisplaySaveButton(false)
@@ -479,16 +476,15 @@ extension EqualizerView {
     }
 
     @objc func resetEqualizer() {
-        let userDefaults = UserDefaults.standard
-        let isEqualizerDisabled = userDefaults.bool(forKey: kVLCSettingEqualizerProfileDisabled)
-        let isCustomProfile = userDefaults.bool(forKey: kVLCCustomProfileEnabled)
+        let isEqualizerDisabled = VLCDefaults.shared.equalizerProfileDisabled
+        let isCustomProfile = VLCDefaults.shared.customEqualizerProfileEnabled
 
         let profile: Int
         if !isCustomProfile {
-            profile = isEqualizerDisabled ? 0 : userDefaults.integer(forKey: kVLCSettingEqualizerProfile) + 1
+            profile = isEqualizerDisabled ? 0 : VLCDefaults.shared.equalizerProfile + 1
             delegate?.resetEqualizer(fromProfile: UInt32(profile))
         } else {
-            profile = userDefaults.integer(forKey: kVLCSettingEqualizerProfile)
+            profile = VLCDefaults.shared.equalizerProfile
             applyCustomProfile(profile)
         }
 
@@ -502,11 +498,7 @@ extension EqualizerView {
     }
 
     private func applyCustomProfile(_ index: Int) {
-        let userDefaults = UserDefaults.standard
-        let encodedData = userDefaults.data(forKey: kVLCCustomEqualizerProfiles)
-
-        guard let encodedData = encodedData,
-              let customProfiles = NSKeyedUnarchiver(forReadingWith: encodedData).decodeObject(forKey: "root") as? CustomEqualizerProfiles,
+        guard let customProfiles = VLCDefaults.shared.customEqualizerProfiles,
               index < customProfiles.profiles.count else {
             return
         }
@@ -518,9 +510,9 @@ extension EqualizerView {
             playbackService.setAmplification(CGFloat(frequency), forBand: UInt32(bandIndex))
         }
 
-        userDefaults.setValue(index, forKey: kVLCSettingEqualizerProfile)
-        userDefaults.setValue(false, forKey: kVLCSettingEqualizerProfileDisabled)
-        userDefaults.setValue(true, forKey: kVLCCustomProfileEnabled)
+        VLCDefaults.shared.equalizerProfile = index
+        VLCDefaults.shared.equalizerProfileDisabled = false
+        VLCDefaults.shared.customEqualizerProfileEnabled = true
     }
 
     private func shouldDisplaySaveButton(_ display: Bool) {
@@ -558,15 +550,13 @@ extension EqualizerView: EqualizerPresetSelectorDelegate {
 
         if type == .delete {
             action = UIAlertAction(title: NSLocalizedString("BUTTON_DELETE", comment: ""), style: .destructive) { _ in
-                let customEncodedProfiles = UserDefaults.standard.data(forKey: kVLCCustomEqualizerProfiles)
-                guard let customEncodedProfiles = customEncodedProfiles,
-                      var customProfiles = NSKeyedUnarchiver(forReadingWith: customEncodedProfiles).decodeObject(forKey: "root") as? CustomEqualizerProfiles,
+                guard var customProfiles = VLCDefaults.shared.customEqualizerProfiles,
                       index.row < customProfiles.profiles.count else {
                     return
                 }
 
                 customProfiles.profiles.remove(at: index.row)
-                UserDefaults.standard.setValue(NSKeyedArchiver.archivedData(withRootObject: customProfiles), forKey: kVLCCustomEqualizerProfiles)
+                VLCDefaults.shared.customEqualizerProfiles = customProfiles
                 self.presetSelectorView?.presetsTableView.reloadData()
             }
         } else {
@@ -576,9 +566,7 @@ extension EqualizerView: EqualizerPresetSelectorDelegate {
             }
 
             action = UIAlertAction(title: NSLocalizedString("BUTTON_RENAME", comment: ""), style: .default) { _ in
-                let customEncodedProfiles = UserDefaults.standard.data(forKey: kVLCCustomEqualizerProfiles)
-                guard let customEncodedProfiles = customEncodedProfiles,
-                      let customProfiles = NSKeyedUnarchiver(forReadingWith: customEncodedProfiles).decodeObject(forKey: "root") as? CustomEqualizerProfiles,
+                guard let customProfiles = VLCDefaults.shared.customEqualizerProfiles,
                       index.row < customProfiles.profiles.count else {
                     return
                 }
@@ -589,7 +577,7 @@ extension EqualizerView: EqualizerPresetSelectorDelegate {
                 }
 
                 customProfiles.profiles[index.row].name = newName
-                UserDefaults.standard.setValue(NSKeyedArchiver.archivedData(withRootObject: customProfiles), forKey: kVLCCustomEqualizerProfiles)
+                VLCDefaults.shared.customEqualizerProfiles = customProfiles
                 self.presetSelectorView?.presetsTableView.reloadData()
             }
         }
diff --git a/Sources/Playback/Player/VideoPlayer-iOS/Subviews/PlaybackSpeedView.swift b/Sources/Playback/Player/VideoPlayer-iOS/Subviews/PlaybackSpeedView.swift
index d81e91b2e96835c6d49b78d6cdaed7135fc655c3..d53050b291b2926ff06092904f64b4465cec663c 100644
--- a/Sources/Playback/Player/VideoPlayer-iOS/Subviews/PlaybackSpeedView.swift
+++ b/Sources/Playback/Player/VideoPlayer-iOS/Subviews/PlaybackSpeedView.swift
@@ -50,7 +50,7 @@ class PlaybackSpeedView: UIView {
     private var currentSpeed: Float = 1.0
 
     private let defaultDelay: Float = 0.0
-    private var defaultSpeed: Float = UserDefaults.standard.float(forKey: kVLCSettingPlaybackSpeedDefaultValue)
+    private var defaultSpeed: Float = VLCDefaults.shared.playbackSpeedDefaultValue
 
     let vpc = PlaybackService.sharedInstance()
     let notificationCenter = NotificationCenter.default
@@ -164,7 +164,7 @@ class PlaybackSpeedView: UIView {
         shortcutLabel.text = NSLocalizedString("DISPLAY_PLAYBACK_SPEED_SHORTCUT", comment: "")
         shortcutLabel.accessibilityLabel = NSLocalizedString("DISPLAY_PLAYBACK_SPEED_SHORTCUT", comment: "")
         shortcutLabel.accessibilityHint = NSLocalizedString("DISPLAY_PLAYBACK_SPEED_SHORTCUT_HINT", comment: "")
-        shortcutSwitch.isOn = UserDefaults.standard.bool(forKey: kVLCPlayerShowPlaybackSpeedShortcut)
+        shortcutSwitch.isOn = VLCDefaults.shared.playerShowPlaybackSpeedShortcut
     }
 
     @objc func playbackSpeedHasChanged(_ notification: NSNotification) {
@@ -264,7 +264,7 @@ class PlaybackSpeedView: UIView {
     }
 
     func reset() {
-        defaultSpeed = UserDefaults.standard.float(forKey: kVLCSettingPlaybackSpeedDefaultValue)
+        defaultSpeed = VLCDefaults.shared.playbackSpeedDefaultValue
         currentSpeed = defaultSpeed
         vpc.playbackRate = currentSpeed
         notificationCenter.post(name: Notification.Name("ChangePlaybackSpeed"), object: nil)
@@ -345,7 +345,7 @@ class PlaybackSpeedView: UIView {
 
     @IBAction func handleShortcutSwitch(_ sender: Any) {
         let isSwitchOn: Bool = shortcutSwitch.isOn
-        UserDefaults.standard.setValue(isSwitchOn, forKey: kVLCPlayerShowPlaybackSpeedShortcut)
+        VLCDefaults.shared.playerShowPlaybackSpeedShortcut = isSwitchOn
         delegate?.playbackSpeedViewHandleShortcutSwitchChange(displayView: isSwitchOn)
     }
 }
diff --git a/Sources/Playback/Player/VideoPlayer-iOS/VideoPlayerViewController.swift b/Sources/Playback/Player/VideoPlayer-iOS/VideoPlayerViewController.swift
index 33dde55a82f41e053a65dac71833a35e1e15fed4..bab5c9a09114893c6a24a8561200fddabe934ae7 100644
--- a/Sources/Playback/Player/VideoPlayer-iOS/VideoPlayerViewController.swift
+++ b/Sources/Playback/Player/VideoPlayer-iOS/VideoPlayerViewController.swift
@@ -401,11 +401,10 @@ class VideoPlayerViewController: PlayerViewController {
         super.viewDidAppear(animated)
 
 #if os(iOS)
-        let defaults = UserDefaults.standard
-        if defaults.bool(forKey: kVLCPlayerShouldRememberBrightness) {
-            if let brightness = defaults.value(forKey: KVLCPlayerBrightness) as? CGFloat {
-                animateBrightness(to: brightness)
-                self.brightnessControl.value = Float(brightness)
+        if VLCDefaults.shared.playerShouldRememberBrightness {
+            if let brightness = VLCDefaults.shared.playerBrightness {
+                animateBrightness(to: CGFloat(brightness))
+                self.brightnessControl.value = brightness
             }
         }
 #endif
@@ -468,11 +467,10 @@ class VideoPlayerViewController: PlayerViewController {
         super.viewDidDisappear(animated)
         deviceMotion.stopDeviceMotion()
 #if os(iOS)
-        let defaults = UserDefaults.standard
-        if defaults.bool(forKey: kVLCPlayerShouldRememberBrightness) {
-            let currentBrightness = UIScreen.main.brightness
-            self.brightnessControl.value = Float(currentBrightness) // helper in indicating change in the system brightness
-            defaults.set(currentBrightness, forKey: KVLCPlayerBrightness)
+        if VLCDefaults.shared.playerShouldRememberBrightness {
+            let currentBrightness = Float(UIScreen.main.brightness)
+            self.brightnessControl.value = currentBrightness // helper in indicating change in the system brightness
+            VLCDefaults.shared.playerBrightness = currentBrightness
         }
 
         //set the value of system brightness after closing the app x
@@ -865,13 +863,11 @@ class VideoPlayerViewController: PlayerViewController {
     }
 
     private func setupSeekDurations() {
-        let defaults = UserDefaults.standard
-
-        tapSwipeEqual = defaults.bool(forKey: kVLCSettingPlaybackTapSwipeEqual)
-        forwardBackwardEqual = defaults.bool(forKey: kVLCSettingPlaybackForwardBackwardEqual)
-        seekForwardBy = defaults.integer(forKey: kVLCSettingPlaybackForwardSkipLength)
-        seekBackwardBy = forwardBackwardEqual ? seekForwardBy : defaults.integer(forKey: kVLCSettingPlaybackBackwardSkipLength)
-        seekForwardBySwipe = tapSwipeEqual ? seekForwardBy : defaults.integer(forKey: kVLCSettingPlaybackForwardSkipLengthSwipe)
+        tapSwipeEqual = VLCDefaults.shared.playbackTapSwipeEqual
+        forwardBackwardEqual = VLCDefaults.shared.playbackForwardBackwardEqual
+        seekForwardBy = VLCDefaults.shared.playbackForwardSkipLength
+        seekBackwardBy = forwardBackwardEqual ? seekForwardBy : VLCDefaults.shared.playbackBackwardSkipLength
+        seekForwardBySwipe = tapSwipeEqual ? seekForwardBy : VLCDefaults.shared.playbackForwardSkipLengthSwipe
 
         if tapSwipeEqual, forwardBackwardEqual {
             // if tap = swipe, and backward = forward, then backward swipe = forward tap
@@ -884,7 +880,7 @@ class VideoPlayerViewController: PlayerViewController {
             seekBackwardBySwipe = seekForwardBySwipe
         } else {
             // otherwise backward swipe = backward swipe
-            seekBackwardBySwipe = defaults.integer(forKey: kVLCSettingPlaybackBackwardSkipLengthSwipe)
+            seekBackwardBySwipe = VLCDefaults.shared.playbackBackwardSkipLengthSwipe
         }
     }
 
@@ -992,7 +988,7 @@ class VideoPlayerViewController: PlayerViewController {
     }
 
     @objc func handleTapOnVideo() {
-        if UserDefaults.standard.bool(forKey: kVLCSettingPauseWhenShowingControls) && playbackService.isPlaying {
+        if VLCDefaults.shared.pauseWhenShowingControls && playbackService.isPlaying {
             playbackService.pause()
         }
 
@@ -1349,8 +1345,7 @@ class VideoPlayerViewController: PlayerViewController {
     }
 
     private func resetIdleTimer() {
-        let intervalSetting = UserDefaults.standard
-            .integer(forKey: kVLCSettingPlayerControlDuration)
+        let intervalSetting = VLCDefaults.shared.playerControlDuration
 
         let interval = TimeInterval(max(intervalSetting, 4))
 
@@ -1385,16 +1380,13 @@ class VideoPlayerViewController: PlayerViewController {
     }
 
     private func applyCustomEqualizerProfileIfNeeded() {
-        let userDefaults = UserDefaults.standard
-        guard userDefaults.bool(forKey: kVLCCustomProfileEnabled) else {
+        guard VLCDefaults.shared.customEqualizerProfileEnabled else {
             return
         }
 
-        let profileIndex = userDefaults.integer(forKey: kVLCSettingEqualizerProfile)
-        let encodedData = userDefaults.data(forKey: kVLCCustomEqualizerProfiles)
+        let profileIndex = VLCDefaults.shared.equalizerProfile
 
-        guard let encodedData = encodedData,
-              let customProfiles = NSKeyedUnarchiver(forReadingWith: encodedData).decodeObject(forKey: "root") as? CustomEqualizerProfiles,
+        guard let customProfiles = VLCDefaults.shared.customEqualizerProfiles,
               profileIndex < customProfiles.profiles.count else {
             return
         }
@@ -1535,7 +1527,7 @@ extension VideoPlayerViewController {
 
         if currentState == .opening {
             updateAudioInterface(with: playbackService.metadata)
-            if UserDefaults.standard.bool(forKey: kVLCSettingRotationLock) {
+            if VLCDefaults.shared.rotationLock {
                 videoPlayerControls.handleRotationLockButton(videoPlayerControls)
             }
         }
diff --git a/Sources/Playback/Player/VideoPlayer-tvOS/Playback Info/VLCPlaybackInfoPlaybackTVViewController.m b/Sources/Playback/Player/VideoPlayer-tvOS/Playback Info/VLCPlaybackInfoPlaybackTVViewController.m
index 51f2fa49c4f8489148c6379afed543f40d1040a9..201fc1410699337ca979fd9c43ba199e42b5cfec 100644
--- a/Sources/Playback/Player/VideoPlayer-tvOS/Playback Info/VLCPlaybackInfoPlaybackTVViewController.m	
+++ b/Sources/Playback/Player/VideoPlayer-tvOS/Playback Info/VLCPlaybackInfoPlaybackTVViewController.m	
@@ -12,6 +12,7 @@
  *****************************************************************************/
 
 #import "VLCPlaybackInfoPlaybackTVViewController.h"
+#import "VLC-Swift.h"
 
 @interface VLCPlaybackInfoPlaybackTVViewController ()
 @property (nonatomic) VLCPlaybackService *playbackService;
@@ -93,7 +94,7 @@
     _decreaseSpeed = -0.05;
 
     _defaultDelay = 0.0;
-    _defaultSpeed = [[[NSUserDefaults standardUserDefaults] valueForKey:kVLCSettingPlaybackSpeedDefaultValue] doubleValue];
+    _defaultSpeed = (double)VLCDefaults.shared.playbackSpeedDefaultValue;
 
     _titleLabel.textColor = UIColor.VLCLightTextColor;
     _valueLabel.textColor = UIColor.VLCLightTextColor;
diff --git a/Sources/Playback/Player/VideoPlayer-tvOS/VLCFullscreenMovieTVViewController.m b/Sources/Playback/Player/VideoPlayer-tvOS/VLCFullscreenMovieTVViewController.m
index c1d89fe983b830fb73976a37d7279654675b25ed..acb41cefcae7d3a5fe27d65f6262b4d32c662478 100644
--- a/Sources/Playback/Player/VideoPlayer-tvOS/VLCFullscreenMovieTVViewController.m
+++ b/Sources/Playback/Player/VideoPlayer-tvOS/VLCFullscreenMovieTVViewController.m
@@ -18,6 +18,7 @@
 #import "VLCNetworkImageView.h"
 #import "VLCMetaData.h"
 #import "VLCActivityManager.h"
+#import "VLC-Swift.h"
 
 typedef NS_ENUM(NSInteger, VLCPlayerScanState)
 {
@@ -172,12 +173,12 @@ typedef NS_ENUM(NSInteger, VLCPlayerScanState)
     vpc.delegate = self;
 
     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
-    if ([defaults boolForKey:kVLCPlayerShouldRememberState]) {
-        vpc.shuffleMode = [defaults boolForKey:kVLCPlayerIsShuffleEnabled];
-        vpc.repeatMode = [defaults integerForKey:kVLCPlayerIsRepeatEnabled];
+    if (VLCDefaults.shared.playerShouldRememberState) {
+        vpc.shuffleMode = VLCDefaults.shared.playerIsShuffleEnabled;
+        vpc.repeatMode = VLCDefaults.shared.playerIsRepeatEnabled;
     }
 
-    self.playbackUIShouldHide = [defaults boolForKey:kVLCPlayerUIShouldHide];
+    self.playbackUIShouldHide = VLCDefaults.shared.playerUIShouldHide;
     if (self.playbackUIShouldHide) {
         self.activityIndicator.alpha = 0.;
         [self.activityIndicator stopAnimating];
diff --git a/Sources/Playback/Subtitles Downloading/VLCPlaybackInfoSubtitlesFetcherViewController.m b/Sources/Playback/Subtitles Downloading/VLCPlaybackInfoSubtitlesFetcherViewController.m
index 8320861dd724d9416963bf3af87a0c9297c92ad8..144e28ad3635d5406f9dc9d3288d44d3ca45dc28 100644
--- a/Sources/Playback/Subtitles Downloading/VLCPlaybackInfoSubtitlesFetcherViewController.m	
+++ b/Sources/Playback/Subtitles Downloading/VLCPlaybackInfoSubtitlesFetcherViewController.m	
@@ -231,7 +231,7 @@
             return;
         }
 
-        if ([[NSUserDefaults standardUserDefaults] integerForKey:kVLCSettingAppTheme] == kVLCSettingAppThemeSystem) {
+        if (VLCDefaults.shared.appThemeIsSystem) {
             [PresentationTheme themeDidUpdate];
         }
         [self themeDidChange];
diff --git a/Sources/Settings/Controller/PasscodeLockController.swift b/Sources/Settings/Controller/PasscodeLockController.swift
index 6bfedb1a38c9a6ab1bc756063ada5682d57a77fd..dc76f785539dad814aaa29b5381659cc6a54b0cd 100644
--- a/Sources/Settings/Controller/PasscodeLockController.swift
+++ b/Sources/Settings/Controller/PasscodeLockController.swift
@@ -24,7 +24,6 @@ enum PasscodeAction {
 
 class PasscodeLockController: UIViewController {
     // - MARK: Properties
-    private let userDefaults = UserDefaults.standard
     private let notificationCenter = NotificationCenter.default
 
     let action: PasscodeAction
diff --git a/Sources/Settings/Controller/SettingsController.swift b/Sources/Settings/Controller/SettingsController.swift
index bd9aa32be8d81aa9d54793aa073796afd6851f6c..14ccd5584fdf2fc8c8f5e420d411a682510937c3 100644
--- a/Sources/Settings/Controller/SettingsController.swift
+++ b/Sources/Settings/Controller/SettingsController.swift
@@ -11,6 +11,7 @@
  *          Diogo Simao Marques <dogo@videolabs.io>
  *          Felix Paul Kühne <fkuehne # videolan.org>
  *          Andrew Breckenridge <asbreckenridge@me.com>
+ *          Craig Reyenga <craig.reyenga # gmail.com>          
  *
  * Refer to the COPYING file of the official project for license.
  *****************************************************************************/
@@ -20,13 +21,13 @@ import UIKit
 
 extension Notification.Name {
     static let VLCDisableGroupingDidChangeNotification = Notification.Name("disableGroupingDidChangeNotfication")
+    static let VLCSettingsShouldReloadNotification = Notification.Name("settingsShouldReloadNotification")
 }
 
 class SettingsController: UITableViewController {
     private let cellReuseIdentifier = "settingsCell"
     private let sectionHeaderReuseIdentifier = "sectionHeaderReuseIdentifier"
     private let sectionFooterReuseIdentifier = "sectionFooterReuseIdentifier"
-    private let userDefaults = UserDefaults.standard
     private let notificationCenter = NotificationCenter.default
     private let actionSheet = ActionSheet()
     private let specifierManager = ActionSheetSpecifier()
@@ -95,7 +96,11 @@ class SettingsController: UITableViewController {
     private func addObservers() {
         notificationCenter.addObserver(self,
                                        selector: #selector(reloadSettingsSections),
-                                       name: UserDefaults.didChangeNotification,
+                                       name: .VLCDefaultsDidUpdate,
+                                       object: nil)
+        notificationCenter.addObserver(self,
+                                       selector: #selector(reloadSettingsSections),
+                                       name: .VLCSettingsShouldReloadNotification,
                                        object: nil)
         notificationCenter.addObserver(self,
                                        selector: #selector(themeDidChange),
@@ -256,7 +261,7 @@ class SettingsController: UITableViewController {
         actionSheet.numberOfColums = numberOfColumns
 
         present(actionSheet, animated: false) {
-            if preferenceKey != kVLCAutomaticallyPlayNextItem {
+            if preferenceKey != VLCDefaults.Compat.automaticallyPlayNextItemKey {
                 self.actionSheet.collectionView.selectItem(at: self.specifierManager.selectedIndex, animated: false, scrollPosition: .centeredVertically)
             }
         }
@@ -296,19 +301,18 @@ class SettingsController: UITableViewController {
     }
 
     private func resetOptions() {
-        // note that [NSUserDefaults resetStandardUserDefaults] will NOT correctly reset to the defaults
-        let appDomain = Bundle.main.bundleIdentifier!
-        UserDefaults().removePersistentDomain(forName: appDomain)
+        VLCDefaults.shared.reset()
     }
 }
 
 extension SettingsController {
     @objc func reloadSettingsSections() {
         settingsSections = SettingsSection
-            .sections(isLabActivated: isLabActivated,
+            .sections(mediaLibraryService: mediaLibraryService,
+                      isLabActivated: isLabActivated,
                       isBackingUp: isBackingUp,
-                      isForwardBackwardEqual: userDefaults.bool(forKey: kVLCSettingPlaybackForwardBackwardEqual),
-                      isTapSwipeEqual: userDefaults.bool(forKey: kVLCSettingPlaybackTapSwipeEqual))
+                      isForwardBackwardEqual: VLCDefaults.shared.playbackForwardBackwardEqual,
+                      isTapSwipeEqual: VLCDefaults.shared.playbackTapSwipeEqual)
     }
 
     override func numberOfSections(in _: UITableView) -> Int {
@@ -428,21 +432,6 @@ extension SettingsController: MediaLibraryHidingDelegate {
 // MARK: - SwitchOn Delegates
 
 extension SettingsController: SettingsCellDelegate {
-    func settingsCellDidChangeSwitchState(cell _: SettingsCell, preferenceKey: String, isOn: Bool) {
-        switch preferenceKey {
-        case kVLCSettingPasscodeOnKey:
-            passcodeLockSwitchOn(state: isOn)
-        case kVLCSettingHideLibraryInFilesApp:
-            medialibraryHidingLockSwitchOn(state: isOn)
-        case kVLCSettingBackupMediaLibrary:
-            mediaLibraryBackupActivateSwitchOn(state: isOn)
-        case kVLCSettingsDisableGrouping:
-            medialibraryDisableGroupingSwitchOn(state: isOn)
-        default:
-            break
-        }
-    }
-
     func settingsCellInfoButtonPressed(cell: SettingsCell, preferenceKey: String) {
         guard let settingSpecifier = getSettingsSpecifier(for: preferenceKey) else {
             return
@@ -466,56 +455,16 @@ extension SettingsController: SettingsCellDelegate {
     }
 }
 
-extension SettingsController {
-    func passcodeLockSwitchOn(state: Bool) {
-        if state {
-            KeychainCoordinator.passcodeService.setSecret { success in
-                // If the user cancels setting the password, the toggle should revert to the unset state.
-                // This ensures the UI reflects the correct state.
-                UserDefaults.standard.set(success, forKey: kVLCSettingPasscodeOnKey)
-                self.reloadSettingsSections() // To show/hide biometric row
-            }
-        } else {
-            // When disabled any existing passcode should be removed.
-            // If user previously set a passcode and then disable and enable it
-            // the new passcode view will be showed, but if user terminates the app
-            // passcode will remain open even if the user doesn't set the new passcode.
-            // So, this may cause the app being locked.
-            try? KeychainCoordinator.passcodeService.removeSecret()
-
-            reloadSettingsSections()
-        }
-    }
-}
-
-extension SettingsController {
-    func medialibraryHidingLockSwitchOn(state: Bool) {
-        mediaLibraryService.hideMediaLibrary(state)
-    }
-}
-
-extension SettingsController {
-    func mediaLibraryBackupActivateSwitchOn(state: Bool) {
-        mediaLibraryService.excludeFromDeviceBackup(state)
-    }
-}
-
-extension SettingsController {
-    func medialibraryDisableGroupingSwitchOn(state _: Bool) {
-        notificationCenter.post(name: .VLCDisableGroupingDidChangeNotification, object: self)
-    }
-}
-
 extension SettingsController: ActionSheetSpecifierDelegate {
     func actionSheetSpecifierHandleToggleSwitch(for cell: ActionSheetCell, state: Bool) {
         switch cell.identifier {
         case .blackBackground:
-            userDefaults.setValue(state, forKey: kVLCSettingAppThemeBlack)
+            VLCDefaults.shared.appThemeBlack = state
             PresentationTheme.themeDidUpdate()
         case .playNextItem:
-            userDefaults.setValue(state, forKey: kVLCAutomaticallyPlayNextItem)
+            VLCDefaults.shared.automaticallyPlayNextItem = state
         case .playlistPlayNextItem:
-            userDefaults.setValue(state, forKey: kVLCPlaylistPlayNextItem)
+            VLCDefaults.shared.playlistPlayNextItem = state
         default:
             break
         }
diff --git a/Sources/Settings/Model/ActionSheetSpecifier.swift b/Sources/Settings/Model/ActionSheetSpecifier.swift
index ed4e4db9fa7f3b67273604649b6713ac07330b60..e1b5dd369698686982e528444186b13b6392cb3c 100644
--- a/Sources/Settings/Model/ActionSheetSpecifier.swift
+++ b/Sources/Settings/Model/ActionSheetSpecifier.swift
@@ -60,20 +60,20 @@ extension ActionSheetSpecifier: ActionSheetDelegate {
             return
         }
 
-        guard preferenceKey != kVLCSettingAppTheme ||
+        guard preferenceKey != VLCDefaults.Compat.appThemeKey ||
                 (!PresentationTheme.current.isDark || indexPath.row != numberOfRows() - 1) else {
             // Disable the selection for the black background option cell in the appearance action sheet
             return
         }
 
-        guard preferenceKey != kVLCAutomaticallyPlayNextItem else {
+        guard preferenceKey != VLCDefaults.Compat.automaticallyPlayNextItemKey else {
             // Disable the selection for the automatically play next item options
             return
         }
 
         userDefaults.set(settingSpecifier?.specifier[indexPath.row].value, forKey: preferenceKey)
 
-        if preferenceKey == kVLCSettingAppTheme {
+        if preferenceKey == VLCDefaults.Compat.appThemeKey {
             PresentationTheme.themeDidUpdate()
         }
 
@@ -106,7 +106,7 @@ extension ActionSheetSpecifier: ActionSheetDataSource {
             return 0
         }
 
-        if preferenceKey == kVLCSettingAppTheme {
+        if preferenceKey == VLCDefaults.Compat.appThemeKey {
             let isThemeDark: Bool = PresentationTheme.current.isDark
             if #available(iOS 13, *) {
                 return isThemeDark ? rowCount : rowCount - 1
@@ -128,11 +128,11 @@ extension ActionSheetSpecifier: ActionSheetDataSource {
             return UICollectionViewCell()
         }
 
-        if preferenceKey == kVLCSettingAppTheme &&
+        if preferenceKey == VLCDefaults.Compat.appThemeKey &&
             PresentationTheme.current.isDark && indexPath.row == numberOfRows() - 1 {
             // Update the black background option cell
             cell.setAccessoryType(to: .toggleSwitch)
-            cell.setToggleSwitch(state: UserDefaults.standard.bool(forKey: kVLCSettingAppThemeBlack))
+            cell.setToggleSwitch(state: VLCDefaults.shared.appThemeBlack)
             cell.name.text = settingsBundle.localizedString(forKey: "SETTINGS_THEME_BLACK", value: "", table: "Root")
             let cellIdentifier = ActionSheetCellIdentifier.blackBackground
             cell.identifier = cellIdentifier
@@ -141,14 +141,14 @@ extension ActionSheetSpecifier: ActionSheetDataSource {
             cell.delegate = self
 
             return cell
-        } else if preferenceKey == kVLCAutomaticallyPlayNextItem {
+        } else if preferenceKey == VLCDefaults.Compat.automaticallyPlayNextItemKey {
             cell.setAccessoryType(to: .toggleSwitch)
             let isFirstRow: Bool = indexPath.row == 0
 
             if isFirstRow {
-                cell.setToggleSwitch(state: userDefaults.bool(forKey: kVLCAutomaticallyPlayNextItem))
+                cell.setToggleSwitch(state: VLCDefaults.shared.automaticallyPlayNextItem)
             } else {
-                cell.setToggleSwitch(state: userDefaults.bool(forKey: kVLCPlaylistPlayNextItem))
+                cell.setToggleSwitch(state: VLCDefaults.shared.playlistPlayNextItem)
             }
 
             let cellIdentifier: ActionSheetCellIdentifier = isFirstRow ? .playNextItem : .playlistPlayNextItem
@@ -166,7 +166,7 @@ extension ActionSheetSpecifier: ActionSheetDataSource {
 
 extension ActionSheetSpecifier: ActionSheetCellDelegate {
     func actionSheetCellShouldUpdateColors() -> Bool {
-        guard preferenceKey != kVLCAutomaticallyPlayNextItem else {
+        guard preferenceKey != VLCDefaults.Compat.automaticallyPlayNextItemKey else {
             return false
         }
 
diff --git a/Sources/Settings/Model/SettingsSection.swift b/Sources/Settings/Model/SettingsSection.swift
index a7b6ffbe598ddc6c2906c0d78aae9d2ab5a88f9c..5283340e14b075136092ae92b4539dc60e4bfacb 100644
--- a/Sources/Settings/Model/SettingsSection.swift
+++ b/Sources/Settings/Model/SettingsSection.swift
@@ -10,6 +10,7 @@
  *          Diogo Simao Marques <dogo@videolabs.io>
  *          Felix Paul Kühne <fkuehne # videolan.org>
  *          Eshan Singh <eeeshan789@icloud.com>
+ *          Craig Reyenga <craig.reyenga # gmail.com>          
  *
  * Refer to the COPYING file of the official project for license.
  *****************************************************************************/
@@ -28,8 +29,6 @@ struct SettingsItem: Equatable {
     @available(*, deprecated, message: "access from self.action")
     var preferenceKey: String? {
         switch action {
-        case let .toggle(toggle):
-            return toggle.preferenceKey
         case let .showActionSheet(_, preferenceKey, _):
             return preferenceKey
         default:
@@ -44,8 +43,8 @@ struct SettingsItem: Equatable {
         self.isTitleEmphasized = isTitleEmphasized
     }
 
-    static func toggle(title: String, subtitle: String?, preferenceKey: String) -> Self {
-        return Self(title: title, subtitle: subtitle, action: .toggle(Toggle(preferenceKey: preferenceKey)))
+    static func toggle(title: String, subtitle: String? = nil, keyPath: WritableKeyPath<VLCDefaults, Bool>, onChange: ((Bool) -> Void)? = nil) -> Self {
+        return Self(title: title, subtitle: subtitle, action: .toggle(Toggle(keyPath: keyPath, onChange: onChange)))
     }
 
     enum Action: Equatable {
@@ -62,10 +61,11 @@ struct SettingsItem: Equatable {
     final class Toggle: Equatable {
         typealias Observer = (Bool) -> Void
 
-        let preferenceKey: String
+        private let keyPath: WritableKeyPath<VLCDefaults, Bool>
+        private let onChange: ((Bool) -> Void)?
 
         var isOn: Bool {
-            UserDefaults.standard.bool(forKey: preferenceKey)
+            VLCDefaults.shared[keyPath: keyPath]
         }
 
         private var observers: [Int: Observer] = [:]
@@ -77,13 +77,19 @@ struct SettingsItem: Equatable {
             set { lock.withLock { _lastId = newValue } }
         }
 
-        init(preferenceKey: String) {
-            self.preferenceKey = preferenceKey
-            NotificationCenter.default.addObserver(self, selector: #selector(didChange), name: UserDefaults.didChangeNotification, object: nil)
+        init(keyPath: WritableKeyPath<VLCDefaults, Bool>, onChange: ((Bool) -> Void)? = nil) {
+            self.keyPath = keyPath
+            self.onChange = onChange
+            NotificationCenter.default.addObserver(self,
+                                                   selector: #selector(defaultsDidUpdate),
+                                                   name: .VLCDefaultsDidUpdate,
+                                                   object: nil)
         }
 
         func set(isOn: Bool) {
-            UserDefaults.standard.set(isOn, forKey: preferenceKey)
+            var defaults = VLCDefaults.shared
+            defaults[keyPath: keyPath] = isOn
+            onChange?(isOn)
         }
 
         // does not call out initially.
@@ -98,12 +104,12 @@ struct SettingsItem: Equatable {
             observers.removeValue(forKey: Int)
         }
 
-        @objc private func didChange(_: Notification) {
+        @objc private func defaultsDidUpdate(_: Notification) {
             notifyObservers()
         }
 
         private func notifyObservers() {
-            precondition(!isNotifyingObservers, "[\(preferenceKey)] updating the toggle switch from an observer is illegal")
+            precondition(!isNotifyingObservers, "updating the toggle switch from an observer is illegal")
 
             isNotifyingObservers = true
 
@@ -120,7 +126,7 @@ struct SettingsItem: Equatable {
         }
 
         static func == (lhs: SettingsItem.Toggle, rhs: SettingsItem.Toggle) -> Bool {
-            lhs.preferenceKey == rhs.preferenceKey
+            lhs.keyPath == rhs.keyPath
         }
     }
 }
@@ -140,18 +146,22 @@ struct SettingsSection: Equatable {
         self.items = items
     }
 
-    static func sections(isLabActivated: Bool, isBackingUp: Bool, isForwardBackwardEqual: Bool, isTapSwipeEqual: Bool) -> [SettingsSection] {
+    static func sections(mediaLibraryService: MediaLibraryService,
+                         isLabActivated: Bool,
+                         isBackingUp: Bool,
+                         isForwardBackwardEqual: Bool,
+                         isTapSwipeEqual: Bool) -> [SettingsSection] {
         [
             MainOptions.section(),
             DonationOptions.section(),
             GenericOptions.section(),
-            PrivacyOptions.section(),
+            PrivacyOptions.section(mediaLibraryService: mediaLibraryService),
             GestureControlOptions.section(isForwardBackwardEqual: isForwardBackwardEqual, isTapSwipeEqual: isTapSwipeEqual),
             VideoOptions.section(),
             SubtitlesOptions.section(),
             AudioOptions.section(),
             CastingOptions.section(),
-            MediaLibraryOptions.section(isBackingUp: isBackingUp),
+            MediaLibraryOptions.section(mediaLibraryService: mediaLibraryService, isBackingUp: isBackingUp),
             NetworkOptions.section(),
             Accessibility.section(),
             Lab.section(isLabActivated: isLabActivated),
@@ -170,7 +180,7 @@ enum MainOptions {
     }
 
     static var appearance: SettingsItem {
-        let k = kVLCSettingAppTheme
+        let k = VLCDefaults.Compat.appThemeKey
         return .init(title: "SETTINGS_DARKTHEME",
                      subtitle: Localizer.getSubtitle(for: k),
                      action: .showActionSheet(title: "SETTINGS_DARKTHEME", preferenceKey: k, hasInfo: false))
@@ -202,14 +212,14 @@ enum DonationOptions {
 
 enum GenericOptions {
     static var defaultPlaybackSpeed: SettingsItem {
-        let k = kVLCSettingPlaybackSpeedDefaultValue
+        let k = VLCDefaults.Compat.playbackSpeedDefaultValueKey
         return .init(title: "SETTINGS_PLAYBACK_SPEED_DEFAULT",
                      subtitle: Localizer.getSubtitle(for: k),
                      action: .showActionSheet(title: "SETTINGS_PLAYBACK_SPEED_DEFAULT", preferenceKey: k, hasInfo: false))
     }
 
     static var continueAudioPlayback: SettingsItem {
-        let k = kVLCSettingContinueAudioPlayback
+        let k = VLCDefaults.Compat.continueAudioPlaybackKey
         return .init(title: "SETTINGS_CONTINUE_AUDIO_PLAYBACK",
                      subtitle: Localizer.getSubtitle(for: k),
                      action: .showActionSheet(title: "SETTINGS_CONTINUE_AUDIO_PLAYBACK", preferenceKey: k, hasInfo: true))
@@ -217,19 +227,18 @@ enum GenericOptions {
 
     static var playVideoInFullScreen: SettingsItem {
         .toggle(title: "SETTINGS_VIDEO_FULLSCREEN",
-                subtitle: nil,
-                preferenceKey: kVLCSettingVideoFullscreenPlayback)
+                keyPath: \.videoFullscreenPlayback)
     }
 
     static var continueVideoPlayback: SettingsItem {
-        let k = kVLCSettingContinuePlayback
+        let k = VLCDefaults.Compat.continuePlaybackKey
         return .init(title: "SETTINGS_CONTINUE_VIDEO_PLAYBACK",
                      subtitle: Localizer.getSubtitle(for: k),
                      action: .showActionSheet(title: "SETTINGS_CONTINUE_VIDEO_PLAYBACK", preferenceKey: k, hasInfo: true))
     }
 
     static var automaticallyPlayNextItem: SettingsItem {
-        let k = kVLCAutomaticallyPlayNextItem
+        let k = VLCDefaults.Compat.automaticallyPlayNextItemKey
         return .init(title: "SETTINGS_NETWORK_PLAY_ALL",
                      subtitle: Localizer.getSubtitle(for: k),
                      action: .showActionSheet(title: "SETTINGS_NETWORK_PLAY_ALL", preferenceKey: k, hasInfo: false))
@@ -237,20 +246,17 @@ enum GenericOptions {
 
     static var enableTextScrollingInMediaList: SettingsItem {
         .toggle(title: "SETTINGS_ENABLE_MEDIA_CELL_TEXT_SCROLLING",
-                subtitle: nil,
-                preferenceKey: kVLCSettingEnableMediaCellTextScrolling)
+                keyPath: \.enableMediaCellTextScrolling)
     }
 
     static var rememberPlayerState: SettingsItem {
         .toggle(title: "SETTINGS_REMEMBER_PLAYER_STATE",
-                subtitle: nil,
-                preferenceKey: kVLCPlayerShouldRememberState)
+                keyPath: \.playerShouldRememberState)
     }
 
     static var restoreLastPlayedMedia: SettingsItem {
         .toggle(title: "SETTINGS_RESTORE_LAST_PLAYED_MEDIA",
-                subtitle: nil,
-                preferenceKey: kVLCRestoreLastPlayedMedia)
+                keyPath: \.restoreLastPlayedMedia)
     }
 
     static func section() -> SettingsSection? {
@@ -273,7 +279,25 @@ enum PrivacyOptions {
     static var passcodeLock: SettingsItem {
         .toggle(title: "SETTINGS_PASSCODE_LOCK",
                 subtitle: "SETTINGS_PASSCODE_LOCK_SUBTITLE",
-                preferenceKey: kVLCSettingPasscodeOnKey)
+                keyPath: \.passcodeOn) { isOn in
+            if isOn {
+                KeychainCoordinator.passcodeService.setSecret { success in
+                    // If the user cancels setting the password, the toggle should revert to the unset state.
+                    // This ensures the UI reflects the correct state.
+                    VLCDefaults.shared.passcodeOn = success
+                    NotificationCenter.default.post(name: .VLCSettingsShouldReloadNotification, object: nil) // To show/hide biometric row
+                }
+            } else {
+                // When disabled any existing passcode should be removed.
+                // If user previously set a passcode and then disable and enable it
+                // the new passcode view will be showed, but if user terminates the app
+                // passcode will remain open even if the user doesn't set the new passcode.
+                // So, this may cause the app being locked.
+                try? KeychainCoordinator.passcodeService.removeSecret()
+
+                NotificationCenter.default.post(name: .VLCSettingsShouldReloadNotification, object: nil)
+            }
+        }
     }
 
     static var enableBiometrics: SettingsItem? {
@@ -283,16 +307,13 @@ enum PrivacyOptions {
             switch authContext.biometryType {
             case .touchID:
                 return .toggle(title: "SETTINGS_PASSCODE_LOCK_ALLOWTOUCHID",
-                               subtitle: nil,
-                               preferenceKey: kVLCSettingPasscodeEnableBiometricAuth)
+                               keyPath: \.passcodeEnableBiometricAuth)
             case .faceID:
                 return .toggle(title: "SETTINGS_PASSCODE_LOCK_ALLOWFACEID",
-                               subtitle: nil,
-                               preferenceKey: kVLCSettingPasscodeEnableBiometricAuth)
+                               keyPath: \.passcodeEnableBiometricAuth)
             case .opticID:
                 return .toggle(title: "SETTINGS_PASSCODE_LOCK_ALLOWOPTICID",
-                               subtitle: nil,
-                               preferenceKey: kVLCSettingPasscodeEnableBiometricAuth)
+                               keyPath: \.passcodeEnableBiometricAuth)
             case .none:
                 fallthrough
             @unknown default:
@@ -303,17 +324,19 @@ enum PrivacyOptions {
         return nil
     }
 
-    static var hideLibraryInFilesApp: SettingsItem {
+    static func hideLibraryInFilesApp(mediaLibraryService: MediaLibraryService) -> SettingsItem {
         .toggle(title: "SETTINGS_HIDE_LIBRARY_IN_FILES_APP",
                 subtitle: "SETTINGS_HIDE_LIBRARY_IN_FILES_APP_SUBTITLE",
-                preferenceKey: kVLCSettingHideLibraryInFilesApp)
+                keyPath: \.hideLibraryInFilesApp) { isOn in
+            mediaLibraryService.hideMediaLibrary(isOn)
+        }
     }
 
-    static func section() -> SettingsSection? {
+    static func section(mediaLibraryService: MediaLibraryService) -> SettingsSection? {
         .init(title: "SETTINGS_PRIVACY_TITLE", items: [
             passcodeLock,
             enableBiometrics,
-            hideLibraryInFilesApp,
+            hideLibraryInFilesApp(mediaLibraryService: mediaLibraryService),
         ].compactMap { $0 })
     }
 }
@@ -323,69 +346,62 @@ enum PrivacyOptions {
 enum GestureControlOptions {
     static var swipeUpDownForVolume: SettingsItem {
         .toggle(title: "SETTINGS_GESTURES_VOLUME",
-                subtitle: nil,
-                preferenceKey: kVLCSettingVolumeGesture)
+                keyPath: \.volumeGesture)
     }
 
     static var twoFingerTap: SettingsItem {
         .toggle(title: "SETTINGS_GESTURES_PLAYPAUSE",
-                subtitle: nil,
-                preferenceKey: kVLCSettingPlayPauseGesture)
+                keyPath: \.playPauseGesture)
     }
 
     static var swipeUpDownForBrightness: SettingsItem {
         .toggle(title: "SETTINGS_GESTURES_BRIGHTNESS",
-                subtitle: nil,
-                preferenceKey: kVLCSettingBrightnessGesture)
+                keyPath: \.brightnessGesture)
     }
 
     static var swipeRightLeftToSeek: SettingsItem {
         .toggle(title: "SETTINGS_GESTURES_SEEK",
-                subtitle: nil,
-                preferenceKey: kVLCSettingSeekGesture)
+                keyPath: \.seekGesture)
     }
 
     static var pinchToClose: SettingsItem {
         .toggle(title: "SETTINGS_GESTURES_CLOSE",
-                subtitle: nil,
-                preferenceKey: kVLCSettingCloseGesture)
+                keyPath: \.closeGesture)
     }
 
     static var forwardBackwardEqual: SettingsItem {
         .toggle(title: "SETTINGS_GESTURES_FORWARD_BACKWARD_EQUAL",
-                subtitle: nil,
-                preferenceKey: kVLCSettingPlaybackForwardBackwardEqual)
+                keyPath: \.playbackForwardBackwardEqual)
     }
 
     static var tapSwipeEqual: SettingsItem {
         .toggle(title: "SETTINGS_GESTURES_TAP_SWIPE_EQUAL",
-                subtitle: nil,
-                preferenceKey: kVLCSettingPlaybackTapSwipeEqual)
+                keyPath: \.playbackTapSwipeEqual)
     }
 
     static var forwardSkipLength: SettingsItem {
-        let k = kVLCSettingPlaybackForwardSkipLength
+        let k = VLCDefaults.Compat.playbackForwardSkipLengthKey
         return .init(title: dynamicForwardSkipDescription(),
                      subtitle: Localizer.getSubtitle(for: k),
                      action: .showActionSheet(title: dynamicForwardSkipDescription(), preferenceKey: k, hasInfo: false))
     }
 
     static var backwardSkipLength: SettingsItem {
-        let k = kVLCSettingPlaybackBackwardSkipLength
+        let k = VLCDefaults.Compat.playbackBackwardSkipLengthKey
         return .init(title: dynamicBackwardSkipDescription(),
                      subtitle: Localizer.getSubtitle(for: k),
                      action: .showActionSheet(title: dynamicBackwardSkipDescription(), preferenceKey: k, hasInfo: false))
     }
 
     static var forwardSkipLengthSwipe: SettingsItem {
-        let k = kVLCSettingPlaybackForwardSkipLengthSwipe
+        let k = VLCDefaults.Compat.playbackForwardSkipLengthSwipeKey
         return .init(title: dynamicForwardSwipeDescription(),
                      subtitle: Localizer.getSubtitle(for: k),
                      action: .showActionSheet(title: dynamicForwardSwipeDescription(), preferenceKey: k, hasInfo: false))
     }
 
     static var backwardSkipLengthSwipe: SettingsItem {
-        let k = kVLCSettingPlaybackBackwardSkipLengthSwipe
+        let k = VLCDefaults.Compat.playbackBackwardSkipLengthSwipeKey
         return .init(title: "SETTINGS_PLAYBACK_SKIP_BACKWARD_SWIPE",
                      subtitle: Localizer.getSubtitle(for: k),
                      action: .showActionSheet(title: "SETTINGS_PLAYBACK_SKIP_BACKWARD_SWIPE", preferenceKey: k, hasInfo: false))
@@ -393,19 +409,18 @@ enum GestureControlOptions {
 
     static var longTouchToSpeedUp: SettingsItem {
         .toggle(title: "SETINGS_LONG_TOUCH_SPEED_UP",
-                subtitle: nil,
-                preferenceKey: kVLCSettingPlaybackLongTouchSpeedUp)
+                keyPath: \.playbackLongTouchSpeedUp)
     }
 
     static var lockScreenSkip: SettingsItem {
-        let k = kVLCSettingPlaybackLockscreenSkip
+        let k = VLCDefaults.Compat.lockscreenSkipKey
         return .init(title: "SETTINGS_PLAYBACK_LOCKSCREEN_SKIP",
                      subtitle: Localizer.getSubtitle(for: k),
                      action: .showActionSheet(title: "SETTINGS_PLAYBACK_LOCKSCREEN_SKIP", preferenceKey: k, hasInfo: false))
     }
 
     static var externalControlsSkip: SettingsItem {
-        let k = kVLCSettingPlaybackRemoteControlSkip
+        let k = VLCDefaults.Compat.remoteControlSkipKey
         return .init(title: "SETTINGS_PLAYBACK_EXTERNAL_CONTROLS_SKIP",
                      subtitle: Localizer.getSubtitle(for: k),
                      action: .showActionSheet(title: "SETTINGS_PLAYBACK_EXTERNAL_CONTROLS_SKIP", preferenceKey: k, hasInfo: false))
@@ -431,8 +446,8 @@ enum GestureControlOptions {
     }
 
     private static func dynamicForwardSkipDescription() -> String {
-        let forwardBackwardEqual = UserDefaults.standard.bool(forKey: kVLCSettingPlaybackForwardBackwardEqual)
-        let tapSwipeEqual = UserDefaults.standard.bool(forKey: kVLCSettingPlaybackTapSwipeEqual)
+        let forwardBackwardEqual = VLCDefaults.shared.playbackForwardBackwardEqual
+        let tapSwipeEqual = VLCDefaults.shared.playbackTapSwipeEqual
 
         if forwardBackwardEqual && tapSwipeEqual {
             return "SETTINGS_PLAYBACK_SKIP_GENERIC"
@@ -446,7 +461,7 @@ enum GestureControlOptions {
     }
 
     private static func dynamicBackwardSkipDescription() -> String {
-        let tapSwipeEqual = UserDefaults.standard.bool(forKey: kVLCSettingPlaybackTapSwipeEqual)
+        let tapSwipeEqual = VLCDefaults.shared.playbackTapSwipeEqual
 
         if tapSwipeEqual {
             return "SETTINGS_PLAYBACK_SKIP_BACKWARD"
@@ -456,7 +471,7 @@ enum GestureControlOptions {
     }
 
     private static func dynamicForwardSwipeDescription() -> String {
-        let forwardBackwardEqual = UserDefaults.standard.bool(forKey: kVLCSettingPlaybackForwardBackwardEqual)
+        let forwardBackwardEqual = VLCDefaults.shared.playbackForwardBackwardEqual
 
         if forwardBackwardEqual {
             return "SETTINGS_PLAYBACK_SKIP_SWIPE"
@@ -470,21 +485,21 @@ enum GestureControlOptions {
 
 enum VideoOptions {
     static var deBlockingFilter: SettingsItem {
-        let k = kVLCSettingSkipLoopFilter
+        let k = VLCDefaults.Compat.skipLoopFilterKey
         return .init(title: "SETTINGS_SKIP_LOOP_FILTER",
                      subtitle: Localizer.getSubtitle(for: k),
                      action: .showActionSheet(title: "SETTINGS_SKIP_LOOP_FILTER", preferenceKey: k, hasInfo: true))
     }
 
     static var deInterlace: SettingsItem {
-        let k = kVLCSettingDeinterlace
+        let k = VLCDefaults.Compat.deinterlaceKey
         return .init(title: "SETTINGS_DEINTERLACE",
                      subtitle: Localizer.getSubtitle(for: k),
                      action: .showActionSheet(title: "SETTINGS_DEINTERLACE", preferenceKey: k, hasInfo: true))
     }
 
     static var hardwareDecoding: SettingsItem {
-        let k = kVLCSettingHardwareDecoding
+        let k = VLCDefaults.Compat.hardwareDecodingKey
         return .init(title: "SETTINGS_HWDECODING",
                      subtitle: Localizer.getSubtitle(for: k),
                      action: .showActionSheet(title: "SETTINGS_HWDECODING", preferenceKey: k, hasInfo: true))
@@ -492,14 +507,12 @@ enum VideoOptions {
 
     static var rememberPlayerBrightness: SettingsItem {
         .toggle(title: "SETTINGS_REMEMBER_PLAYER_BRIGHTNESS",
-                subtitle: nil,
-                preferenceKey: kVLCPlayerShouldRememberBrightness)
+                keyPath: \.playerShouldRememberBrightness)
     }
 
     static var lockRotation: SettingsItem {
         .toggle(title: "SETTINGS_LOCK_ROTATION",
-                subtitle: nil,
-                preferenceKey: kVLCSettingRotationLock)
+                keyPath: \.rotationLock)
     }
 
     static func section() -> SettingsSection? {
@@ -519,18 +532,18 @@ enum SubtitlesOptions {
     static var disableSubtitles: SettingsItem {
         .toggle(title: "SETTINGS_SUBTITLES_DISABLE",
                 subtitle: "SETTINGS_SUBTITLES_DISABLE_LONG",
-                preferenceKey: kVLCSettingDisableSubtitles)
+                keyPath: \.disableSubtitles)
     }
 
     static var font: SettingsItem {
-        let k = kVLCSettingSubtitlesFont
+        let k = VLCDefaults.Compat.subtitlesFontKey
         return .init(title: "SETTINGS_SUBTITLES_FONT",
                      subtitle: Localizer.getSubtitle(for: k),
                      action: .showActionSheet(title: "SETTINGS_SUBTITLES_FONT", preferenceKey: k, hasInfo: true))
     }
 
     static var relativeFontSize: SettingsItem {
-        let k = kVLCSettingSubtitlesFontSize
+        let k = VLCDefaults.Compat.subtitlesFontSizeKey
         return .init(title: "SETTINGS_SUBTITLES_FONTSIZE",
                      subtitle: Localizer.getSubtitle(for: k),
                      action: .showActionSheet(title: "SETTINGS_SUBTITLES_FONTSIZE", preferenceKey: k, hasInfo: true))
@@ -538,19 +551,18 @@ enum SubtitlesOptions {
 
     static var useBoldFont: SettingsItem {
         .toggle(title: "SETTINGS_SUBTITLES_BOLDFONT",
-                subtitle: nil,
-                preferenceKey: kVLCSettingSubtitlesBoldFont)
+                keyPath: \.subtitlesBoldFont)
     }
 
     static var fontColor: SettingsItem {
-        let k = kVLCSettingSubtitlesFontColor
+        let k = VLCDefaults.Compat.subtitlesFontColorKey
         return .init(title: "SETTINGS_SUBTITLES_FONTCOLOR",
                      subtitle: Localizer.getSubtitle(for: k),
                      action: .showActionSheet(title: "SETTINGS_SUBTITLES_FONTCOLOR", preferenceKey: k, hasInfo: true))
     }
 
     static var textEncoding: SettingsItem {
-        let k = kVLCSettingTextEncoding
+        let k = VLCDefaults.Compat.textEncodingKey
         return .init(title: "SETTINGS_SUBTITLES_TEXT_ENCODING",
                      subtitle: Localizer.getSubtitle(for: k),
                      action: .showActionSheet(title: "SETTINGS_SUBTITLES_TEXT_ENCODING", preferenceKey: k, hasInfo: true))
@@ -574,11 +586,11 @@ enum CastingOptions {
     static var audioPassThrough: SettingsItem {
         .toggle(title: "SETTINGS_PTCASTING",
                 subtitle: "SETTINGS_PTCASTINGLONG",
-                preferenceKey: kVLCSettingCastingAudioPassthrough)
+                keyPath: \.castingAudioPassthrough)
     }
 
     static var conversionQuality: SettingsItem {
-        let k = kVLCSettingCastingConversionQuality
+        let k = VLCDefaults.Compat.castingConversionQualityKey
         return .init(title: "SETTINGS_CASTING_CONVERSION_QUALITY",
                      subtitle: Localizer.getSubtitle(for: k),
                      action: .showActionSheet(title: "SETTINGS_CASTING_CONVERSION_QUALITY", preferenceKey: k, hasInfo: false))
@@ -596,7 +608,7 @@ enum CastingOptions {
 
 enum AudioOptions {
     static var preampLevel: SettingsItem {
-        let k = kVLCSettingDefaultPreampLevel
+        let k = VLCDefaults.Compat.defaultPreampLevelKey
         return .init(title: "SETTINGS_AUDIO_PREAMP_LEVEL",
                      subtitle: Localizer.getSubtitle(for: k),
                      action: .showActionSheet(title: "SETTINGS_AUDIO_PREAMP_LEVEL", preferenceKey: k, hasInfo: false))
@@ -605,13 +617,12 @@ enum AudioOptions {
     static var timeStretchingAudio: SettingsItem {
         .toggle(title: "SETTINGS_TIME_STRETCH_AUDIO",
                 subtitle: "SETTINGS_TIME_STRETCH_AUDIO_LONG",
-                preferenceKey: kVLCSettingStretchAudio)
+                keyPath: \.stretchAudio)
     }
 
     static var audioPlaybackInBackground: SettingsItem {
         .toggle(title: "SETTINGS_BACKGROUND_AUDIO",
-                subtitle: nil,
-                preferenceKey: kVLCSettingContinueAudioInBackgroundKey)
+                keyPath: \.continueAudioInBackgroundKey)
     }
 
     static func section() -> SettingsSection? {
@@ -635,32 +646,31 @@ enum MediaLibraryOptions {
 
     static var optimiseItemNamesForDisplay: SettingsItem {
         .toggle(title: "SETTINGS_DECRAPIFY",
-                subtitle: nil,
-                preferenceKey: kVLCSettingsDecrapifyTitles)
+                keyPath: \.optimizeTitles)
     }
 
     static var disableGrouping: SettingsItem {
         .toggle(title: "SETTINGS_DISABLE_GROUPING",
-                subtitle: nil,
-                preferenceKey: kVLCSettingsDisableGrouping)
+                keyPath: \.disableGrouping) { isOn in
+            NotificationCenter.default.post(name: .VLCDisableGroupingDidChangeNotification, object: nil)
+        }
     }
 
     static var showVideoThumbnails: SettingsItem {
         .toggle(title: "SETTINGS_SHOW_THUMBNAILS",
-                subtitle: nil,
-                preferenceKey: kVLCSettingShowThumbnails)
+                keyPath: \.showThumbnails)
     }
 
     static var showAudioArtworks: SettingsItem {
         .toggle(title: "SETTINGS_SHOW_ARTWORKS",
-                subtitle: nil,
-                preferenceKey: kVLCSettingShowArtworks)
+                keyPath: \.showArtworks)
     }
 
-    static var includeMediaLibInDeviceBackup: SettingsItem {
+    static func includeMediaLibInDeviceBackup(mediaLibraryService: MediaLibraryService) -> SettingsItem {
         .toggle(title: "SETTINGS_BACKUP_MEDIA_LIBRARY",
-                subtitle: nil,
-                preferenceKey: kVLCSettingBackupMediaLibrary)
+                keyPath: \.backupMediaLibrary) { isOn in
+            mediaLibraryService.excludeFromDeviceBackup(isOn)
+        }
     }
 
     static var includeMediaLibInDeviceBackupWhenBackingUp: SettingsItem {
@@ -669,7 +679,7 @@ enum MediaLibraryOptions {
               action: .isLoading)
     }
 
-    static func section(isBackingUp: Bool) -> SettingsSection? {
+    static func section(mediaLibraryService: MediaLibraryService, isBackingUp: Bool) -> SettingsSection? {
         var options = [forceVLCToRescanTheMediaLibrary,
                        optimiseItemNamesForDisplay,
                        disableGrouping,
@@ -679,7 +689,7 @@ enum MediaLibraryOptions {
         if isBackingUp {
             options.append(includeMediaLibInDeviceBackupWhenBackingUp)
         } else {
-            options.append(includeMediaLibInDeviceBackup)
+            options.append(includeMediaLibInDeviceBackup(mediaLibraryService: mediaLibraryService))
         }
 
         return .init(title: "SETTINGS_MEDIA_LIBRARY", items: options)
@@ -690,7 +700,7 @@ enum MediaLibraryOptions {
 
 enum NetworkOptions {
     static var networkCachingLevel: SettingsItem {
-        let k = kVLCSettingNetworkCaching
+        let k = VLCDefaults.Compat.networkCachingKey
         return .init(title: "SETTINGS_NETWORK_CACHING_TITLE",
                      subtitle: Localizer.getSubtitle(for: k),
                      action: .showActionSheet(title: "SETTINGS_NETWORK_CACHING_TITLE", preferenceKey: k, hasInfo: true))
@@ -698,20 +708,18 @@ enum NetworkOptions {
 
     static var ipv6SupportForWiFiSharing: SettingsItem {
         .toggle(title: "SETTINGS_WIFISHARING_IPv6",
-                subtitle: nil,
-                preferenceKey: kVLCSettingWiFiSharingIPv6)
+                keyPath: \.wifiSharingIPv6)
     }
 
     static var forceSMBv1: SettingsItem {
         .toggle(title: "SETTINGS_FORCE_SMBV1",
                 subtitle: "SETTINGS_FORCE_SMBV1_LONG",
-                preferenceKey: kVLCForceSMBV1)
+                keyPath: \.forceSMBV1)
     }
 
     static var rtspctp: SettingsItem {
         .toggle(title: "SETTINGS_RTSP_TCP",
-                subtitle: nil,
-                preferenceKey: kVLCSettingNetworkRTSPTCP)
+                keyPath: \.networkRTSPTCP)
     }
 
     static func section() -> SettingsSection? {
@@ -728,16 +736,15 @@ enum NetworkOptions {
 
 enum Accessibility {
     static var playerControlDuration: SettingsItem {
-        let k = kVLCSettingPlayerControlDuration
+        let k = VLCDefaults.Compat.playerControlDurationKey
         return .init(title: "SETTINGS_PLAYER_CONTROL_DURATION",
                      subtitle: Localizer.getSubtitle(for: k),
-                     action: .showActionSheet(title: "SETTINGS_PLAYER_CONTROL_DURATION", preferenceKey: kVLCSettingPlayerControlDuration, hasInfo: false))
+                     action: .showActionSheet(title: "SETTINGS_PLAYER_CONTROL_DURATION", preferenceKey: k, hasInfo: false))
     }
 
     static var pauseWhenShowingControls: SettingsItem {
         .toggle(title: "SETTINGS_PAUSE_WHEN_SHOWING_CONTROLS",
-                subtitle: nil,
-                preferenceKey: kVLCSettingPauseWhenShowingControls)
+                keyPath: \.pauseWhenShowingControls)
     }
 
     static func section() -> SettingsSection? {
@@ -753,8 +760,7 @@ enum Accessibility {
 enum Lab {
     static var debugLogging: SettingsItem {
         .toggle(title: "SETTINGS_DEBUG_LOG",
-                subtitle: nil,
-                preferenceKey: kVLCSaveDebugLogs)
+                keyPath: \.saveDebugLogs)
     }
 
     static var exportLibrary: SettingsItem {
diff --git a/Sources/Settings/View/SettingsCell.swift b/Sources/Settings/View/SettingsCell.swift
index 0f9a69edc21716f0a46359fadf41f10d97a63945..c244d7c33d7316ea58f8509cdd1bb8f8672a58fb 100644
--- a/Sources/Settings/View/SettingsCell.swift
+++ b/Sources/Settings/View/SettingsCell.swift
@@ -5,6 +5,7 @@
  * Copyright (c) 2020 VideoLAN. All rights reserved.
  *
  * Authors: Swapnanil Dhol <swapnanildhol # gmail.com>
+ *          Craig Reyenga <craig.reyenga # gmail.com>
  *
  * Refer to the COPYING file of the official project for license.
  *****************************************************************************/
@@ -12,11 +13,6 @@
 import UIKit
 
 protocol SettingsCellDelegate: AnyObject {
-    /// Implementations should only perform side effects on
-    /// specific preferences; updating the preference itself
-    /// is handled by the cell.
-    func settingsCellDidChangeSwitchState(cell: SettingsCell, preferenceKey: String, isOn: Bool)
-
     func settingsCellInfoButtonPressed(cell: SettingsCell, preferenceKey: String)
 }
 
@@ -288,7 +284,6 @@ class SettingsCell: UITableViewCell {
         switch settingsItem.action {
         case let .toggle(toggle):
             toggle.set(isOn: sender.isOn)
-            delegate?.settingsCellDidChangeSwitchState(cell: self, preferenceKey: toggle.preferenceKey, isOn: sender.isOn)
 
         default:
             // we should never get here; only toggles have a switch
diff --git a/Sources/Settings/tvOS/VLCSettingsViewController.m b/Sources/Settings/tvOS/VLCSettingsViewController.m
index 74834c75207f9cb3487532b84caa70ecdcf1f33d..09bea9240bec34205ea19890d55ddfbbb29f9149 100644
--- a/Sources/Settings/tvOS/VLCSettingsViewController.m
+++ b/Sources/Settings/tvOS/VLCSettingsViewController.m
@@ -14,6 +14,7 @@
 #import "IASKSettingsReader.h"
 #import "IASKSpecifier.h"
 #import "VLCAboutViewController.h"
+#import "VLC-Swift.h"
 
 #define SettingsReUseIdentifier @"SettingsReUseIdentifier"
 
@@ -45,7 +46,7 @@
     self.tableView.opaque = NO;
     self.tableView.backgroundColor = [UIColor clearColor];
 
-    _debugLoggingOn = [self.userDefaults boolForKey:kVLCSaveDebugLogs];
+    _debugLoggingOn = VLCDefaults.shared.saveDebugLogs;
 }
 
 - (NSString *)title
@@ -58,13 +59,11 @@
     [super viewWillDisappear:animated];
 
     /* if debug logging was disabled in this session of the settings screen, delete all the logs */
-    if (_debugLoggingOn) {
-        if (![self.userDefaults boolForKey:kVLCSaveDebugLogs]) {
-            NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
-            NSString* logFilePath = [searchPaths.firstObject stringByAppendingPathComponent:@"Logs"];
-            NSFileManager *fileManager = [NSFileManager defaultManager];
-            [fileManager removeItemAtPath:logFilePath error:nil];
-        }
+    if (_debugLoggingOn && !VLCDefaults.shared.saveDebugLogs) {
+        NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
+        NSString* logFilePath = [searchPaths.firstObject stringByAppendingPathComponent:@"Logs"];
+        NSFileManager *fileManager = [NSFileManager defaultManager];
+        [fileManager removeItemAtPath:logFilePath error:nil];
     }
 }
 
diff --git a/Sources/UI Elements/ActionSheet/ActionSheetSortSectionHeader.swift b/Sources/UI Elements/ActionSheet/ActionSheetSortSectionHeader.swift
index 0e277e7861afb8db57ee9a9b515b837ce7760e81..1b2bbd8dc3ca11c9109a77071603a1320e209986 100644
--- a/Sources/UI Elements/ActionSheet/ActionSheetSortSectionHeader.swift	
+++ b/Sources/UI Elements/ActionSheet/ActionSheetSortSectionHeader.swift	
@@ -30,7 +30,6 @@ class ActionSheetSortSectionHeader: ActionSheetSectionHeader {
 
     private var sortModel: SortModel
     private var secondSortModel: SortModel?
-    private let userDefaults = UserDefaults.standard
     private var isAdditionalOptionShown: Bool = false
 
     private let descendingStackView: UIStackView = {
@@ -251,8 +250,12 @@ class ActionSheetSortSectionHeader: ActionSheetSectionHeader {
     }
 
     private func setSwitchIsOnFromUserDefaults() {
-        let key = isVideoModel ? kVLCVideoLibraryGridLayout : kVLCAudioLibraryGridLayout
-        layoutChangeSwitch.isOn = UserDefaults.standard.bool(forKey: key + modelType)
+        switch isVideoModel {
+        case true:
+            layoutChangeSwitch.isOn = VLCDefaults.shared.videoLibraryGridLayout(name: modelType)
+        case false:
+            layoutChangeSwitch.isOn = VLCDefaults.shared.audioLibraryGridLayout(name: modelType)
+        }
     }
 
     private func setupStackView() {
@@ -296,7 +299,7 @@ class ActionSheetSortSectionHeader: ActionSheetSectionHeader {
 
         secondaryStackView.addArrangedSubview(hideFeatArtistsStackView)
 
-        hideFeatArtistsSwitch.isOn = UserDefaults.standard.bool(forKey: kVLCAudioLibraryHideFeatArtists)
+        hideFeatArtistsSwitch.isOn = VLCDefaults.shared.audioLibraryHideFeatArtists
     }
 
     func updateHeaderForAlbums() {
@@ -306,6 +309,6 @@ class ActionSheetSortSectionHeader: ActionSheetSectionHeader {
 
         secondaryStackView.addArrangedSubview(hideTrackNumbersStackView)
 
-        hideTrackNumbersSwitch.isOn = UserDefaults.standard.bool(forKey: kVLCAudioLibraryHideTrackNumbers)
+        hideTrackNumbersSwitch.isOn = VLCDefaults.shared.audioLibraryHideTrackNumbers
     }
 }
diff --git a/Sources/UI Elements/PresentationTheme.swift b/Sources/UI Elements/PresentationTheme.swift
index 10585824d615117d0918763f826bdb617a6c728d..bbf2e92f1f97f43c7b27232b749471564118c1f0 100644
--- a/Sources/UI Elements/PresentationTheme.swift	
+++ b/Sources/UI Elements/PresentationTheme.swift	
@@ -104,6 +104,17 @@ enum PresentationThemeType: Int {
     case bright = 0
     case dark
     case auto
+
+    static func from(appTheme: VLCDefaults.AppTheme) -> PresentationThemeType {
+        switch appTheme {
+        case .dark, .black:
+            return .dark
+        case .bright:
+            return .bright
+        case .system:
+            return .auto
+        }
+    }
 }
 
 @objcMembers class PresentationTheme: NSObject {
@@ -117,12 +128,11 @@ enum PresentationThemeType: Int {
     }
 
     var isBlack: Bool {
-        return UserDefaults.standard.bool(forKey: kVLCSettingAppThemeBlack)
+        VLCDefaults.shared.appThemeBlack
     }
 
     static var current: PresentationTheme = {
-        let themeSettings = UserDefaults.standard.integer(forKey: kVLCSettingAppTheme)
-        return PresentationTheme.respectiveTheme(for: PresentationThemeType(rawValue: themeSettings))
+        return PresentationTheme.respectiveTheme(for: PresentationThemeType.from(appTheme: VLCDefaults.shared.appTheme))
     }() {
         didSet {
             AppearanceManager.setupAppearance(theme: self.current)
@@ -144,9 +154,7 @@ enum PresentationThemeType: Int {
     }
 
     @objc static func themeDidUpdate() {
-        let themeSettings = UserDefaults.standard.integer(forKey: kVLCSettingAppTheme)
-        PresentationTheme.current = PresentationTheme.respectiveTheme(for:
-            PresentationThemeType(rawValue: themeSettings))
+        PresentationTheme.current = PresentationTheme.respectiveTheme(for: PresentationThemeType.from(appTheme: VLCDefaults.shared.appTheme))
     }
 
     static func respectiveTheme(for theme: PresentationThemeType?, excludingBlackTheme: Bool = false) -> PresentationTheme {
@@ -157,7 +165,7 @@ enum PresentationThemeType: Int {
         var presentationTheme = PresentationTheme.brightTheme
         var darkTheme: PresentationTheme
 
-        if UserDefaults.standard.bool(forKey: kVLCSettingAppThemeBlack) {
+        if VLCDefaults.shared.appThemeBlack {
             darkTheme = PresentationTheme.blackTheme
         } else {
             darkTheme = PresentationTheme.darkTheme
diff --git a/Sources/WiFi Sharing/VLCHTTPUploaderController.m b/Sources/WiFi Sharing/VLCHTTPUploaderController.m
index 866c7f3fce2039ed0df0a253bf5a9361dbd7a47f..1a168c4cf2f6d9cb3ec88284514f17d5ef26c56e 100644
--- a/Sources/WiFi Sharing/VLCHTTPUploaderController.m	
+++ b/Sources/WiFi Sharing/VLCHTTPUploaderController.m	
@@ -27,8 +27,9 @@
 
 #import "NSString+SupportedMedia.h"
 
-#if TARGET_OS_IOS || TARGET_OS_VISION
 #import "VLC-Swift.h"
+
+#if TARGET_OS_IOS || TARGET_OS_VISION
 #import "VLCMediaFileDiscoverer.h"
 #endif
 
@@ -256,7 +257,7 @@ NSString *VLCHTTPUploaderBackgroundTaskName = @"VLCHTTPUploaderBackgroundTaskNam
     [_httpServer setInterface:_nameOfUsedNetworkInterface];
 
     [_httpServer setIPv4Enabled:YES];
-    [_httpServer setIPv6Enabled:[[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingWiFiSharingIPv6] boolValue]];
+    [_httpServer setIPv6Enabled:VLCDefaults.shared.wifiSharingIPv6];
 
     // Tell the server to broadcast its presence via Bonjour.
     // This allows browsers such as Safari to automatically discover our service.
diff --git a/VLC.xcodeproj/project.pbxproj b/VLC.xcodeproj/project.pbxproj
index 4f743280f4f004331d3647178a63a614cc99cd41..ce107c77084308237bbea231ef15142826b59662 100644
--- a/VLC.xcodeproj/project.pbxproj
+++ b/VLC.xcodeproj/project.pbxproj
@@ -95,6 +95,9 @@
 		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 */; };
+		446AA2322D6A91620026F3C8 /* VLCDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 446AA2312D6A91520026F3C8 /* VLCDefaults.swift */; };
+		446AA2332D6A91620026F3C8 /* VLCDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 446AA2312D6A91520026F3C8 /* VLCDefaults.swift */; };
+		446AA2342D6C08C50026F3C8 /* VLCDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 446AA2312D6A91520026F3C8 /* VLCDefaults.swift */; };
 		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 */; };
@@ -1012,6 +1015,7 @@
 		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>"; };
+		446AA2312D6A91520026F3C8 /* VLCDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VLCDefaults.swift; 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>"; };
@@ -1898,6 +1902,7 @@
 		44C8BBA124AF2B5C003F8940 /* Helpers */ = {
 			isa = PBXGroup;
 			children = (
+				446AA2312D6A91520026F3C8 /* VLCDefaults.swift */,
 				419A2C651F37A4B70069D224 /* VLCStringsForLocalization.m */,
 				4152F1611FEF19BD00F1908B /* KeychainCoordinator.swift */,
 				7D0C209A28C89F5400CCFFEF /* Network */,
@@ -4240,6 +4245,7 @@
 				6C5B0C9E27A43098005AE25B /* PlaybackServiceAdjustFilter.swift in Sources */,
 				7DC869B828CB794A00EE99F8 /* VLCDeletionCapableViewController.m in Sources */,
 				DD4089F21BF6467E0022745E /* VLCPlaybackInfoTVCollectionViewCell.m in Sources */,
+				446AA2342D6C08C50026F3C8 /* VLCDefaults.swift in Sources */,
 				4342C3C327474CA000E52334 /* SortedMediaFiles.swift in Sources */,
 				DD3EAC0A1BE2192A003668DA /* VLCServerBrowsingController.m in Sources */,
 				D9B36AF62AF4020000A10C99 /* AspectRatio.swift in Sources */,
@@ -4462,6 +4468,7 @@
 				7D50C6A42BBD20DF00B9F1A0 /* VLCDownloadController.m in Sources */,
 				7D50C6A52BBD20DF00B9F1A0 /* RemoteNetworkCell.swift in Sources */,
 				7D50C6A62BBD20DF00B9F1A0 /* VLCNetworkLoginDataSourceLogin.m in Sources */,
+				446AA2332D6A91620026F3C8 /* VLCDefaults.swift in Sources */,
 				7D50C6A72BBD20DF00B9F1A0 /* VLCAccessibilityIdentifier.swift in Sources */,
 				7D50C6A82BBD20DF00B9F1A0 /* VLCPagingViewController.swift in Sources */,
 				7D50C6A92BBD20DF00B9F1A0 /* VLCLocalNetworkServiceBrowserBonjour.m in Sources */,
@@ -4563,6 +4570,7 @@
 				7DBF605C2B5D652900F16BB4 /* VLCFirstStepsDonateViewController.m in Sources */,
 				7D1471752B88A7BA00AB642B /* VLCCharge.m in Sources */,
 				DD3EFF451BDEBCE500B68579 /* VLCLocalNetworkServiceBrowserManualConnect.m in Sources */,
+				446AA2322D6A91620026F3C8 /* VLCDefaults.swift in Sources */,
 				8DF966B121188BDB00D0FCD6 /* EditController.swift in Sources */,
 				8D144D6322298E8E00984C46 /* AudioMiniPlayer.swift in Sources */,
 				7DC7BAB528C8958900109F28 /* UIColor+Presets.m in Sources */,