Ответы пользователя по тегу Swift
  • Как runloop указывает потоку спать?

    doublench21
    @doublench21 Куратор тега Swift
    t.me/jeudesprits
    У тебя неправильное понимание.

    По своей сути - поток выполняет свою задачу и умирает. Но это иногда плохо, ибо на создание тратятся ресурсы системы. Эту проблему можно решить несколькими способами... но один из них это RunLoop. RunLoop заставляет поток оставаться живым, даже если никакой задачи на нём не выполняется (Но это не основная его задача. Основная его задача "слушать"). RunLoop стал настолько полезной штукой, что его принудительно привязали к каждому созданному потоку разработчики Apple. По-умолчанию, RunLoop не активирован. То есть для обычных потоков вам даже и знать о нём не нужно. ДА вы даже не будете создавать потоки, ибо в 99% вы работаете с абстракциями DispatchQueue/OperationQueue.

    Итак, когда RunLoop активирован, то он заставляет поток жить всё время, пока активен он сам (Я напомню, что для RunLoop это не основная цель). RunLoop способен принимать какие-либо события, но это не тема этого разговора. Так вот... когда RunLoop видит, что на "поле тишина", он переходит в спящий режим.

    А спящий режим у RunLoop это просто вечный цикл. Не верите? Смотрите:
    #define	CFRUNLOOP_SLEEP() do { } while (0)
    #define	CFRUNLOOP_SLEEP_ENABLED() (0)
    Ответ написан
  • Как визуализировать граф на Swift?

    doublench21
    @doublench21 Куратор тега Swift
    t.me/jeudesprits
    Я смотрел ваш вопрос. Проблема в том, что я не знаю алгоритма, по которому можно красиво разместить все вершины исходя из матрицы смежности. Если такой есть, скиньте. Сама рисовка трудов не составит.
    Ответ написан
  • Как спарить сложные данные JSON?

    doublench21
    @doublench21 Куратор тега Swift
    t.me/jeudesprits
    У вас JSON кривой по ссылке. В этой строчке
    {"id":67,"iconImage":"image\/catalog\/style\/modile\/icons-03.png"
    id имеет целочисленный тип, хотя во всех остальных местах это строка.
    5dd182bfe2d36713711596.jpeg

    Если не учитывать этот косяк в JSON, то такое можно спарсить как-то так:
    5dd181e2cd959773181456.png

    Код
    struct EntryList: Decodable {
      struct DynamicCodingKey: CodingKey {
        var stringValue: String
        init?(stringValue: String) { self.stringValue = stringValue }
        var intValue: Int? { nil }
        init?(intValue: Int) { nil }
      }
    
      struct Entry: Decodable {
        struct Content: Decodable {
          struct Subcategory: Decodable {
            let id: String
            let iconImage: String
            let name: String
            let sortOrder: String
            let type: String
          }
          
          let iconImage: String
          let iconImageActive: String
          let image: String
          let name: String
          let sortOrder: String
          let subcategories: [Subcategory]
        }
        
        let name: String
        let content: Content
      }
     
      let entries: [Entry]
      
      init(from decoder: Decoder) throws {
        let entriesContainer = try decoder.container(keyedBy: DynamicCodingKey.self)
        
        entries = try entriesContainer.allKeys.map { key in
          print(key)
          let content = try entriesContainer.decode(Entry.Content.self, forKey: key)
          return Entry(name: key.stringValue, content: content)
        }
      }
    }
    
    var entryList: EntryList?
    
    let task = URLSession
      .shared
      .dataTask(with: URL(string: "https://blackstarshop.ru/index.php?route=api/v1/categories")!) { (data, _, error) in
        guard error == nil else { return }
        guard let data = data else { return }
        
        entryList = try! JSONDecoder().decode(EntryList.self, from: data)
      }
    
    task.resume()
    Ответ написан
  • Как получить данные из ячеек таблицы?

    doublench21
    @doublench21 Куратор тега Swift
    t.me/jeudesprits
    Ну так заведи словарь var data = [IndexPath: String]() и UITextFieldDelegate. А в методе делегата сделай так:
    func textFieldDidEndEditing(_ textField: UITextField){
        let pointInTable = textField.convert(textField.bounds.origin, to: tableView)
        guard let textFieldIndexPath = tableView.indexPathForRow(at: pointInTable) else { return }
        guard let text = textField.text else { return }
        data[textFieldIndexPath] = data[textFieldIndexPath, default: ""] + text
    }
    Ответ написан
  • Как отслеживать изменения в массиве, с разделением на вставку / удаление и модификацию?

    doublench21
    @doublench21 Куратор тега Swift
    t.me/jeudesprits
    5dbd5546661d4815276402.png

    Code
    ["John", "Alpha", "Toster"]
      .difference(from: ["John", "Omega", "Alpha", "Toster"])
      .forEach { print($0) }
    
    // remove(offset: 1, element: "Omega", associatedWith: nil)
    Ответ написан
  • Как обновить интерфейс при нажатии кнопки(проблема с потоками)?

    doublench21
    @doublench21 Куратор тега Swift
    t.me/jeudesprits
    Остановку индикатора нужно делать по завершению работы. Работа с URLSession является асинхронной, поэтому останавливать индикатор нужно в completion таска, который вы запускаете.

    Все примеры показанные вами можно просто выкинуть. Это полнейший ужас.

    Нельзя пользователю запрещать что-то делать. По крайне мере он должен иметь возможность переключаться между табами, если они есть.

    Если вы хотите показать пользователю, что идет загрузка, то сделайте оверлай, который будет показываться во время работы таска и скроется по его окончанию. Примеров миллион. Первый из гугла:
    https://stackoverflow.com/questions/27960556/loadi...

    UPD:
    5dbaf3a561387056015331.png

    Текстом
    class ViewController: UIViewController {
      
      @IBOutlet var button: UIButton!
      
      
      var alert: UIAlertController?
      
      func displayActivityIndicatorAlert() {
        alert = UIAlertController(title: "Deleting from black list...", message: nil, preferredStyle: .alert)
        let activityIndicator = UIActivityIndicatorView(style: .medium)
        activityIndicator.translatesAutoresizingMaskIntoConstraints = false
        activityIndicator.isUserInteractionEnabled = false
        activityIndicator.startAnimating()
        
        alert!.view.addSubview(activityIndicator)
        alert!.view.heightAnchor.constraint(equalToConstant: 95).isActive = true
        
        activityIndicator.centerXAnchor.constraint(equalTo: alert!.view.centerXAnchor, constant: 0).isActive = true
        activityIndicator.bottomAnchor.constraint(equalTo: alert!.view.bottomAnchor, constant: -20).isActive = true
        
        present(alert!, animated: true)
      }
      
      func dismissActivityIndicatorAlert() {
        alert?.dismiss(animated: true)
        alert = nil
      }
      
      var blackList = [Int](0...9)
      let lock = NSLock()
      
      func asyncDeleteBlackList() {
        var tasks = [URLSessionTask]()
        tasks.reserveCapacity(10)
        
        var results = [Int: String]()
        results.reserveCapacity(10)
        
        let group = DispatchGroup()
        
        displayActivityIndicatorAlert()
        
        for item in blackList {
          group.enter()
          
          let url = URL(string: "https://.../api/v1/today/batch")!
          let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
            if let error = error {
              // Что-то делаем с ошибкой на клиенте...
              return
            }
            
            guard let httpResponse = response as? HTTPURLResponse,
              (200...299).contains(httpResponse.statusCode) else
            {
              // Что-то делаем с ошибкой на сервере...
              return
            }
            
            if let mimeType = httpResponse.mimeType,
              mimeType == "application/json",
              let data = data,
              let string = String(data: data, encoding: .utf8)
            {
              print("Запрос №\(item) завершён.")
              self.lock.lock()
              results[item] = string
              self.lock.unlock()
              group.leave()
            }
          }
          
          tasks.append(task)
          task.resume()
        }
        
        group.notify(queue: .main) {
          print("\nЗадача завершена.")
          self.dismissActivityIndicatorAlert()
        }
      }
      
      override func viewDidLoad() {
        button.addTarget(self, action: #selector(pressed(sender:)), for: .touchUpInside)
      }
      
      @objc
      func pressed(sender: UIButton) {
        asyncDeleteBlackList()
      }
    }


    Результат: (Видео)
    https://disk.yandex.ru/i/S5yG9YQsRRJSZg
    Ответ написан
  • Как обьеденить два словаря?

    doublench21
    @doublench21 Куратор тега Swift
    t.me/jeudesprits
    5db18233eab0c251752676.png

    Текстом
    let table1 = ["box": 3, "lamp": 1, "pen": 2]
    let table2 = ["box": 2, "PC": 1, "pen": 3]
    
    let table = table1.merging(table2) { $0 + $1 }
    
    // ["pen": 5, "lamp": 1, "PC": 1, "box": 5]
    Ответ написан
  • Как отсортировать в словаре массивы по значению ключа?

    doublench21
    @doublench21 Куратор тега Swift
    t.me/jeudesprits
    UPD: Автор сменил вопрос. Ответ не актуален.

    Домашку нужно делать самому! А ещё закрывать вопросы.

    5db064f48049e807948975.png
    Текстом
    [
      "primes": [2, 3, 5, 7, 11, 13, 15],
      "triangular": [1, 3, 6, 10, 15, 21, 28],
      "hexagonal": [1, 6, 15, 28, 45, 66, 91],
    ].sorted { $0.key < $1.key }
    
    // [
    //   (key: "hexagonal", value: [1, 6, 15, 28, 45, 66, 91])
    //   (key: "primes", value: [2, 3, 5, 7, 11, 13, 15]),
    //   (key: "triangular", value: [1, 3, 6, 10, 15, 21, 28]),
    // ]
    Ответ написан
  • Как найти самый повторяемый символ в строке?

    doublench21
    @doublench21 Куратор тега Swift
    t.me/jeudesprits
    5daf297d86524526184825.png

    Текстом
    "adflksdlfdfddddflsdkflsdkflsdfZ"
      .lazy
      .reduce(into: [Character:Int]()) { $0[$1] = $0[$1, default: 0] + 1 }
      .sorted { $0.value < $1.value }
      .last // (key: "d", value: 10)


    З.Ы. Работает крайне быстро.
    Ответ написан
  • Как убрать выделение segmentedControl?

    doublench21
    @doublench21 Куратор тега Swift
    t.me/jeudesprits
    let sc = UISegmentedControl()
    sc.selectedSegmentIndex = UISegmentedControl.noSegment
    Ответ написан
  • Как лучше обновлять контент в приложении?

    doublench21
    @doublench21 Куратор тега Swift
    t.me/jeudesprits
    Дисклеймер: Не изобретайте велосипед!


    У меня глаза на лоб лезут от подобных вопросов.

    Ну есть у Вас данные, которые на момент запуска приложения должны быть строго актуальными, ТО это самые обычные запросы на сервер. Ровно так же как и в любом другом приложении. Заходишь в приложение, приложение отправляет запрос на сервер, а сервер отдаёт. Вы парсите, после отображаете информацию. НЕТ абсолютно никакой разницы изменилось там что-то или нет. Вы ведь просто запрашиваете JSON.

    Я ещё могу понять, если вы передаёте некий файл размеров в 10GB и который не хочется качать снова, если он не изменился. Тут я могу понять ваши опасения. Ну даже в таком случае, такие вопросы решаются на сервере путём нужных заголовков. Один из них E-TAG. Всё уже давно за вас придумали.

    Возвращаясь к вопросу автора. Если всё о чем мы говорим - это лишь JSON составленный из БД, то не мучайте сами знаете что. Просто сделайте запрос и всё. Не важно изменились ли там данные или нет. Обо всём остальном позаботится кэш, как на клиенте(URLSession), так и на сервере.

    ---------------------------------------------------------------------------------------------------

    Когда делать запросы на сервер?

    Ну смотря где эти данные отображаются.
    • данные относятся глобально ко всему приложения и могут использоваться в любом контроллере, тогда:
      func application(
              _ application: UIApplication,
              willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
          ) -> Bool {
              // ...
              return true
          }

      или
      func application(
              _ application: UIApplication,
              didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
          ) -> Bool {
             // ...
              return true
          }

      или как инициализация св-ва вообще в AppDelegate

    • если данные нужны лишь для конкретного контроллера, то как уже написали выше:
      override func viewDidLoad()  {
          super.viewDidLoad()
         // ...
       }

    Ответ написан
  • Какие книги лучше выбрать для iOS-разработки?

    doublench21
    @doublench21 Куратор тега Swift
    t.me/jeudesprits
    Одно и тоже каждый месяц.

    - Знаешь swift?
    - Нет
    https://books.apple.com/us/book/the-swift-programm...

    - Знаешь английский?
    - Да
    https://www.youtube.com/playlist?list=PLPA-ayBrweU...

    - Не можешь понять что-то из курса выше?
    - Да
    https://www.raywenderlich.com/
    Ответ написан
  • Как грамотно реализовать задачу(описание ниже)?

    doublench21
    @doublench21 Куратор тега Swift
    t.me/jeudesprits
    Странный у вас выбор тегов и способ решения.

    Это называется балансировкой нагрузки и такие задачи должен решать 4-ый сервер, который будет следить за нагрузкой 3-х остальных. Это задача нисколько не про клиент, ибо клиенту доверять нельзя, да и не решаются такие задачи на клиенте, данных мало.

    Балансировка видео потоков это вообще тема для отдельного разговора. Вам лучше не изобретать велосипед, а использовать готовые балансировщикии нагрузки.
    Ответ написан
  • Как улучшить надежность сканнера BLE в бекграунде в iOS?

    doublench21
    @doublench21 Куратор тега Swift
    t.me/jeudesprits
    Каждый раз одно и тоже. Запомните уже наконец:

    - Cостояние, когда ваше приложение находится в выключенном состоянии, вы не можете делать абсолютно НИЧЕГО.

    - Состояние, когда ваше приложение находится в свернутом состоянии, вы можете делать лишь очень малое количество действий с ещё более малым отведенным под это дело временем. Время в данном случае абсолютно неконтролируемое. Система сама решает, когда и что нужно запускать в таком состоянии.

    Поэтому такого рода приложения, который пытаются что-то делать в свёрнутом состоянии в нужное ИМ время не могут и не будут существовать.

    Коим я с Apple полностью согласен. Кому нужно приложение, которое по его прихоти будет делать в фоне, то, что оно хочет и главное, когда хочет. Если я его закрыл или свернул, значит оно мне не интересно.
    Ответ написан
  • Прочитал The Swift Programming Language (Swift 5.0). Куда двигаться дальше?

    doublench21
    @doublench21 Куратор тега Swift
    t.me/jeudesprits
    А теперь тебя ждёт разочарование, ведь для разработки под iOS - это ничто :) Начинай смотреть/читать материалы. Одним из лучших является Стэнфордский куре по iOS разработке. Ютуб и вперёд.

    Книга это хорошо и ты её ещё не раз будешь перечитывать, ЕСЛИ не будешь практиковаться. Придумай для себя какую-то полезную программу и делай. Или попробуй сделать аналог того, что уже есть. Пусть и без каких-либо сложных данных, просто UI.
    Ответ написан
  • Как в массиве, состоящем из элементов дочерних классов, обратиться к свойствам дочерних классов без приведения типа?

    doublench21
    @doublench21 Куратор тега Swift
    t.me/jeudesprits
    Нельзя! Это и есть суть строго типизированных языков.
    type(of: ) это вывод типа динамически, но компилятору глубоко и с колокольни до этого. Он видит только тип, который Вы указали изначально. Это и правильно! Нет никаких если, если вам нужна некая функциональность у потомков в рамках родителя, то создавайте протокол и описывайте их. Это основы ООП
    Ответ написан
  • Запустить мое приложение программно в IOS, swift?

    doublench21
    @doublench21 Куратор тега Swift
    t.me/jeudesprits
    Будильник ты никогда не создашь! В iOS неактивное приложение, остаётся неактивным. Аналог системного будильника не сделать.

    А чтобы не страдать херней и сделать что-то подобное, можно и нужно использовать Shortcuts.
    Ответ написан
  • Как реализовать фоновую задачу когда приложение выключено?

    doublench21
    @doublench21 Куратор тега Swift
    t.me/jeudesprits
    НЕЛЬЗЯ! И вполне правильно, если я закрыл приложение, я должен быть уверен, что оно не будет работать от слова совсем, иначе это уже какое-то Ведро выходит.

    BackgroundFetching - это только про свёрнутое приложение.
    Ответ написан