Substantially rewrite UserDefaults to:
 - Add persistent domains
 - Guarantee that all values returned are value types.
diff --git a/Foundation/UserDefaults.swift b/Foundation/UserDefaults.swift
index dfcd89c..73379da 100644
--- a/Foundation/UserDefaults.swift
+++ b/Foundation/UserDefaults.swift
@@ -9,139 +9,78 @@
 
 import CoreFoundation
 
-private var registeredDefaults = [String: NSObject]()
+private var registeredDefaults = [String: Any]()
 private var sharedDefaults = UserDefaults()
 
-internal func plistValueAsNSObject(_ value: Any) -> NSObject? {
-    let nsValue: NSObject
-    
-    // Converts a value to the internal representation. Internalized values are
-    // stored as NSObject derived objects in the registration dictionary.
-    if let val = value as? String {
-        nsValue = val._nsObject
-    } else if let val = value as? URL {
-        nsValue = val.path._nsObject
-    } else if let val = value as? Int {
-        nsValue = NSNumber(value: val)
-    } else if let val = value as? Double {
-        nsValue = NSNumber(value: val)
-    } else if let val = value as? Bool {
-        nsValue = NSNumber(value: val)
-    } else if let val = value as? Data {
-        nsValue = val._nsObject
-    } else if let val = value as? Date {
-        nsValue = val._nsObject
-    } else if let val = value as? [Any] {
-        var nsValues: [NSObject] = []
-        for innerValue in val {
-            guard let nsInnerValue = plistValueAsNSObject(innerValue) else { return nil }
-            nsValues.append(nsInnerValue)
-        }
-        return NSArray(array: nsValues)
-    } else if let val = value as? [String: Any] {
-        var nsValues: [String: NSObject] = [:]
-        for (key, innerValue) in val {
-            guard let nsInnerValue = plistValueAsNSObject(innerValue) else { return nil }
-            nsValues[key] = nsInnerValue
-        }
-        return NSDictionary(dictionary: nsValues)
-    } else if let val = value as? NSObject {
-        nsValue = val
-    } else {
-        return nil
-    }
-    
-    return nsValue
-}
-
-internal func plistNSObjectAsValue(_ nsValue: NSObject) -> Any {
-    let value: Any
-    
-    // Converts a value to the internal representation. Internalized values are
-    // stored as NSObject derived objects in the registration dictionary.
-    if let val = nsValue as? NSString {
-        value = val._swiftObject
-    } else if let val = nsValue as? NSNumber {
-        value = val._swiftValueOfOptimalType
-    } else if let val = nsValue as? NSData {
-        value = val._swiftObject
-    } else if let val = nsValue as? NSArray {
-        value = val._swiftObject.map { plistNSObjectAsValue($0 as! NSObject) }
-    } else if let val = nsValue as? NSDictionary {
-        var values: [String: Any] = [:]
-        for (currentKey, currentInnerValue) in val {
-            let key: String
-            
-            if let swiftKey = currentKey as? String {
-                key = swiftKey
-            } else if let nsKey = currentKey as? NSString {
-                key = nsKey._swiftObject
-            } else {
-                continue
-            }
-            
-            if let nsInnerValue = currentInnerValue as? NSObject {
-                values[key] = plistNSObjectAsValue(nsInnerValue)
-            } else {
-                values[key] = currentInnerValue
-            }
-        }
-        value = values
-    } else if let val = nsValue as? NSDate {
-        value = val._swiftObject
-    } else {
-        value = nsValue
-    }
-    
-    return value
-}
-
-private extension Dictionary {
-    func convertingValuesToNSObjects() -> [Key: NSObject]? {
-        var result: [Key: NSObject] = [:]
-        
-        for (key, value) in self {
-            if let nsValue = plistValueAsNSObject(value) {
-                result[key] = nsValue
-            } else {
-                return nil
-            }
-        }
-        
-        return result
-    }
-}
-
-private extension Dictionary where Value == NSObject {
-    mutating func merge(convertingValuesToNSObject source: [Key: Any], uniquingKeysWith block: (NSObject, NSObject) throws -> Value) rethrows -> Bool {
-        if let converted = source.convertingValuesToNSObjects() {
-            try self.merge(converted, uniquingKeysWith: block)
-            return true
-        } else {
-            return false
-        }
-    }
-    
-    func convertingValuesFromPlistNSObject() -> [Key: Any] {
-        var result: [Key: Any] = [:]
-        for (key, value) in self {
-            result[key] = plistNSObjectAsValue(value)
-        }
-        return result
-    }
-}
-
 open class UserDefaults: NSObject {
+    static private func _isValueAllowed(_ nonbridgedValue: Any) -> Bool {
+        let value = _SwiftValue.fetch(nonOptional: nonbridgedValue as AnyObject)
+        
+        if let value = value as? [Any] {
+            for innerValue in value {
+                if !_isValueAllowed(innerValue) {
+                    return false
+                }
+            }
+            
+            return true
+        }
+        
+        if let value = value as? [AnyHashable: Any] {
+            for (key, innerValue) in value {
+                if !(key is String) {
+                    return false
+                }
+                
+                if !_isValueAllowed(innerValue) {
+                    return false
+                }
+            }
+            
+            return true
+        }
+        
+        let isOfCommonTypes =  value is String || value is Data || value is Date || value is Int || value is Bool || value is CGFloat
+        if isOfCommonTypes {
+            return true
+        }
+        
+        // NSNumber doesn't quite bridge -- treat it specially.
+        if value is NSNumber {
+            return true
+        }
+        
+        let isOfUncommonNumericTypes = value is Double || value is Float || value is Float || value is Int8 || value is UInt8 || value is Int16 || value is UInt16 || value is Int32 || value is UInt32 || value is Int64 || value is UInt64
+        return isOfUncommonNumericTypes
+    }
+    
+    static private func _unboxingNSNumbers(_ value: Any?) -> Any? {
+        if value == nil {
+            return nil
+        }
+        
+        if let number = value as? NSNumber {
+            return number._swiftValueOfOptimalType
+        }
+        
+        if let value = value as? [Any] {
+            return value.map(_unboxingNSNumbers)
+        }
+        
+        if let value = value as? [AnyHashable: Any] {
+            return value.mapValues(_unboxingNSNumbers)
+        }
+        
+        return value
+    }
+    
     private let suite: String?
     
     open class var standard: UserDefaults {
         return sharedDefaults
     }
     
-    open class func resetStandardUserDefaults() {
-        //sharedDefaults.synchronize()
-        //sharedDefaults = UserDefaults()
-    }
+    open class func resetStandardUserDefaults() {}
     
     public convenience override init() {
         self.init(suiteName: nil)!
@@ -162,35 +101,17 @@
         }
         
         func getFromRegistered() -> Any? {
-            return registeredDefaults[defaultName]
+            return UserDefaults._unboxingNSNumbers(registeredDefaults[defaultName])
         }
         
         guard let anObj = CFPreferencesCopyAppValue(defaultName._cfObject, suite?._cfObject ?? kCFPreferencesCurrentApplication) else {
             return getFromRegistered()
         }
         
-        //Force the returned value to an NSObject
-        switch CFGetTypeID(anObj) {
-        case CFStringGetTypeID():
-            return unsafeBitCast(anObj, to: NSString.self)
-            
-        case CFNumberGetTypeID():
-            return unsafeBitCast(anObj, to: NSNumber.self)
-            
-        case CFURLGetTypeID():
-            return unsafeBitCast(anObj, to: NSURL.self)
-            
-        case CFArrayGetTypeID():
-            return unsafeBitCast(anObj, to: NSArray.self)
-            
-        case CFDictionaryGetTypeID():
-            return unsafeBitCast(anObj, to: NSDictionary.self)
-            
-        case CFDataGetTypeID():
-            return unsafeBitCast(anObj, to: NSData.self)
-            
-        default:
-            return getFromRegistered()
+        if let fetched = _SwiftValue.fetch(anObj) {
+            return UserDefaults._unboxingNSNumbers(fetched)
+        } else {
+            return nil
         }
     }
 
@@ -200,147 +121,103 @@
             return
         }
         
-        let cfType: CFTypeRef
-		
-		// Convert the input value to the internal representation. All values are
-        // represented as CFTypeRef objects internally because we store the defaults
-        // in a CFPreferences type.
-        if let bType = value as? NSNumber {
-            cfType = bType._cfObject
-        } else if let bType = value as? NSString {
-            cfType = bType._cfObject
-        } else if let bType = value as? NSArray {
-            cfType = bType._cfObject
-        } else if let bType = value as? NSDictionary {
-            cfType = bType._cfObject
-        } else if let bType = value as? NSData {
-            cfType = bType._cfObject
-        } else if let bType = value as? NSURL {
-            set(URL(reference: bType), forKey: defaultName)
+        if let url = value as? URL {
+            set(url.absoluteURL.path, forKey: defaultName)
             return
-        } else if let bType = value as? String {
-            cfType = bType._cfObject
-        } else if let bType = value as? URL {
-			set(bType, forKey: defaultName)
-			return
-        } else if let bType = value as? Int {
-            var cfValue = Int64(bType)
-            cfType = CFNumberCreate(nil, kCFNumberSInt64Type, &cfValue)
-        } else if let bType = value as? Double {
-            var cfValue = bType
-            cfType = CFNumberCreate(nil, kCFNumberDoubleType, &cfValue)
-        } else if let bType = value as? Data {
-            cfType = bType._cfObject
-        } else {
-            fatalError("The type of 'value' passed to UserDefaults.set(forKey:) is not supported.")
         }
         
-        CFPreferencesSetAppValue(defaultName._cfObject, cfType, suite?._cfObject ?? kCFPreferencesCurrentApplication)
+        if let url = value as? NSURL, let path = url.absoluteURL?.path {
+            set(path, forKey: defaultName)
+            return
+        }
+        
+        guard UserDefaults._isValueAllowed(value) else {
+            fatalError("This value is not supported by set(_:forKey:)")
+        }
+        
+        CFPreferencesSetAppValue(defaultName._cfObject, _SwiftValue.store(value), suite?._cfObject ?? kCFPreferencesCurrentApplication)
     }
     open func removeObject(forKey defaultName: String) {
         CFPreferencesSetAppValue(defaultName._cfObject, nil, suite?._cfObject ?? kCFPreferencesCurrentApplication)
     }
+    
     open func string(forKey defaultName: String) -> String? {
-        guard let aVal = object(forKey: defaultName),
-              let bVal = aVal as? NSString else {
-            return nil
-        }
-        return bVal._swiftObject
+        return object(forKey: defaultName) as? String
     }
+    
     open func array(forKey defaultName: String) -> [Any]? {
-        guard let aVal = object(forKey: defaultName),
-              let bVal = aVal as? NSArray else {
-            return nil
-        }
-        return bVal._swiftObject
+        return object(forKey: defaultName) as? [Any]
     }
+    
     open func dictionary(forKey defaultName: String) -> [String : Any]? {
-        guard let aVal = object(forKey: defaultName),
-              let bVal = aVal as? NSDictionary else {
-            return nil
-        }
-        //This got out of hand fast...
-        let cVal = bVal._swiftObject
-        enum convErr: Swift.Error {
-            case convErr
-        }
-        do {
-            let dVal = try cVal.map({ (key, val) -> (String, Any) in
-                if let strKey = key as? NSString {
-                    return (strKey._swiftObject, val)
-                } else {
-                    throw convErr.convErr
-                }
-            })
-            var eVal = [String : Any]()
-            
-            for (key, value) in dVal {
-                eVal[key] = value
-            }
-            
-            return eVal
-        } catch _ { }
-        return nil
+        return object(forKey: defaultName) as? [String: Any]
     }
+    
     open func data(forKey defaultName: String) -> Data? {
-        guard let aVal = object(forKey: defaultName),
-              let bVal = aVal as? NSData else {
-            return nil
-        }
-        return Data(referencing: bVal)
+        return object(forKey: defaultName) as? Data
     }
+    
     open func stringArray(forKey defaultName: String) -> [String]? {
-        guard let aVal = object(forKey: defaultName),
-              let bVal = aVal as? NSArray else {
-            return nil
-        }
-        return _SwiftValue.fetch(nonOptional: bVal) as? [String]
+        return object(forKey: defaultName) as? [String]
     }
+    
     open func integer(forKey defaultName: String) -> Int {
         guard let aVal = object(forKey: defaultName) else {
             return 0
         }
-        if let bVal = aVal as? NSNumber {
-            return bVal.intValue
+        if let bVal = aVal as? Int {
+            return bVal
         }
-        if let bVal = aVal as? NSString {
-            return bVal.integerValue
+        if let bVal = aVal as? String {
+            return NSString(string: bVal).integerValue
         }
         return 0
     }
+    
     open func float(forKey defaultName: String) -> Float {
         guard let aVal = object(forKey: defaultName) else {
             return 0
         }
-        if let bVal = aVal as? NSNumber {
-            return bVal.floatValue
+        if let bVal = aVal as? Float {
+            return bVal
         }
-        if let bVal = aVal as? NSString {
-            return bVal.floatValue
+        if let bVal = aVal as? String {
+            return NSString(string: bVal).floatValue
         }
         return 0
     }
+    
     open func double(forKey defaultName: String) -> Double {
         guard let aVal = object(forKey: defaultName) else {
             return 0
         }
-        if let bVal = aVal as? NSNumber {
-            return bVal.doubleValue
+        if let bVal = aVal as? Double {
+            return bVal
         }
-        if let bVal = aVal as? NSString {
-            return bVal.doubleValue
+        if let bVal = aVal as? String {
+            return NSString(string: bVal).doubleValue
         }
         return 0
     }
+    
     open func bool(forKey defaultName: String) -> Bool {
         guard let aVal = object(forKey: defaultName) else {
             return false
         }
-        if let bVal = aVal as? NSNumber {
-            return bVal.boolValue
+        if let bVal = aVal as? Bool {
+            return bVal
         }
-        if let bVal = aVal as? NSString {
-            return bVal.boolValue
+        if let bVal = aVal as? Int {
+            return bVal != 0
+        }
+        if let bVal = aVal as? Float {
+            return bVal != 0
+        }
+        if let bVal = aVal as? Double {
+            return bVal != 0
+        }
+        if let bVal = aVal as? String {
+            return NSString(string: bVal).boolValue
         }
         return false
     }
@@ -349,11 +226,10 @@
             return nil
         }
         
-        if let bVal = aVal as? NSURL {
-            return URL(reference: bVal)
-        } else if let bVal = aVal as? NSString {
-            let cVal = bVal.expandingTildeInPath
-            
+        if let bVal = aVal as? URL {
+            return bVal
+        } else if let bVal = aVal as? String {
+            let cVal = NSString(string: bVal).expandingTildeInPath
             return URL(fileURLWithPath: cVal)
         } else if let bVal = aVal as? Data {
             return NSKeyedUnarchiver.unarchiveObject(with: bVal) as? URL
@@ -392,9 +268,7 @@
     }
     
     open func register(defaults registrationDictionary: [String : Any]) {
-        if !registeredDefaults.merge(convertingValuesToNSObject: registrationDictionary, uniquingKeysWith: { $1 }) {
-            fatalError("The type of 'value' passed to UserDefaults.register(defaults:) is not supported.")
-        }
+        registeredDefaults.merge(registrationDictionary.mapValues { _SwiftValue.fetch(nonOptional: $0 as AnyObject) }, uniquingKeysWith: { $1 })
     }
 
     open func addSuite(named suiteName: String) {
@@ -411,24 +285,28 @@
     private func _dictionaryRepresentation(searchingOutsideOfSuite: Bool) -> [String: Any] {
         let registeredDefaultsIfAllowed = searchingOutsideOfSuite ? registeredDefaults : [:]
         
-        guard let aPref = CFPreferencesCopyMultiple(nil, kCFPreferencesCurrentApplication, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost),
-            let bPref = (aPref._swiftObject) as? [NSString: Any] else {
-                return registeredDefaultsIfAllowed
+        guard let defaultsFromDiskCF = CFPreferencesCopyMultiple(nil, suite?._cfObject ?? kCFPreferencesCurrentApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost) else {
+            return registeredDefaultsIfAllowed
         }
-        var allDefaults = registeredDefaultsIfAllowed
         
-        for (key, value) in bPref {
-            if let value = plistValueAsNSObject(value) {
-                allDefaults[key._swiftObject] = value
+        let defaultsFromDiskWithNumbersBoxed = _SwiftValue.fetch(defaultsFromDiskCF) as? [String: AnyObject] ?? [:]
+        
+        if registeredDefaultsIfAllowed.count == 0 {
+            return UserDefaults._unboxingNSNumbers(defaultsFromDiskWithNumbersBoxed) as! [String: AnyObject]
+        } else {
+            var allDefaults = registeredDefaultsIfAllowed
+            
+            for (key, value) in defaultsFromDiskWithNumbersBoxed {
+                allDefaults[key] = value
             }
+            
+            return UserDefaults._unboxingNSNumbers(allDefaults) as! [String: Any]
         }
-        
-        return allDefaults
     }
     
-    private static let _parsedArgumentsDomain: [String: NSObject] = UserDefaults._parseArguments(ProcessInfo.processInfo.arguments).convertingValuesToNSObjects() ?? [:]
+    private static let _parsedArgumentsDomain: [String: Any] = UserDefaults._parseArguments(ProcessInfo.processInfo.arguments)
     
-    private var _volatileDomains: [String: [String: NSObject]] = [:]
+    private var _volatileDomains: [String: [String: Any]] = [:]
     private let _volatileDomainsLock = NSLock()
     
     open var volatileDomainNames: [String] {
@@ -444,16 +322,18 @@
         let domain = _volatileDomains[domainName]
         _volatileDomainsLock.unlock()
         
-        return domain?.convertingValuesFromPlistNSObject() ?? [:]
+        return domain ?? [:]
     }
     
     open func setVolatileDomain(_ domain: [String : Any], forName domainName: String) {
-        _volatileDomainsLock.lock()
-        var convertedDomain: [String: NSObject] = _volatileDomains[domainName] ?? [:]
-        if !convertedDomain.merge(convertingValuesToNSObject: domain, uniquingKeysWith: { $1 }) {
-            fatalError("The type of 'value' passed to UserDefaults.setVolatileDomain(_:forName:) is not supported.")
+        if !UserDefaults._isValueAllowed(domain) {
+            fatalError("The content of 'domain' passed to UserDefaults.setVolatileDomain(_:forName:) is not supported.")
         }
-        _volatileDomains[domainName] = convertedDomain
+        
+        _volatileDomainsLock.lock()
+        var storedDomain: [String: Any] = _volatileDomains[domainName] ?? [:]
+        storedDomain.merge(domain, uniquingKeysWith: { $1 })
+        _volatileDomains[domainName] = storedDomain
         _volatileDomainsLock.unlock()
     }
     
@@ -469,30 +349,44 @@
     
     open func setPersistentDomain(_ domain: [String : Any], forName domainName: String) {
         if let defaults = UserDefaults(suiteName: domainName) {
+            var removedAny = false
             for key in defaults._dictionaryRepresentation(searchingOutsideOfSuite: false).keys {
                 defaults.removeObject(forKey: key)
+                removedAny = true
             }
             
+            var addedAny = false
             for (key, value) in domain {
                 defaults.set(value, forKey: key)
+                addedAny = true
+            }
+            
+            _ = defaults.synchronize()
+            
+            if removedAny || addedAny {
+                NotificationCenter.default.post(name: UserDefaults.didChangeNotification, object: self)
             }
         }
-        
-        NotificationCenter.default.post(name: UserDefaults.didChangeNotification, object: self)
     }
     
     open func removePersistentDomain(forName domainName: String) {
         if let defaults = UserDefaults(suiteName: domainName) {
+            var removedAny = false
             for key in defaults._dictionaryRepresentation(searchingOutsideOfSuite: false).keys {
                 defaults.removeObject(forKey: key)
+                removedAny = true
             }
             
-            NotificationCenter.default.post(name: UserDefaults.didChangeNotification, object: self)
+            _ = defaults.synchronize()
+            
+            if removedAny {
+                NotificationCenter.default.post(name: UserDefaults.didChangeNotification, object: self)
+            }
         }
     }
     
     open func synchronize() -> Bool {
-        return CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication)
+        return CFPreferencesAppSynchronize(suite?._cfObject ?? kCFPreferencesCurrentApplication)
     }
     
     open func objectIsForced(forKey key: String) -> Bool {
diff --git a/TestFoundation/TestUserDefaults.swift b/TestFoundation/TestUserDefaults.swift
index 4ad257f..dc3f657 100644
--- a/TestFoundation/TestUserDefaults.swift
+++ b/TestFoundation/TestUserDefaults.swift
@@ -293,7 +293,7 @@
 	
 	func test_persistentDomain() {
 		let int = (key: "An Integer", value: 1234)
-		let double = (key: "A Double", value: 5678.0)
+		let double = (key: "A Double", value: 5678.1234)
 		let string = (key: "A String", value: "Some string")
 		let array = (key: "An Array", value: [ 1, 2, 3, 4, "Surprise" ] as [AnyHashable])
 		let dictionary = (key: "A Dictionary", value: [ "Swift": "Imperative", "Haskell": "Functional", "LISP": "LISP", "Today": Date() ] as [String: AnyHashable])
@@ -303,30 +303,37 @@
 		let defaults1 = UserDefaults(suiteName: nil)!
 		
 		defaults1.removePersistentDomain(forName: domainName)
-		XCTAssertNil(defaults1.persistentDomain(forName: domainName))
+		if let domain = defaults1.persistentDomain(forName: domainName) {
+			XCTAssertEqual(domain.count, 0)
+		} // else it's nil, which is also OK.
 		
-		defaults1.setPersistentDomain([int.key: int.value,
-									  double.key: double.value,
-									  string.key: string.value,
-									  array.key: array.value,
-									  dictionary.key: dictionary.value],
-									 forName: domainName)
+		let defaultsIn: [String : Any] =
+			[ int.key: int.value,
+			  double.key: double.value,
+			  string.key: string.value,
+			  array.key: array.value,
+			  dictionary.key: dictionary.value ]
+		
+		defaults1.setPersistentDomain(defaultsIn, forName: domainName)
 		
 		let defaults2 = UserDefaults(suiteName: nil)!
 		let returned = defaults2.persistentDomain(forName: domainName)
 		XCTAssertNotNil(returned)
 		
 		if let returned = returned {
-			XCTAssertEqual(returned[int.key] as! Int, int.value)
-			XCTAssertEqual(returned[double.key] as! Double, double.value)
-			XCTAssertEqual(returned[string.key] as! String, string.value)
-			XCTAssertEqual(returned[array.key] as! [AnyHashable], array.value)
-			XCTAssertEqual(returned[dictionary.key] as! [String: AnyHashable], dictionary.value)
+			XCTAssertEqual(returned.count, defaultsIn.count)
+			XCTAssertEqual(returned[int.key] as? Int, int.value)
+			XCTAssertEqual(returned[double.key] as? Double, double.value)
+			XCTAssertEqual(returned[string.key] as? String, string.value)
+			XCTAssertEqual(returned[array.key] as? [AnyHashable], array.value)
+			XCTAssertEqual(returned[dictionary.key] as? [String: AnyHashable], dictionary.value)
 		}
 		
-		defaults2.removeSuite(named: domainName)
+		defaults2.removePersistentDomain(forName: domainName)
 		
 		let defaults3 = UserDefaults(suiteName: nil)!
-		XCTAssertNil(defaults3.persistentDomain(forName: domainName))
+		if let domain = defaults3.persistentDomain(forName: domainName) {
+			XCTAssertEqual(domain.count, 0)
+		} // else it's nil, which is also OK.
 	}
 }