Ответы пользователя по тегу Swift
  • Как сделать локальные уведомления по расписанию на Swift?

    maestrro712
    @maestrro712
     iOS Developer
    Вы уже практически сделали все, что было нужно. Осталось несколько штрихов.

    1. Сейчас функция scheduleNotification создаёт нотификации с одним и тем же id. Новые нотификации перетирают старые. Лучше в качестве id использовать или константы, или что-то связанное с заголовком: "\(content.title.hash)".
    2. Текст надо вынести в параметры функции:
    func scheduleNotification(inSeconds seconds:TimeInterval, text: String, completion: (Bool) -> ())
    . Тогда можно будет его использовать для создания нотификации: content.title = text.
    3. deinit нужно убрать совсем. Он удаляет вашу нотификацию при удалении экрана из памяти.
    4. Если нужно ставить нотификации без нажатия кнопки, то можно использовать метод viewDidLoad() из этого же класса.
    Ответ написан
    2 комментария
  • Связывание view model с view controller используя замыкания. Корректен ли данный подход в MVVM?

    maestrro712
    @maestrro712
     iOS Developer
    У меня был опыт использования MVVM без реактивщины. Немного длиннее код, но в целом нормально. Большая часть реактивщины избыточна, если вам нужен только байндинг.

    Что касается конкретно этого кода, то все таки в парадигме MVVM не надо связывать действие пользователя с результатом. То есть у вас есть отдельно метод deleteFood(IndexPath) и отдельно
    var animatableChanges: ((OldData, NewData) -> Void)?
    куда View может засунуть свой байндинг. Такой подход позволяет ViewModel обновлять вьюху тогда, когда она посчитает нужным, а с другой стороны держать код всех байндингов в одном месте (к примеру, в специальном методе, вызываемом из viewDidLoad).
    Ответ написан
    4 комментария
  • Как перезагрузить Webview только после того как страница загружена?

    maestrro712
    @maestrro712
     iOS Developer
    Я думаю, это можно сделать, отменяя загрузку в методе делегата и делая ее вручную:
    func webView(_ webView: WKWebView,
                     decidePolicyFor navigationAction: WKNavigationAction,
                     decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
            URLSession.shared.dataTask(with: navigationAction.request) { [weak webView] (data, _, _) in
                decisionHandler(.cancel)
                guard let html = data.flatMap({ String(data: $0, encoding: .utf8) }) else {
                    return
                }
                webView?.loadHTMLString(html, baseURL: navigationAction.request.url)
            }.resume()
        }

    Естественно в такой ситуации надо тщательно проработать всяческие граничные условия, к примеру редиректы.
    Ответ написан
    Комментировать
  • Как отследить состояние textField?

    maestrro712
    @maestrro712
     iOS Developer
    UITextField наследуется от UIControl, а значит вы можете подписываться на его controlEvents. Пример:

    override func viewDidLoad() {
            super.viewDidLoad()
            textField.addTarget(self, action: #selector(editingBegan(_:)), for: .editingDidBegin)
            textField.addTarget(self, action: #selector(editingChanged(_:)), for: .editingChanged)
            textField.addTarget(self, action: #selector(editingEnded(_:)), for: .editingDidEnd)
        }
    
        @objc func editingBegan(_ textField: UITextField) {
            //пользователь попал в поле, но еще ничего не ввел
        }
    
        @objc func editingChanged(_ textField: UITextField) {
            //текст изменился
        }
    
        @objc func editingEnded(_ textField: UITextField) {
            //ввод окончен, к примеру нажали "return" на клавиатуре
        }
    Ответ написан
    1 комментарий
  • Как сделать фотографию программно, не вызывая камеру?

    maestrro712
    @maestrro712
     iOS Developer
    Это можно сделать, но только когда приложение активно. Работу с камерой надо вести через AVFoundation. Тема довольно сложная, чтобы ее сейчас расписывать, погуглите "AVFoundation capture session"
    Ответ написан
    Комментировать
  • Как правильно реализовать UIPasteboard.general.string + actionSheet?

    maestrro712
    @maestrro712
     iOS Developer
    очень просто :) заведите "white list" для ссылок:

    1. Заводим простой Set, который хранится в вашем экземпляре AppDelegate
    2. Для UIAlertAction, который соответсвует кнопке cancel, добавляем action:
    self.whiteList.insert(link)
    3.Прежде, чем показать UIAlertController, проверяем white list:
    guard !whiteList.contains(link) else {
       return
    }
    let alertController = ...
    Ответ написан
  • Как перебрать массив словарей в swift?

    maestrro712
    @maestrro712
     iOS Developer
    К сожалению вы неверно подобрали структуру данных. Ваша проблема в том, что ваш код не перебирает словари, а перебирает содержимое внутри словаря. Тут надо знать, что порядок записей в словаре не гарантируется. То есть когда на месте HomeTeam и GuestTeam окажутся реальные данные, вы никак не сможете определить кто есть кто.
    Я не знаю источника выших данных и где они еще используются, но чисто под эту задачу рекомендовал бы такую структуру:

    typealias ResultsArray = [[(homeTeam: (name: String, score: String), guestTeam: (name: String, score: String))]]
    
    //для сравнения, ваша структура:
    typealias YourStructure = [[[String: String]]
    Ответ написан
    1 комментарий
  • Почему происходит ошибка разворачивания инициализированного опционала,?

    maestrro712
    @maestrro712
     iOS Developer
    Я вижу 2 случая, когда переменная не инициализируется:

    1. `fetch` произошел с ошибкой. Ошибка поймалась в `catch`, исполнение счастливо пошло дальше, но `per2` так и осталась не инициализированной
    2. массив `fetchResult` - пустой, тогда тело цикла не выполнится ни разу
    Ответ написан
    4 комментария
  • Как обратиться из своего класса к классу ViewController для изменения IBOutlet или вызова методов?

    maestrro712
    @maestrro712
     iOS Developer
    Описываемый вами подход не самый удачный. ViewController неспроста так называется, именно он управляет всем view и его содержимым, соответственно, изменять свойства у IBOutlet'а должен только он. Чтобы избежать соблазна, лучше сразу декларировать все IBOutlet как private.
    Вместо вашего подхода лучше сделать так, чтобы ViewController был оповещен о произошедших изменениях и соответствующе апдейтил свой view. Сделать это можно многими способами, вот лишь некоторые из них:

    1) У вашего класса логики есть делегат. ViewController в методе viewDidLoad находит экземпляр вашего класса и назначает себя делегатом:

    class ViewController: NSViewController {
        
        @IBOutlet private weak var usernameLabel: NSTextField!
    
        func viewDidLoad() {
            super.viewDidLoad()
            let someInstance = //тут мы находим ваш экземляр (к примеру, он синглтон)
            someInstance.delegate = self
        }
    }
    
    extension ViewController: SomeInstanceDelegate {
        func updateUsernameOnUI(_ username: String) {
            usernameLabel.stringValue = username
        }
    }


    2) Ваш класс логики может оповещать своих обзерверов и ViewController становится обзервером:

    class ViewController: NSViewController {
        
        @IBOutlet private weak var usernameLabel: NSTextField!
    
        func viewDidLoad() {
            super.viewDidLoad()
            let someInstance = //тут мы находим ваш экземляр (к примеру, он синглтон)
            someInstance.addObserver(self)
        }
    }
    
    extension ViewController: SomeInstanceObserver {
        func usernameUpdated(_ username: String) {
            usernameLabel.stringValue = username
        }
    }


    3) Если никак не получается при инициализации связать класс логики и ViewController (что, в общем, не гуд), можно использовать NotificationCenter:

    class ViewController: NSViewController {
        
        @IBOutlet private weak var usernameLabel: NSTextField!
    
        func viewDidLoad() {
            super.viewDidLoad()
            NotificationCenter.default.addObserver(self, selector: #selector(usernameUpdate(:)), 
                                                                           name: Notification.Name("usernameUpdated"), object: nil)
        }
    }
    
    extension ViewController {
        @objc func usernameUpdate(_ notification: Notification) {
            guard let username = notification.userInfo?["username"] as? String else {
                  return
            }
            usernameLabel.stringValue = username
        }
    }
    Ответ написан
    Комментировать
  • Как реализовать фоновое выполнение задач?

    maestrro712
    @maestrro712
     iOS Developer
    У iOS приложений весьма жесткий подход к background режиму. Для ваших задач действительно подходит Background fetch. Однако есть 2 момента:

    1. система сама выбирает переодичность запуска приложения. Вы можете повлиять на ее "предпочтения", делая запрос как можно быстрее, и возвращая сообщение, что вы скачали новые данные.
    2. приложение, которое пользователь убил руками (выкинул из трея) не будет запускаться для фетча совсем.

    Из-за всего этого, правильный подход - когда сервер оповещает приложение по средствам пушей, а не приложение само проверяет данные.
    Ответ написан
    Комментировать
  • Как реализовать Auto-Renewable в iOS?

    maestrro712
    @maestrro712
     iOS Developer
    Все 4 пункта вам поможет сделать онлайн-валидация чека покупки. Вкратце это выглядит так:

    1. Приложение получает у iOS чек прилоложения (receipt). При получении его желательно обновить.
    2. Приложение отправляет receipt на ваш сервер.
    3. Ваш сервер делает запрос в AppStore с этим receipt и shared secret.
    4. AppStore возвращает вам json, в котором есть информация о всех покупках в этом приложении пользователя с этим Apple id.
    5. В полученной информации нас интересуют:
    а) original_purchase_date - дата оформления подписки
    б) purchase_date - дата последней оплаты
    в) is_trial_period - показывает, что пользователь пользует пробный период
    г) cancellation_date - если не равно null, покупка была отменена
    6. PROFIT!

    Подробно процесс по шагам описан здесь: https://developer.apple.com/library/content/releas...
    Ответ написан
    1 комментарий
  • Кто как мокирует в swift, и как тестировать сервисы?

    maestrro712
    @maestrro712
     iOS Developer
    В рантайме создать mock невозможно в свифте к сожалению :( более того, поведение структуры нельзя никак изменить. Поэтому мы используем 2 вещи:

    1. Зависимости везде указываются через протоколы
    2. Моки нельзя подсунуть в рантайме, но можно генерить на этапе сборки с помощью Sourcery. Эта тула помогает генерировать код автоматически по темплейтам.
    Ответ написан
    5 комментариев
  • Как сделать так, чтобы ViewContreoller был виден поверх предыдущего? Наподобие UIAlertController?

    maestrro712
    @maestrro712
     iOS Developer
    все просто:

    topViewController.view.backgroundColor = UIColor.clear
    topViewController.modalPresentationStyle = .overFullScreen
    bottomViewController.present(topViewController, animated: true, completion: nil)
    Ответ написан
    2 комментария
  • Как правильно адаптировать UICollectionView?

    maestrro712
    @maestrro712
     iOS Developer
    Тут есть несколько нюансов.
    Для коллекции отступ - это второстепенная величина. В первую очередь, вы управляете размером ячейки. Для этого вам необходимо, что ваш ViewController реализовывал протоколUICollectionViewDelegateFlowLayout и был задан в качестве delegate у коллекции. Тогда вы сможете задать размер ячейки, которая заполнит экран по ширине:

    extension /*Ваш класс*/ : UICollectionViewDelegateFlowLayout {
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
            return CGSize(width: UIScreen.main.bounds.size.width - 2*/*размер вашего отступа*/, height: /*желаемая высота*/)
        }
    }


    P.S. новичку для построения простой таблице возможно удобнее будет использовать UITableView
    Ответ написан
    Комментировать
  • Почему такая долгая инициализация UILabel?

    maestrro712
    @maestrro712
     iOS Developer
    Давайте разберемся со всем по порядку:

    1. Инициализация UILabel происходит во время загрузки view из xib'a и происходит это задолго до того, как вы введете текст :)

    2. Изходя из первого пункта, я думаю, вы хотели спросить, "почему ответ появляется так поздно?".
    Тут уже все сказал ManWithBear . Лишь дополню, как это должно выглядеть:
    DispatchQueue.main.async { [weak self] in
        self?.label.text = temp
    }


    3. Ну и в конце хочется дать несколько рекомендаций по код-стайлу:
    а) писать именно weakSelf не стоит. просто [weak self] отлично работает. В такой ситуации код читается легче и подсветка синтаксиса работает корректнее;
    б)методы с большой буквы не называют
    в)если вам надо извлечь несколько optianal'ов подряд, это можно делать в одном операторе if:
    if let source = parsedObject as? [String: Any], let text = source["text"] as? [String] {
        for temp in text {
            print(temp)
            self?.label.text = temp
        }
    }
    Ответ написан
    Комментировать
  • Как исправить ошибку в XCode (Thread 1: SIGABRT)?

    maestrro712
    @maestrro712
     iOS Developer
    В случае таких проблем надо делать Product -> Clean (Shift+Cmd+K)
    Если не помогло, надо удалить Derived data, это можно сделать, открыв Window -> Projects и выбрав свой проект
    Ответ написан
  • Какой ресурс поможет изучить Swift с нуля до свободного владения языком?

    maestrro712
    @maestrro712
     iOS Developer
    На сайте coursera есть неплохой курс от торонтского университета. Если учить чисто для себя, то это бесплатно
    Ответ написан
    Комментировать
  • Как повторить цикл отсчета таймера?

    maestrro712
    @maestrro712
     iOS Developer
    Заведите переменную var cycles = 0, поставьте условие if i + 1 > exersiseName.count первым и модифицируйте следующим образом:

    if i + 1 > exersiseName.count { //Конец цикла
                count+=1
                if (count >= sets) {
                       timerLabel.text = "0"
                       timer.invalidate()
                       timerRunning = false
                }else{
                       i = 0
                }     
     } //важно: не надо ставить здесь else
    if ....
    Ответ написан
    1 комментарий
  • Как прокрутить UITableView при изменении высоты?

    maestrro712
    @maestrro712
     iOS Developer
    Перенесите код
    self.tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: self.messages.count-1, inSection: 0), atScrollPosition: UITableViewScrollPosition.Top, animated: true)

    в completionBlock.
    Сейчас у вас на момент вызова таблица имеет еще старые размеры. В то же время scrollToRow.... самостоятельно создаст анимацию.
    Ответ написан