Social Icons

пятница, 19 июня 2015 г.

Optionals in Swift.

Введение

Считаю, что для тех, кто начал программировать под iOS сразу на языке Swift, минуя Objective-C, трудно понять смысл языковой конструкции Optionals. 
Вот что сказано в документации  [1]: "Optionals are similar to using nil with pointers in Objective-C, but they work for any type, not just classes."
Что это означает. В Objective-C не все типы являются классами. Есть базовые типы, которые не являются классами в терминах ООП.
В Objective-C, если говорят об объектах, то имеют ввиду объкты классов (или инстанс-переменная класса). Так, объявление переменной-указателя на объект выглядит следующим образом:
     UIView* view;
Пока нам известно только одно, что переменная имеет тип UIView. UIView - это класс. Все классы имеют одного общего родителя - класс NSObject. Т.е., все объекты в  Objective-С обладают некими общими свойствами, которые они наследуют от NSObject.  Переменная с именем view хранит адрес в памяти, где находится объект. Пока еще объект не создан. Что это значит? Это значит, что не выдедена память для объекта. Переменная указывает в никуда. Тут возникает вопрос: а что если послать сообщение такой переменной? Ведь не всегда одновременно с объявлением переменной мы инициализируем ее значением, а именно, выделяем память для объекта. К счастью такие переменные инициализируются таким объектом, который ничего не выполняет если ему отправить любое сообщение (вызвать любую функцию у такой переменной). Этот объект -  nil. Транслятор не анализирует, что содержит переменная: nil, ссылку на реально существующий объект, или объект, который уже не существует. В последнем случае программа закрешится. Чтобы этого избежать, надо присвоить переменной nil, если точно известно, что объект уничтожен ранее, или сам факт присвоения nil влечет за собой уничтожение объекта (если переменная владела объектом. Эти ньюансы в данном контексте не важны). В любом случае nil - это такая удобная вещь, которую можно всегда проверить:
    if (view != nil){
...
}  
    или так
    if (view){...
}  
Плюс к тому, что как сказано ранее, вызов метода, например 
    [view setNeedsDisplay];
ни к чему плохому не приведет. По крайней мере программа продолжит выполнение.
Надеюсь теперь это выражение: "Optionals are similar to using nil with pointers in Objective-C, but they work for any type, not just classes."
стало более понятным. 
В Swift есть несколько основных положений. Первое - все является объектом. Даже число 1 - объект (так скажем объект не класса, как можно подумать, а структуры, но сейчас это не важно. Он остается объектом и может принимать сообщения. И классы и структуры - типы. Поэтому можно сказать точнее: в Swift все является объектом какого-то типа). В этом есть большое отличие Swift от Objective-C. Второе - безопасность типов (type safe). Тип переменной, однажды объявленный (если тип явно не указан, то транслятор сам определит его из контекста (называется это type inference)), не может быть изменен. Уже в момент объявления переменной транслятор должен зать тип этой переменнной. И дальше он будет контролировать: в переменную указанного типа невозможно записать значение переменной другого типа. При попытке сделть это транслятор предупредит. Кстати, для совместимости, существует тип AnyObject, который может принимать переменную любого типа. 
Все типы в Swift едины в том смысле что абсолютно для всех переменных мы можем проверить, содержит ли она что-то полезное, была ли она проинициализирована, присвоено ли ей значение, а не только для объектов как в Objective-C.
Понятно, что если мы проинициализировали переменную в момент ее объявления или в инициализаторе, тогда нет смысла проверять. 
В каких же случаях надо проверять? В случаях, когда переменная была объявлена, но ей при этом не было присвоено значение. 
В процессе выполнения программы ей возможно будет оно присвоено, но транслятор об этом не знает, где и когда. Он будет строго реагировать на тот факт, что переменная не проинициализирована. Чтобы "обмануть" транслятор, существует Optionals. Optional это такая обертка над переменной. Мы всегда сможем развернуть ее и посмотреть что там внутри. Ради этого есть конструкция. Транслятор о ней знает. Иногда мы точно знаем, что там под оберткой 100% что-то есть. Можно не проверять, не разворачивать. Это тоже предусмотрено. Об этом потом. И надо понимать, что то, что лежит под оберткой в случае, если там ничего нет, то это оно ведет себя так же как nil в Objective-C. Это надо на случай когда мы пытаемся что-то выполнить для этого "нечто".
Короче говоря, не закрешится, перейдет просто к следующему оператору. 

Как это работает?

Например, 
   var optionalValue: Int?
 Обратите внимание, переменная не проинициализирована. Ей не присвоено значение. Транслятор не ругается только потому, что он видит 
это волшебный значек "?". Эта конструкция Int? называется "optional int". Это значит, что переменная optionalValue может хранить что-то одно из двух (и только): это значение типа Int (структура) или nil - которое говорит об отстутсвии (временном) какого-либо значения внутри. Второе значение (nil) появляется только потому, что это Optionals. Понятие nil больше нигде не появляется и его нельзя использовать для не Optionals переменных. Итак, optionalValue сейчас содержит nil. Как же мы сможем проверить, есть ли что-то под оберткой или нет?
Это можно сделать просто указав эту переменную в условии:
if optionalValue {   ...
} else {
   ...
}
Это называется "Force Unwrapping". Независимо от типа переменной условие вернет true если под оберткой что-то есть. Сейчас важно понять, что если мы попытаемся использовать нашу переменную внутри "истинного" блока, транслятор не даст нам это сделать. Так как он знает точно, 
что эта переменная не была проинициализирована и тут возможен креш. Чтобы использовать эту переменную, надо еще раз обмануть транслятор. Для этого так называемый "Optional Binding": 
   if let unwrappedOptionalValue = optionalValue {       println("\(unwrappedOptionalValue)")
   }
На этот раз мы можем смело использовать значение unwrappedOptionalValue в вычислениях внутри блока. Транслятор уже понимает. Молчит.
Эта конструкция (оператор if let, именно "if let" - это можно понимать как самостоятельный оператор) широко используется. Но понятно не всегда удобно так делать. Наш весь код будет состоять на 30% из таких условий.
Читать такой код будет не очень удобно. Если сразу после имени переменной поставить другой волшебный знак "!", то 
переменную можно использовать везде и без проверки. Транслятор будт спокойно все пропускать. Это ("!") называется "implicitly unwrapped optionals". Единственно, если окажется что в этой переменной нет значения в данный момент, то произойдет креш (ошибка времени выполнения). Итак, первое условие можно переписать используя implicitly unwrapped optionals:
if optionalValue {   println("\(optionalValue!)")
} else {
   ...
}
и все. Транслятор молчит.
После использования переменной ей можно присвоить значение nil: 
optionalValue = nil 
Теперь optionalValue опять не содержит полезных значений. Но! Присвоить nil можно только переменной, объявленной как Optional. Во всех других случаях, если вы объявили переменную, проинициализировали ее (неважно, это let или var), вы не сможете присвоить ей nil. Кстати, всеми вопросами выделения и освобождения памяти занимается Swift. Тут не обязательно присвоить nil, чтобы подсказать среде, что все, объект дальше не нужен, и если на него нет 
больше ссылок, то его можно удалить. Swift сам все посчитает и удалит.
Это почти все, что надо знать про Optionals для того, чтобы начать пробовать.
Дальше начинаются проблемы. Это грубо говоря функции (обычные, встроенные, конструкторы объектов и т.д.), которые могут возвращать что-то. Это что-то может быть Optionals. Вы ставите "!" чтоб не заморачиваться с обертками. А функция реально вернула nil. Тут "креш" неизбежен. Это надо понимать. 
Другой случай. Это преобразование типа (Downcast). Как это принято в полиморфизме, вы получаете переменную базового типа в качестве параметра функции. 
Вам надо преобразовать ее в конкретный тип, который возможно есть где-то в иерархии ниже, чтобы отработать ее конкретное поведение. 
Вы пытаетесь преобразовать. Тут тоже преобразование упирается в Optionals. Если преобразование возможно, то получаете переменную нужного типа, если нет, и
переменная объявлена как Optionals, можно развернуть и проверить. Если нет - "креш". Нельзя присвоить переменной nil.
Конечно если вы точно уверены, что нужный класс наследует базовый (выше в иерархии), то тут все в порядке. Ставьте "!" и все работает!
А если нет (не наследуется), то закрешится. Тут надо разворачивать и проверять. В этом безопасность Swift.
Например:
if let navController = self.menuController()?.mainViewController as? UINavigationController {
    if navController.topViewController.isKindOfClass(SKAboutViewController) {
        self.menuController()?.removeGestures()
    }
}
В первой строке такая вот конструкция: self.menuController()?.mainViewController называется Optional Chaining. Имеется ввиду "?.".
Здесь свойство self.menuController()? является Optionals, поэтому возвращает или объект или nil. Если nil, продолжаем выполнять программу после закрывающей скобки if. Если объект - обращаемся к свойству mainViewController, который должен что-то вернуть. Мы это что-то пытаемся преобразовать к типу UINavigationController. Точнее может сказать интерпретировать как тип UINavigationController. То, что получилось станет значением константы navController. Если удачно получилось преобразование, используем константу в дальнейших вычислениях. Если нет-продолжаем выполнять после закрывающей скобки if. Если бы мы поставили as! вместо as?, и при этом преобразование не удалось, то был бы "креш" (другими словами, as! буквально означает то, что написано: внимание! Возможна проблема). Преимущество as! как вы уже знаете,
в том, что не обязательно оборачивать. Можно было бы написать (уже без условий):
let navController = self.menuController()?.mainViewController as! UINavigationController 
 В Swift 2 Apple предпринял шаги по сокращению использования Optionals. Как сказано Swift blog:
"The SDKs have been updated to annotate API that cannot return nil so you don’t need to use optionals as often."
SDK не будет возвращать nil. Т.е. не будут возвращаться Optionals. 

Литература.

Руководство по Swift от Apple на сайте для разработчиков Apple. Оно же находится в бесплатном доступе на iBook store. Можно открыть через приложение iBook.   
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/index.html.
https://itunes.apple.com/us/book/swift-programming-language/id881256329?mt=11&ign-mpt=uo%3D4

воскресенье, 7 июня 2015 г.

GenStringResources. Application for localization the Xcode projects

Introduction

GenStringResources is a Mac OS application. It requirements MacOS at least 10.6.4 and above, Xcode 4.3 and above. The application uses ibtool and genstrings utilities from Xcode. 
GenStringResources may be used for localization of the Xcode projects in Objective-C.
Application is divided on following parts:
⁃    scanner of the string resources from the Localizable.strings files;
⁃    scanner of the string resources from the XIB files;
⁃    of the string resources creator;
⁃    presenting total information about all string resources for all languages for all projects;
⁃    setting up all options of the application.
Scanner of the resources is passed all Localizable.strings by genstrings utility. It passed by recursive  all folder with modules with the source code of a project. It generates Localizable.strings file with total string resources for the project.
It orders in ascending of key (it is a left part of the line before '=' in the file with resources). Then, it takes the current files with string resources for every language scheme (lproj-folder with according Localizable.strings file) and uses for comparing them with new 
string resources on existing by key. In result, we have total string resources with new string without translating and old string with them. The string without translating are kept in separate Localizable.strings which can be used for translate.
After translate, all new strings can be merged with current resources by the application. 
The same as the scanner of the string resources, the scanner of the XIB, used ibtool utility, builds the similar total file as the previous scanner, just for XIB strings resources.
All operation are carried out in automatic mode for every active project. You can set up active mode for every project in settings. 
So, all string resources are unit in following total resources:
- current dictionary. It contains all phrases with translate for all projects. 
- actual dictionary. In result of scanning that collected all resources for all projects. They are actual on now. Some phrases are translated (they present the current dictionary) and another part is waiting to translate.
After translate, all new strings can be merged with actual resources and become as current. 

User interface

Main window is presented as many documents interface and consists from tools pane and tab bar.
Every document window presented as table with context menu. Tools pane is repeat of the main menu.
Menu item 'Open strings' and 'Start scanning' are calculating and presenting of the string resources.
Menu item 'Open XIBs' and 'Start scanning XIBs' are calculating and presenting of the string resources from the XIBs. 
Menu item 'Total' presents total information about all string resources for all projects in one table.
Menu item 'Settings' are used for setting up all optional parameters. There are path to Xcode project, path to work folder and so on. 
Then menu item for showing readme file and menu item for setup of the tools pane.

Data store

All data is kept in work directory. By default it is '~/Documents/LocalizableStrings'. 
It can be changed in settings. The work directory is divided on three. They are: '/strings', '/XIBs', '/XcodeProjects'. 
Every folder consists from project subdirectories by they name.
In result of scanning all string resources accumulate in the working directory. In main window, the user can see information view. By menu item, user can open information for researching.

User guide


Settings




SwiftOrganizer. Application in Swift for iOS.

SwiftOrganizer is a simple organizer application for an iPhone and for an iPad. 
Was implemented for getting some practice of using the Swift.

Were implemented two types of database:
- Local database on Core Data
- Remote database on the Parse.com server

Also were implemented the following features:
- autolayout (on Interfase Builder Level only)
- switching of the database types from Core Data to remote database and vice versa
- used iCloud key-value storage for switching database types (if you change database type on your iPad, then on your iPhone is changing after some seconds too)
- Core Data on iCloud (fallback store)
- push notifications via Parse.com server
- events can be saved to the Calendar (then you can see your events in the Calendar app on your device) by using EventKit.
- localization app
- local notifications
- sharing via UIActivityViewController
- UIDocument. Enter and Edit files in the Documents folder.
- Document Picker - access to files on remote storage like iCloud, DropBox and etc with download to local Documents folder.
- extension
- todaye widget
- all database function are in the DataBaseKit.framework.

Were applied the following design patterns:
- abstract method;
- observer;
- singleton;
- builder;

Some notes for explain how it works:
On first start are created some tasks by default.
You can add a new task by pressing on button '+' on the navigator bar.
You can select line in tableview to start editing current task.
Any task may be deleted by left swipe gesture on the table view line.
For fetching tasks are used two tab bar. Upper tab bar is used for filtering by category. Bottom tab bar is used for filetering by icons.

Category - entity which used for featching.
There are some predefined category: 'ToDo', 'Work', 'Life', 'Events'.
Icons are used for featching too. They are predefined too.

The Project is in progress.
In future:
- add or remove icons and categories
- make login (at present default user and password are used for login to the backend (parse.com server)).
- will be able to add events to the Reminder app
- local database will be on the iCloud.

Requirements

- Xcode 6
- iOS 8


Tab bars use for filtering task. On the table presented data from local or remote databases. Switch type of a database can be done by settings, by press Activity button, by iCloud from other device, by Today Widget as well.

Today Widget. Current data from database presented on the table. Here you can change of the database type and select task. Application gets parameters from widget and makes appropriate actions.


Action may be done by using of the UIActivityViewController. As you can see were added some buttons in to the activity controller menu.


Sharing data in social networks  by using activity controller:


Some custom controllers:



All source code you can find on GitHub