class_copyPropertyList not working in Swift 5. Whats the reason? - ios

class_copyPropertyList is giving empty properties in Swift 5 and it was working correct in Swift 3
extension NSObject {
func toDictionary(from classType: NSObject.Type) -> [String: Any] {
var propertiesCount : CUnsignedInt = 0
let propertiesInAClass = class_copyPropertyList(classType, &propertiesCount)
let propertiesDictionary : NSMutableDictionary = NSMutableDictionary()
for i in 0 ..< Int(propertiesCount) {
let property = propertiesInAClass?[i]
let strKey = NSString(utf8String: property_getName(property)) as String?
if let key = strKey {
propertiesDictionary.setValue(self.value(forKey: key), forKey: key)
}
}
return propertiesDictionary as! [String : Any]
}
}
// call this for NSObject subclass
let product = Product()
let dict = product.toDictionary(from: Product.self)
print(dict)

Related

i want to add only the values of 2 dictionaries

I have a swift code which is auto migrated from objective C to swift where is am getting an error
"Binary operator '+' cannot be applied to two 'Dictionary.Values' operands".
Because i trying to add the values of a dictionary which is not allowed. but how to add the values others wise. Below is the code where i am getting this error.
var result = cellAttrDict.values + supplHeaderAttrDict.values
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var i: Int
var begin = 0
var end = unionRects.count
var cellAttrDict: [AnyHashable : Any] = [:]
var supplHeaderAttrDict: [AnyHashable : Any] = [:]
var supplFooterAttrDict: [AnyHashable : Any] = [:]
var decorAttrDict: [AnyHashable : Any] = [:]
for i in 0..<unionRects.count {
if rect.intersects(unionRects[i] as! CGRect) {
begin = i * unionSize
break
}
}
i = unionRects.count - 1
while i >= 0 {
if rect.intersects(unionRects[i] as! CGRect) {
end = min((i + 1) * unionSize, allItemAttributes.count)
break
}
i -= 1
}
for i in begin..<end {
let attr = allItemAttributes[i] as? UICollectionViewLayoutAttributes
if rect.intersects(attr?.frame ?? 0 as! CGRect) {
switch attr?.representedElementCategory {
case .supplementaryView?:
if (attr?.representedElementKind == CHTCollectionElementKindSectionHeader) {
if let indexPath = attr?.indexPath, let attr = attr {
supplHeaderAttrDict[indexPath] = attr
}
} else if (attr?.representedElementKind == CHTCollectionElementKindSectionFooter) {
if let indexPath = attr?.indexPath, let attr = attr {
supplFooterAttrDict[indexPath] = attr
}
}
case .decorationView?:
if let indexPath = attr?.indexPath, let attr = attr {
decorAttrDict[indexPath] = attr
}
case .cell?:
if let indexPath = attr?.indexPath, let attr = attr {
cellAttrDict[indexPath] = attr
}
#unknown default:
break
}
}
}
var result = cellAttrDict.values + supplHeaderAttrDict.values
result = result + supplFooterAttrDict.values
result = result + decorAttrDict.values
return result as? [UICollectionViewLayoutAttributes]
}
Dictionary.Values is a special lightweight view to the dictionary values to avoid allocating extra memory.
To be able to concatenate multiple values you have to create regular arrays
var result = Array(cellAttrDict.values) + Array(supplHeaderAttrDict.values)
result += Array(supplFooterAttrDict.values)
result += Array(decorAttrDict.values)
return result as? [UICollectionViewLayoutAttributes]

How to add array of dictionaries from NSArray to Existing NSMutableArray in swift?

In my project I have one NSMutableArray and NSArray named logOrderMutArray and openOrderArray, here I want to merge the openOrderArray with logOrderMutArray based on same "AccountNumber". If both array have same "AccountNumber" means I want to replace the particular index in logOrderMutArray with openOrderArray index else if "AccountNumber" was not same in some index means I want to add the openOrderArray to logOrderMutArray.
But I have made some mistakes. How can I solve the issue?
Here is my code:
func mergeOrderAndOpenOrderArrayData() {
let validateMutArray = NSMutableArray()
for i in 0..<self.logOrderMutArray.count {
var dictMutableCopy = NSDictionary()
var editDict = NSMutableDictionary()
for j in 0..<self.openOrderArray.count {
var newDict = NSDictionary()
if let logOrderfips:NSNumber = (logOrderMutArray[i] as AnyObject).value(forKey: "FIPS") as? NSNumber {
if let openOrderfips:NSNumber = (openOrderArray[j] as AnyObject).value(forKey: "FIPS") as? NSNumber {
if let logOrderAccNum:String = (logOrderMutArray[i] as AnyObject).value(forKey: "AccountNumber") as? String {
if let openOrderAccNum:String = (openOrderArray[j] as AnyObject).value(forKey: "AccountNumber") as? String {
if logOrderfips == openOrderfips && logOrderAccNum == openOrderAccNum {
dictMutableCopy = logOrderMutArray[i] as! NSDictionary
editDict = NSMutableDictionary(dictionary: dictMutableCopy)
newDict = openOrderArray[j] as! NSDictionary
editDict["newlyAdded"] = newDict
logOrderMutArray.replaceObject(at: i, with: editDict)
}
else {
logOrderMutArray.add(openOrderArray[j])
}
}
}
}
}
}
}
}

String interpretation in Swift

I have a struct that looks like this:
struct colorShapeSize{
let color: String!
let shape: String!
let size: UIImage!
}
and I have a string that looks something like this:
"color:{Blue}shape:{round}size:{medium}"
All of the strings will be in the same format (i.e. color will always come first, shape second, and size third).
How would I extract the data from the string and put it into a colorShapeSize struct?
How about this?
struct ColorShapeSize {
let color: String
let shape: String
let size: String
init(rawValue: String) {
var dictionary: [String: String] = [:]
var sorted = rawValue.components(separatedBy: "}").filter({ return $0.components(separatedBy: ":{").count == 2 })
for s in sorted {
let kv = s.components(separatedBy: ":{")
let key = kv[0]
let value = kv[1]
dictionary[key] = value
}
color = dictionary["color"] ?? ""
shape = dictionary["shape"] ?? ""
size = dictionary["size"] ?? ""
}
}
let str = "color:{Blue}shape:{round}size:{medium}"
let css = ColorShapeSize(rawValue: str)
print(css.color, css.shape, css.size)
try this, it will extract the string in array, then you can do what you want with the value
func test() {
let givenString = "color:{Blue}shape:{round}size:{medium}"
var results = [String]()
do {
let regex = try NSRegularExpression(pattern: "\\{(.*?)\\}", options: [])
let tempString = givenString as NSString
regex.enumerateMatches(in: givenString, options: [], range: NSMakeRange(0, givenString.characters.count), using: { (result, flag, stop) in
if let range = result?.rangeAt(1) {
let number = tempString.substring(with: range)
results.append(number)
}
})
print(results) //["Blue", "round", "medium"] (Here you can initialize your struct with the values)
}
catch(let error) {
print("Unable to extract string : \(error.localizedDescription)")
}
}

Force casts should be avoided

I am getting "Force cast violation : Force casts should be avoided warning"
on my code :
daysCombinedFinal = daysCombined[0] as! [Any?]
The screenshot is attached below:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "location", for: indexPath) as? TravelShopCustomCell {
if !isFirstTime && self.nameArray.count != 0 {
var daysCombined = [Any]()
var daysCombinedFinal = [Any?]()
daysCombined = [self.combinedArray[0]]
daysCombinedFinal = daysCombined[0] as? [Any?] ?? []
let str = daysCombinedFinal.flatMap { $0 as? String }.joined(separator:" ")
var startAMCombined = [Any]()
var startAMCombinedFinal = [Any?]()
startAMCombined = [self.combinedStartAMArray[0]]
startAMCombinedFinal = startAMCombined[0] as? [Any?] ?? []
var endPMCombined = [Any]()
var endPMCombinedFinal = [Any?]()
endPMCombined = [self.combinedEndPMArray[0]]
endPMCombinedFinal = endPMCombined[0] as? [Any?] ?? []
cell.operatingDaysLabel.text = str
let isAMEqual = checkArrayStatus(testArray: [startAMCombinedFinal as Any])
let isPMEqual = checkArrayStatus(testArray: [endPMCombinedFinal as Any])
if isAMEqual && isPMEqual {
self.mergedArray = arrayMerger(array1: startAMCombinedFinal, array2: endPMCombinedFinal)
}
let startTimeString = self.mergedArray[0] as? String
let endTimeString = self.mergedArray[1] as? String
cell.operatingTimeLabel.text = startTimeString! + " - " + endTimeString!
return cell
} else if isFirstTime && self.nameArray.count != 0 {
var daysCombined = [Any]()
var daysCombinedFinal = [Any?]()
daysCombined = [self.combinedArray[indexPath.row]]
daysCombinedFinal = daysCombined[0] as! [Any?]
let str = daysCombinedFinal.flatMap { $0 as? String }.joined(separator:" ")
var startAMCombined = [Any]()
var startAMCombinedFinal = [Any?]()
startAMCombined = [self.combinedStartAMArray[indexPath.row]]
startAMCombinedFinal = startAMCombined[0] as! [Any?]
var endPMCombined = [Any]()
var endPMCombinedFinal = [Any?]()
endPMCombined = [self.combinedEndPMArray[indexPath.row]]
endPMCombinedFinal = endPMCombined[0] as! [Any?]
cell.operatingDaysLabel.text = str
let isAMEqual = checkArrayStatus(testArray: [startAMCombinedFinal as Any])
let isPMEqual = checkArrayStatus(testArray: [endPMCombinedFinal as Any])
if isAMEqual && isPMEqual {
self.mergedArray = arrayMerger(array1: startAMCombinedFinal, array2: endPMCombinedFinal)
}
let startTimeString = self.mergedArray[0] as? String
let endTimeString = self.mergedArray[1] as? String
cell.operatingTimeLabel.text = startTimeString! + " - " + endTimeString!
return cell
}
return cell
} else {
fatalError("Dequeueing SomeCell failed")
}
}
The array declaration is:
var dateArray = [Any]()
var endAmTimeArray = [Any]()
var endPmTimeArray = [Any]()
var startAmTimeArray = [Any]()
var startPmTimeArray = [Any]()
var combinedArray = [Any]()
var combinedStartAMArray = [Any]()
var combinedEndPMArray = [Any]()
var mergedArray = [Any?]()
Your problem has relation with concept of 'optional' & 'unwrapper'. Here is brief about both and difference between them: How to understand ! and ? in swift?
? (Optional) indicates your variable may contain a nil value while ! (unwrapper) indicates your variable must have a memory (or value) when it is used (tried to get a value from it) at runtime.
In your case, you are trying to get value from array using index number. Now type of elements of your array is 'Any'
So, there may be any kind of value/information contained by element of array. It will result into app crash, if you try to force unwrap a value of element, when it won't return a value or value with type that you're casting with unwrapper.
Here is basic tutorial in detail, by Apple Developer Committee.
This warning is indicating you, that your app may crash on force unwrapping optional value.
As a solution you should use ? (optional) with if-let block, to avoid force unwrapping and safe execution of your code, like:
if let daysC = daysCombined[0] as? [Any] {
daysCombinedFinal = daysC
}
Share your full source code, to get better solution of your query as you have not shared declaration of your array in your question. Because I've confusion about optional array [Any?] elements. Swift not allows array elements as optional.
Update : By considering elements of all arrays as 'Dictionary < String : Any >', forced unwraps from array assignments are removed here.
var daysCombined = [Any]()
var daysCombinedFinal = [Any?]()
daysCombined = [self.combinedArray[indexPath.row]]
// Update 1
// if combinedArray is an array of array
if let arrayElement = daysCombined[0] as? [Any] {
daysCombinedFinal = arrayElement
}
let str = daysCombinedFinal.flatMap { $0 as? String }.joined(separator:" ")
var startAMCombined = [Any]()
var startAMCombinedFinal = [Any?]()
startAMCombined = [self.combinedStartAMArray[indexPath.row]]
// Update 2
if let arrayElement = startAMCombined[0] as? [Any] {
startAMCombinedFinal = arrayElement
}
var endPMCombined = [Any]()
var endPMCombinedFinal = [Any?]()
endPMCombined = [self.combinedEndPMArray[indexPath.row]]
// Update 3
if let arrayElement = endPMCombined[0] as? [Any] {
endPMCombinedFinal = arrayElement
}
cell.operatingDaysLabel.text = str

How to iterate through Swift array and use data in iOS Chart?

I have a bar chart, which I am trying to populate through data pulled in from an array.
I am trying to iterate through this array in order to populate my chart.
Please note: in this example below there are 3 winningStreakDM variables hardcoded for testing purposes, but in 'real life' the number could be different each API call as it depends on how many 'users' are in that league.
This is why I need to iterate through the array and structure the data to suit.
let winningStreakDM1 : [String : Any] = [
"id" : 2,
"user_name" : "Dicky",
"winning_streak" : 5
]
let winningStreakDM2 : [String : Any] = [
"id" : 6,
"user_name" : "G",
"winning_streak" : 2
]
let winningStreakDM3 : [String : Any] = [
"id" : 5,
"user_name" : "Sultan",
"winning_streak" : 0
]
My issue is that I don't know how to iterate through the initial array to structure my data for it to work with the above code.
This is my full script:
import UIKit
import Charts
class CommunityLeagueStatsVC: UIViewController {
// GRAPHS *********
#IBOutlet weak var chartView: BarChartView!
var values = [BarChartDataEntry]()
// ****************
//********CHART VARIABLES**************//
//WINS LOSSES DRAWS
var winStreak: Double = 0.0
#IBOutlet weak var leagueStatsScrollView: UIScrollView!
var noDefeats: Bool?
var noWins: Bool?
var biggestWin: String?
var biggestWinTeams: String?
var passedCommunityName: String?
#IBOutlet weak var communityName: UILabel!
var playerId2: String?
var communityId2: String?
var eMail2: String?
override func viewDidLoad() {
super.viewDidLoad()
let winningStreak = ["Wins"]
let gamesWon = [winStreak]
setWinStreakChart(dataPoints: winningStreak, values: gamesWon)
let defaults = UserDefaults.standard
let Email = defaults.string(forKey: "userEmail")
let playerId = defaults.string(forKey: "playerId")
let commsId = defaults.string(forKey: "communityId")
self.playerId2 = playerId
self.communityId2 = commsId
self.eMail2 = Email
}
func setWinStreakChart(dataPoints: [String], values: [BarChartDataEntry]){
xAxis.valueFormatter = WinningStreakFormatter(chartView: self.chartView)
let barChartDataSet = BarChartDataSet(values: values, label: "Winning Streak")
barChartDataSet.colors = ChartColorTemplates.material()
let barChartData = BarChartData(dataSet: barChartDataSet)
barChartData.setValueFont(UIFont.systemFont(ofSize: 12.0))
self.chartView.data = barChartData
}
override func viewDidAppear(_ animated: Bool) {
let myUrl = URL(string: "http://www.xxx.uk/xxx/getLeagueStats.php")
var request = URLRequest(url:myUrl!)
request.httpMethod = "POST"
let postString = "player_id=\(self.playerId2!)&community_id=\(communityId2!)";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
DispatchQueue.main.async
{
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:AnyObject]
print (json!)
if let dict = json?["leagueStats"] as? [String:AnyObject] {
//WINNING STREAK
var values = [BarChartDataEntry]()
if let dataWinStreak = dict["winningStreak"] as? [[String : Any]] {
print ("test one")
for (index,item) in dataWinStreak.enumerated() {
if let yValue = item["winning_streak"] as? Int, let userName = item["user_name"] as? String {
print ("test two")
let barChartDataEntry = BarChartDataEntry(x: Double(index), y: Double(yValue), data: userName as AnyObject?)
values.append(barChartDataEntry)
}
}
self.setWinStreakChart(dataPoints: ["wins"], values: values)
}
catch{
print(error)
}
}
}
task.resume()
}
}
UPDATE:
These are the errors that I am currently receiving:
With a bit of playing around and commenting out the line containing the first error I have got to this stage which correctly shows a graph with values, but no xAxis containing the userNames of each player.
You can create array of BarChartDataEntry this way.
var values = [BarChartDataEntry]()
if let dataWinStreak = dict["winningStreak"] as? [[String : Any]] {
for (index,item) in dataWinStreak.enumerated() {
if let yValue = item["winning_streak"] as? Int, let userName = item["user_name"] as? String {
let barChartDataEntry = BarChartDataEntry(x: index, y: yValue, data: userName)
values.append(barChartDataEntry)
}
}
}
//Now use values array
Edit: You just need to change setWinStreakChart function because now you are working with dynamic data not static one.
func setWinStreakChart(dataPoints: [String], values: [BarChartDataEntry]){
let xAxis : XAxis = self.chartView.xAxis;
xAxis.labelFont = UIFont(name: "HelveticaNeue-Light", size: 10.0)!
xAxis.labelTextColor = UIColor.black
xAxis.drawAxisLineEnabled = false
xAxis.drawGridLinesEnabled = true
xAxis.granularity = 1;
xAxis.labelPosition = .bottom
xAxis.valueFormatter = WinningStreakFormatter(chartView: self.chartView)
let barChartDataSet = BarChartDataSet(values: values, label: "Winning Streak")
barChartDataSet.colors = ChartColorTemplates.material()
let barChartData = BarChartData(dataSet: barChartDataSet)
barChartData.setValueFont(UIFont.systemFont(ofSize: 12.0))
self.chartView.data = barChartData
}
And now call this function after the for loop where we are creating array of BarChartDataEntry.
self.setWinStreakChart(dataPoints: ["wins"], values: values)

Resources