2017-04-19 85 views
1

在我的應用程序中,我想要實現單獨的類以保留音樂播放器的正在播放項目的所有臨時變量。 它有許多不同類型的屬性,但它們應該以相同的方式處理。他們應該在類方法「了updateData」(見程序結束)處理如何減少類屬性的代碼重複(Swift 3)

這是我的代碼:

struct DataDefaults { 
    //MARK: Default properties 
    let albumTitle: String? = "Unknown Album" 
    let albumArtist: String? = "Unknown Artist" 
    let title: String? = "Unknown Title" 
    let artist: String? = "Unknown Artist" 
    let artwork: UIImage? = UIImage(named: "noartwork")! 
    let genre: String? = "" 
    let lyrics: String? = "No Lyrics" 
    let releaseDate: Date? = nil 
    let playbackDuration: TimeInterval? = 0 
    let rating: Int? = 0 
    let assetURL: URL? = nil 
    let isExplicitItem: Bool? = false 
    let isCloudItem: Bool? = false 
    let hasProtectedAsset: Bool? = false 
} 

class SongInfo: NSObject { 

    static let sharedData = SongInfo() 

    let defaults = DataDefaults() 

    //MARK: Properties 
    var albumTitle: String 
    var albumArtist: String 
    var title: String 
    var artist: String 
    var artwork: UIImage 
    var genre: String 
    var lyrics: String 
    var releaseDate: Date? 
    var playbackDuration: TimeInterval 
    var rating: Int 
    var assetURL: URL? 
    var isExplicitItem: Bool 
    var isCloudItem: Bool 
    var hasProtectedAsset: Bool 


    //MARK: Init 
    private override init() { 
     self.albumTitle = defaults.albumTitle! 
     self.albumArtist = defaults.albumArtist! 
     self.title = defaults.title! 
     self.artist = defaults.artist! 
     self.artwork = defaults.artwork! 
     self.genre = defaults.genre! 
     self.lyrics = defaults.lyrics! 
     self.releaseDate = defaults.releaseDate 
     self.playbackDuration = defaults.playbackDuration! 
     self.rating = defaults.rating! 
     self.assetURL = defaults.assetURL 
     self.isExplicitItem = defaults.isExplicitItem! 
     self.isCloudItem = defaults.isCloudItem! 
     self.hasProtectedAsset = defaults.hasProtectedAsset! 
    } 

    //MARK: Set properties 
    func updateData(allData: DataDefaults) { 
     var wasUpdated: Bool = false 

     if allData.albumTitle == self.albumTitle { 
      //pass 
     } else if allData.albumTitle == nil || allData.albumTitle == "" { 
      self.albumTitle = defaults.albumTitle! 
      wasUpdated = true 
     } else { 
      self.albumTitle = allData.albumTitle! 
      wasUpdated = true 
     } 

     //Need to repeat same IF for all properties 
    } 
} 

有什麼辦法,我可以使用屬性名也作出了同樣的一些回用代碼而不是複製它?

+1

是什麼目的** **可選非'DataDefaults'結構中的非常量常量,因爲它們永遠不會更改該值? – vadian

回答

2

我沒有試圖找到一個奇怪的設計解決方案,我重新設計了你想要完成的事情

struct SongData: Equatable { 

    static let defaultData = SongData(albumTitle: "Unknown Album", 
             albumArtist: "Unknown Artist", 
             title: "Unknown Title", 
             artist: "Unknown Artist", 
             artwork: UIImage(named: "noartwork"), 
             genre:"", 
             lyrics: "No Lyrics", 
             releaseDate: nil, 
             playbackDuration: 0, 
             rating: 0, 
             assetURL: nil, 
             isExplicitItem: false, 
             isCloudItem: false, 
             hasProtectedAsset: false) 

    //MARK: Default properties 
    var albumTitle: String? 
    var albumArtist: String? 
    var title: String? 
    var artist: String? 
    var artwork: UIImage? 
    var genre: String? 
    var lyrics: String? 
    var releaseDate: Date? 
    var playbackDuration: TimeInterval? 
    var rating: Int? 
    var assetURL: URL? 
    var isExplicitItem: Bool? 
    var isCloudItem: Bool? 
    var hasProtectedAsset: Bool? 

    /// This initializer will set the properties to the defaultData properties if a passed value is nil 
    init(albumTitle: String?, albumArtist: String?, title: String?, artist: String?, artwork: UIImage?, genre: String?, lyrics: String?, releaseDate: Date?, playbackDuration: TimeInterval?, rating: Int?, assetURL: URL?, isExplicitItem: Bool?, isCloudItem: Bool?, hasProtectedAsset: Bool?) { 

     // initialize properties where the default is nil 
     self.releaseDate = releaseDate 
     self.assetURL = assetURL 

     //initialize other properties with the passed values, or use the default value if nil 
     self.albumTitle = SongData.valueOrDefault(albumTitle, SongData.defaultData.albumTitle) 
     self.albumArtist = SongData.valueOrDefault(albumArtist, SongData.defaultData.albumArtist) 
     self.title = SongData.valueOrDefault(title, SongData.defaultData.title) 
     self.artist = SongData.valueOrDefault(artist, SongData.defaultData.artist) 
     self.artwork = artwork ?? SongData.defaultData.artwork 
     self.genre = SongData.valueOrDefault(genre, SongData.defaultData.genre) 
     self.lyrics = SongData.valueOrDefault(lyrics, SongData.defaultData.lyrics) 
     self.playbackDuration = playbackDuration ?? SongData.defaultData.playbackDuration 
     self.rating = rating ?? SongData.defaultData.rating 
     self.isExplicitItem = isExplicitItem ?? SongData.defaultData.isExplicitItem 
     self.isCloudItem = isCloudItem ?? SongData.defaultData.isCloudItem 
     self.hasProtectedAsset = hasProtectedAsset ?? SongData.defaultData.hasProtectedAsset 
    } 

    static func ==(leftItem: SongData, rightItem: SongData) -> Bool { 
     return (leftItem.albumTitle == rightItem.albumTitle) && 
       (leftItem.albumArtist == rightItem.albumArtist) && 
       (leftItem.title == rightItem.title) && 

       // Comparing a reference type here. may need to be handled differently if that's a problem 
       (leftItem.artwork === rightItem.artwork) && 
       (leftItem.genre == rightItem.genre) && 
       (leftItem.lyrics == rightItem.lyrics) && 
       (leftItem.releaseDate == rightItem.releaseDate) && 
       (leftItem.playbackDuration == rightItem.playbackDuration) && 
       (leftItem.rating == rightItem.rating) && 
       (leftItem.assetURL == rightItem.assetURL) && 
       (leftItem.isExplicitItem == rightItem.isExplicitItem) && 
       (leftItem.isCloudItem == rightItem.isCloudItem) && 
       (leftItem.hasProtectedAsset == rightItem.hasProtectedAsset) 
    } 

    //simple helper function to avoid long turneries in the init 
    static func valueOrDefault(_ value: String?, _ defaultValue: String?) -> String? { 
     guard let value = value, !value.isEmpty else { 
      return defaultValue 
     } 
     return value 
    } 
} 

class SongInfo { 

    static let sharedData = SongInfo() 

    var data: SongData 

    //MARK: Init 
    private init() 
    { 
     self.data = SongData.defaultData 
    } 

    //MARK: Set properties 
    func updateData(newData: SongData) { 
     if(newData != self.data) { 
      self.data = newData 
     } 
    } 
} 

我改變了你的結構,使其看起來更像你想要使用的結構,如果初始值爲零,結構的init將回退到使用默認值。我的設計也不包含任何力量解包,這幾乎總是不好的。

+0

夢幻般的答案!謝謝,這絕對是我試圖完成的。 – dandepeched

+0

在這段代碼中,初始化'defaultData'有問題。它會在調用'valueOrDefault'方法時嘗試引用自身,並陷入無限循環。作爲解決方法,我使用開關而不是'return defaultData':'switch defaultValue {「case」albumTitle「: return」Unknown Album「 ...}' – dandepeched

+0

是的,我認爲你是對的。我試圖避免使用2個初始值設定項,但是您可能希望爲默認值設置單獨的初始值設定項,以簡單設置值。 – PeejWeej

1

您可以直接在您的類定義中設置默認值,而無需使用單獨的結構,並使用默認值創建靜態不變的實例。

例如:

class SongInfo: NSObject { 

    static let sharedData = SongInfo() 

    static let defaults = SongInfo() 

    //MARK: Properties 
    var albumTitle: String?  = "Unknown Album" 
    var albumArtist: String?  = "Unknown Artist" 
    var title: String?   = "Unknown Title" 
    var artist: String?   = "Unknown Artist" 
    var artwork: UIImage?  = UIImage(named: "noartwork")! 
    var genre: String?   = "" 
    var lyrics: String?   = "No Lyrics" 
    var releaseDate: Date?  = nil 
    var playbackDuration: TimeInterval? = 0 
    var rating: Int?    = 0 
    var assetURL: URL?   = nil 
    var isExplicitItem: Bool? = false 
    var isCloudItem: Bool?  = false 
    var hasProtectedAsset: Bool? = false 


    //MARK: Init 
    private override init() 
    { 
     // nothing to do here 
    } 

    //MARK: Set properties 
    func updateData(allData: DataDefaults) { 
     var wasUpdated: Bool = false 

     if allData.albumTitle == self.albumTitle { 
      //pass 
     } else if allData.albumTitle == nil || allData.albumTitle == "" { 
      self.albumTitle = SongInfo.defaults.albumTitle! 
      wasUpdated = true 
     } else { 
      self.albumTitle = allData.albumTitle! 
      wasUpdated = true 
     } 

     //Need to repeat same IF for all properties 
    } 
} 

如果您還需要處理的基本數據,而整個班級的功能,你可以定義一個類SongInfoData僅屬性,並SingInfo從類繼承。然後默認的靜態變量可以在SongInfoData類中,並且SingInfo子類不需要任何屬性聲明。

[編輯]避免更新功能代碼的重複...

您可以通過添加一個泛型函數到類概括的屬性更新過程:

例如:

func assign<T:Equatable>(_ variable:inout T?, _ getValue:(SongInfo)->T?) -> Int 
{ 
    let newValue = getValue(self) 

    if variable == newValue 
    { return 0 } 

    var valueIsEmpty = false 
    if let stringValue = newValue as? String, stringValue == "" 
    { valueIsEmpty = true } 

    if newValue == nil || valueIsEmpty 
    { 
     variable = getValue(SongInfo.defaults) 
     return 1 
    } 

    variable = newValue 
    return 1    
} 

func update(with newInfo:SongInfo) 
{ 
    let updates = newInfo.assign(&albumTitle) {$0.albumTitle} 
       + newInfo.assign(&albumArtist) {$0.albumArtist} 
       + newInfo.assign(&title)  {$0.title} 
       + newInfo.assign(&artist)  {$0.artist} 
       + newInfo.assign(&artwork)  {$0.artwork} 
       + newInfo.assign(&genre)  {$0.genre} 
       + newInfo.assign(&lyrics)  {$0.lyrics} 
       // ... 

    if updates > 0 
    { 
    // react to update 
    } 
} 
+0

謝謝,這是一個有趣的觀點,我會考慮它。但我的問題是關於'updateData'方法。首先,我期望它可以接收到我在結構中使用的相同屬性,這就是爲什麼我分別定義它的原因。真正的問題是我可以不會在每個屬性的'updateData'方法中複製粘貼IF語句? – dandepeched

0

在我看來,你正在使用MPMedia項目。 如果是這樣,則不必存儲所有這些屬性。 你只需要存儲項目的持續性ID(從UINT64轉換爲字符串),然後再用MPMediaQuery謂語,像這樣取MPMediaItem

func findSong(persistentIDString: String) -> MPMediaItem? { 
    let predicate = MPMediaPropertyPredicate(value: persistentIDString, forProperty: MPMediaItemPropertyPersistentID) 
    let songQuery = MPMediaQuery() 
    songQuery.addFilterPredicate(predicate) 

    return songQuery.items.first 
} 
+0

是的,我使用'MPMediaItem',但我想要從DataStorage中分離任何MediaPlayer intercations。我想要從單獨的MediaPlayer類重寫DataStorage – dandepeched