How declaring and add data in new object correctly - swift

I have this code:
struct Calculators
{
var calculators: [Calculator]?
var activeCalculator: Int = -1
var activeSummary: Bool = false
var activeProfits: Bool = false
public func getCalculators()-> [Calculator]
{
return calculators!
}
}
struct Calculator
{
var priceSum: Float = 0
var weightSum: Float = 0
var pricePerMix: Float = 0
var pricePerPortion: Decimal?
var portionDivider: Float?
var nettoPortionCost: Float?
var profitPerPortion: Float?
var pricePerKgAfterPrepare: Float?
var weightPerPortionInGrams: Float?
var products : [CountedProduct]?
init() {
createTime = NSDate().timeIntervalSince1970 * 1000
}
}
struct CountedProduct
{
var productCode: String
var productCountable: Bool
var pricePerKg: Decimal?
var weightYieldInPercent: Decimal?
var pieceWeight: Decimal?
}
var countedProduct = CountedProduct(productCode: produkt.code!, productCountable: productCountable, pricePerKg: pricePerKg, weightYieldInPercent: weightYieldInPercent, pieceWeight: pieceWeight)
var activeCalculators = Calculators()
var calculatorToAdd = Calculator()
calculatorToAdd.priceSum = 0
calculatorToAdd.weightSum = 0 //?
calculatorToAdd.pricePerMix = Float(countedProduct.weightAfterPrepareKg as! NSNumber)
calculatorToAdd.pricePerPortion = (countedProduct.costOfTheMixturePerPortion as! NSNumber).decimalValue
calculatorToAdd.portionDivider = 0 //?
calculatorToAdd.nettoPortionCost = Float(countedProduct.costOfTheMixturePerPortion as! NSNumber)
calculatorToAdd.profitPerPortion = Float(countedProduct.overheadOnServing as! NSNumber)
calculatorToAdd.pricePerKgAfterPrepare = Float(countedProduct.pricePerKgAfterPrepare as! NSNumber)
calculatorToAdd.weightPerPortionInGrams = Float(countedProduct.weightAfterPrepareKg as! NSNumber)
calculatorToAdd.products?.append(countedProduct)
calculatorToAdd.products?.append(countedProduct)
I can not change the form of these class.
How to correctly initialize a Calculators object?
I have data in produkt.
Is this code correct?
I would like to create a calculator object and insert it into Calculators.
The class code can not be changed.

I assume you can add to the code at the end
activeCalculators.calculators = []
activeCalculators.calculators.append(calculatorToAdd)

Related

Can't modify variable of a class from closure

I want decodedResult variable assigns to exchangeInformations variable:
self.exchangeInformations = decodedResult
I created an instance of exchangeInfo() class. I try print the value of the exchangeInformations variable but I got nil. Why?
import Cocoa
class exchangeInfo {
var exchangeInformations: exchangeInformation? //Exchange information
let exchangeInformationURL: String = "https://api.binance.com/api/v3/exchangeInfo"
struct exchangeInformation: Codable {
var timezone: String //UTC
var serverTime: Int //1565246363776
var rateLimits: [rateLimits] = []//Rate limits
var exchangeFilters: [exchangeFilters] = [] //Exchange filters
var symbols: [symbols] //Symbols
}
struct rateLimits: Codable { //Rate limits
var rateLimitType: String //REQUEST_WEIGHT, ORDERS, RAW_REQUESTS
var interval: String //interval
var intervalNum: UInt16 //1
var limit: UInt32 //1200
}
struct exchangeFilters: Codable {
var filterType: String
var minPrice, maxPrice, tickSize, multiplierUp, multiplierDown, minQty, maxQty, stepSize, minNotional, maxPosition: String?
var avgPriceMins, limit, maxNumOrders, maxNumAlgoOrders, maxNumIcebergOrders: UInt16?
var applyToMarket: Bool?
}
struct symbols: Codable { //Symbols
var symbol: String //ETHBTC
var status: String //TRADING
var baseAsset: String //ETH
var baseAssetPrecision: UInt16 //8
var quoteAsset: String //BTC
var quotePrecision: UInt16 //8
var quoteAssetPrecision: UInt16 //8
var baseCommissionPrecision: UInt16 //8
var quoteCommissionPrecision: UInt16 //8
var orderTypes: [String] //orderTypes
var icebergAllowed: Bool //true
var ocoAllowed: Bool //true
var quoteOrderQtyMarketAllowed: Bool //true
var isSpotTradingAllowed: Bool //true
var isMarginTradingAllowed: Bool //true
var filters: [exchangeFilters] = [] //Filters
var permissions: [String] //Permissions
}
init() {
guard let url = URL(string: exchangeInformationURL) else {
print("URL is not valid")
return
}
let request = URLRequest(url: url)
let configuration = URLSessionConfiguration.ephemeral
let session = URLSession(configuration: configuration)
session.dataTask(with: request) { data, response, error in
do {
if let adat = data {
if let decodedResult = try? JSONDecoder().decode(exchangeInformation.self, from: adat) {
DispatchQueue.main.async {
self.exchangeInformations = decodedResult
}
}
}
} catch {
print("Error: \(error.localizedDescription)")
}
}.resume()
}
}
var informacio = exchangeInfo()
if let serverTime = informacio.exchangeInformations?.serverTime {
print (serverTime)
}
I got nothing.

Swift – macOS `batteryLevel` property

In iOS there's a simple approach to get a level of iPhone battery:
UIDevice.current.batteryLevel
What's a macOS batteryLevel equivalent for MacBooks?
Getting the battery level in macOS is not as easy as in iOS, but it is still pretty easy. You have to use the IOKit framework for this.
You can use this code to read almost all of the important information from the internal battery.
InternalBattery.swift
import Foundation
import IOKit.ps
public class InternalBattery {
public var name: String?
public var timeToFull: Int?
public var timeToEmpty: Int?
public var manufacturer: String?
public var manufactureDate: Date?
public var currentCapacity: Int?
public var maxCapacity: Int?
public var designCapacity: Int?
public var cycleCount: Int?
public var designCycleCount: Int?
public var acPowered: Bool?
public var isCharging: Bool?
public var isCharged: Bool?
public var amperage: Int?
public var voltage: Double?
public var watts: Double?
public var temperature: Double?
public var charge: Double? {
get {
if let current = self.currentCapacity,
let max = self.maxCapacity {
return (Double(current) / Double(max)) * 100.0
}
return nil
}
}
public var health: Double? {
get {
if let design = self.designCapacity,
let current = self.maxCapacity {
return (Double(current) / Double(design)) * 100.0
}
return nil
}
}
public var timeLeft: String {
get {
if let isCharging = self.isCharging {
if let minutes = isCharging ? self.timeToFull : self.timeToEmpty {
if minutes <= 0 {
return "-"
}
return String(format: "%.2d:%.2d", minutes / 60, minutes % 60)
}
}
return "-"
}
}
public var timeRemaining: Int? {
get {
if let isCharging = self.isCharging {
return isCharging ? self.timeToFull : self.timeToEmpty
}
return nil
}
}
}
InternalFinder.swift:
import Foundation
import IOKit.ps
public class InternalFinder {
private var serviceInternal: io_connect_t = 0 // io_object_t
private var internalChecked: Bool = false
private var hasInternalBattery: Bool = false
public init() { }
public var batteryPresent: Bool {
get {
if !self.internalChecked {
let snapshot = IOPSCopyPowerSourcesInfo().takeRetainedValue()
let sources = IOPSCopyPowerSourcesList(snapshot).takeRetainedValue() as Array
self.hasInternalBattery = sources.count > 0
self.internalChecked = true
}
return self.hasInternalBattery
}
}
fileprivate func open() {
self.serviceInternal = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleSmartBattery"))
}
fileprivate func close() {
IOServiceClose(self.serviceInternal)
IOObjectRelease(self.serviceInternal)
self.serviceInternal = 0
}
public func getInternalBattery() -> InternalBattery? {
self.open()
if self.serviceInternal == 0 {
return nil
}
let battery = self.getBatteryData()
self.close()
return battery
}
fileprivate func getBatteryData() -> InternalBattery {
let battery = InternalBattery()
let snapshot = IOPSCopyPowerSourcesInfo().takeRetainedValue()
let sources = IOPSCopyPowerSourcesList(snapshot).takeRetainedValue() as Array
for ps in sources {
// Fetch the information for a given power source out of our snapshot
let info = IOPSGetPowerSourceDescription(snapshot, ps).takeUnretainedValue() as! Dictionary<String, Any>
// Pull out the name and capacity
battery.name = info[kIOPSNameKey] as? String
battery.timeToEmpty = info[kIOPSTimeToEmptyKey] as? Int
battery.timeToFull = info[kIOPSTimeToFullChargeKey] as? Int
}
// Capacities
battery.currentCapacity = self.getIntValue("CurrentCapacity" as CFString)
battery.maxCapacity = self.getIntValue("MaxCapacity" as CFString)
battery.designCapacity = self.getIntValue("DesignCapacity" as CFString)
// Battery Cycles
battery.cycleCount = self.getIntValue("CycleCount" as CFString)
battery.designCycleCount = self.getIntValue("DesignCycleCount9C" as CFString)
// Plug
battery.acPowered = self.getBoolValue("ExternalConnected" as CFString)
battery.isCharging = self.getBoolValue("IsCharging" as CFString)
battery.isCharged = self.getBoolValue("FullyCharged" as CFString)
// Power
battery.amperage = self.getIntValue("Amperage" as CFString)
battery.voltage = self.getVoltage()
// Various
battery.temperature = self.getTemperature()
// Manufaction
battery.manufacturer = self.getStringValue("Manufacturer" as CFString)
battery.manufactureDate = self.getManufactureDate()
if let amperage = battery.amperage,
let volts = battery.voltage, let isCharging = battery.isCharging {
let factor: CGFloat = isCharging ? 1 : -1
let watts: CGFloat = (CGFloat(amperage) * CGFloat(volts)) / 1000.0 * factor
battery.watts = Double(watts)
}
return battery
}
fileprivate func getIntValue(_ identifier: CFString) -> Int? {
if let value = IORegistryEntryCreateCFProperty(self.serviceInternal, identifier, kCFAllocatorDefault, 0) {
return value.takeRetainedValue() as? Int
}
return nil
}
fileprivate func getStringValue(_ identifier: CFString) -> String? {
if let value = IORegistryEntryCreateCFProperty(self.serviceInternal, identifier, kCFAllocatorDefault, 0) {
return value.takeRetainedValue() as? String
}
return nil
}
fileprivate func getBoolValue(_ forIdentifier: CFString) -> Bool? {
if let value = IORegistryEntryCreateCFProperty(self.serviceInternal, forIdentifier, kCFAllocatorDefault, 0) {
return value.takeRetainedValue() as? Bool
}
return nil
}
fileprivate func getTemperature() -> Double? {
if let value = IORegistryEntryCreateCFProperty(self.serviceInternal, "Temperature" as CFString, kCFAllocatorDefault, 0) {
return value.takeRetainedValue() as! Double / 100.0
}
return nil
}
fileprivate func getDoubleValue(_ identifier: CFString) -> Double? {
if let value = IORegistryEntryCreateCFProperty(self.serviceInternal, identifier, kCFAllocatorDefault, 0) {
return value.takeRetainedValue() as? Double
}
return nil
}
fileprivate func getVoltage() -> Double? {
if let value = getDoubleValue("Voltage" as CFString) {
return value / 1000.0
}
return nil
}
fileprivate func getManufactureDate() -> Date? {
if let value = IORegistryEntryCreateCFProperty(self.serviceInternal, "ManufactureDate" as CFString, kCFAllocatorDefault, 0) {
let date = value.takeRetainedValue() as! Int
let day = date & 31
let month = (date >> 5) & 15
let year = ((date >> 9) & 127) + 1980
var components = DateComponents()
components.calendar = Calendar.current
components.day = day
components.month = month
components.year = year
return components.date
}
return nil
}
}
Usage:
let internalFinder = InternalFinder()
if let internalBattery = internalFinder.getInternalBattery() {
// Internal battery found, access properties here.
}

iOS - UI Label Not Updating

I am trying to build a weather app with AlamoFire and i am getting the JSON Response as expected but when i am trying to update the date to UI Label, my variable is returning a nil. if anyone get a chance to let me know where i am going wrong
import UIKit
class WeatherVC: UIViewController {
#IBOutlet weak var dateLbl: UILabel!
#IBOutlet weak var weatherType: UILabel!
#IBOutlet weak var cityname: UILabel!
#IBOutlet weak var currentTemp: UILabel!
var weatherConstants = WeatherConstant()
override func viewDidLoad() {
super.viewDidLoad()
// updateUI()
//
weatherConstants.downloadCurrentWeather {
self.updateUI()
}
weatherConstants = WeatherConstant()
print(Current_Weather_Url)
}
func updateUI() {
weatherType.text = weatherConstants.weatherType
cityname.text = weatherConstants.cityName
print("current city name is \(weatherConstants.weatherType)")
}
}
Weather Constants
import UIKit
import Alamofire
class WeatherConstant {
var _cityName :String!
var _currentTemp : Double!
var _weatherType : String!
var _highTemp : String!
var _date :String!
var cityName :String {
if _cityName == nil {
_cityName = ""
}
return _cityName
}
var currentTemp : Double{
if _currentTemp == nil {
_currentTemp = 0.0
}
return _currentTemp
}
var weatherType : String {
if _weatherType == nil {
_weatherType = ""
}
return _weatherType
}
var highTemp : String {
if _highTemp == nil {
_highTemp = ""
}
return _highTemp
}
var date : String {
if _date == nil {
_date = ""
}
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .long
dateFormatter.timeStyle = .none
let currentDate = dateFormatter.string(from: Date())
self._date = "Today, \(currentDate)"
return _date
}
//func downloadWeatherDetails(completed : DownloadComplete) ===> downloadWeatherDetails(completed : #escaping DownloadComplete)
func downloadCurrentWeather(completed: #escaping DownloadComplete) {
Alamofire.request(Current_Weather_Url).responseJSON{ response in
let result = response.result
// print(result)
debugPrint(result)
if let dict = result.value as? Dictionary<String, AnyObject>{
if let name = dict["name"] as? String {
self._cityName = name
print("current name is \(self._cityName)")
}
if let currentTemp = dict["weather"] as? [Dictionary<String, AnyObject>] {
if let main = currentTemp[0]["main"] as? String{
self._weatherType = main
print("Current Weather Type \(self._weatherType)")
}
}
}
completed()
}
}
}
JSON Response
https://samples.openweathermap.org/data/2.5/weather?lat=35&lon=139&appid=e9a34088739f38c517b4a45084f5ed82
SUCCESS: {
base = stations;
clouds = {
all = 0;
};
cod = 200;
coord = {
lat = "35.02";
lon = "139.01";
};
dt = 1485792967;
id = 1907296;
main = {
"grnd_level" = "1013.75";
humidity = 100;
pressure = "1013.75";
"sea_level" = "1023.22";
temp = "285.514";
"temp_max" = "285.514";
"temp_min" = "285.514";
};
name = Tawarano;
sys = {
country = JP;
message = "0.0025";
sunrise = 1485726240;
sunset = 1485763863;
};
weather = (
{
description = "clear sky";
icon = 01n;
id = 800;
main = Clear;
}
);
wind = {
deg = 311;
speed = "5.52";
};
}
Current Weather Type Optional("Clear")----> this the weather type i am trying to provide to UIlabel
current city name is Optional("") ------> Here i am trying to print what data is in UILabel but its returning empty string
2018-08-07 11:18:09.567868-0700 WeatherApp1.1[82321:3248301] [BoringSSL] Function boringssl_session_errorlog: line 2881 [boringssl_session_read] SSL_ERROR_ZERO_RETURN(6): operation failed because the connection was cleanly shut down with a close_notify alert
You're overwriting the weatherConstants value with a new object in viewDidLoad. Try removing that.
(Longer explanation: you're correctly creating a weatherConstants object, and asking it to fetch data. But while that's happening, you create a second instance of that object which overwrites the first. So while the first instance completes the network request and tells your UI to redraw, the UI is getting its data from the second instance of WeatherConstants)

Cannot assign value of type '[String]?' to type 'String?'

This is the var var types: [String]? and here i get this error myLabel3.text = place.types how can i adjust it? I looked to other similar questions but not find something that is the same to my problem.
import UIKit
import CoreLocation
private let geometryKey = "geometry"
private let locationKey = "location"
private let latitudeKey = "lat"
private let longitudeKey = "lng"
private let nameKey = "name"
private let openingHoursKey = "opening_hours"
private let openNowKey = "open_now"
private let vicinityKey = "vicinity"
private let typesKey = "types"
private let photosKey = "photos"
class QPlace: NSObject {
var location: CLLocationCoordinate2D?
var name: String?
var photos: [QPhoto]?
var vicinity: String?
var isOpen: Bool?
var types: [String]?
init(placeInfo:[String: Any]) {
// coordinates
if let g = placeInfo[geometryKey] as? [String:Any] {
if let l = g[locationKey] as? [String:Double] {
if let lat = l[latitudeKey], let lng = l[longitudeKey] {
location = CLLocationCoordinate2D.init(latitude: lat, longitude: lng)
}
}
}
// name
name = placeInfo[nameKey] as? String
// opening hours
if let oh = placeInfo[openingHoursKey] as? [String:Any] {
if let on = oh[openNowKey] as? Bool {
isOpen = on
}
}
// vicinity
vicinity = placeInfo[vicinityKey] as? String
// types
types = placeInfo[typesKey] as? [String]
// photos
photos = [QPhoto]()
if let ps = placeInfo[photosKey] as? [[String:Any]] {
for p in ps {
photos?.append(QPhoto.init(photoInfo: p))
}
}
}
this is place class and this is the func of my customTableViewCell where i want to add it
func update(place:QPlace) {
myLabel.text = place.getDescription()
myImage.image = nil
myLabel2.text = place.vicinity
myLabel3.text = place.types
var types: [String]? is an optional array of String; the value of myLabel3.text is an optional String, or String?.
You need to get a value out from the array or join the values in order to set your label text, eg:
myLabel3.text = place.types?.joined()

How to reduce code duplication for class properties (Swift 3)

In my application I want to implement separate class to keep all the temporary variables of Now Playing item for Music Player.
It has lots of properties with different types, but they should be handled in the same way. They should be handled in the class method "updateData" (see the end of code)
This is my code:
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
}
}
Is there any way I can use property name to make some reusage of the same code instead of duplicating it?
Rather than trying to find a solution to a weird design, I re-designed for what you're trying to accomplish 🙂
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
}
}
}
I changed your struct to act more like it appears you're wanting it to be used, and the struct's init will fall back to using the default values if the init values are nil. My design also contains no force unwraps, which are almost always bad.
You could set the defaults directly in your class definition without using a separate struct and have a static unaltered instance with the default values.
For example:
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
}
}
If you also need to manipulate the basic data without the whole class functionality, you could define a SongInfoData class with only the properties and make SingInfo inherit from that class. Then the static variable for defaults could be in the SongInfoData class and the SingInfo subclass wouldn't need any property declarations.
[EDIT] avoiding code repetition in update function ...
You can generalize the property update process by adding a generic function to your class:
For example:
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
}
}
It seems to me, that you're using MPMedia item.
If so, you don't have to store all these properties at all.
You just need to store persistent ID of the item (convert from UInt64 to string), and later fetch MPMediaItem by using MPMediaQuery with predicate, something like this:
func findSong(persistentIDString: String) -> MPMediaItem? {
let predicate = MPMediaPropertyPredicate(value: persistentIDString, forProperty: MPMediaItemPropertyPersistentID)
let songQuery = MPMediaQuery()
songQuery.addFilterPredicate(predicate)
return songQuery.items.first
}