Arm1.ru - блог Makoni https://arm1.ru Arm1.ru - блог Makoni Downloading files using Alamofire in OperationQueue https://arm1.ru/blog/downloading-files-using-alamofire-in-operationqueue Operation object might be deallocated while Alamofire performing async download of a file inside. So if you need to do something that depends of your Operation class property you can override isFinished property to wait while callback will be called:

class TestOperation: Operation {
  private var downloaded: Bool = false
 
  let urlString: String
 
  override var isFinished: Bool {
    get {
      return downloaded
    }
  }
 
 
  // MARK: - Init
  init(urlString: String) {
    self.urlString = urlString
  }
 
 
  override func main() {
    if self.isCancelled {return }
  }
 
  override func start() {
    if self.isCancelled { return }
 
    print("Starting downloading from url: \(urlString)")
 
    let destination = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
 
    Alamofire.download(urlString, to: destination)
      .validate()
      .response { [unowned self] response in
        defer {
          // operation will be deallocated after setting true
          self.downloaded = true
        }
 
        if self.isCancelled { return }
 
        print("Finished downloading from url: \(self.urlString)")
     }
  }
}
 
// create queue 
var downloadQueue: OperationQueue = {
  var queue = OperationQueue()
  queue.name = "Download queue"
  return queue
}()
 
// create operation and add to queue
let op = TestOperation(urlString: "done")
downloadQueue.addOperation(op)

 

]]>
Tue, 05 Mar 2019 19:04:11 +0300 https://arm1.ru/blog/downloading-files-using-alamofire-in-operationqueue https//arm1.ru/blog/downloading-files-using-alamofire-in-operationqueue#comments
Benchmarks: Vapor 3 vs. Vapor 2 https://arm1.ru/blog/benchmarks-vapor-3-vs-vapor-2 After migrating (almost rewriting) my small project from Vapor 2 to Vapor 3 I've run benchmarks to compare performance. I didn't run benchmarks for the last version of Vapor 2.x so I will compare Vapor 2.1.0 to results of Vapor 3.1.0.

My server:

  • 2 GB RAM
  • 1 CPU Core
  • SSD
  • 125 MBPS Out
  • Ubuntu 16.04.2 LTS
  • CouchDB

Benchmark from other server was launched as:

wrk -t4 -c20 -d5m https://my_url

API just gets some data from CouchDB and returns it as JSON. Vapor project was compiled with Swift 4.2.

Total requests

 

Requests per second

 

Average Latency

 

Memory usage

 

Vapor 3 was rewriten and now it's based on Apple's NIO. It's faster than Vapor 2 but using more memory which is not critical.

Previous benchmarks:
Benchmarks: Vapor 2 vs. Vapor 1
Benchmarks for Vapor 1.2.5
Swift Backend with CouchDB: Kitura vs. Vapor vs. Node.js

]]>
Mon, 01 Oct 2018 01:41:20 +0300 https://arm1.ru/blog/benchmarks-vapor-3-vs-vapor-2 https//arm1.ru/blog/benchmarks-vapor-3-vs-vapor-2#comments
Select like a Boss для Safari в Mac App Store https://arm1.ru/blog/select-like-a-boss-dlya-safari-v-mac-app-store

24 сентября вышла новая версия macOS - Mojave. А в месте с ней и Safari 12, который теперь поддерживает установку расширений только из Mac App Store. Пришлось запилить приложение с расширением.

Загрузить

]]>
Thu, 27 Sep 2018 14:32:51 +0300 https://arm1.ru/blog/select-like-a-boss-dlya-safari-v-mac-app-store https//arm1.ru/blog/select-like-a-boss-dlya-safari-v-mac-app-store#comments
JPG to HEIF converter https://arm1.ru/blog/jpg-to-heif-converter

Today I've decided to experiment with HEIF that was introduced by Apple with macOS 10.13 and iOS 11. They said that it has the same quality with much lower file size.

It's supported in last versions of macOS and iOS and last models of iPhone can take pictures in that format. 

I'm thinking about compressing my home archive of photos and videos. I was experimenting with HEVC (H.265) and results was great - I had reduced size of my videos with endoding them to HEVC with awesome util called HandBrake.

I found the only way to convert JPG to HEIF (which has HEIC extension). It's possible with Preview app from macOS. But it was interesting for me if I can do it by myself in Swift. So I did :)

My folder with 64 photos from my action camera was 203 mb in JPG format. And it's only 31.3 mb in HEIC.

macOS and iOS support it from the box. And what's interesing - it's easy to add pictures in this format to Photos library and it will appear on all your devices that uses iCloud. But if you want to export this picture from Photos library - it will be converted to JPG on the fly, so it looks like you can't export original HEIC file.

The converter is free and open source (but it's for macOS only): https://github.com/makoni/jpg-to-heif-converter

]]>
Mon, 21 May 2018 23:19:08 +0300 https://arm1.ru/blog/jpg-to-heif-converter https//arm1.ru/blog/jpg-to-heif-converter#comments
Впечатления от Assassin's Creed Chronicles: China https://arm1.ru/blog/vpechatleniya-ot-assassin-s-creed-chronicles-china

]]>
Sat, 21 Apr 2018 19:49:32 +0300 https://arm1.ru/blog/vpechatleniya-ot-assassin-s-creed-chronicles-china https//arm1.ru/blog/vpechatleniya-ot-assassin-s-creed-chronicles-china#comments
Анимация касания индикатора в UISlider https://arm1.ru/blog/animatsiya-kasaniya-indikatora-v-uislider

Стояла задача - при касании слайдера анимировать в приложении сделать анимированное увеличение ползунка. Так же, как это сделано у Apple в приложениях Apple Music и Podcasts в плеере, когда начинаешь перематывать позицию воспроизведения. Пока искал способ сдандартными средствами, убил немало времени. Очень не хотелось писать прям свой кастомный слайдер, хотелось использовать системный UISlider, что мне, в итоге, и удалось.

]]>
Fri, 24 Nov 2017 16:59:00 +0300 https://arm1.ru/blog/animatsiya-kasaniya-indikatora-v-uislider https//arm1.ru/blog/animatsiya-kasaniya-indikatora-v-uislider#comments
SMS Антикредит https://arm1.ru/blog/sms-antikredit

Надоел мне СМС-спам, который мне периодически приходит - то кредит предлагают, то ипотеку. Как раз, просматривая один из видосов с WWDC, увидел, что Apple дали API для фильтрации SMS-сообщений. Если сработает твой фильтр - то такие сообщения будут приходить без звукового или вибросигнала, втихую, и попадать в отдельную категорию спама в Сообщениях. 

Недолго думая, запили такой вот фильтр и выложил в App Store. Надеюсь, кому ещё, кроме меня, пригодится.

]]>
Tue, 24 Oct 2017 20:52:06 +0300 https://arm1.ru/blog/sms-antikredit https//arm1.ru/blog/sms-antikredit#comments
openssl и Vapor 2 https://arm1.ru/blog/openssl-i-vapor-2 Столкнулся с тем, что перестал компилиться проект на Ubuntu на Vapor 2. Точнее, одна из зависимостей - Crypto. Оказалось, что из-за добавленного репозитория, в котором были более новые версии некоторых библиотек, оно и не компилилось. Запишу сюда - пришлось даунгрейдиться:

apt install libssl-dev=1.0.2g-1ubuntu4.10
apt install openssl=1.0.2g-1ubuntu4.10

]]>
Tue, 05 Sep 2017 23:04:56 +0300 https://arm1.ru/blog/openssl-i-vapor-2 https//arm1.ru/blog/openssl-i-vapor-2#comments
Benchmarks: Vapor 2 vs. Vapor 1 https://arm1.ru/blog/benchmarks-vapor-2-vs-vapor-1 After migrating my pet project from Vapor 1 to Vapor 2 I've run benchmarks to compare performance. I didn't run benchmarks for the last version of Vapor 1.x which is 1.5.15 so I will compare Vapor 2.1.0 to results of Vapor 1.2.5 that I have from my last results.

My server:

  • 2 GB RAM
  • 1 CPU Core
  • SSD
  • 125 MBPS Out
  • Ubuntu 16.04.2 LTS
  • CouchDB

Benchmark from other server was launched as:

wrk -t4 -c20 -d5m https://my_url

API just gets some data from CouchDB and returns it as JSON. Vapor project was compiled with Swift 3.1.1.

Total requests

 

Requests per second

 

Average Latency

 

Memory usage

 

Vapor 2 is about 14-15% faster than Vapor 1.2.5. And what's important - Vapor 1.2.5 grew after benchmarks from 13.4 mb to 59.1 mb and Vapor 2 started with just 3.6 mb and grew to 5.3 mb. That's very impressive.

]]>
Wed, 14 Jun 2017 11:19:24 +0300 https://arm1.ru/blog/benchmarks-vapor-2-vs-vapor-1 https//arm1.ru/blog/benchmarks-vapor-2-vs-vapor-1#comments
Weak delegate в Swift 3 https://arm1.ru/blog/weak-delegate-v-swift-3

Боролся тут с утечками памяти в рабочем проекте. Копание привело к тому, что после ухода из UIViewController далеко не вся память освобождается. Если несколько раз открывать этот UIViewController, возвращаться назад и снова открывать - потребляемая память растёт и не очень слабо освобождается. 

Суть проблемы оказалась в протоколах и делегатах. У меня в UIViewController используется UICollectionView с кастомной ячейкой, у которой есть делегат. Мой UIViewController является для каждой ячейки делегатом. Пример реализации протокола и делегата в интернете и книге по Swift выгядит примерно так:

// Protocol
protocol MyCollectionViewCellDelegate {
  func someFunc()
}

// UICollectionViewCell
final class MessageCollectionViewCell: UICollectionViewCell {
  var delegate: MyCollectionViewCellDelegate?
}

// UIViewController
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  // some code....
  cell.delegate = self
}

Насколько я понял, внутри объекта ячейки получается strong-ссылка на делегата (UIViewController), в результате происходит утечка памяти.

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

// Protocol
protocol MyCollectionViewCellDelegate: class {
  func someFunc()
}

// UICollectionViewCell
final class MessageCollectionViewCell: UICollectionViewCell {
  weak var delegate: MyCollectionViewCellDelegate?
}

После этих простых изменений всё стало прекрасно - после возвращения назад из UIViewController использование памяти возвращается на исходный уровень.

]]>
Wed, 03 May 2017 14:41:31 +0300 https://arm1.ru/blog/weak-delegate-v-swift-3 https//arm1.ru/blog/weak-delegate-v-swift-3#comments
Впечатления от Assassin's Creed Syndicate https://arm1.ru/blog/vpechatleniya-ot-assassin-s-creed-syndicate

]]>
Fri, 24 Mar 2017 11:49:25 +0300 https://arm1.ru/blog/vpechatleniya-ot-assassin-s-creed-syndicate https//arm1.ru/blog/vpechatleniya-ot-assassin-s-creed-syndicate#comments
Впечатления от Assassin's Creed Rogue https://arm1.ru/blog/vpechatleniya-ot-assassin-s-creed-rogue

Прошёл очередную часть Assassin's Creed Rogue (Изгой).

]]>
Tue, 28 Feb 2017 10:55:53 +0300 https://arm1.ru/blog/vpechatleniya-ot-assassin-s-creed-rogue https//arm1.ru/blog/vpechatleniya-ot-assassin-s-creed-rogue#comments
Сайт Space In Box https://arm1.ru/blog/sajt-space-in-box

Решил информацию о своих приложениях вынести с apps.arm1.ru на отдельный домен. Раз я их когда-то начал публиковать под именем Space In Box, то и домен соответствующий: https://spaceinbox.me. Решил убрать уже не актуальные и не доступные приложения и максимально везде использовать векторную графику (иконки, логотипы), ибо на ретине глаз сильно режет.

Был отличный повод сделать что-то на Swift на фреймворке Vapor, пока всё нужное получилось. Обновлять, если что-то меняется в логике, конечно, тяжелее, т.к. приходится билдить на сервере всё заново, а это происходит минуты 3-4 каждый раз. А если что-то касается оформления (подправить html), то шаблоны подхватываются бинарником налету и компилить заново не надо. Скорость работы и потребляемая память (пока 8.8 мб) радуют. Теперь можно обновлять старые и фигачить новые приложения.

UPDATE: С простым обновлением до Vapor 1.5.14 без изменения какого либо кода сайт стал есть ещё меньше памяти - 6.7-7.7 мб.

 

]]>
Thu, 09 Feb 2017 15:33:44 +0300 https://arm1.ru/blog/sajt-space-in-box https//arm1.ru/blog/sajt-space-in-box#comments
Benchmarks for Vapor 1.2.5 https://arm1.ru/blog/benchmarks-for-vapor-1-2-5 About month ago I've launched benchmarks for Server side Swift frameworks. Yesterday I've updated my project to new version of Vapor: 1.2.5. Previous version I used was 1.1.11. 

One of the most important updates in Vapor 1.2.x is that "Vapor is now using a non-blocking server".

So today I decided to run new benchmark for my project updated to Vapor 1.2 and results surprised me. It's not only faster than 1.1.x version, it's now equal to Node.js results!

Vapor vs. Node.js

My server:

  • 2 GB RAM
  • 1 CPU Core
  • SSD
  • 125 MBPS Out
  • Ubuntu 16.10
  • CouchDB

Benchmark from other server was launched as:

wrk -t4 -c20 -d5m https://my_url

API just gets some data from CouchDB and returns it as JSON.

]]>
Thu, 08 Dec 2016 11:21:37 +0300 https://arm1.ru/blog/benchmarks-for-vapor-1-2-5 https//arm1.ru/blog/benchmarks-for-vapor-1-2-5#comments
Swift Backend with CouchDB: Kitura vs. Vapor vs. Node.js https://arm1.ru/blog/swift-backend-with-couchdb-kitura-vs--vapor-vs-node-js I made few test projects to implement API for my simple app to run some benchmarks. It just makes 1 request to Database and returns JSON data.

All projects was executed on Linode VPS in London: 

  • 2 GB RAM
  • 1 CPU Core
  • SSD
  • 125 MBPS Out
  • Ubuntu 16.10
  • MySQL and CouchDB with equal data (about 13k rows/documents)

Benchmarks was run on another, more powerful dedicated server in Germany using wrk:

wrk -t4 -c20 -d5m https://my_url

Results are very different from another benchmarks by Ryan Collins.

What did I try:

  • Node.js 7.0 + MySQL 5.7.16
  • Node.js 7.0 + CouchDB 2.0 (via node-couchdb)
  • Vapor 1.1.11 + CouchDB 2.0 (via HTTP)
  • Kitura 1.1.1  + CouchDB 2.0 (via Kitura-CouchDB)

Total requests

 

Requests per second

 

Average Latency

 

Results are very disappointing for me. Node.js was 50% faster than Swift. It looks like it's still not right time to make backends on Swift right now, unless you don't expect hight load and just want to write in Swift.

UPDATE: after about a month I run another tests for new version of Vapor (1.2.5) vs. Node.js and Vapor was very very fast. So now I'm very optimistic with using Swift as backend.

]]>
Sat, 12 Nov 2016 15:16:54 +0300 https://arm1.ru/blog/swift-backend-with-couchdb-kitura-vs--vapor-vs-node-js https//arm1.ru/blog/swift-backend-with-couchdb-kitura-vs--vapor-vs-node-js#comments
Фреймворк Vapor для бэкэнда на Swift https://arm1.ru/blog/frejmvork-vapor-dlya-bekenda-na-swift

В предыдущем посте я описывал свои впечатления от 4 фреймворков на Swift. Точнее написал я про 3, а 4 меня заинтересовал и руки до него дошли только сейчас. В кратце - я в восторге. Он прекрасен.

В первую очередь у проекта хорошая документация. Не идеальная, но очень хорошая. С примерами, обучающими материалами. Всё в одном месте на сайте, класс.

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

Во-вторых, набор библиотек у фреймворка отличный. Тут и HTTP-клиент, и обёртки для баз данных, и поддержка WebSocket, шаблоны, по умолчанию предлагается хорошая структура проекта, всё по папочкам. Сразу из коробки отличная работа с JSON и данными, которые можно перегонять и из JSON, и в JSON. Даже есть механизм для локализации.

В-третьих, мне удалось сделать свой мини-клиент для CouchDB через просто HTTP-запросы, и он работает. Пока только на чтение, на запись ещё не успел проверить, но то, что всё заработало, очень понравилось.

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

В-пятых - весь мой небольшой тестовый проект скомпилился и заработал на Linux без проблем и падений. Компилился долго, но зато работает, это классно.

По памяти пока всё хуже, чем у Perfect. Perfect стартовал с 3.5 мб памяти, дорос до 5 мб. Vapor стартует с 11 мб памяти, растёт до 14.9 мб. Посмотрим что будет дальше, пока это всё равно в 2 раза меньше, чем у Node.js.

Пожалуй, пока на Vapor я и остановлюсь, по-моему он прекрасен :)

]]>
Wed, 09 Nov 2016 21:40:54 +0300 https://arm1.ru/blog/frejmvork-vapor-dlya-bekenda-na-swift https//arm1.ru/blog/frejmvork-vapor-dlya-bekenda-na-swift#comments
Фреймворки для серверного Swift https://arm1.ru/blog/frejmvork-perfect-dlya-servernogo-swift Пару месяцев назад мне попался пост про серверные фреймворки для Swift - сравнение производительности 4-х фреймворков на Swift для сервера между собой и с Node.js. Производительность меня заинтересовала. Судя по результатам, лучшим оказался Perfect. Через месяц автор выложил ещё одно сравнение производительности, но тестировал уже не в macOS, а в Linux. И снова Perfect оказался в лидерах.

На этих выходных я решил как следует его потрогать. Перед этим я бегло посмотрел в 4 сравниваемых фреймворка: Perfect, Kitura, Vapor и Zewo. Требований у меня к фреймворку было не много, кроме стандартного роутинга запросов и выдачи ответов, мне нужно:

  • Поддержка шаблонизаторов, чтобы отдавать не только JSON, но и HTML.
  • Поддержка сетевых запросов, т.к. в самом Foundation ничего для сетевых запросов ещё не готово.
  • Желательно, чтобы были готовые библиотеки для работы с разными БД. 

Естественно, всё это нужно под Linux. 

]]>
Sun, 06 Nov 2016 18:01:19 +0300 https://arm1.ru/blog/frejmvork-perfect-dlya-servernogo-swift https//arm1.ru/blog/frejmvork-perfect-dlya-servernogo-swift#comments
Установка Swift 3 на Ubuntu Server 16.10 https://arm1.ru/blog/ustanovka-swift-3-na-ubuntu-server-16-10 Записал видео-туториал по установке Swift 3 на Ubuntu Server 16.10

]]>
Sun, 06 Nov 2016 16:20:10 +0300 https://arm1.ru/blog/ustanovka-swift-3-na-ubuntu-server-16-10 https//arm1.ru/blog/ustanovka-swift-3-na-ubuntu-server-16-10#comments
Shake эффект для UIView https://arm1.ru/blog/shake-effekt-dlya-uiview // MARK: - UIView Extension, or NSView
extension UIView {

  /// Shake animation
  func shake() {
    let animation = CAKeyframeAnimation(keyPath: "transform.rotation")
    animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
    animation.duration = 1.0
    animation.values = [-Double.pi / 6, Double.pi / 6, -Double.pi / 6, Double.pi / 6, -Double.pi / 7, Double.pi / 7, -Double.pi / 8, Double.pi / 8, 0.0 ]

    layer.add(animation, forKey: "shake")
  }

}

myView.shake()

]]>
Fri, 28 Oct 2016 15:57:39 +0300 https://arm1.ru/blog/shake-effekt-dlya-uiview https//arm1.ru/blog/shake-effekt-dlya-uiview#comments
Про переход на Swift 3 и Swift 2.4 https://arm1.ru/blog/pro-perehod-na-swift-3-i-swift-2-4 За последние пару недель состоялся релиз новых версия macOS, iOS, tvOS и watchOS. Вместе с ними вышел новый Xcode и новая версия Swift.

При открытии проекта в новом Xcode он сразу предлагает конвертировать проект в новую версию Swift - или в версию 2.3, которая почти без изменений, как промежуточная версия, или сразу в новую версию Swift 3, в котором изменений прилично.

Сам проект конвертировать труда не составляет, ибо есть встроенный в Xcode конвертер. Я решил сначала попробовать 2.3, чтобы не углубляться в дебри изменений. Рабочий проект сконвертировался без проблем, а потом начался ад с зависимостями. Я пользуюсь Carthage, все зависимости компилируются в .framework, который ты импортируешь в свой проект. 1 раз импортировав, потом уже сам проект трогать не приходится, просто пересобирать зависимости. В первую неделю после релиза многие разработчики быстро выкатили обновления - и релиз на Swift 2.3, и релиз на Swift 3. Но не все. Например, SwiftyJSON - разработчики на какое-то время пропали, а их фреймворк очень популярен. Пришлось копать - те зависимости, которые не обновились, благо, подхватили сторонние разработчики. Они их форкнули, всё перевели на Swift 2.3 и отправили pull request'ы в оригинальные репозитории. 

Пришлось потратить какое-то время и заменить некоторые зависимости на форки. И ура, проект собрался наконец. В течение следующей недели авторы всех зависимостей, которые я использую, уже выкатили релизы для Swift 2.3. Я с чистым сердцем вернул зависимости на оригинальные.

Само собой, есть смысл побыстрее перейти на Swift 3, потому что 2.3 - версия временная и поддержка её в будущих релизах Xcode исчезнет. Да и 3-й Swift обещают по возможности последней версией, которая сломает совместимость с предыдущими версиями.

Опять же - сам проект без особых проблем сконвертировался в Swift 3. Не всё, но многое. А то, что само не конвертнулось, Xcode сам при компиляции предлагал исправить, в основном надо было только тыкать мышкой и разрешать ему это поправить. Хоть я и дождался, когда все зависимости обновятся на Swift 3, пришлось немного попотеть. К слову - если framework скомпилен в Swift 2.3, то в проект на Swift 3 его нельзя включить. И обратно тоже. Разгрёб разные проблемы - начался гемор другой. AlamoFire для Swift 3 не просто обновился, а мажорно обновился - с переименованием классов и изменением API. В итоге пришлось некоторые методы вообще переписать на две трети. Хотя в большинстве случаев просто пришлось поменять местами аргументы и где-то что-то переименовать. Плюс, теперь чтобы передать get-параметры, нужно их ручками в строку URL добавлять, раньше это мог сделать аргумент parameters, но теперь эти данные шлются в теле запроса, как будто мы форму заполнили.

Но в итоге всё закончилось хорошо, хотя миграция и заняла время и силы. Вперёд, в будущее.

P.S. опенсорсная версия Swift 3, которую можно запускать на Linux, всё ещё не имеет полной реализации классов для работы с сетью. А фреймворк для бэкенда Perfect не компилится в Ubuntu 16.04. Пока Swift для бэкенда опять откладывается.

]]>
Thu, 29 Sep 2016 10:22:07 +0300 https://arm1.ru/blog/pro-perehod-na-swift-3-i-swift-2-4 https//arm1.ru/blog/pro-perehod-na-swift-3-i-swift-2-4#comments
Элегантный extension для Notification.Name в Swift 3 https://arm1.ru/blog/elegantnyj-extension-dlya-notification-name-v-swift-3 В новой версии Swift 3 теперь другой конструктор для NSNotification (теперь уже просто Notification, префикс NS отбросили):

struct Config {
  static let ShouldCloseBrowserNotification = "ShouldCloseBrowserNotification"
}

let notification = Notification(
  name: Notification.Name(rawValue: Config.ShouldUpdateDialogNotification),
  object: nil,
  userInfo: nil
)
NotificationCenter.default.post(notification)

Выглядит параметр name не очень то красиво теперь. Зато можно расширить Notification.Name, чтобы было красиво:

extension Notification.Name {
  static let shouldCloseBrowserNotification = Notification.Name("ShouldCloseBrowserNotification")
}

let notification = Notification(
  name: .ShouldUpdateDialogNotification,
  object: nil,
  userInfo: nil
)
NotificationCenter.default.post(notification)

Примерно так же сделано и в самом UIKit:

]]>
Mon, 26 Sep 2016 13:27:30 +0300 https://arm1.ru/blog/elegantnyj-extension-dlya-notification-name-v-swift-3 https//arm1.ru/blog/elegantnyj-extension-dlya-notification-name-v-swift-3#comments
Про Mac App Store https://arm1.ru/blog/pro-mac-app-store Читал раньше упоминания о том, что Mac App Store is dead. Примерно месяц назад я запилил свой информер для яндексовской AppMetrica и залил в MAS. Стал наблюдать за чартами (недолго, правда). Собственно, на старте было 2 покупки (я даже знаю от кого обе). Результат меня удивил.

Сразу первое место в категории Бизнес в российском Mac App Store. Приятно, конечно, когда твоя иконка украшает список категорий.

Вот только всего 2 покупки говорят о том, что в Mac App Store прям очень мало покупают и скачивают приложения. Бесплатные, может быть, качают больше, но платные - точно очень мало. По крайней мере в России. Ни в какое сравнение с App Store с приложениями для iOS. Кроме того, приложение заняло 8-е место в общем топе магазина.

Значит дело не в категории. В других странах, скорее всего, приложений покупают больше, но в России - совсем мало. Правда, для macOS и пиратство развито гораздо сильнее, чем для iOS, потому что не нужен никакой геморой с jailbreak. Поэтому, наверное, люди и не очень часто покупают у нас. 

]]>
Sun, 25 Sep 2016 15:13:07 +0300 https://arm1.ru/blog/pro-mac-app-store https//arm1.ru/blog/pro-mac-app-store#comments
Как сделать сочную иконку приложению https://arm1.ru/blog/kak-sdelat-sochnuyu-ikonku-prilozheniyu Секрет прост. Идём на iconfinder.com, ищем годную flat-иконку. Видимо, что она в довольно тусклых тонах. Качаем SVG, открываем в Sketch. Добавляем элементам градиенты и тени - получаем контрастную сочную иконку. По крайней мере, лучше, чем было :)

]]>
Wed, 31 Aug 2016 13:05:34 +0300 https://arm1.ru/blog/kak-sdelat-sochnuyu-ikonku-prilozheniyu https//arm1.ru/blog/kak-sdelat-sochnuyu-ikonku-prilozheniyu#comments
AppMetric для macOS https://arm1.ru/blog/appmetric-dlya-macos

Сделал за пару вечеров простой информер для macOS, который показывает статистику по приложениям из сервиса AppMetrica от Яндекса. Висит знаком в статус баре:

  • Выводит список всех приложений из AppMetrica.
  • Показывает количество пользователей, сессий и крэшей за сегодня.
  • Автоматически обновляет статистику в фоновом режиме.

Загрузить

]]>
Sun, 28 Aug 2016 12:26:15 +0300 https://arm1.ru/blog/appmetric-dlya-macos https//arm1.ru/blog/appmetric-dlya-macos#comments
Блок (замыкание) как свойство класса в Swift https://arm1.ru/blog/blok-zamykanie--kak-svojstvo-klassa-v-swift Пример использования блока (замыкания) как свойства класса в Swift. Инициализируется как nil.

class ChatViewController: UIViewController {

  var someClosure: (() -> Void)! // nil
  var anotherClosure: ((arg: Double) -> Bool)! // nil

  func executeClosures() {
    if self.someClosure != nil {
      self.someClosure()
    }

    if self.anotherClosure != nil {
      let boolResult = self.anotherClosure(arg: 2.0)
    }
  }

  func addSelfClosure(closure: (() -> Void)!) {
    self.someClosure = closure
  }

  func printSomething() {
    self.addSelfClosure() {
      print("closure called")
    }
    self.executeClosures() // prints: closure called
  }

}

]]>
Thu, 18 Aug 2016 17:33:12 +0300 https://arm1.ru/blog/blok-zamykanie--kak-svojstvo-klassa-v-swift https//arm1.ru/blog/blok-zamykanie--kak-svojstvo-klassa-v-swift#comments
CompareShots v1.2 https://arm1.ru/blog/compareshots-v1-2

Вышло обновление CompareShots версии 1.2

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

Загрузить

]]>
Thu, 18 Aug 2016 12:03:50 +0300 https://arm1.ru/blog/compareshots-v1-2 https//arm1.ru/blog/compareshots-v1-2#comments
Впечатления от Assassin's Creed Unity https://arm1.ru/blog/vpechatleniya-ot-assassin-s-creed-unity

]]>
Wed, 17 Aug 2016 21:44:51 +0300 https://arm1.ru/blog/vpechatleniya-ot-assassin-s-creed-unity https//arm1.ru/blog/vpechatleniya-ot-assassin-s-creed-unity#comments
Кастомный бейдж для UITabBarController на Swift https://arm1.ru/blog/kastomnyj-bejdzh-dlya-uitabbarcontroller-na-swift Шёл 2016 год, а для того, чтобы изменить цвета для бейджа внутри таба всё ещё необходимо создавать свой UI-элемент и добавлять его в таббар вручную. Работающее решение на Swift:

// MARK: - UITabBarController extension for badges
extension UITabBarController {

  /**
  Set badges

  - parameter badgeValues: values array
  */
  func setBadges(badgeValues: [Int]) {

    for view in self.tabBar.subviews {
      if view is CustomTabBadge {
        view.removeFromSuperview()
      }
    }

    for index in 0...badgeValues.count-1 {
      if badgeValues[index] != 0 {
        addBadge(index,
                 value: badgeValues[index],
                 color:UIColor.yellowColor(),
                 font: UIFont.systemFontOfSize(11)
        )
      }
    }

  }

  /**
  Add badge for tab

  - parameter index: index of tab
  - parameter value: badge value
  - parameter color: badge color
  - parameter font: badge font
  */
  func addBadge(index: Int, value: Int, color: UIColor, font: UIFont) {
    let badgeView = CustomTabBadge()

    badgeView.clipsToBounds = true
    badgeView.textColor = UIColor.whiteColor()
    badgeView.textAlignment = .Center
    badgeView.font = font
    badgeView.text = String(value)
    badgeView.backgroundColor = color
    badgeView.tag = index
    tabBar.addSubview(badgeView)

    self.positionBadges()
  }

  override public func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    self.tabBar.setNeedsLayout()
    self.tabBar.layoutIfNeeded()
    self.positionBadges()
  }

  /**
  Positioning
  */
  func positionBadges() {

    var tabbarButtons = self.tabBar.subviews.filter { (view: UIView) -> Bool in
      return view.userInteractionEnabled // only UITabBarButton are userInteractionEnabled
    }

    tabbarButtons = tabbarButtons.sort({ $0.frame.origin.x < $1.frame.origin.x })

    for view in self.tabBar.subviews {
      if view is CustomTabBadge {
        let badgeView = view as! CustomTabBadge
        self.positionBadge(badgeView, items:tabbarButtons, index: badgeView.tag)
      }
    }
  }

  /**
  Position for badge

  - parameter badgeView: badge view
  - parameter items: tab bar buttons array
  - parameter index: index of tab
  */
  func positionBadge(badgeView: UIView, items: [UIView], index: Int) {

    let itemView = items[index]
    let center = itemView.center

    let xOffset: CGFloat = 12
    let yOffset: CGFloat = -14
    badgeView.frame.size = CGSizeMake(17, 17)
    badgeView.center = CGPointMake(center.x + xOffset, center.y + yOffset)
    badgeView.layer.cornerRadius = badgeView.bounds.width/2
    tabBar.bringSubviewToFront(badgeView)
  }

}

/// Custom UILabel class for badge view
class CustomTabBadge: UILabel {}

]]>
Fri, 12 Aug 2016 15:00:25 +0300 https://arm1.ru/blog/kastomnyj-bejdzh-dlya-uitabbarcontroller-na-swift https//arm1.ru/blog/kastomnyj-bejdzh-dlya-uitabbarcontroller-na-swift#comments
Функция задержки на Swift https://arm1.ru/blog/funktsiya-zaderzhki-na-swift Очень приятный синтаксический сахар - функция для запуска кода с задержкой через Grand Central Dispatch с помощью dispatch_after:

Swift 2:

/**
Delay function using GCD. Syntax sugar.

- parameter delay: delay in seconds
- parameter closure: closure code to execute after delay
*/
func delay(delay:Double, closure:()->()) {
  dispatch_after(
    dispatch_time(
      DISPATCH_TIME_NOW,
      Int64(delay * Double(NSEC_PER_SEC))
    ),
  dispatch_get_main_queue(), closure)
}

Swift 3:

/**
Delay function using GCD. Syntax sugar.

- parameter delay: delay in seconds
- parameter closure: closure code to execute after delay
*/
func delay(_ delay:Double, closure:()->()) {
  let when = DispatchTime.now() + delay
  DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}

Использование:

delay(0.4) {
  // код
}

]]>
Tue, 09 Aug 2016 16:30:53 +0300 https://arm1.ru/blog/funktsiya-zaderzhki-na-swift https//arm1.ru/blog/funktsiya-zaderzhki-na-swift#comments
Модальный UIVIewController с прозрачностью https://arm1.ru/blog/modal-nyj-uiviewcontroller-s-prozrachnost-yu Шпаргалка - как запрезентить UIViewController поверх другого так, чтобы он был полупрозрачный и под ним просвечивал контент.

Выставляем в Storyboard для Segue поле Kind как "Present Modally". Для UIViewController, который показываем, выставляем цвет фона Clear Color, делаем внутри фон (картинка или другой UIView) со значением alpha, например, 0.5.

В коде:

self.performSegueWithIdentifier("segueName", sender: self)

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
  if segue.identifier == "segueName" {
    let vc = segue.destinationViewController as! YourViewController
    vc.modalPresentationStyle = .Custom
  }
}

 

]]>
Tue, 14 Jun 2016 15:02:04 +0300 https://arm1.ru/blog/modal-nyj-uiviewcontroller-s-prozrachnost-yu https//arm1.ru/blog/modal-nyj-uiviewcontroller-s-prozrachnost-yu#comments