Function in Swift will run, but will not execute "completion" in Swift - ios

I am trying to call a function in Swift. The first time I call it, it runs as intended, but the second time the completion handler if and else statements don't run. Despite this, I still know the function runs because I had it print "RAN" every time it ran. I have no idea what I am doing wrong. Any help?
My Code:
[other code]
self.movieNum = Int(arc4random_uniform(50) + 1)
// Int movieNum is turned into String
let movieNumNSNumber = self.movieNum as NSNumber
let movieNumString: String = movieNumNSNumber.stringValue
self.checkIfMovieHasAlreadyBeenShown(movieID: movieNumString, completion: { seen in
if seen {
var i = 1
var haveAllBeenSeen = 1
while i <= 50 {
let iNSNumber = i as NSNumber
let iString: String = iNSNumber.stringValue
self.checkIfMovieHasAlreadyBeenShown(movieID: movieNumString, completion: { seen in
if seen {
print("SEEN")
}
else {
print("NOT SEEN")
haveAllBeenSeen = 0
}
})
i += 1
}
if haveAllBeenSeen == 0 {
self.swipingView()
}
else {
[other code]
}
else {
return
}
})

Related

UITableView scrolling performance problem

I am currently working as a 5 month junior ios developer.
The project I'm working on is an application that shows the prices of 70 cryptocurrencies realtime with websocket connection.
we used websocket connection, UItableview, UITableViewDiffableDataSource, NSDiffableDataSourceSnapshot while developing the application.
But right now there are problems such as slowdown scrolling or not stop scroling and UI locking while scrolling in the tableview because too much data is processed at the same time.
after i check cpu performance with timer profiler I came to the conclusion that updateDataSource and updateUI functions exhausting the main thread.
func updateDataSource(model: [PairModel]) {
var snapshot = DiffableDataSourceSnapshot()
let diff = model.difference(from: snapshot.itemIdentifiers)
let currentIdentifiers = snapshot.itemIdentifiers
guard let newIdentifiers = currentIdentifiers.applying(diff) else {
return
}
snapshot.appendSections([.first])
snapshot.deleteItems(currentIdentifiers)
snapshot.appendItems(newIdentifiers)
dataSource?.apply(snapshot, animatingDifferences: false, completion: nil)
}
func updateUI(data: SocketData) {
guard let newData = data.data else { return }
guard let current = data.data?.price else { return }
guard let closed = data.data?.lastDayClosePrice else { return }
let dailyChange = ((current - closed)/closed)*100
DispatchQueue.main.async { [self] in
if model.filter({ $0.symbol == newData.pairSymbol }).first != nil {
let index = model.enumerated().first(where: { $0.element.symbol == newData.pairSymbol})
guard let location = index?.offset else { return }
model[location].price = current
model[location].dailyPercent = dailyChange
if calculateLastSignalTime(alertDate: model[location].alertDate) > 0 {
//Do Nothing
} else {
model[location].alertDate = ""
model[location].alertType = ""
}
if let text = allSymbolsView.searchTextField.text {
if text != "" {
filteredModel = model.filter({ $0.name.contains(text) || $0.symbol.contains(text) })
updateDataSource(model: filteredModel)
} else {
filteredModel = model
updateDataSource(model: filteredModel)
}
}
}
delegate?.pricesChange(data: self.model)
}
}
Regards.
ALL of your code is running on the main thread. You have to wrap your entire updateUI function inside a DispatchQueue.global(qos:), and then wrap your dataSource.apply(snapshot) line inside a DispatchQueue.main.async. the dataSource.apply(snapshot) line is the only UI work you're doing in all that code you posted.

How to fix memory issues given by Instrument tools in Swift?

I have memory issues, especially for the error
XPC connection interrupted
The screen is freezing for a few seconds..
So, I've been learning how to use the Instruments tools and try to fix this error. However, I've been trying to find the error in my code and it's apparently not the fault of my code but maybe the libraries?
As a result of this test, I've got some warnings (the purple ones):
Memory Issues (3 leaked types):
- 1 instance of _DateStorage leaked (0x10b1eb060)
- 1 instance of OS_dispatch_data leaked (0x10b0b1ac0)
- 1 32-byte malloc block leaked (x10b1eb040)
Could you tell me how to fix these warnings, knowing there is no backtrace available? Or how could I find somewhere that could tell me to fix those?
EDIT:
Thanks to Instrument tools, I found the function that caused the problem! So, I don't know if it is really about memory or Idk but here's the function!
The accurate and useful error I get is : "Closure #1 in closure #1 in MessagesTableViewController.getLastMessages"
I found here What is a closure #1 error in swift?, the error is probably caused by forced optional types. So, I am going to try to remove those.
func getLastMessages(cell: ContactMessageTableViewCell, index: IndexPath) {
// first, we get the total number of messages in chatRoom
var numberOfMessagesInChatRoom = 0
let previousCellArray = self.tableView.visibleCells as! [ContactMessageTableViewCell]
var index1 = 0
var messages = [JSQMessage]()
var sortedMessages = [JSQMessage]()
var messagesSortedByChatRooms = [String: [JSQMessage]]()
var doesHaveMessagesCount = false
var doesHaveSortedMessagesCount = false
let firstQuery = Constants.refs.databaseChats.queryOrderedByKey()
_ = firstQuery.observe(.childAdded, with: { [weak self] snapshot in
if let data = snapshot.value as? [String: String],
let id = data["sender_id"],
let name = data["name"],
let text = data["text"],
let chatRoom = data["chatRoom"],
!text.isEmpty
{
if let message = JSQMessage(senderId: id, displayName: name, text: text)
{
messages.append(message)
var arrayVariable = [JSQMessage]()
// we wanna get all messages and put it in the array corresponding to the chat room key
if messagesSortedByChatRooms[chatRoom] != nil { // if there is already the chatRoom key in dictionary
if let message1 = messagesSortedByChatRooms[chatRoom] {
arrayVariable = message1
}
arrayVariable.append(message)
messagesSortedByChatRooms[chatRoom] = arrayVariable
} else { // if there isn't the chatRoom key
arrayVariable.append(message)
messagesSortedByChatRooms[chatRoom] = arrayVariable
}
}
}
DispatchQueue.main.async {
// we have to sort messages by date
for (chatRoom, messagesArray) in messagesSortedByChatRooms {
var loopIndex = 0
var lastMessage: JSQMessage?
var array = [JSQMessage]()
for message in messagesArray { // we run through the messages array
array.removeAll()
loopIndex += 1
if loopIndex != 1 {
if message.date > lastMessage!.date {
array.append(message)
messagesSortedByChatRooms[chatRoom] = array
} else {
array.append(lastMessage!)
messagesSortedByChatRooms[chatRoom] = array
}
} else {
lastMessage = message
if messagesArray.count == 1 {
array.append(message)
messagesSortedByChatRooms[chatRoom] = array
}
}
}
}
if !doesHaveMessagesCount {
//doesHaveMessagesCount = true
// we have the number of chats in database
let secondQuery = Constants.refs.databaseChats.queryOrderedByPriority()
_ = secondQuery.observe(.childAdded, with: { [ weak self] snapshot in
if let data = snapshot.value as? [String: String],
let id = data["sender_id"],
let name = data["name"],
let text = data["text"],
let chatRoom = data["chatRoom"],
!text.isEmpty
{
if let message = JSQMessage(senderId: id, displayName: name, text: text)
{
index1 += 1
if chatRoom != nil {
if let unwrappedSelf = self {
if unwrappedSelf.sortedChatRoomsArray.contains(chatRoom) {
sortedMessages.append(message)
for (chatRoomKey, messageArray) in messagesSortedByChatRooms {
unwrappedSelf.lastMessages[chatRoomKey] = messageArray[0]
}
}
}
}
if let unwrappedSelf = self {
if index1 == messages.count && chatRoom != unwrappedSelf.roomName {
sortedMessages.append(JSQMessage(senderId: id, displayName: name, text: "no message"))
}
}
}
}
DispatchQueue.main.async {
if let unwrappedSelf = self {
if !doesHaveSortedMessagesCount {
//doesHaveSortedMessagesCount = true
if unwrappedSelf.sortedChatRoomsArray.indices.contains(index.row) {
if unwrappedSelf.lastMessages[unwrappedSelf.sortedChatRoomsArray[index.row]] != nil {
if unwrappedSelf.lastMessagesArray.count != 0 {
let currentChatRoom = unwrappedSelf.sortedChatRoomsArray[index.row]
if unwrappedSelf.lastMessages[unwrappedSelf.sortedChatRoomsArray[index.row]]?.text != "no message" {
if UUID(uuidString: unwrappedSelf.lastMessages[unwrappedSelf.sortedChatRoomsArray[index.row]]!.text) == nil {
cell.contactLastMessageLabel.text = unwrappedSelf.lastMessages[unwrappedSelf.sortedChatRoomsArray[index.row]]?.text
} else {
cell.contactLastMessageLabel.text = "New image"
}
} else {
cell.contactLastMessageLabel.text = ""
cell.contactLastMessageLabel.font = UIFont(name:"HelveticaNeue-Light", size: 16.0)
}
if unwrappedSelf.lastMessagesArray.indices.contains(index.row) {
if unwrappedSelf.lastMessagesArray[index.row] != unwrappedSelf.lastMessages[unwrappedSelf.sortedChatRoomsArray[index.row]]?.text {
if unwrappedSelf.lastMessages[unwrappedSelf.sortedChatRoomsArray[index.row]]?.senderId != PFUser.current()?.objectId {
if unwrappedSelf.lastMessages[unwrappedSelf.sortedChatRoomsArray[index.row]]?.text != "no message" {
cell.contactLastMessageLabel.font = UIFont(name:"HelveticaNeue-Bold", size: 16.0)
}
var numberOfDuplicates = 0
for cell in previousCellArray {
if cell.contactLastMessageLabel.text == unwrappedSelf.lastMessages[unwrappedSelf.sortedChatRoomsArray[index.row]]?.text {
numberOfDuplicates += 1
}
}
if numberOfDuplicates == 0 {
if unwrappedSelf.selectedUserObjectId != "" {
unwrappedSelf.changeCellOrder(index: index.row, selectedUserObjectId: unwrappedSelf.selectedUserObjectId, lastMessage: unwrappedSelf.lastMessages[unwrappedSelf.sortedChatRoomsArray[index.row]]!.text)
} else {
unwrappedSelf.changeCellOrder(index: index.row, selectedUserObjectId: "none", lastMessage: unwrappedSelf.lastMessages[unwrappedSelf.sortedChatRoomsArray[index.row]]!.text)
}
} else {
unwrappedSelf.tableView.reloadData()
}
cell.activityIndicatorView.stopAnimating()
}
} else {
cell.contactLastMessageLabel.font = UIFont(name:"HelveticaNeue-Light", size: 16.0)
}
}
}
} else {
}
}
}
}
}
})
}
}
})
}
FINL EDIT: I put a closure inside of another closure so it created an infine loop ;)

GCD: Making Sequential Download Calls; ONE BY ONE

SCENARIO
The app downloads user subscriptions one by one. This call will be made in multiple places (in completion block after another network call and from a button press from a UIAlertController). The logic is to download all the subscriptions and once one subscription download is down it goes to the next until all have been downloaded and then our SVProgressHUD dismisses. The code works great when we build and run from Xcode. But when we build the IPA and send to our customer, this logic creates some sort of a stall, and the SVProgressHUD alert keeps spinning:
This is a big problem because our application is focused around downloading content from subscriptions.
Why is this logic stalling after I Archive and build an IPA from it, but not when I build and run from Xcode?
Code Below:
// Making the call
DispatchQueue.main.async {
DGWebService().syncUserSubscribedContent {
DispatchQueue.main.async {
self.finishLogin()
}
}
}
// Sequentially going through each subscription and downloading them
func syncUserSubscribedContent(completion: #escaping Constants.WebService.ContentCompletion) {
let subscriptions = MPTUser.sharedUser.getSubscriptionsForDownload()
DispatchQueue.global().async {
if subscriptions.count > 0 {
var index:Int = 0
var t = subscriptions.count
var downloading: Bool = false
while t != 0 {
if downloading == false {
downloading = true
if index < 0 {
index = 0
}
if index > subscriptions.count - 1 {
index = subscriptions.count - 1
}
if index <= subscriptions.count {
let subscription = subscriptions[index]
if subscription.didDownloadContent == false {
if let subscriptionID = subscription.subscriptionID {
DispatchQueue.main.async {
SVProgressHUD.show(withStatus: "Downloading Documents\nfor\n\(subscription.functionalGroupName!)\n\(index+1) of \(subscriptions.count)")
}
self.getUserSubscribedContent(subscriptionID: subscriptionID, completion: { (success) in
subscription.didDownloadContent = true
index += 1
t -= 1
downloading = false
})
}
else {
index += 1
t -= 1
downloading = false
}
}
}
else {
index += 1
t -= 1
downloading = false
}
}
}
}
completion()
}
}
self.getUserSubscribedContent is a function that downloads the content and sends a completion back in the block.
If someone could help me out here it would be much appreciated.
You can try using a DispatchGroup. Here's a rough (and untested) example:
DispatchQueue.global().async {
let subscriptions = MPTUser.sharedUser.getSubscriptionsForDownload()
let group = DispatchGroup()
var completed = 0
let completion: (Bool) -> Void = {
if $0 {
completed += 1
}
group.leave()
DispatchQueue.main.async {
SVProgressHUD.show(withStatus: "Downloading Documents\nfor\n\(subscription.functionalGroupName!)\n\(completed) of \(subscriptions.count)")
}
}
for subscription in subscriptions {
self.getUserSubscribedContent(subscriptionID: subscription.subscriptionID, completion: completion)
group.enter()
}
// However long you want to wait (in seconds) before timing out
_ = group.wait(timeout: .now() + 30)
}

Stuck in a loop. Very strange because the code sometimes work and sometimes just freezes

I am writing a puzzle game for an IOS. In my code I need to fill an array with some random (and non-random numbers) that will represent the main data structure.
func Random(r : Range<Int>) -> Int {
return Int(arc4random_uniform(UInt32(r.endIndex - r.startIndex))) + r.startIndex
} // function to generate random number
var arrWithColors = [Int]() // array that will hold the numbers
//function to that check if an array contains a number in a range already
func checkForNumberInArrayWithRange(r: Range <Int>, n: Int ) -> Bool {
for i in r.startIndex..<r.endIndex {
if arrWithColors[i] == n { return true }
}
return false
}
// here is the main function where i keep getting stuck. Out of let's say five times it would only work 3 or even less.
func randHexWithTag() {
var rNumber : Int = Random(0...5)
for i in 0...5 {
while (true) {
rNumber = Random(0...5)
if !checkForNumberInArrayWithRange(0...5, n: rNumber) {
break
}
}
arrWithColors[i] = rNumber
}
arrWithColors[10] = arrWithColors[1]
for i in 6...11 {
while(true) {
rNumber = Random(0...5)
if ((rNumber == arrWithColors[0] && i == 9) || (rNumber == arrWithColors[2] && i == 11)) {
continue
}
if !checkForNumberInArrayWithRange(6...11, n: rNumber) {
break
}
}
if (i != 10) {
arrWithColors[i] = rNumber
}
}
arrWithColors[13] = arrWithColors[4]
for i in 12...17 {
while(true) {
rNumber = Random(0...5)
if (rNumber == arrWithColors[3] && i == 12) || (rNumber == arrWithColors[5] && i == 14) {
continue
}
if !checkForNumberInArrayWithRange(12...17, n: rNumber) {
break
}
}
if (i != 13) {
arrWithColors[i] = rNumber
}
}
}
The above code will ALWAYS fail during the first call to checkForNumberInArrayWithRange on this line:
if arrWithColors[i] == n { return true }
That is because arrWithColors is empty, and index i is out of range

'SKNode?' does not have a member named 'position'

What am I doing wrong? I can't seem to figure this out. I have tried putting an exclamation mark behind: var thisBlock = self.childNodeWithName(block),
this gives me a new error saying. type () does not confirm to protocol 'BooleanType'.
func blockRunner() {
for(block, blockStatus) in self.blockStatuses {
var thisBlock = self.childNodeWithName(block)
if blockStatus.shouldRunBlock() {
blockStatus.timeGapForNextRun = random()
blockStatus.currentInterval = 0
blockStatus.isRunning = true
}
if blockStatus.isRunning {
if thisBlock.position.x = blockMaxX{
thisBlock.position.x -= CGFloat(self.groundSpeed)
} else {
thisBlock.position.x = self.origBlockPositionX
blockStatus.isRunning = false
self.score++
if ((self.score % 5) == 0) {
self.groundSpeed++
}
self.scoreText.text = String(self.score)
}
} else {
blockStatus.currentInterval++
}
}
}
childNodeWithName() does return an optional SKNode? which you have to unwrap to use. I don't know why var thisBlock = self.childNodeWithName(block)! didn't solve your issue. I would recommend using optional binding (if let) syntax:
if let thisBlock = self.childNodeWithName(block) {
if blockStatus.shouldRunBlock() {
blockStatus.timeGapForNextRun = random()
blockStatus.currentInterval = 0
blockStatus.isRunning = true
}
if blockStatus.isRunning {
... rest of your code
}
This has the added advantage of not crashing if there are no children nodes. It just won't enter the block.

Resources