want to set a self var using guard statement - ios

I have this code, where I'm trying to set a self variable (self?.users) from a view model call. The code snippet looks like this.
override func viewWillAppear(_ animated: Bool) {
DispatchQueue.global().async { [weak self] in
self?.model?.findAll() { [weak self] users, exception in // network call
guard users != nil, self?.users = users else { // Optional type ()? cannot be used as a boolean; test for !=nil instead
}
}
DispatchQueue.main.async {
self?.tableView.reloadData()
}
}
}
I'm capturing [weak self] twice, is that okay?, can I capture it once as weak in the enclosing closure?
Should I use this instead of guard statement?
self?.model?.findAll() { [weak self] users, exception in
if exception != nil {
self?.users = users
}
}

DispatchQueue closures don't cause retain cycles so capture lists are not necessary.
Something like this, to avoid confusion I'd recommend to rename the incoming users and the code to reload the table view must be inside the closure
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DispatchQueue.global().async {
self.model?.findAll() { [weak self] foundUsers, exception in // network call
guard let foundUsers = foundUsers else { return }
self?.users = foundUsers
DispatchQueue.main.async {
self?.tableView.reloadData()
}
}
}
}
And don’t forget to call super

Related

Why does self become nil once creating init for a ViewController?

private var messages = [Message]() is instantiated in the ConversationView Class
In this instance, I pass in the otherUser's email and the conversationID. I use this init once the user's chat is pressed to pass in their information in the ConversationsView.
class ConversationsView: MessagesViewController {
init(with email: String, convo_id: String?) {
self.otherUserEmail = email
self.conversationID = convo_id
super.init(nibName: nil, bundle: nil)
if let id = conversationID {
listenForMessages(convo_id: id, shouldScrollToBottom: true)
}
}
}
The issue is, is that self becomes nil somewhere in the process of the init, where it initializes the ConversationView and passes the values to the messages screen. In this case since self is nil, self.messages is not being updated. Is there any way to solve this problem?
class ConversationsView: MessagesViewController {
private func listenForMessages(convo_id: String, shouldScrollToBottom: Bool){
// Referencing a property on self in a closures, causes a retain cycle. Declaring weak self, makes the refernce weak, causing it not be a retain cycle.
DatabaseManager.shared.getAllMessagesForConversation(with: convo_id, completion: { [weak self] result in
switch result {
case .success(let messages):
print("Messages received \(messages)")
guard !messages.isEmpty else {return}
self?.messages = messages
DispatchQueue.main.async {
self?.messagesCollectionView.reloadDataAndKeepOffset()
if shouldScrollToBottom {
self?.messagesCollectionView.scrollToLastItem()
}
}
case .failure(let error):
print("failure: \(error)")
}
})
}
}
Here is where I init the params into the ConversationView
class MessagesView: UIViewController {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let model = conversations[indexPath.row]
let vc = ConversationsView(with: model.otherUserEmail, convo_id: model.id)
completionFunc()
vc.title = model.name
let targetUser = connections[indexPath.row]
dismiss(animated: true, completion: { [weak self] in
self?.completion?(targetUser)
})
}
}
Self is nil because ConversationView has been initialized.
Make sure ConversationView is retained after you initialized it.
// I'm guessing this is in some view controller, so self is the view controller
self.conversationView = ConversationView(with: "email#email.com", convo_id: "X")

Is 2nd [weak self] ever required in a closure inside another closure?

I always like to use [weak self], to prevent any potential memory leakage.
I was wondering, is 2nd [weak self] required, in a closure inside another closure? For instance
2 [weak self]
func takePhoto() {
AVCaptureDevice.requestAccess(for: AVMediaType.video) { [weak self] response in
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
Or, it is redundant to do so for the above case, and single [weak self] will be sufficient?
Single [weak self]
func takePhoto() {
AVCaptureDevice.requestAccess(for: AVMediaType.video) { [weak self] response in
DispatchQueue.main.async {
guard let self = self else { return }
May I know, which one is the most accurate approach?

Firestore iOS - Why does my array get printed out multiple times?

I'm successfully able to display a users friends array in a table view, but when I print out the array it get's printed out 3 times instead of 1 time, how do I fix this?
Friend System model:
var removeFriendListener: ListenerRegistration!
func addFriendObserver(_ update: #escaping () -> Void) {
removeFriendListener = CURRENT_USER_FRIENDS_REF.addSnapshotListener{ snapshot, error in
self.friendList.removeAll()
guard error == nil else {
#if DEBUG
print("Error retreiving collection")
#endif
return
}
for document in snapshot!.documents {
let id = document.documentID
self.getUser(id, completion: { (user) in
self.friendList.append(user)
update()
})
}
if snapshot!.documents.count == 0 {
update()
}
}
}
func removeFriendObserver() {
removeFriendListener.remove()
}
Friend System View Controller:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
FriendSystem.system.addFriendObserver { () in
DispatchQueue.main.async {
self.tableView.reloadData()
}
print(FriendSystem.system.friendList)
}
}
Array printed out
[App.User]
[App.User, App.User]
[App.User, App.User, App.User]
you should not call your update() here:-
for document in snapshot!.documents {
let id = document.documentID
self.getUser(id, completion: { (user) in
self.friendList.append(user)
update()
})
}
instead you should call it after the for loop gets completed.

swift - performSegue not working in RxSwift Observable subscribe

I have the following code:
loginViewModel.facebookLogin
.asObservable()
subscribe() { [unowned self] facebookLogin in
if let isLoggedIn = facebookLogin.element?.isLoggedIn {
if isLoggedIn {
elf.performSegue(withIdentifier: "toRestaurantSelect", sender: self)
}
}
if let didLoginFail = facebookLogin.element?.didLoginFail {
self.errorLabel.isHidden = !didLoginFail
}
}
.disposed(by: disposeBag)
The facebookLogin is a Variable and is updated when the user logs in. However, the performSegue is not called (the condition is true). Strangely enough, if I turn on Slow animations in the emulator the segue is executed. When Slow animations are turned off the segue doesn't execute (the Facebook login works). Any help is appreciated. Thanks!
i think you should use the main thread to make it work
loginViewModel.facebookLogin
.asObservable()
.subscribe() { [unowned self] facebookLogin in
if let isLoggedIn = facebookLogin.element?.isLoggedIn {
if isLoggedIn {
DispatchQueue.main.async {
self.performSegue(withIdentifier: "toRestaurantSelect", sender: self)
}
}
}
if let didLoginFail = facebookLogin.element?.didLoginFail {
self.errorLabel.isHidden = !didLoginFail
}
}
.disposed(by: disposeBag)
Do the observation with the main scheduler:
loginViewModel.facebookLogin
.asObservable()
// Switch to the main scheduler
.observeOn(MainScheduler.instance)
subscribe() { [unowned self] facebookLogin in
if let isLoggedIn = facebookLogin.element?.isLoggedIn {
if isLoggedIn {
elf.performSegue(withIdentifier: "toRestaurantSelect", sender: self)
}
}
if let didLoginFail = facebookLogin.element?.didLoginFail {
self.errorLabel.isHidden = !didLoginFail
}
}
.disposed(by: disposeBag)

is it possible to record part of screen with Replaykit?

Here is the start and stop functions.
#IBAction func startRecordingAction(sender: AnyObject) {
activityView.hidden = false
// start recording
recorder.startRecordingWithMicrophoneEnabled(true) { [unowned self] (error) in
dispatch_async(dispatch_get_main_queue()) {
[unowned self] in
self.activityView.hidden = true
}
if let error = error {
print("Failed start recording: \(error.localizedDescription)")
return
}
print("Start recording")
self.buttonEnabledControl(true)
}
}
#IBAction func stopRecordingAction(sender: AnyObject) {
activityView.hidden = false
//end recording
recorder.stopRecordingWithHandler({ [unowned self] (previewViewController, error) in
dispatch_async(dispatch_get_main_queue()) {
self.activityView.hidden = true
}
self.buttonEnabledControl(false)
if let error = error {
print("Failed stop recording: \(error.localizedDescription)")
return
}
print("Stop recording")
previewViewController?.previewControllerDelegate = self
dispatch_async(dispatch_get_main_queue()) { [unowned self] in
// show preview vindow
self.presentViewController(previewViewController!, animated: true, completion: nil)
}
})
}
func screenRecorderDidChangeAvailability(screenRecorder: RPScreenRecorder) {
let availability = screenRecorder.available
print("Availability: \(availability)\n");
}
// MARK: - RPPreviewViewControllerDelegate
// called when preview is finished
func previewControllerDidFinish(previewController: RPPreviewViewController) {
print("Preview finish");
dispatch_async(dispatch_get_main_queue()) {
[unowned previewController] in
// close preview window
previewController.dismissViewControllerAnimated(true, completion: nil)
}
}
I want to record only a part of screen and I want to show a custom alert, not ReplayKit standard alert message. I can use 3 party pods, no problem.
Maybe you can advice me a different way, without ReplayKit.
Unfortunately, you cannot record a particular UIView for now with/Using Replay Kit.
For recording particular view here are some alternative's hope this helps you out.
https://github.com/wess/Glimpse
https://github.com/adam-roth/screen-cap-view
https://github.com/andydrizen/UIViewRecorder
Hope this helps you out.

Resources