This code leads to crash somewhere later with EXC_BAD_ACCESS
SomeLoader().selectedIndex = 1
class SomeLoader {
// MARK: - Public
var selectedIndex: Int? {
get {
return dataStorage.selectedIndex
}
set {
dataStorage.selectedIndex = newValue
}
}
}
this code is not crashed:
SomeLoader().selectedIndex = 1
class SomeLoader {
// MARK: - Public
var selectedIndex: Int? {
get {
return dataStorage.selectedIndex
}
set {
dataStorage.updateSelected(index: newValue)
}
}
}
where:
struct DataStorage<T: Hashable> {
enum Keys: String {
case selectedIndex
}
private func get<U>(forKey key: String) -> U? {
guard let objectData = getData(forKey: key) else {
return nil
}
let object: U? = get(forData: objectData)
return object
}
private func get<U>(forData objectData: Data) -> U? {
return NSKeyedUnarchiver.unarchiveObject(with: objectData) as? U
}
private func save<U>(forKey key: String, object: U) {
let encodedData = NSKeyedArchiver.archivedData(withRootObject: object)
UserDefaults.standard.set(encodedData, forKey: key)
}
}
extension DataStorage {
func updateSelected(index: Int?) {
guard let index = index else {
remove(forKey: Keys.selectedIndex.rawValue)
return
}
saveSelected(index: index)
}
var selectedIndex: Int? {
get {
return get(forKey: Keys.selectedIndex.rawValue)
}
set {
guard let index = newValue else {
remove(forKey: Keys.selectedIndex.rawValue)
return
}
saveSelected(index: index)
}
}
}
Why? Is it a bug?
Screenshots with an error and a callstack. The error appears later in the other part of the code.
The code below crashes in the iOS. But works in the playground.
//: Playground - noun: a place where people can play
import UIKit
struct DataStorage<T: Hashable> {
enum Keys: String {
case selectedIndex
}
private func get<U>(forKey key: String) -> U? {
guard let objectData = getData(forKey: key) else {
return nil
}
let object: U? = get(forData: objectData)
return object
}
private func get<U>(forData objectData: Data) -> U? {
return NSKeyedUnarchiver.unarchiveObject(with: objectData) as? U
}
private func save<U>(forKey key: String, object: U) {
let encodedData = NSKeyedArchiver.archivedData(withRootObject: object)
UserDefaults.standard.set(encodedData, forKey: key)
}
private func remove(forKey key: String) {
UserDefaults.standard.removeObject(forKey: key)
}
private func saveSelected(index: Int) {
save(forKey: Keys.selectedIndex.rawValue, object: index)
}
private func getData(forKey key: String) -> Data? {
return getContent(forKey: key) as? Data
}
private func getContent(forKey key: String) -> Any? {
return UserDefaults.standard.value(forKey: key)
}
}
extension DataStorage {
func updateSelected(index: Int?) {
guard let index = index else {
remove(forKey: Keys.selectedIndex.rawValue)
return
}
saveSelected(index: index)
}
var selectedIndex: Int? {
get {
return get(forKey: Keys.selectedIndex.rawValue)
}
set {
guard let index = newValue else {
remove(forKey: Keys.selectedIndex.rawValue)
return
}
saveSelected(index: index)
}
}
}
class SomeLoader {
// MARK: - Public
var dataStorage = DataStorage<Int>()
var selectedIndex: Int? {
get {
return dataStorage.selectedIndex
}
set {
dataStorage.selectedIndex = newValue
}
}
}
let someLoader = SomeLoader()
someLoader.selectedIndex = 1
print(someLoader)
The code below works everywhere
//: Playground - noun: a place where people can play
import UIKit
struct DataStorage<T: Hashable> {
enum Keys: String {
case selectedIndex
}
private func get<U>(forKey key: String) -> U? {
guard let objectData = getData(forKey: key) else {
return nil
}
let object: U? = get(forData: objectData)
return object
}
private func get<U>(forData objectData: Data) -> U? {
return NSKeyedUnarchiver.unarchiveObject(with: objectData) as? U
}
private func save<U>(forKey key: String, object: U) {
let encodedData = NSKeyedArchiver.archivedData(withRootObject: object)
UserDefaults.standard.set(encodedData, forKey: key)
}
private func remove(forKey key: String) {
UserDefaults.standard.removeObject(forKey: key)
}
private func saveSelected(index: Int) {
save(forKey: Keys.selectedIndex.rawValue, object: index)
}
private func getData(forKey key: String) -> Data? {
return getContent(forKey: key) as? Data
}
private func getContent(forKey key: String) -> Any? {
return UserDefaults.standard.value(forKey: key)
}
}
extension DataStorage {
func updateSelected(index: Int?) {
guard let index = index else {
remove(forKey: Keys.selectedIndex.rawValue)
return
}
saveSelected(index: index)
}
var selectedIndex: Int? {
get {
return get(forKey: Keys.selectedIndex.rawValue)
}
set {
guard let index = newValue else {
remove(forKey: Keys.selectedIndex.rawValue)
return
}
saveSelected(index: index)
}
}
}
class SomeLoader {
// MARK: - Public
var dataStorage = DataStorage<Int>()
var selectedIndex: Int? {
get {
return dataStorage.selectedIndex
}
set {
dataStorage.updateSelected(index: newValue)
}
}
}
let someLoader = SomeLoader()
someLoader.selectedIndex = 1
print(someLoader)
Related
By convention, each Cocoa and Core Foundation object listed in Table
2-1 is called a property-list object.
So, does this protocol (known as PropertyListObject in the code below) exist, or do we need to make our own? If we do need to make our own, has someone made a definitive one?
public protocol UserDefaults_Value_WrappedValue {
associatedtype PropertyListObject: HM.PropertyListObject
init?(propertyListObject: Any)
var convertertedToPropertyListObject: PropertyListObject { get }
}
public protocol PropertyListObject: UserDefaults_Value_WrappedValue
where PropertyListObject == Self { }
public extension PropertyListObject {
init?(propertyListObject: Any) {
guard let object = propertyListObject as? Self
else { return nil }
self = object
}
var convertertedToPropertyListObject: Self { self }
}
extension Bool: PropertyListObject { }
extension Data: PropertyListObject { }
extension Date: PropertyListObject { }
extension String: PropertyListObject { }
extension URL: PropertyListObject { }
extension Int: PropertyListObject { }
extension Int8: PropertyListObject { }
extension Int16: PropertyListObject { }
extension Int32: PropertyListObject { }
extension Int64: PropertyListObject { }
extension UInt: PropertyListObject { }
extension UInt8: PropertyListObject { }
extension UInt16: PropertyListObject { }
extension UInt32: PropertyListObject { }
extension UInt64: PropertyListObject { }
extension Float: PropertyListObject { }
extension Double: PropertyListObject { }
extension Array: PropertyListObject & UserDefaults_Value_WrappedValue
where Element: HM.PropertyListObject {
public typealias PropertyListObject = Self
}
extension Dictionary: UserDefaults_Value_WrappedValue
where Key: LosslessStringConvertible, Value: HM.PropertyListObject {
public typealias PropertyListObject = PropertyListDictionary<Value>
public init?(propertyListObject: Any) {
guard let dictionary = propertyListObject as? PropertyListObject
else { return nil }
self.init(dictionary)
}
public var convertertedToPropertyListObject: PropertyListObject {
.init(self)
}
}
extension Dictionary: PropertyListObject
where Key == String, Value: HM.PropertyListObject { }
public typealias PropertyListDictionary<Value: PropertyListObject> = [String: Value]
import CoreGraphics
extension CGPoint: PropertyListObject { }
extension CGVector: PropertyListObject { }
extension CGSize: PropertyListObject { }
extension CGRect: PropertyListObject { }
extension CGAffineTransform: PropertyListObject { }
Use case:
The value parameter can be only property list objects: NSData,
NSString, NSNumber, NSDate, NSArray, or NSDictionary. For NSArray and
NSDictionary objects, their contents must be property list objects.
final class UserDefaultsTestCase: XCTestCase {
func test_subscript() {
let key = "🔑"
UserDefaults[key] = true
XCTAssert(UserDefaults[key] == true)
UserDefaults[key] = 9
XCTAssertEqual(UserDefaults[key], 9)
}
func test_Dictionary() {
let key = "🔑"
UserDefaults[key] = Day.ta
XCTAssertEqual(UserDefaults["🔑"], Day.ta)
UserDefaults[key] = [1: "🌞", 2: "🌛"]
XCTAssertEqual(UserDefaults["🔑"], Day.ta)
UserDefaults.standard[key] = ["1": "🌞", "2": "🌛"]
XCTAssertEqual(UserDefaults["🔑"], Day.ta)
}
func test_propertyWrapper() {
struct Type {
#UserDefaults.Value(key: "🗝") var dayta = Day.ta
}
var instance = Type()
XCTAssertEqual(instance.dayta, Day.ta)
instance.dayta = nil
XCTAssertNil(instance.dayta)
}
}
private enum Day: Int, LosslessStringConvertible {
case sunday = 1, monday
static let ta = [Day.sunday: "🌞", .monday: "🌛"]
}
public extension UserDefaults {
#propertyWrapper struct Value<WrappedValue: UserDefaults_Value_WrappedValue> {
public init(
wrappedValue: WrappedValue?,
key: String,
defaults: UserDefaults = .standard
) {
self.key = key
self.defaults = defaults
self.wrappedValue = wrappedValue
}
public var wrappedValue: WrappedValue? {
get { defaults[key] }
set { defaults[key] = newValue }
}
public let key: String
private let defaults: UserDefaults
}
static subscript<Object: UserDefaults_Value_WrappedValue>(key: String) -> Object? {
get { standard[key] }
set { standard[key] = newValue }
}
subscript<Object: UserDefaults_Value_WrappedValue>(key: String) -> Object? {
get { object(forKey: key).flatMap(Object.init) }
set { set(newValue?.convertertedToPropertyListObject, forKey: key) }
}
}
More reading: https://github.com/apple/swift-evolution/blob/master/proposals/0139-bridge-nsnumber-and-nsvalue.md
I'm trying to save a custom class array to UserDefaults but it doesn't work. I get nil back on if let. I looked everywhere online. I'm using Swift 4.2
extension UserDefaults {
func saveReciters(_ reciters: [Reciter]) {
do {
let encodedData = try NSKeyedArchiver.archivedData(withRootObject: reciters, requiringSecureCoding: false)
self.set(encodedData, forKey: UD_RECITERS)
} catch {
debugPrint(error)
return
}
}
func getReciters() -> [Reciter] {
if let reciters = self.object(forKey: UD_RECITERS) as? Data {
return NSKeyedUnarchiver.unarchiveObject(with: reciters) as! [Reciter]
} else {
print("EMPTY RECITERS")
return [Reciter]()
}
}
}
UserInfo={NSDebugDescription=Caught exception during archival: -[_SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x600001babcc0
Thats my class:
class Reciter: NSCoding {
private(set) public var name: String
private(set) public var image: UIImage?
private(set) public var surahs: [Surah]
private(set) public var documentID: String
private let quranData = QuranData()
init(name: String, image: UIImage?, surahCount: Int?, documentID: String) {
self.name = name
self.image = image
self.documentID = documentID
if let surahCount = surahCount {
surahs = Array(quranData.getAllSurahs().prefix(surahCount))
} else {
surahs = quranData.getAllSurahs()
}
}
func encode(with aCoder: NSCoder) {
}
required init?(coder aDecoder: NSCoder) {
}
}
On my Surah class i get nil back. All other properties i get back succesfully
Most often I see developer's use codeable, here I am using user as an example:
YourDataModel.swift
struct User: Codable {
var userId: String = ""
var name: String = ""
var profileImageData: Data? }
UserDefaults.swift
import Foundation
extension UserDefaults {
/// The current user of the application, see `./Models/User.swift`
var currentUser: User? {
get {
guard let userData = self.object(forKey: #function) as? Data else { return nil }
return try? JSONDecoder().decode(User.self, from: userData)
}
set {
guard let newuser = newValue else { return }
if let userData = try? JSONEncoder().encode(newuser) {
self.set(userData, forKey: #function)
}
}
}
}
Transform the data into json data... #function is the function or value name i.e.
// For the case the user doesn't yet exist.
if ( UserDefaults.standard.currentUser == nil ) {
// Create a new user
user = User()
// Generate an id for the user, using a uuid.
user?.userId = UUID().uuidString
} else {
// otherwise, fetch the user from user defaults.
user = UserDefaults.standard.currentUser
}
I am trying to fetch a data from UserDefault but when I am doing this I am getting error
var sharedPreference: UserDefaults = UserDefaults.init(suiteName: "user-key-value")!
func getLastLoginClientId() -> Int64? {
for (key, value) in sharedPreference.dictionaryRepresentation() {
if key == LAST_USER {
return value as! Int64
}
}
return nil
}
I am getting that my key is having some value but when returning it, it throws error.
This is how I save
func setLastLoginClientId(clientId: Int64) {
sharedPreference.set(clientId, forKey: LAST_USER)
sharedPreference.synchronize()
}
I think you could do something as simple as
func getLastLoginClientId() -> Int64? {
return sharedPreference.value(forKey: LAST_USER) as? Int64
}
Here is what I've tested
struct CustomUserDefaults {
var sharedPreference : UserDefaults = UserDefaults.init(suiteName: "user-key-value")!
let LAST_USER = "test"
func test() {
let value = Int64(20.0)
self.setLastLoginClientId(value)
let testValue = getLastLoginClientId()
print(testValue) // 20.0
}
func setLastLoginClientId(_ value: Int64) {
sharedPreference.set(value, forKey: LAST_USER)
}
func getLastLoginClientId() -> Int64? {
return sharedPreference.value(forKey: LAST_USER) as? Int64
}
}
public class LessonAssignment {
private var title : String?
private var category : String?
private var week : Int?
private var day : Int?
//Title
public func getTitle() -> String {
return title!
}
public func setTitle(title : String) {
self.title = title
}
//Category
public func getCategory() -> String {
return category!
}
public func setCategory(category : String) {
self.category = category
}
//Week
public func getWeek() -> Int {
return week!
}
public func setWeek(week : Int) {
self.week = week
}
//Day
public func getDay() -> Int {
return day!
}
public func setDay(day : Int) {
self.day = day
}
/**
Returns an array of models based on given dictionary.
Sample usage:
let lessonAssignment_list = LessonAssignment.modelsFromDictionaryArray(someDictionaryArrayFromJSON)
- parameter array: NSArray from JSON dictionary.
- returns: Array of LessonAssignment Instances.
*/
public class func modelsFromDictionaryArray(array:NSArray) -> [LessonAssignment]
{
var models = [LessonAssignment]()
for item in array {
models.append(LessonAssignment(dictionary: item as! NSDictionary)!)
}
return models
}
/**
Constructs the object based on the given dictionary.
Sample usage:
let lessonAssignment = LessonAssignment(someDictionaryFromJSON)
- parameter dictionary: NSDictionary from JSON.
- returns: LessonAssignment Instance.
*/
init() { }
required public init?(dictionary: NSDictionary) {
title = dictionary["title"] as? String
category = dictionary["category"] as? String
week = dictionary["week"] as? Int
day = dictionary["day"] as? Int
}
/**
Returns the dictionary representation for the current instance.
- returns: NSDictionary.
*/
public func dictionaryRepresentation() -> NSDictionary {
let dictionary = NSMutableDictionary()
dictionary.setValue(self.title, forKey: "title")
dictionary.setValue(self.category, forKey: "category")
dictionary.setValue(self.week, forKey: "week")
dictionary.setValue(self.day, forKey: "day")
return dictionary
}
func toDictionary() -> [String : Any] {
var dictionary = [String:Any]()
let otherSelf = Mirror(reflecting: self)
for child in otherSelf.children {
if let key = child.label {
dictionary[key] = child.value
}
}
return dictionary
}
}
public class LessonPlan {
private var name : String?
private var weeks : Int?
private var days : Int?
private var hours : Int?
private var lessonAssignment = [LessonAssignment]()
private var lessonNote = [LessonNote]()
//Name
public func getName() -> String {
if name == nil {
return ""
} else {
return name!
}
}
public func setName(name : String) {
self.name = name
}
//Weeks
public func getWeeks() -> Int {
if weeks == 0 {
return 0
} else {
return weeks!
}
}
public func setWeeks(weeks : Int) {
self.weeks = weeks
}
//Days
public func getDays() -> Int {
if days == 0 {
return 0
} else {
return days!
}
}
public func setDays(days : Int) {
self.days = days
}
//Hours
public func getHours() -> Int {
if days == 0 {
return 0
} else {
return hours!
}
}
public func setHours(hours : Int) {
self.hours = hours
}
//LessonAssignment
public func getLessonAssignment() -> [LessonAssignment] {
return lessonAssignment
}
public func setLessonAssignment(lessonAssignment : [LessonAssignment]) {
self.lessonAssignment = lessonAssignment
}
//LessonNote
public func getLessonNote() -> [LessonNote] {
return lessonNote
}
public func setLessonNote(lessonNote : [LessonNote]) {
self.lessonNote = lessonNote
}
/**
Returns an array of models based on given dictionary.
Sample usage:
let lessonPlan_list = LessonPlan.modelsFromDictionaryArray(someDictionaryArrayFromJSON)
- parameter array: NSArray from JSON dictionary.
- returns: Array of LessonPlan Instances.
*/
public class func modelsFromDictionaryArray(array:NSArray) -> [LessonPlan]
{
var models:[LessonPlan] = []
for item in array
{
models.append(LessonPlan(dictionary: item as! NSDictionary)!)
}
return models
}
/**
Constructs the object based on the given dictionary.
Sample usage:
let lessonPlan = LessonPlan(someDictionaryFromJSON)
- parameter dictionary: NSDictionary from JSON.
- returns: LessonPlan Instance.
*/
init() { }
required public init?(dictionary: NSDictionary) {
name = dictionary["name"] as? String
weeks = dictionary["weeks"] as? Int
days = dictionary["days"] as? Int
hours = dictionary["hours"] as? Int
lessonAssignment = LessonAssignment.modelsFromDictionaryArray(array:dictionary["lessonAssignment"] as! NSArray)
lessonNote = LessonNote.modelsFromDictionaryArray(array: dictionary["lessonNote"] as! NSArray)
}
/**
Returns the dictionary representation for the current instance.
- returns: NSDictionary.
*/
public func dictionaryRepresentation() -> NSDictionary {
let dictionary = NSMutableDictionary()
dictionary.setValue(self.name, forKey: "name")
dictionary.setValue(self.weeks, forKey: "weeks")
dictionary.setValue(self.days, forKey: "days")
dictionary.setValue(self.hours, forKey: "hours")
return dictionary
}
func toDictionary() -> [String : Any] {
var dictionary = [String:Any]()
let otherSelf = Mirror(reflecting: self)
for child in otherSelf.children {
print("Child = \(child)")
print("Child Label = \(String(describing: child.label))")
print("Child Value = \(child.value)")
if let key = child.label {
dictionary[key] = child.value
}
}
return dictionary
}
}
public class ServerRequest {
private var lessonPlan : LessonPlan?
init() {
}
//LessonPlan
public func getLessonPlan() -> LessonPlan {
return lessonPlan!
}
public func setLessonPlan(lessonPlan : LessonPlan) {
self.lessonPlan = lessonPlan
}
func toDictionary() -> [String : Any] {
var dictionary = [String:Any]()
let otherSelf = Mirror(reflecting: self)
for child in otherSelf.children {
if let key = child.label {
dictionary[key] = lessonPlan?.toDictionary()
}
}
return dictionary
}
}
Current Response = ["lessonPlan": ["name": "test", "days": 2, "weeks": 1,
"hours": 1, "lessonAssignment": [HSP.LessonAssignment,
HSP.LessonAssignment], "lessonNote": []]]
Expected Response = ["lessonPlan": ["name": "test", "days": 2,
"weeks": 1, "hours": 1, "lessonAssignment": [["title" : "some value 1", "category" : "some value", "week" : 1, "day" : 2]], "lessonNote": []]]
Instead of the LessinAssignment Object I would like to add the actual values which is array. Any clue how to resolve this. I know i have to add some more logic inside the toDictionary method and based on the key "lessonAssignment" I have to get each array and add the value in the key as final Array.
From my comment, something like this:
assignments.map({
(value: HSP.LessonAssignment) -> NSDictionary in
return value.toDictionary()
})
Here is the code :
private func filter(searchText: String) -> [MICountry] {
filteredList.removeAll()
sections.forEach { (section) -> () in
section.countries.forEach({ (country) -> () in
let result = country.name.compare(searchText, options: [.CaseInsensitiveSearch, .DiacriticInsensitiveSearch], range: searchText.startIndex ..< searchText.endIndex)
if (result == .OrderedSame) {
filteredList.append(country)
}
})
}
return filteredList
}
My Country and section classes are as follows:
class MICountry: NSObject {
let name: String
var section: Int?
init(name: String) {
self.name = name
}
}
struct MISection {
var countries: [MICountry] = []
mutating func addCountry(country: MICountry) {
countries.append(country)
}
}