четверг, 22 июня 2017 г.

Swift 3. The Basics (Tuples)

В 3 уроке рассматриваются кортежи - Tuples.

Домашнее задание:

1. Создать тюпл с тремя параметрами:

- максимальное количество отжиманий
- максимальное количество подтягиваний
- максимальное количество приседаний

Заполните его своими достижениями :)

Распечатайте его через println()

2. Также сделайте три отдельных вывода в консоль для каждого параметра

При том одни значения доставайте по индексу, а другие по параметру

3. Создайте такой же тюпл для другого человека (супруги или друга)

с такими же параметрами, но с другими значениями

Используйте промежуточную переменную чтобы поменять соответствующие значения

первого тюпла на значения второго

4. Создайте третий тюпл с теми же параметрами, но значения это разница

между соответствующими значениями первого и второго тюплов

Результат выведите в консоль


//Lesson 3. Tuples

/*
 1. Создать тюпл с тремя параметрами:

 - максимальное количество отжиманий
 - максимальное количество подтягиваний
 - максимальное количество приседаний

 Заполните его своими достижениями :)

 Распечатайте его через println()
 */

print("1\n")

var myResult = (pushUps: 20, pullUps: 7, squats: 30)
print("My result\nPushUps: \(myResult.pushUps). PullUps: \(myResult.pullUps). Squats: \(myResult.squats)")

/*
 2. Также сделайте три отдельных вывода в консоль для каждого параметра
 При том одни значения доставайте по индексу, а другие по параметру
 */

print("\n2\n")

var (_, _, mySquats) = myResult

print("PushUps: \(myResult.0)")
print("PullUps: \(myResult.pullUps)")
print("Squats: \(mySquats)")

/*
 3. Создайте такой же тюпл для другого человека (супруги или друга)
 с такими же параметрами, но с другими значениями
 Используйте промежуточную переменную чтобы поменять соответствующие значения
 первого тюпла на значения второго
 */

print("\n3\n")

let tempResult = myResult

var friendResult = (pushUps: 25, pullUps: 10, squats: 35)

myResult = friendResult
friendResult = tempResult

print("My Result:", myResult)
print("Friend Result:", friendResult)

/*
 4. Создайте третий тюпл с теми же параметрами, но значения это разница
 между соответствующими значениями первого и второго тюплов
 Результат выведите в консоль
 */

print("\n4\n")

let substractPushUps = abs(myResult.pushUps - friendResult.pushUps)
let substractPullUps = abs(myResult.pullUps - friendResult.pullUps)
let substractSquats = abs(myResult.squats - friendResult.squats)


var substractResult = (pushUps: substractPushUps, pullUps: substractPullUps, squats: substractSquats)

*************************************************************

1

My result
PushUps: 20. PullUps: 7. Squats: 30

2

PushUps: 20
PullUps: 7
Squats: 30

3

My Result: (pushUps: 25, pullUps: 10, squats: 35)
Friend Result: (pushUps: 20, pullUps: 7, squats: 30)

4

Substraction Result:  (pushUps: 5, pullUps: 3, squats: 5)

Swift 2. The Basics (Base Type)

Во втором уроке Алексей продолжает рассмотрение главы документации The Basics и рассматривает базовые типы, запись целых чисел в 10, 2, 8 и 16 - ричных формах, размеры различных целых типов и создания собственного алиаса (другого названия) для базового типа.

Домашнее задание к уроку 2:

1. Выведите в консоль минимальные и максимальные значения базовых типов, не ленитесь :)

2. Создайте 3 константы с типами Int, Float и Double

Создайте другие 3 константы, тех же типов: Int, Float и Double,

при чем каждая из них это сумма первых трех, но со своим типом

3. Сравните Int результат суммы с Double и выведите отчет в консоль

 Код:

// The Basics 2 Home work

/*
 1 Выведите в консоль минимальные и максимальные значения базовых типов, не ленитесь :)
 */

print("\n1.\n")
print("For 'Int' type. MIN value \(Int.min), MAX value: \(Int.max).")
print("For 'UInt' type. MIN value \(UInt.min), MAX value: \(UInt.max).")
print("For 'Int8' type. MIN value \(Int8.min), MAX value: \(Int8.max).")
print("For 'UInt8' type. MIN value \(UInt8.min), MAX value: \(UInt8.max).")
print("For 'Int16' type. MIN value \(Int16.min), MAX value: \(Int16.max).")
print("For 'UInt16' type. MIN value \(UInt16.min), MAX value: \(UInt16.max).")
print("For 'Int32' type. MIN value \(Int32.min), MAX value: \(Int32.max).")
print("For 'UInt32' type. MIN value \(UInt32.min), MAX value: \(UInt32.max).")
print("For 'Int64' type. MIN value \(Int64.min), MAX value: \(Int64.max).")
print("For 'UInt64' type. MIN value \(UInt64.min), MAX value: \(UInt64.max).")


/*
 2. Создайте 3 константы с типами Int, Float и Double
 Создайте другие 3 константы, тех же типов: Int, Float и Double,
 при чем каждая из них это сумма первых трех, но со своим типом
 */

print("\n2.\n")

let intConst = 3
let doubleConst = 2.8
let floatConst: Float = 1.7

let intSum = Int( Double(intConst) + doubleConst + Double(floatConst))
let floatSum = Float( Double(intConst) + doubleConst + Double(floatConst))
let doubleSum = Double(intConst) + doubleConst + Double(floatConst)

print("Int Const: \(intConst), Double Const: \(doubleConst), Float Const: \(floatConst)")

print("Int Sum: \(intSum), Double Sum: \(doubleSum), Float Sum: \(floatSum)")


/*
 3. Сравните Int результат суммы с Double и выведите отчет в консоль
 */

print("\n3.\n")

if doubleSum > Double(intSum) {
    print("Double Sum > Int Sum")
} else if doubleSum == Double(intSum) {
    print("Int Sum == Double Sum")
} else {
    print("Int Sum > Double Sum")

}


*****************************************************************

1.

For 'Int' type. MIN value -9223372036854775808, MAX value: 9223372036854775807.
For 'UInt' type. MIN value 0, MAX value: 18446744073709551615.
For 'Int8' type. MIN value -128, MAX value: 127.
For 'UInt8' type. MIN value 0, MAX value: 255.
For 'Int16' type. MIN value -32768, MAX value: 32767.
For 'UInt16' type. MIN value 0, MAX value: 65535.
For 'Int32' type. MIN value -2147483648, MAX value: 2147483647.
For 'UInt32' type. MIN value 0, MAX value: 4294967295.
For 'Int64' type. MIN value -9223372036854775808, MAX value: 9223372036854775807.
For 'UInt64' type. MIN value 0, MAX value: 18446744073709551615.

2.

Int Const: 3, Double Const: 2.8, Float Const: 1.7
Int Sum: 7, Double Sum: 7.50000004768372, Float Sum: 7.5

3.


Double Sum > Int Sum

Swift 1. The Basics

Я решил пройти курс Алексея Скутаренко по Swift, который находится https://vk.com/iosdevcourse. На текущий момент вышел уже Swift 4.0, поэтому синтаксис языка может иметь различия, но тем не менее суть не изменилась.

Алексей в каждом уроке рассматривает главы из официальной документации Apple, которая находится здесь

В первой главе рассматриваются основные понятия константы и переменные, основные типы, способы вывода в консоль и др. базовые понятия.

В домашнее задание входит ознакомление полностью с главой The Basics и для закрепления небольшой кусочек кода с переменными и константами, который выводит информацию о себе.


// The Basics example

let greeting = "Hello!"
let firstName, lastName: String
firstName = "Alex"
lastName = "Tim"
let yearBorn: Int
yearBorn = 1_983
var weight = 63.5
weight = 64.7
var city = "Novosibirsk"

print(greeting)
print("My name:",firstName, lastName, separator: " ", terminator: "\n")
print("My weight is " + String(weight) + "kg")
print("I live in \(city). ", terminator: "")

print("I was born in \(yearBorn) year")


Вывод в консоли:
________________________

Hello!
My name: Alex Tim
My weight is 64.7kg
I live in Novosibirsk. I was born in 1983 year

воскресенье, 17 июля 2016 г.

44. CoreData Part 4 FRC

Это заключительный урок по CoreData.
В этом уроке рассматривается создание графического представления той базы данных, которую мы делали в прошлых уроках на базе стандартного шаблона приложений для CoreData.
Видео к этому урок находится здесь.
Исходный файл с базой и функциями для работы с ней можно скачать в прошлом уроке 43.

Чтобы использовать базу данных из любого контроллера, удобно вынести все функции в синглтон.
Для создания синглатона создаем файл TADataManager наследника от NSObject.

В  TADataManager.h создаем статический метод   + (TADataManager *)sharedManager; .
В .m файле определяем этот метод. Объявляем статическую переменную типа совпадающую с типом класса  и  вставляем сниппет/шаблон для однократного выполнения - dispatch_once:

+ (TADataManager *)sharedManager
{
    static TADataManager *manager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[TADataManager alloc] init];
    });
    return manager;

}

Код внутри блока выполнится один раз в течении жизни приложения. Это сделано, чтобы другие потоки не смогли одновременно обратиться  к свойству manager.

Теперь при вызове     TADataManager *sharedManager = [TADataManager sharedManager];
Мы получаем один и тот же  объект для работы с базой.
Этот объект не будет удаляться из памяти и будет жить до закрытия приложения.

Теперь перенесем, все что касается CoreData из AppDelegate в TADataManager.


Исходный код (незакончено) проекта к 44 уроку.

воскресенье, 10 июля 2016 г.

43. CoreData Part 3 Fetching

Продолжаем изучение CoreData. Видео к уроку 43 находится здесь.
В этом уроке мы разберемся как извлекать данные из базы, применяя условия, сортировки и т.д.
В прошлом уроке мы добавили сущность TACourse которая соответсвует предметам в университете.
Для вывода названия курса и студентов, добавим метод:

- (void)printCourse:(TACourse *) courseForPrint
{
    NSString *strReturn = [NSString stringWithFormat:@"TACourse: %@. Students:  ",
                           courseForPrint.nameCourse];
    for (TAStudent *student in courseForPrint.relationStudent) {
        strReturn = [NSString stringWithFormat:@"%@ %@", strReturn, student.lastName];
    }
    NSLog(@"%@", strReturn);

}

Так же добавим информацию о  курсе в функции вывода студента и университета.

Создадим университет и добавим в него 5 курсов.
Затем для 100 студентов  добавим курсы (рандомно до 5 курсов для каждого студента).

    TAUniversity *university = [self addUniversity];
    NSArray *arrayCourses = @[
                              [self addCourseWithName:courseName[0]],
                              [self addCourseWithName:courseName[1]],
                              [self addCourseWithName:courseName[2]],
                              [self addCourseWithName:courseName[3]],
                              [self addCourseWithName:courseName[4]]
                              ];
    [university addRelationCourse:[NSSet setWithArray:arrayCourses]];
    

    for (int i = 0; i < 100; i++) {
        TAStudent *student = [self addRandomStudent];
        TACar *car = [self addRandomCar];
        student.carRelation = car;
        [university addRelationStudentObject:student];
        NSInteger numberCourses = arc4random_uniform(5);
        while (student.relationCourse.count < numberCourses) {
            TACourse *courseForStudent = arrayCourses[arc4random_uniform(5)];
            if (![student.relationCourse containsObject:courseForStudent]) {
                [student addRelationCourseObject:courseForStudent];
            }
        }
    }
    
    NSError *error;
    if (![self.managedObjectContext save:&error]) {
        NSLog(@"%@",[error localizedDescription]);
    };


    [self printAllObjects];


Рассмотрим запрос студентов из базы. Нам редко нужны сразу все студенты содержащиеся в базе. К примеру для первичного вывода в таблицу нужны 20 студентов, а остальные записи нужно подгружать по мере скрола таблицы.
Для реализации этой функции в запрос нужно прописать размер пачки, по которой будет браться студенты из базы: [request setFetchBatchSize:20];

    NSFetchRequest *request = [[NSFetchRequest alloc]init];
    
    NSEntityDescription *description  = [NSEntityDescription entityForName:@"TAStudent" inManagedObjectContext:self.managedObjectContext];
    [request setEntity:description];
    [request setFetchBatchSize:20];
    NSError *requestError = nil;
    NSArray *arrayResult = [self.managedObjectContext executeFetchRequest:request error:&requestError];
    

    [self printObjects:arrayResult];

Если например взять первые 50 записей в массиве, то видно, что запрос сделан 3 раза по 20 записей:

    arrayResult = [arrayResult subarrayWithRange:NSMakeRange(0, 50)];

Для того, чтобы взять первые 35 записей из базы, нужно установить лимит:

[request setFetchLimit:35];

Для того, чтобы установить смещение от начала базы устанавливаем оффсет:

    [request setFetchOffset:10];

Теперь по запросу будут выведены студенты с 10 по 45 по 20 штук в пачке.

Если мы выводе студента используем информаци о курсах, то каждый раз делается дополнительный запрос в базу на загрузку курсов связанных с текущим студентом.
Это замедляет работу.
Чтобы данные с нужными связями подгрузились сразу, нужно указать какие связи подгружать:

    [request setRelationshipKeyPathsForPrefetching:@[@"relationCourse"]];

Таким образом, если свойство не будет обязательно использоваться для вывода, то оставляем загрузку по умолчанию, а если свойство 100% нам понадобится, делаем предазгрузку.

Сортировку получаемых данных лучше делать средствами CoreData, а не сортировать массивы после получения, т.к. сортировка через CoreData  занимает меньше времени и ресурсов.
Для сортировки студентов по фамилии, а при одинаковой фамилии по имени задаем два дескриптора в котором указываем по какому полю сортировать и вставляем их в запрос в нужном порядке (сначала сортировка фамилии, затем имени):

    NSSortDescriptor *firstNameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"firstName" ascending:YES];
    NSSortDescriptor *lastNameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastName" ascending:YES];

    [request setSortDescriptors:@[lastNameDescriptor, firstNameDescriptor]];

Для того, чтобы брать из базы студентов, удовлетворяющих определенным условиям, нужно использовать  NSPredicate:

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"score > %@", @3];
    

    [request setPredicate:predicate];

Мы создали предикат и указали, что нам нужны студенты с оценкой больше 3. Добавили предикат к запросу.

Если нам нужно устновить несколько условий (например вывести score >3 и <=3.5):

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"score > 3 AND score <= 3.5"];
или
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"score > %@ && score <= %@", @3, @3.5];

Для того, чтобы вывести студентов с оценкой  3 < score <=3.5 и количеством курсов больше 3, нужно использовать оператор коллекции @count:

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"score > %@ && score <= %@ && relationCourse.@count >= %@", @3, @3.5, @3];

Для того, чтобы к этим условиям добавить новое: имена студентов должны быть в массиве, строка предиката будет выглядеть так:

    NSArray *arrayValideName = @[@"Vanetta",@"Tran",@"Tandra"];
    

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"score > %@ && score <= %@ && relationCourse.@count >= %@ && firstName IN %@", @3, @3.5, @3, arrayValideName];

Сделаем запрос на вывод курсов, у которых средний бал студентов больше 2.9

    NSFetchRequest *request = [[NSFetchRequest alloc]init];
    
    NSEntityDescription *description  = [NSEntityDescription entityForName:@"TACourse" inManagedObjectContext:self.managedObjectContext];
    [request setEntity:description];
    
    [request setRelationshipKeyPathsForPrefetching:@[@"relationStudent"]];
    
    NSSortDescriptor *courseNameSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"nameCourse" ascending:YES];

    [request setSortDescriptors:@[courseNameSortDescriptor]]; 
    
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"@avg.relationStudent.score > %@", @2.9];
    
    [request setPredicate:predicate];
    
    NSError *requestError = nil;
    NSArray *arrayResult = [self.managedObjectContext executeFetchRequest:request error:&requestError];
    

    [self printObjects:arrayResult];

Чтобы вывести курсы с суммой оценок студентов 160

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"@sum.relationStudent.score > %@", @160];

Чтобы вывести курсы, которые имеют студентов с оценкой больше 3.9

   NSPredicate *predicate = [NSPredicate predicateWithFormat:@"@max.relationStudent.score > %@", @3.9];

Чтобы сделать подзапрос для определения курсов у которых студенты ездят более, чем на  3 BMW, предикат выглядит так:

 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SUBQUERY(relationStudent, $student, $student.carRelation.model == %@).@count >= %@", @"BMW", 3];

Но у меня этот запрос вываливается с EXC_BAD_ACCESS (я к сожаению не разобрался, почему).

Альтернативным способом для выборки из базы сущностей с определенными критериями является добавление в файле модели нового Fetch Request.
Назовем наш запрос FetchStudents.
В списке Fetch All выбираем TAStudent.
Добавляем условие отбора  - жмем плюсик и выбираем  score -> is greater than -> 3.0.
Еще одно условие score -> is less than or equal to -> 3.5.
Если поменять режим страницы (правая кнопка), то мы увидим, как эти условия будут выглядеть в предикате: score > 3 AND score <= 3.5

Для того, чтобы воспользоваться интерактивно созданным запросом, нужно указать его имя:

    NSFetchRequest *request = [self.managedObjectModel fetchRequestTemplateForName:@"FetchStudents"];
    NSError *requestError = nil;
    NSArray *arrayResult = [self.managedObjectContext executeFetchRequest:request error:&requestError];
    

    [self printObjects:arrayResult];

Это удобно, когда нужно много раз использовать один и тот же запрос, т.е. вызывать запрос по имени. Если изменить этот запрос, то он изменится во всем приложении, что так же удобно.

Можно в этот запрос добавить сортировочные дескрипторы (в старом xCode для этого надо было сделать копию запроса (сейчас этого делать не нужно):

request = [request copy];

    NSSortDescriptor *firstNameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"firstName" ascending:YES];
    NSSortDescriptor *lastNameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastName" ascending:YES];

    [request setSortDescriptors:@[lastNameDescriptor, firstNameDescriptor]];

Последнее понятие рассмотренное в этом уроке это  Fetched Properties.
Это проперти возвращает всегда массив.
В файле модели выберем сущность TACourse и нажмем правую кнопку с плюсиком - Add Fetched Property.
Назовем новую Property - bestStudents.
Destination выбираем TAStudent.
В Predicate прописываем условие score > 3.5, т.е. в это проперти будет возвращены все студенты курса, у которых оценка больше 3.5 баллов (по аналогии с SUBQUERY).

Чтобы  обращаться через точку к новому проперти, добавим в файл с пропертями TACourse (TACourse+CoreDataProperties.h):

@property (nullable, nonatomic, retain) NSArray *bestStudents;

И объявим в файле TACourse+CoreDataProperties.m

@dynamic bestStudents;

Пересоздадим базу.

Выведем для каждого курса массив с лучшими студентами

    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription * description = [NSEntityDescription entityForName:@"TACourse" inManagedObjectContext:self.managedObjectContext];
    [request setEntity:description];
    [request setRelationshipKeyPathsForPrefetching:@[@"relationStudent"]];
    NSArray *arrayResult = [self.managedObjectContext executeFetchRequest:request error:nil];
    
    for (TACourse *course in arrayResult) {
        NSLog(@"Course: %@. BestStudents:", course.nameCourse);
        [self printObjects:course.bestStudents];
    }

Аналогично создаем  и выводим еще одно Fetched Property для студентов подписанных на много курсов: studentsWithManyCourses
Destination: TAStudent
Predicate: relationCourse.@count >= 4

Когда нужно обратиться к проперти в предикате, то используется выражение:
$FETCH_SOURCE.имя проперти

Файл с исходным кодом по уроку можно скачать здесь.