2016-09-15 3 views
3

Итак, я только что преобразовал небольшое приложение из Swift 2.2 в Swift 3. Я избавился от обычных ошибок и бит mop up, необходимых после автоматического конвертера, но я У меня есть проблема времени выполнения, которую я не могу решить.Преобразование в Swift 3 сломало пользовательское кодирование/декодирование класса

У меня есть пользовательский класс, который я сохраняю в NSUserDefaults с протоколом NSCoding. Когда я пытаюсь декодировать закодированный объект из NSUserDefaults, он выходит из строя на линии guard let duration = decoder.decodeObject(forKey: "duration") as? Int, поскольку продолжительность печати равна нулю. Декодирование строки заголовка отлично работает, однако, по крайней мере, эта строка функции кодирования работает правильно.

Это отлично работает в версии 2.2, и я не могу найти ничего, что указывало бы на то, что Swift 3 внес изменения в NSCoding. Любая помощь приветствуется.

class TimerModel: NSObject, NSCoding, AVAudioPlayerDelegate { 

    var name: String 
    var active: Bool 
    var paused: Bool 
    var duration: Int 
    var remainingWhenPaused: Int? 
    var timerEndTime: Date? 
    var timerStartTime: Date? 
    var audioAlert: AlertNoise 
    var UUID: String 
    var colorScheme: BaseColor 
    var alarmRepetitions: Int 
    var timerRepetitions: Int 
    var currentTimerRepetition: Int 
    var audioPlaying: Bool 
    var player: AVAudioPlayer = AVAudioPlayer() 
    var countDownTimer: Timer = Timer() 
    var delegate: timerProtocol? = nil 

    init(withName name: String, duration: Int, UUID: String, color: BaseColor, alertNoise: AlertNoise, timerRepetitions: Int, alarmRepetitions: Int) { 
     self.name = name 
     self.active = false 
     self.paused = false 
     self.duration = duration 
     self.UUID = UUID 
     self.audioAlert = alertNoise 
     self.colorScheme = color 
     self.alarmRepetitions = alarmRepetitions 
     self.audioPlaying = false 
     self.timerRepetitions = timerRepetitions 
     self.currentTimerRepetition = 0 

     super.init() 
    } 

    convenience override init() { 
     self.init(withName: "Tap Timer 1", duration: 10, UUID: Foundation.UUID().uuidString, color: .Red, alertNoise: .ChurchBell, timerRepetitions: 1, alarmRepetitions: 0) 
    } 

    // MARK: NSCoding 

    required convenience init? (coder decoder: NSCoder) { 
     print("in init coder:") 
     print("Name: \(decoder.decodeObject(forKey: "name"))") 
     print("Duration: \(decoder.decodeObject(forKey: "duration"))") 
     guard let name = decoder.decodeObject(forKey: "name") as? String 
     else { 
      print("init coder name guard failed") 
      return nil 
     } 
     guard let duration = decoder.decodeObject(forKey: "duration") as? Int 
     else { 
      print("init coder duration guard failed") 
      print("duration: \(decoder.decodeObject(forKey: "duration"))") 
      return nil 
     } 
     guard let audioAlertRawValue = decoder.decodeObject(forKey: "audioAlert") as? String 
     else { 
      print("init coder audioAlert guard failed") 
      return nil 
     } 
     guard let UUID = decoder.decodeObject(forKey: "UUID") as? String 
     else { 
      print("init coder UUID guard failed") 
      return nil 
     } 
     guard let colorSchemeRawValue = decoder.decodeObject(forKey: "colorScheme") as? String 
     else { 
      print("init coder colorScheme guard failed") 
      return nil 
     } 
     guard let alarmRepetitions = decoder.decodeObject(forKey: "alarmRepetitions") as? Int 
     else { 
      print("init coder alarmRepetitions guard failed") 
      return nil 
     } 
     guard let timerRepetitions = decoder.decodeObject(forKey: "timerRepetitions") as? Int 
     else { 
      print("init coder timerRepetitions guard failed") 
      return nil 
     } 

     guard let audioAlert = AlertNoise(rawValue: audioAlertRawValue) 
      else{ 
       print("No AlertNoise rawValue case found") 
       return nil 
     } 
     guard let colorScheme = BaseColor(rawValue: colorSchemeRawValue) 
      else{ 
       print("No BaseColor rawValue case found") 
       return nil 
     } 

     print("initCoder guards passed, initing timer") 
     print("\(name), \(duration), \(UUID), \(colorScheme), \(audioAlert), \(timerRepetitions), \(alarmRepetitions)") 

     self.init(withName: name, duration: duration, UUID: UUID, color: colorScheme, alertNoise: audioAlert, timerRepetitions: timerRepetitions, alarmRepetitions: alarmRepetitions) 
    } 

    func encode(with aCoder: NSCoder) { 

     aCoder.encode(self.name, forKey: "name") 
     aCoder.encode(self.duration, forKey: "duration") 
     aCoder.encode(self.audioAlert.rawValue, forKey: "audioAlert") 
     aCoder.encode(self.UUID, forKey: "UUID") 
     aCoder.encode(self.colorScheme.rawValue, forKey: "colorScheme") 
     aCoder.encode(self.alarmRepetitions, forKey: "alarmRepetitions") 
     aCoder.encode(self.timerRepetitions, forKey: "timerRepetitions") 
    } 
+0

'Init (withName ... 'не соответствует соглашению об именах' init (name ... '. У вас могут возникнуть проблемы при взаимодействии с Objective-C. – vadian

+0

Спасибо @vadian Я просто расчесываю условные обозначения – SimonBarker

ответ

6

Так что кажется, что это решение просто, если немного неинтуитивно.

Так что я закодированы Ивар класса с общим методом encode(self.ivar, forKey: "keyName") однако если Ивар является INT он должен быть расшифрованы с decodeInteger(forKey: "keyName") - это влечет за собой избавление от заявлений охраны, а так как этот метод возвращать не опциональным. Кажется нечетным, чтобы декодировать с помощью целочисленного специфического метода, если он был декодирован с помощью метода generalist - в Swift 2.2 этого не было.

+1

« Похоже, чтобы декодировать с помощью целочисленного специфического метода, если он был декодирован с помощью метода обобщения » Это вызывает у меня столько головных болей. – George

+0

Согласовано @George, чувствует себя как ошибка, но, честно говоря, я не собираюсь проходить через препятствовать отправке порт, поскольку Apple, вероятно, не обратится к нему – SimonBarker

+1

По моим наблюдениям decodeObject() декодирует штраф для Int? но должен быть изменен на decodeInteger(), если у вас есть Int. Теперь я задаюсь вопросом обо всех других необязательных типах. – Vitalii

1

Отличный ответ от SimonBarker, и он решил ту же проблему, что и у меня. В конце концов я применил свое решение к своему собственному коду и пересмотрел его, чтобы кодирование выполнялось с помощью метода generalist. Вы можете «заставить» кодирование с методом широкого профиля с помощью:

func encode(_ objv: Any?, forKey key: String) 

Так что в вашем коде, вы можете использовать:

aCoder.encode(self.name as Any?, forKey: "name") 

Таким образом self.name кодируется как объект и не разговляйся существующий код, а именно: decoder.decodeObject (forKey: «name») как? Строка

Это может быть не самое элегантное решение, но, по крайней мере, он работал на меня без необходимости изменения кода, который работал красиво Swift 2.3, но был разбит в Swift 3 ...

Смежные вопросы