I am looking for a more efficient solution for exhaustively comparing objects of the same array to each in Swift. Nesting for-in loops is exhaustive, simple, and returns correct results but the time complexity is O(n^2) which nobody recommends when it comes to performance and larger data sets. I hunted around for solutions for a long time, and only found people describing the for-in loop scenario as a classic example of quadratic time complexity. Help?
EDIT (now includes func tableView(tableView: cellForRowAt:) code ) :
Ok let’s go deeper as requested.
Think of a UITableView where you have an array of Event objects.
Event is a class, and you want to exhaustively check the array’s Event objects against each other for conflicts.
The array is already sorted chronologically by each Event element’s startTike property. There could be duplicate events because maybe a user would want that (who knows why, but that’s the end user’s choice to make).
Each Event object has a DateInterval property for this check.
If a conflict is found, you will need to run logic to evaluate the conflict.
If certain criteria are met, then you set a Boolean property (isConflicted) on the Event object.
The resulting array is then used to pass the indexPath.row specific Event object to a custom UITableViewCell for presentation in the UITableView where that ‘isConflicted’ Boolean is integral to the UITableViewCell’s layout/presentation.
Here is the nested for in loop solution I have working just fine, though the run time complexity is not great.
apologies for not including code earlier. i, mistakenly, didn't think it was necessary.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// unwrap my custom cell
guard let cell = tableView.dequeueReusableCell(withIdentifier: "eventCell", for: indexPath) as? EventDetailTableViewCell else {
return UITableViewCell()
}
// get the relevant section's events array from EventModelController.shared.groupedEventsDictionary check for nil
guard let events = EventModelController.shared.groupedEventsDictionary[sections[indexPath.section]] else {
print("ERROR: no event value found in EventModelController.shared.groupedEventsDictionary[sections[indexPath.section]] in EventsTableViewController.swift -> tableView(cellForRowAt:) - line 117.")
return UITableViewCell()
}
var eventDictionary: [DateInterval : Event] = [:]
for event in events {
eventDictionary[event.dateInterval] = event
}
// START: - option with time complexity O(n^2) that always works
// check the array for conflicts and set relevant event's isConflicted property
for event in events {
for anotherEvent in events {
// ensure that we don't redundantly check the same events against themselves
if event != anotherEvent {
if event.dateInterval.intersects(anotherEvent.dateInterval) {
if event.stopTime == anotherEvent.startTime || event.startTime == anotherEvent.stopTime {
event.isConflicted = false
} else {
event.isConflicted = true
}
}
}
}
}
// END: - option with time complexity O(n^2) that always works
// Configure the cell...
cell.event = events[indexPath.row]
return cell
}
There is a suggestion.
extension Array where Element: Hashable {
var containsDouplicated: Bool {
Set<Element>.init(self).count < self.count
}
}
I created this extension in order to use power of Sets to eliminate duplicated values, and after that I checked if the set (which has only unique values) has the same number of elements.
I think this is O(2n) so O(n).
There is an example of how to use it.
let a1 = [1,2,3]
let a2 = [1,2,3,4,1]
a1.containsDouplicated // false
a2.containsDouplicated // true
extension Array where Element: Hashable {
var containsDouplicated: Bool {
Set<Element>.init(self).count < self.count
}
}
EDIT: After your edit I suggest you to make Event : Hashable and implement a kind of:
extension Array where Element: Hashable {
var containsDouplicates: Bool {
Set<Element>.init(self).count < self.count
}
var orderedUniqueElements: [Element] {
var uniq: Set<Element> = .init(self)
let filtered: [Element] = self.filter { element -> Bool in
if !uniq.contains(element) {
return false
} else {
uniq.remove(element)
}
return true
}
return filtered
}
}
in order to preserve indexPath.
Related
In my WalletTableViewController I have two functions, used to calculate the Wallet Value:
A. updateCellValue() Is called by reloadData() with the tableView and uses indexPath.row to fetch a value (price) and an amount (number of coins) corresponding to the cell and make a calculation to get the total value of that coin (amountValue = value * amount). That is then saved with Core Data.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! WalletTableViewCell
cell.delegate = self
cell.amountTextField.delegate = self
updateCellValue(cell, atRow: indexPath.row)
return cell
}
func updateCellValue(_ walletTableViewCell: WalletTableViewCell, atRow row: Int) {
var newCryptos : [CryptosMO] = []
var doubleAmount = 0.0
if CoreDataHandler.fetchObject() != nil {
newCryptos = CoreDataHandler.fetchObject()!
}
cryptoPrice = cryptos[row].code!
guard let cryptoDoublePrice = CryptoInfo.cryptoPriceDic[cryptoPrice] else { return }
let selectedAmount = newCryptos[row]
guard let amount = selectedAmount.amount else { return }
var currentAmountValue = selectedAmount.amountValue
doubleAmount = Double(amount)!
let calculation = cryptoDoublePrice * doubleAmount
currentAmountValue = String(calculation)
CoreDataHandler.editObject(editObject: selectedAmount, amount: amount, amountValue: currentAmountValue)
updateWalletValue()
}
B. updateWalletValue() Is a function that fetches all the amountValue objects in Core Data and adds them together to calculate the Wallet Value.
func updateWalletValue() {
var items : [CryptosMO] = []
if CoreDataHandler.fetchObject() != nil {
items = CoreDataHandler.fetchObject()!
}
total = items.reduce(0.0, { $0 + Double($1.amountValue)! } )
WalletTableViewController.staticTotal = total
}
In my MainViewController, the Wallet Value is displayed too, but how can I refresh it's value?
func updateMainVCWalletLabel() {
//... what can I do here??
}
This works great for the WalletViewController of course with the TableView and indexPath, but how can I call updateCellValue from the MainViewController to keep the value updated?
The WalletViewController is instantiated and pushed from the MainViewController :
#IBAction func walletButtonTapped(_ sender: Any) {
let walletViewController = self.storyboard?.instantiateViewController(withIdentifier: "walletTableViewController")
self.present(walletViewController!, animated: true)
}
If you want to use a single method in multiple view controllers you should implement that method where you can call that method from anywhere. For example you can use singleton class here.
Create a swift file and name it as your wish (like WalletHelper or WalletManager)
Then you will get a file with the following format
class WalletHelper: NSObject
{
}
Create a shared instance for that class
static let shared = WalletHelper()
Implement the method you want
func getWalletValue() -> Float {
// write your code to get wallet value`
// and return the calculated value
}
Finally call that method like
let walletValue = WalletHelper.shared. getWalletValue()
WalletHelper.swift looks like
import UIKit
class WalletHelper: NSObject
{
static let shared = WalletHelper()
func getWalletValue() -> Float {
// write your code to get wallet value
// and return the calculated value
}
}
Update (old answer below)
To me it is absolutly unclear what you want to achieve: Which value do you want to be updated? The staticTotal?
Seems a litte like an XYProblem to me. As #vadian commented yesterday, please clearly describe where the data is stored, how the controllers are connected, what you want to update when in order to achieve what. You could also provide a MCVE which makes clear what you are asking, instead of adding more and more code snippets.
And, even more interesting: Why do you modify CoreData entries (CoreDataHandler.editObject) when you are in the call stack of tableView(_: cellForRowAt:)? Never ever ever do so! You are in a reading case - reloadData is intended to update the table view to reflect the data changes after the data has been changed. It is not intended to update the data itself. tableView(_: cellForRowAt:) is called many many times, especially when the user scrolls up and down, so you are causing large write impacts (and therefore: performance losses) when you write into the database.
Old Post
You could just call reloadData on the table view, which then will update it's cells.
There are also a few issues with your code:
Why do you call updateWalletValue() that frequently? Every time a cell is being displayed, it will be called, run through the whole database and do some reduce work. You should cache the value and only update it if the data itself is modified
Why do you call fetchObject() twice (in updateWalletValue())?
You should do this:
func updateWalletValue() {
guard let items:[CryptosMO] = CoreDataHandler.fetchObject() else {
WalletTableViewController.staticTotal = 0.0
return
}
total = items.reduce(0.0, { $0 + Double($1.amountValue)! } )
WalletTableViewController.staticTotal = total
}
Please forgive if this is a duplicate question. I did search as thoroughly as I could, but none of the other questions that I've seen regarding core data in a UITableView seem to quite fit what I am trying to do.
Basically, I have two "tables" in my Core Data:
cstProjects and plProjects
Both tables have attributes called "propertyOwner" and "propertyID" (among others), that are set as Strings.
Next, in a ViewController, I have a table view instance, with the style set to Subtitle, and the cell identifier set to "projectCell".
Because I have two tables in my core data, I initialize the table to having two sections. I would like the first section to show all of the projects in the cstProjects table, and the second section to show all of the projects in the plProjects table.
Here is my ViewController code so far. I've put comments in, to explain it as best as I can. The functions that I have created might be a bit overkill to figure out how many "rows" each section should have, but I didn't know of a simpler way of going about it.
In the code below, where you see the double question marks "??" is where I am not sure what to put in order to show the current "row" of the type of project that we're currently iterating through.
Ultimately though, if I had to simplify my question down as much as possible, I just need to know how to show the rows of a core data table in a UITableView.
import UIKit
import CoreData
class LoadProjectViewController: UIViewController, UITableViewDataSource, NSFetchedResultsControllerDelegate {
let projectSectionCount: Int = 2
let cstSection: Int = 0
let plSection: Int = 1
// Implement UITableViewDataSource methods
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return projectSectionCount
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch(section) {
case cstSection:
return getCSTProjects()
case plSection:
return getPLProjects()
default:
return 0
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("projectCell")! as UITableViewCell
switch (indexPath.section) {
case cstSection:
//this is where I need to set the property owner for the CST Project
cell.textLabel!.text = ??
//this is where I need to set the project ID for the CST Project
cell.detailTextLabel!.text = ??
case plSection:
//this is where I need to set the property owner for the PL Project
cell.textLabel!.text = ??
//this is where I need to set the project ID for the PL Project
cell.detailTextLabel!.text = ??
default:
cell.textLabel!.text = "Unknown"
}
return cell
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch section {
case cstSection:
return "Standing Timber Projects"
case plSection:
return "Purchased Logs"
default:
return "Unknown"
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func getCSTProjects() -> Int {
let appDel:AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
let context: NSManagedObjectContext = appDel.managedObjectContext
let request = NSFetchRequest(entityName: "CSTProjects")
var results = [AnyObject]()
request.returnsObjectsAsFaults = false
do {
results = try context.executeFetchRequest(request)
} catch let error {
print("There was an error loading the data. \(error)")
}
if (results.count > 0) {
return results.count
} else {
return 0
}
}
func getPLProjects() -> Int {
let appDel:AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
let context: NSManagedObjectContext = appDel.managedObjectContext
let request = NSFetchRequest(entityName: "PLProjects")
var results = [AnyObject]()
request.returnsObjectsAsFaults = false
do {
results = try context.executeFetchRequest(request)
} catch let error {
print("There was an error loading the data. \(error)")
}
if (results.count > 0) {
return results.count
} else {
return 0
}
}
}
The first thing you need to do in that method is look up which instance you want to display. To do that, you should modify your getCSTProjects and getPLProjects methods to save the results of the fetch request instead of throwing them away. You'd do that by adding new attributes to the view controllers to hold the arrays. On a related note, you really do not want to call those methods from tableView(tableView:, numberOfRowsInSection:), because that method can get called a lot. Doing fetches there pretty much guarantees poor performance.
To get a specific instance, look it up in one of the arrays you now have, using the indexPath argument to get the array index.
Once you have that specific instance, you need to look up values of the attributes you have declared on those Core Data entity types. What to put there depends on how your CSTProjects and PLProjects entities are defined and on whether you have created subclasses of NSManagedObject for them.
For example if your CSTProjects entity has a string attribute called name that you want to use as the title, and you do not have an NSManagedObject subclass, you could use this:
cell.textLabel!.text = currentInstance.value(forKey: "name") as? String
If you do have an NSManagedObject subclass (and it's always a good idea), this would be
cell.textLabel!.text = currentInstance.name
Rather than try to merge two entities (or tables) into a single fetchedResultsController, it would be easier to bring the data into a single table and access that.
If the tables are sufficiently similar, you could do this in a single table with a type code to differentiate the entries where you need to, for example using a filtered view to make the tables look like one type or the other in other parts of your code. This would mean making any fields limited to one type optional so the other could ignore them. Then the frc could use the code field as a section break.
Alternatively, if the tables are sufficiently different you could add a third table, say Projects, which had optional one-to-one links to the other two tables and drive the frc off of that third table using the links to populate fields in your tableView in the configure cell delegate method.
Besides making it easier to build the current frc structure, either of these approaches would make adding a third or fourth type much easier if your requirements expand in the future, and would avoid the complications that inevitably follow from bending an API to fit a use it wasn't designed for.
Here's what I'm trying to do:
Provide a tableView that allows the user to select a formula name (an enum) that will be set as their default
In this view I'll place a checkmark next to the formula that is the current default
Their chosen default formula is what will determine which formula is used to calculate a 1 rep maximum amount (the theoretical amount the user could lift based on actual lifts of lesser weight)
After calculating the value, the user will be able to save a record of the lifts they did, the formula used to calculate the maximum, and the calculated maximum weight.
That record will be saved in Core Data with one of the entities being Lift which will simply be the formula name
I've created a class that I want to handle the work of providing the current default formula to what ever part of the app needs it as well as set the default when a new one is selected.
Here is my enum of the formula names:
enum CalculationFormula: Int {
case Epley
case Brzychi
case Baechle
case Lander
case Lombardi
case MayhewEtAl
case OConnerEtAl
}
and with help from the SO community, I created this class to handle the management of userDefaults:
class UserDefaultsManager: NSUserDefaults {
let formulas = [CalculationFormula.Baechle, CalculationFormula.Brzychi, CalculationFormula.Epley, CalculationFormula.Lander, CalculationFormula.Lombardi, CalculationFormula.MayhewEtAl, CalculationFormula.OConnerEtAl]
let formulaNameDictionary: [CalculationFormula : String] =
[.Epley : "Epley", .Brzychi: "Brzychi", .Baechle: "Baechle", .Lander: "Lander", .Lombardi: "Lombardi", .MayhewEtAl: "Mayhew Et.Al.", .OConnerEtAl: "O'Conner Et.Al"]
let userDefaults = NSUserDefaults.standardUserDefaults()
func getPreferredFormula() -> CalculationFormula? {
guard NSUserDefaults.standardUserDefaults().dictionaryRepresentation().keys.contains("selectedFormula") else {
print("No value found")
return nil
}
guard let preferredFormula = CalculationFormula(rawValue: NSUserDefaults.standardUserDefaults().integerForKey("selectedFormula")) else {
print("Wrong value found")
return nil
}
return preferredFormula
}
func setPreferredFormula(formula: CalculationFormula) {
NSUserDefaults.standardUserDefaults().setInteger(formula.rawValue, forKey: "selectedFormula")
}
You can see I have an array of the enums in the order I want them displayed in the tableView and a dictionary of the enums so I can get each enum's string representation to display in each cell of the tableView. Here's how I populate the cell text label which works:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("formulasCell", forIndexPath: indexPath)
let currentFormula = formulas[indexPath.row].formulaName
cell.textLabel?.text = currentFormula
return cell
}
and here's where I'm setting the checkmark anytime a cell in the tableView is selected
func refresh() {
let preferredFormula = defaults.getPreferredFormula()
for index in 0 ... formulas.count {
let indexPath = NSIndexPath(forItem: index, inSection: 0)
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
cell.accessoryType = preferredFormula == index ? .Checkmark : .None
}
}
}
As I mentioned at the beginning, I need to do many different things with this enum but to keep this question focused on one thing, I'll stay with this checkmark example which now doesn't work after creating my UserDefaultsManager class
The problem is obvious - preferredFormula is now an enum and I can't compare that to the index value which is an Int - but the solution is not. I could get the raw value of the enum but the rawValues aren't guaranteed to be in alignment with the cell indexPaths. Some ideas I've had are:
I could probably change the order of the enum cases so their raw values match the order I've put them in my formulas array, but that seems silly and unreliable
I could use the array index values but that seems equally silly and unreliable
If I just use the array, I don't have the string representations of the cases to display in the cells
It seems that using the array and dictionary together is a viable but the best I could come up with is maybe creating another dictionary that maps the enums to Ints but that would have the same issues I just listed.
Any guidance someone could provide would be greatly appreciated.
You seem to have made things a little more complicated than they need to be.
Firstly, you can use a String raw value for your enum and avoid the associated dictionary:
enum CalculationFormula: String {
case Epley = "Epley"
case Brzychi = "Brzychi"
case Baechle = "Baechle"
case Lander = "Lander"
case Lombardi = "Lombardi"
case MayhewEtAl = "Mayhew Et.Al."
case OConnerEtAl = "O'Conner Et.Al"
}
Second, Your UserDefaultsManager doesn't need to subclass NSUserDefaults, it is simply some utility functions. Also, you are doing a lot of checking in getPreferredFormula that you don't need to. I would suggest re-writing that class to use a computed property like so:
class UserDefaultsManager {
static let sharedInstance = UserDefaultsManager()
private var defaultsFormula: CalculationFormula?
var preferredFormula: CalculationFormula? {
get {
if self.defaultsFormula == nil {
let defaults = NSUserDefaults.standardUserDefaults()
if let defaultValue = defaults.objectForKey("selectedFormula") as? String {
self.defaultsFormula = CalculationFormula(rawValue: defaultValue)
}
}
return self.defaultsFormula
}
set {
self.defaultsFormula = newValue
let defaults = NSUserDefaults.standardUserDefaults()
if (self.defaultsFormula == nil) {
defaults.removeObjectForKey("selectedFormula")
} else {
defaults.setObject(self.defaultsFormula!.rawValue, forKey: "selectedFormula")
}
}
}
}
I have made the class a singleton; although this may have an impact on testability it simplifies issues that could arise if the default is changed in multiple places.
The appropriate place to set/clear the check mark is in cellForRowAtIndexPath so that cell reuse is handled appropriately. This code assumes that formulas is an array of CalculationFormula:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("formulasCell", forIndexPath: indexPath)
let currentFormula = formulas[indexPath.row]
cell.textLabel?.text = currentFormula.rawValue
let preferredFormula = UserDefaultsManager.sharedInstance.preferredFormula
cell.accessoryType = currentForumula == preferredFormula ? .Checkmark : .None
return cell
}
I would really like to use a more simple classic try catch block in my Swift code but I can't find anything that does it.
I just need:
try {
// some code that causes a crash.
}
catch {
// okay well that crashed, so lets ignore this block and move on.
}
Here's my dilema, when the TableView is reloaded with new data, some information is still siting in RAM which calls didEndDisplayingCell on a tableView with a freshly empty datasource to crash.
So I frequently thrown the exception Index out of range
I've tried this:
func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
do {
let imageMessageBody = msgSections[indexPath.section].msg[indexPath.row] as? ImageMessageBody
let cell = tableView.dequeueReusableCellWithIdentifier("ImageUploadCell", forIndexPath: indexPath) as! ImageCell
cell.willEndDisplayingCell()
} catch {
print("Swift try catch is confusing...")
}
}
I've also tried this:
func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
print(indexPath.section)
print(indexPath.row)
if msgSections.count != 0 {
if let msg = msgSections[indexPath.section].msg[indexPath.row] as? ImageMessageBody {
let cell = tableView.dequeueReusableCellWithIdentifier("ImageUploadCell", forIndexPath: indexPath) as! ImageCell
cell.willEndDisplayingCell()
}
}
}
This is a very low priority block of code, and I have wasted a lot of time with trial and error figuring out which error handler built into swift works for what seems to be extremely unique situations when I have tons of scenarios just like this one where the code can crash and it will not have any effect on the user experience.
In short, I don't need anything fancy but Swift seems to have extremely specific error handlers that differ based on whether I'm getting a value from a functions return value or getting a value from an array's index which may not exist.
Is there a simple try catch on Swift like every other popular programming language?
As suggested in comments and other answers it is better to avoid this kind of situations. However, in some cases you might want to check if an item exists in an array and if it does safely return it. For this you can use the below Array extension for safely returning an array item.
Swift 5
extension Collection where Indices.Iterator.Element == Index {
subscript (safe index: Index) -> Iterator.Element? {
return indices.contains(index) ? self[index] : nil
}
}
Swift 4
extension Collection where Indices.Iterator.Element == Index {
subscript (safe index: Index) -> Iterator.Element? {
return indices.contains(index) ? self[index] : nil
}
}
Swift 3
extension Collection where Indices.Iterator.Element == Index {
subscript (safe index: Index) -> Generator.Element? {
return indices.contains(index) ? self[index] : nil
}
}
Swift 2
extension Array {
subscript (safe index: Int) -> Element? {
return indices ~= index ? self[index] : nil
}
}
This way you'll never hit Index out of range
You'll have to check if the item is nil
refer this question for more
Trying the Swift3 code in a Playground in Xcode 8.3.2 still leads to a
"crash" when I do let ar = [1,3,4], then let v = ar[5]. Why? – Thomas
Tempelmann May 17 at 17:40
You have to use our customized subscript so instead of let v = ar[5], it wll be let v = ar[safe: 5].
Default getting value from array.
let boo = foo[index]
Add use the customized subscript.
let boo = fee[safe: index]
// And we can warp the result using guard to keep the code going without throwing the exception.
guard let boo = foo[safe: index] else {
return
}
Swift's Error Handling (do/try/catch) is not the solution to runtime exceptions like "index out of range".
A runtime exception (you might also see these called trap, fatal error, assertion failure, etc.) is a sign of programmer error. Except in -Ounchecked builds, Swift usually guarantees that these will crash your program, rather than continuing to execute in a bad/undefined state. These sorts of crashes can arise from force-unwrapping with !, implicit unwrapping, misuse of unowned references, integer operations/conversions which overflow, fatalError()s and precondition()s and assert()s, etc. (And, unfortunately, Objective-C exceptions.)
The solution is to simply avoid these situations. In your case, check the bounds of the array:
if indexPath.section < msgSections.count && indexPath.row < msgSections[indexPath.section].msg.count {
let msg = msgSections[indexPath.section].msg[indexPath.row]
// ...
}
(Or, as rmaddy says in comments — investigate why this problem is occurring! It really shouldn't happen at all.)
Swift 4:
extension Collection where Indices.Iterator.Element == Index {
subscript (exist index: Index) -> Iterator.Element? {
return indices.contains(index) ? self[index] : nil
}
}
Usage:
var index :Int = 6 // or whatever number you need
if let _ = myArray[exist: index] {
// do stuff
}
or
var index :Int = 6 // or whatever number you need
guard let _ = myArray[exist: index] else { return }
Don't it's not considered a unusual state error, its a state of its completely screwed up, do something like
guard array.indicies.contains(index) else { return }
Swift 5
This answer in Swift 5 become:
extension Collection where Indices.Iterator.Element == Index {
subscript (safe index: Index) -> Iterator.Element? {
return indices.contains(index) ? self[index] : nil
}
}
Otherwise from Swift 4 there's the ability to have clauses on associated types and it can become:
extension Collection {
subscript (safe index: Index) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}
You can try a different approach to this. Will surely work!
if msgSections != nil {
for msg in msgSections[indexPath.section] {
if msgSections[indexPath.section].index(of: msg) == indexPath.row {
(Code)
}
}
What is the best practice to check if an array of objects has been loaded in Swift?
Say, if I declare an array in a class, and it is lazily loaded. Apple's docs say the array declaration / initialization is something like
var events = [Event]()
I suppose the above means the array is already initialized (ie. not nil).
Then, I need a function like:
func getEvents() -> [Event] {
// check if array is nil, and if so, load the events (not: which could be 0 events)
return events
}
In Java, I would declare something like
ArrayList<Event> events;
public ArrayList<Event> getEvents() {
if(!events) { // null means never been loaded
events = new ArrayList<Event>();
events = loadEvents(); // load the events, which could be zero
}
}
What is the best practice to code the equivalent in Swift?
Lazy Stored Property
In Swift you can declare a lazy stored property: it does exactly what you need.
struct Event { }
class Foo {
lazy var events: [Event] = { self.loadEvents() }()
private func loadEvents() -> [Event] {
print("Loading events")
return [Event(), Event(), Event()]
}
}
We associated a closure to the events property. The closure defines how the events property should be populated and will be executed only when the property is used.
let foo = Foo()
print(foo.events) // now the events property is populated
You may experience an Array in these ways:
var vect = [String]()
if vect.isEmpty {
print ("true")
}else{
print("false")
}
Or
func ceck()->Void{
guard !vect.isEmpty else{
print ("true")
return // return, break, or another logic this is example
}
}