SlideShare ist ein Scribd-Unternehmen logo
1 von 74
Downloaden Sie, um offline zu lesen
TelevisiĂł de Catalunya
FormaciĂłn en movilidad
Conceptos de desarrollo en iOS
4ÂȘ sesiĂłn mayo 2013
1
Qué veremos hoy
Alert
Search Bar
Action Sheet
Activity
Customizing
Testing
2
Alert
UIAlertView
“Use the UIAlertView class to display an alert
message to the user”
3
Alert
UIAlertView
// MasterViewController.m
- (void)insertNewObject:(NSDictionary *)values
{
// ...
if (![context save:&error])
{
// ...
}
else
{
UIAlertView *notice = [[UIAlertView alloc]
initWithTitle:@"Nuevo vĂ­deo"
message:@"Se a creado un nuevo vĂ­deo correctamente"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil, nil];
[notice show];
}
4
Alert
UIAlertView
// MasterViewController.m
- (void)insertNewObject:(NSDictionary *)values
{
// ...
if (![context save:&error])
{
// ...
}
else
{
UIAlertView *notice = [[UIAlertView alloc]
initWithTitle:@"Nuevo vĂ­deo"
message:@"Se a creado un nuevo vĂ­deo correctamente"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil, nil];
[notice show];
}
5
Alert
UIAlertView
// MasterViewController.m
- (void)insertNewObject:(NSDictionary *)values
{
// ...
if (![context save:&error])
{
// ...
}
else
{
UIAlertView *notice = [[UIAlertView alloc]
initWithTitle:@"Nuevo vĂ­deo"
message:@"Se a creado un nuevo vĂ­deo correctamente"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:@"Deshacer", nil];
[notice show];
}
6
Alert
UIAlertView
// MasterViewController.m
- (void)insertNewObject:(NSDictionary *)values
{
// ...
if (![context save:&error])
{
// ...
}
else
{
UIAlertView *notice = [[UIAlertView alloc]
initWithTitle:@"Nuevo vĂ­deo"
message:@"Se a creado un nuevo vĂ­deo correctamente"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:@"Deshacer", nil];
[notice show];
}
7
“The UISearchBar object does not actually
perform any searches.You use the
UISearchBarDelegate protocol to implement
the actions when text is entered and buttons
are clicked”
Search Bar
8
Search Bar
UISearchBar
9
Search Bar
UISearchBar
Placeholder ‘Buscar por título o autor’
Marcar ‘Shows Cancel Button’
Seleccionar ‘Correction: No’
Conectar ‘Search Bar’ delegate con ‘MasterView Controller’
10
Search Bar
UISearchBarDelegate
// MasterViewController.m
@interface MasterViewController ()
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
- (void)performSearch:(NSString *)searchText;
@end
11
Search Bar
UISearchBarDelegate
// MasterViewController.m
@interface MasterViewController ()
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
- (void)performSearch:(NSString *)searchText;
@end
Declarar método privado performSearch:
12
Search Bar
UISearchBarDelegate
// MasterViewController.m
@interface MasterViewController ()
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
- (void)performSearch:(NSString *)searchText;
@end
- (NSFetchedResultsController *)fetchedResultsController {
// ...
NSFetchedResultsController *aFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
// ...
}
13
Search Bar
UISearchBarDelegate
// MasterViewController.m
@interface MasterViewController ()
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
- (void)performSearch:(NSString *)searchText;
@end
- (NSFetchedResultsController *)fetchedResultsController {
// ...
NSFetchedResultsController *aFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
// ...
}
Anular uso de caché en initWithFetchRequest:
14
Search Bar
UISearchBarDelegate
// MasterViewController.m
#pragma mark - UISearchBarDelegate
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
}
Acciones para
botones ‘Search’ y ‘Cancel’ e introducción de texto
15
Search Bar
UISearchBarDelegate
// MasterViewController.m
#pragma mark - UISearchBarDelegate
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[self performSearch:searchBar.text];
[searchBar resignFirstResponder];
[self.tableView reloadData];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
}
searchBarSearchButtonClicked:
16
Search Bar
UISearchBarDelegate
// MasterViewController.m
#pragma mark - UISearchBarDelegate
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[self performSearch:searchBar.text];
[searchBar resignFirstResponder];
[self.tableView reloadData];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
[searchBar resignFirstResponder];
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
}
searchBarCancelButtonClicked:
17
Search Bar
UISearchBarDelegate
// MasterViewController.m
#pragma mark - UISearchBarDelegate
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[self performSearch:searchBar.text];
[searchBar resignFirstResponder];
[self.tableView reloadData];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
[searchBar resignFirstResponder];
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
[self performSearch:searchText];
}
searchBar:textDidChange:
18
Search Bar
UISearchBarDelegate + Core Data
// MasterViewController.m
#pragma mark - Private
- (void)performSearch:(NSString *)searchText {
NSPredicate *predicate;
NSError *error = nil;
if(searchText && searchText.length > 0) {
predicate = [NSPredicate predicateWithFormat:
@"title contains[cd] %@ or author contains[cd] %@", searchText, searchText];
[self.fetchedResultsController.fetchRequest setPredicate:predicate];
} else {
[self.fetchedResultsController.fetchRequest setPredicate:nil];
}
if(![self.fetchedResultsController performFetch:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
[self.tableView reloadData];
}
Implementar método privado performSearch:
19
Search Bar
UISearchBarDelegate + Core Data
// MasterViewController.m
#pragma mark - Private
- (void)performSearch:(NSString *)searchText {
NSPredicate *predicate;
NSError *error = nil;
if(searchText && searchText.length > 0) {
predicate = [NSPredicate predicateWithFormat:
@"title contains[cd] %@ or author contains[cd] %@", searchText, searchText];
[self.fetchedResultsController.fetchRequest setPredicate:predicate];
} else {
[self.fetchedResultsController.fetchRequest setPredicate:nil];
}
if(![self.fetchedResultsController performFetch:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
[self.tableView reloadData];
}
contains[cd] is case and diacritic insensitive
20
Search Bar
UISearchBarDelegate + Core Data
// MasterViewController.m
#pragma mark - Private
- (void)performSearch:(NSString *)searchText {
NSPredicate *predicate;
NSError *error = nil;
if(searchText && searchText.length > 0) {
predicate = [NSPredicate predicateWithFormat:
@"title contains[cd] %@ or author contains[cd] %@", searchText, searchText];
[self.fetchedResultsController.fetchRequest setPredicate:predicate];
} else {
[self.fetchedResultsController.fetchRequest setPredicate:nil];
}
if(![self.fetchedResultsController performFetch:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
[self.tableView reloadData];
}
Anular criterios = Buscar todos
21
Search Bar
UISearchBarDelegate
22
“Use the UIActionSheet class to present the
user with a set of alternatives for how to
proceed with a given task”
Action Sheet
23
Action Sheet
24
Action Sheet
Seleccionar ‘IdentiïŹer:Action’
25
Action Sheet
Seleccionar ‘IdentiïŹer:Action’
Conectar ‘Bar Button Item - Action’ con DetailView Controller
Connection:Action
Name: shareByEmail
Type: id
26
Action Sheet
TARGETS : MyVideos : Build Phases : Link With Binary Libraries
MessageUI.framework
27
Action Sheet
UIActionSheet
// DetailViewController.h
#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>
#import <MessageUI/MFMailComposeViewController.h>
@interface DetailViewController : UIViewController <
UISplitViewControllerDelegate, UIWebViewDelegate,
UIActionSheetDelegate, MFMailComposeViewControllerDelegate>
// ...
@end
Importar MessageUI.h y MFMailComposeViewController.h
28
Action Sheet
UIActionSheet
// DetailViewController.h
#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>
#import <MessageUI/MFMailComposeViewController.h>
@interface DetailViewController : UIViewController <
UISplitViewControllerDelegate, UIWebViewDelegate,
UIActionSheetDelegate, MFMailComposeViewControllerDelegate>
// ...
@end
UIActionSheetDelegate
29
// DetailViewController.h
#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>
#import <MessageUI/MFMailComposeViewController.h>
@interface DetailViewController : UIViewController <
UISplitViewControllerDelegate, UIWebViewDelegate,
UIActionSheetDelegate, MFMailComposeViewControllerDelegate>
// ...
@end
Action Sheet
UIActionSheet
MFMailComposeViewControllerDelegate
30
Action Sheet
UIActionSheet
// DetailViewController.m
- (IBAction)shareByEmail:(id)sender {
UIActionSheet *actionSheet = [[UIActionSheet alloc]
initWithTitle:@"Compartir"
delegate:self
cancelButtonTitle:@"Cancelar"
destructiveButtonTitle:nil
otherButtonTitles:@"Email", nil];
[actionSheet showInView:self.view];
}
initWithTitle:
31
Action Sheet
UIActionSheet
showInView:
// DetailViewController.m
- (IBAction)shareByEmail:(id)sender {
UIActionSheet *actionSheet = [[UIActionSheet alloc]
initWithTitle:@"Compartir"
delegate:self
cancelButtonTitle:@"Cancelar"
destructiveButtonTitle:nil
otherButtonTitles:@"Email", nil];
[actionSheet showInView:self.view];
}
32
Action Sheet
UIActionSheetDelegate
// DetailViewController.m
#pragma mark - UIActionSheetDelegate
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
}
@end
Implementar actionSheet:clickedButtonAtIndex:
33
Action Sheet
UIActionSheetDelegate
// DetailViewController.m
#pragma mark - UIActionSheetDelegate
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if(buttonIndex == 0 && [MFMailComposeViewController canSendMail]) {
NSString *subject = @"Mira este vĂ­deo!";
NSArray *recipients = @[ @"sergi.hernando@mobivery.com" ];
NSString *url = self.webView.request.URL.absoluteString;
NSString *body = [NSString stringWithFormat:@"Échale un ojo: %@", url];
}
}
@end
ParĂĄmetros del email
34
Action Sheet
UIActionSheetDelegate
// DetailViewController.m
#pragma mark - UIActionSheetDelegate
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if(buttonIndex == 0 && [MFMailComposeViewController canSendMail]) {
NSString *subject = @"Mira este vĂ­deo!";
NSArray *recipients = @[ @"sergi.hernando@mobivery.com" ];
NSString *url = self.webView.request.URL.absoluteString;
NSString *body = [NSString stringWithFormat:@"Échale un ojo: %@", url];
MFMailComposeViewController *mailComposer = [[MFMailComposeViewController alloc] init];
}
}
@end
Instanciar mail compose view controller
35
Action Sheet
UIActionSheetDelegate
// DetailViewController.m
#pragma mark - UIActionSheetDelegate
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if(buttonIndex == 0 && [MFMailComposeViewController canSendMail]) {
NSString *subject = @"Mira este vĂ­deo!";
NSArray *recipients = @[ @"sergi.hernando@mobivery.com" ];
NSString *url = self.webView.request.URL.absoluteString;
NSString *body = [NSString stringWithFormat:@"Échale un ojo: %@", url];
MFMailComposeViewController *mailComposer = [[MFMailComposeViewController alloc] init];
[mailComposer setSubject:subject];
[mailComposer setToRecipients:recipients];
[mailComposer setMessageBody:body isHTML:YES];
[mailComposer setMailComposeDelegate:self];
}
}
@end
Asignar parĂĄmetros a mail compose view controller
36
Action Sheet
UIActionSheetDelegate
// DetailViewController.m
#pragma mark - UIActionSheetDelegate
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if(buttonIndex == 0 && [MFMailComposeViewController canSendMail]) {
NSString *subject = @"Mira este vĂ­deo!";
NSArray *recipients = @[ @"sergi.hernando@mobivery.com" ];
NSString *url = self.webView.request.URL.absoluteString;
NSString *body = [NSString stringWithFormat:@"Échale un ojo: %@", url];
MFMailComposeViewController *mailComposer = [[MFMailComposeViewController alloc] init];
[mailComposer setSubject:subject];
[mailComposer setToRecipients:recipients];
[mailComposer setMessageBody:body isHTML:YES];
[mailComposer setMailComposeDelegate:self];
[self presentViewController:mailComposer animated:YES completion:nil];
}
}
@end
Mostrar mail compose view controller
37
Action Sheet
MFMailComposeViewControllerDelegate
Implementar mailComposeController:didFinishWithResult:error:
#pragma mark - MFMailComposeViewControllerDelegate
- (void)mailComposeController:(MFMailComposeViewController *)controller
didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
}
38
Action Sheet
MFMailComposeViewControllerDelegate
Cerrar mail compose view controller
* [mailComposer setMailComposeDelegate:self];
#pragma mark - MFMailComposeViewControllerDelegate
- (void)mailComposeController:(MFMailComposeViewController *)controller
didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
! [self dismissViewControllerAnimated:YES completion:nil];
}
39
Activity
UIActivityViewController
// DetailViewController.m
- (IBAction)shareByEmail:(id)sender {
}
40
Activity
UIActivityViewController
// DetailViewController.m
- (IBAction)shareByEmail:(id)sender {
NSString *url = self.webView.request.URL.absoluteString;
NSString *body = [NSString stringWithFormat:@"Échale un ojo a este vídeo: %@", url];
}
41
Activity
UIActivityViewController
// DetailViewController.m
- (IBAction)shareByEmail:(id)sender {
NSString *url = self.webView.request.URL.absoluteString;
NSString *body = [NSString stringWithFormat:@"Échale un ojo a este vídeo: %@", url];
UIActivityViewController *activity;
activity = [[UIActivityViewController alloc]
initWithActivityItems:@[body] applicationActivities:nil];
}
42
Activity
UIActivityViewController
// DetailViewController.m
- (IBAction)shareByEmail:(id)sender {
NSString *url = self.webView.request.URL.absoluteString;
NSString *body = [NSString stringWithFormat:@"Échale un ojo a este vídeo: %@", url];
UIActivityViewController *activity;
activity = [[UIActivityViewController alloc]
initWithActivityItems:@[body] applicationActivities:nil];
activity.excludedActivityTypes =
@[UIActivityTypeMessage, UIActivityTypeMail, UIActivityTypePostToWeibo];
}
43
Activity
UIActivityViewController
// DetailViewController.m
- (IBAction)shareByEmail:(id)sender {
NSString *url = self.webView.request.URL.absoluteString;
NSString *body = [NSString stringWithFormat:@"Échale un ojo a este vídeo: %@", url];
UIActivityViewController *activity;
activity = [[UIActivityViewController alloc]
initWithActivityItems:@[body] applicationActivities:nil];
activity.excludedActivityTypes =
@[UIActivityTypeMessage, UIActivityTypeMail, UIActivityTypePostToWeibo];
[self presentViewController:activity animated:YES completion:nil];
}
44
Coffee Break!
45
Customizing
UIAppearance
// AppDelegate.m
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// ...
return YES;
}
application:didFinishLaunchingWithOptions:
46
Customizing
UIAppearance
// AppDelegate.m
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// ...
[[UINavigationBar appearance] setTintColor:[UIColor blackColor]];
return YES;
}
UINavigationBar
47
Customizing
UIAppearance
// AppDelegate.m
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// ...
[[UINavigationBar appearance] setTintColor:[UIColor blackColor]];
[[UIBarButtonItem appearance] setTintColor:[UIColor blackColor]];
return YES;
}
UIBarButtonItem
48
Customizing
UIAppearance
// AppDelegate.m
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// ...
[[UINavigationBar appearance] setTintColor:[UIColor blackColor]];
[[UIBarButtonItem appearance] setTintColor:[UIColor blackColor]];
[[UISearchBar appearance] setTintColor:[UIColor blackColor]];
return YES;
}
UISearchBar
49
Customizing
→
+
Bar Button Item
+
Round Rect Button
50
Customizing
+
Type: Custom
Title: vacĂ­o
Image: comun-back-button.png
51
Customizing
→
52
Customizing
53
Customizing
54
Customizing
// VideoCell.h
#import <UIKit/UIKit.h>
@interface VideoCell : UITableViewCell
@property (nonatomic, strong) IBOutlet UILabel *titleLabel;
@property (nonatomic, strong) IBOutlet UILabel *authorLabel;
@end
55
Customizing
TableView Cell - Style: Custom
Custom Class - Class:VideoCell
56
Customizing
57
Customizing
Conectar labels con titleLabel y authorLabel
58
Customizing
// MasterViewController.m
#import "VideoCell.h"
// ...
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [object valueForKey:@"title"];
}
59
Customizing
// MasterViewController.m
#import "VideoCell.h"
// ...
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
if([cell isKindOfClass:[VideoCell class]]) {
VideoCell *videoCell = (VideoCell *)cell;
videoCell.titleLabel.text = [object valueForKey:@"title"];
videoCell.authorLabel.text = [object valueForKey:@"author"];
} else {
cell.textLabel.text = [object valueForKey:@"title"];
}
}
60
Testing
OCUnit
61
Testing
OCUnit
validate:
“Devuelve ‘cierto’ si título, autor y URL están
informados
y URL tiene el formato correcto.
Devuelve ‘falso’ en caso contrario.”
62
Testing
OCUnit
// MyVideosTests.m
- (void)testValidateMandatoryFields {
}
63
Testing
OCUnit
// MyVideosTests.m
- (void)testValidateMandatoryFields {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"http://vimeo.com/m/31158841"
};
}
64
Testing
OCUnit
// MyVideosTests.m
- (void)testValidateMandatoryFields {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"http://vimeo.com/m/31158841"
};
BOOL result = [viewController validate:values];
}
65
Testing
OCUnit
// MyVideosTests.m
- (void)testValidateMandatoryFields {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"http://vimeo.com/m/31158841"
};
BOOL result = [viewController validate:values];
!
STAssertTrue(result, @"validate: returned false");
}
66
Testing
OCUnit
// MyVideosTests.m
- (void)testValidateMandatoryFields {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"http://vimeo.com/m/31158841"
};
BOOL result = [viewController validate:values];
!
STAssertTrue(result, @"validate: returned false");
}
“result” should be true. validate: returned false
67
Testing
OCUnit
// MyVideosTests.m
- (void)testValidateMandatoryFields {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"http://vimeo.com/m/31158841"
};
BOOL result = [viewController validate:values];
!
STAssertTrue(result, @"validate: returned false");
}
// MasterViewController.m
- (BOOL)validate:(NSDictionary *)values {
! return NO;
}
“result” should be true. validate: returned false
68
Testing
OCUnit
// MyVideosTests.m
- (void)testValidateMandatoryFields {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"http://vimeo.com/m/31158841"
};
BOOL result = [viewController validate:values];
!
STAssertTrue(result, @"validate: returned false");
}
// MasterViewController.m
- (BOOL)validate:(NSDictionary *)values {
return([values objectForKey:@"title"]
&& [values objectForKey:@"author"]
&& [values objectForKey:@"url"]);
}
69
Testing
OCUnit
// MyVideosTests.m
- (void)testValidateMandatoryFields {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"http://vimeo.com/m/31158841"
};
BOOL result = [viewController validate:values];
!
STAssertTrue(result, @"validate: returned false");
}
// MasterViewController.m
- (BOOL)validate:(NSDictionary *)values {
return([values objectForKey:@"title"]
&& [values objectForKey:@"author"]
&& [values objectForKey:@"url"]);
}
Test Case ‘-[MyVideosTests testValidateMandatoryFields]’ passed
70
Testing
OCUnit
// MyVideosTests.m
- (void)testValidatetMalformedURL {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"not an url"
};
BOOL result = [viewController validate:values];
!
STAssertFalse(result, @"validate: returned true");
}
71
Testing
OCUnit
// MyVideosTests.m
- (void)testValidatetMalformedURL {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"not an url"
};
BOOL result = [viewController validate:values];
!
STAssertFalse(result, @"validate: returned true");
}
“result” should be false. validate: returned true
72
Testing
OCUnit
// MyVideosTests.m
- (void)testValidatetMalformedURL {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"not an url"
};
BOOL result = [viewController validate:values];
!
STAssertFalse(result, @"validate: returned true");
}
// MasterViewController.m
- (BOOL)validate:(NSDictionary *)values {
return([values objectForKey:@"title"]
&& [values objectForKey:@"author"]
&& [values objectForKey:@"url"]
&& [NSURL URLWithString:[values objectForKey:@"url"]]);
}
73
Testing
OCUnit
// MyVideosTests.m
- (void)testValidatetMalformedURL {
MasterViewController *viewController = [MasterViewController new];
NSDictionary *values = @{
@"title": @"Murmuration",
@"author": @"Islands & Rivers",
@"url": @"not an url"
};
BOOL result = [viewController validate:values];
!
STAssertFalse(result, @"validate: returned true");
}
// MasterViewController.m
- (BOOL)validate:(NSDictionary *)values {
return([values objectForKey:@"title"]
&& [values objectForKey:@"author"]
&& [values objectForKey:@"url"]
&& [NSURL URLWithString:[values objectForKey:@"url"]]);
}
Test Case ‘-[MyVideosTests testValidateMalformedURL]’ passed
74

Weitere Àhnliche Inhalte

Was ist angesagt?

Django quickstart
Django quickstartDjango quickstart
Django quickstart
Marconi Moreto
 
ĐžĐ±Đ·ĐŸŃ€ фрДĐčĐŒĐČĐŸŃ€ĐșĐ° Twisted
ĐžĐ±Đ·ĐŸŃ€ фрДĐčĐŒĐČĐŸŃ€ĐșĐ° TwistedĐžĐ±Đ·ĐŸŃ€ фрДĐčĐŒĐČĐŸŃ€ĐșĐ° Twisted
ĐžĐ±Đ·ĐŸŃ€ фрДĐčĐŒĐČĐŸŃ€ĐșĐ° Twisted
Maxim Kulsha
 
iOS. EventKit Framework. Work with calendars and reminders
iOS. EventKit Framework. Work with calendars and remindersiOS. EventKit Framework. Work with calendars and reminders
iOS. EventKit Framework. Work with calendars and reminders
Voityuk Alexander
 

Was ist angesagt? (20)

Node js mongodriver
Node js mongodriverNode js mongodriver
Node js mongodriver
 
Nodejs do teste de unidade ao de integração
Nodejs  do teste de unidade ao de integraçãoNodejs  do teste de unidade ao de integração
Nodejs do teste de unidade ao de integração
 
Component lifecycle hooks in Angular 2.0
Component lifecycle hooks in Angular 2.0Component lifecycle hooks in Angular 2.0
Component lifecycle hooks in Angular 2.0
 
The zen of async: Best practices for best performance
The zen of async: Best practices for best performanceThe zen of async: Best practices for best performance
The zen of async: Best practices for best performance
 
Angular 2.0 Views
Angular 2.0 ViewsAngular 2.0 Views
Angular 2.0 Views
 
“iOS 11 ĐČ App in the Air”, ĐŸŃ€ĐŸĐœĐžĐœ ХДргДĐč, App in the Air
“iOS 11 ĐČ App in the Air”, ĐŸŃ€ĐŸĐœĐžĐœ ХДргДĐč, App in the Air“iOS 11 ĐČ App in the Air”, ĐŸŃ€ĐŸĐœĐžĐœ ХДргДĐč, App in the Air
“iOS 11 ĐČ App in the Air”, ĐŸŃ€ĐŸĐœĐžĐœ ХДргДĐč, App in the Air
 
Zabbix LLD from a C Module by Jan-Piet Mens
Zabbix LLD from a C Module by Jan-Piet MensZabbix LLD from a C Module by Jan-Piet Mens
Zabbix LLD from a C Module by Jan-Piet Mens
 
AngularJS Services
AngularJS ServicesAngularJS Services
AngularJS Services
 
Django quickstart
Django quickstartDjango quickstart
Django quickstart
 
Practical JavaScript Programming - Session 1/8
Practical JavaScript Programming - Session 1/8Practical JavaScript Programming - Session 1/8
Practical JavaScript Programming - Session 1/8
 
Jest
JestJest
Jest
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & REST
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design Patterns
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistence
 
ĐžĐ±Đ·ĐŸŃ€ фрДĐčĐŒĐČĐŸŃ€ĐșĐ° Twisted
ĐžĐ±Đ·ĐŸŃ€ фрДĐčĐŒĐČĐŸŃ€ĐșĐ° TwistedĐžĐ±Đ·ĐŸŃ€ фрДĐčĐŒĐČĐŸŃ€ĐșĐ° Twisted
ĐžĐ±Đ·ĐŸŃ€ фрДĐčĐŒĐČĐŸŃ€ĐșĐ° Twisted
 
Introduction to CQRS and Event Sourcing
Introduction to CQRS and Event SourcingIntroduction to CQRS and Event Sourcing
Introduction to CQRS and Event Sourcing
 
Design Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleDesign Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et Pimple
 
CQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony applicationCQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony application
 
Why Sifu
Why SifuWhy Sifu
Why Sifu
 
iOS. EventKit Framework. Work with calendars and reminders
iOS. EventKit Framework. Work with calendars and remindersiOS. EventKit Framework. Work with calendars and reminders
iOS. EventKit Framework. Work with calendars and reminders
 

Ähnlich wie Formacion en movilidad: Conceptos de desarrollo en iOS (IV)

Beginning icloud development - Cesare Rocchi - WhyMCA
Beginning icloud development - Cesare Rocchi - WhyMCABeginning icloud development - Cesare Rocchi - WhyMCA
Beginning icloud development - Cesare Rocchi - WhyMCA
Whymca
 
MobileCity:Core Data
MobileCity:Core DataMobileCity:Core Data
MobileCity:Core Data
Allan Davis
 
Apple Templates Considered Harmful
Apple Templates Considered HarmfulApple Templates Considered Harmful
Apple Templates Considered Harmful
Brian Gesiak
 
iOS for ERREST - alternative version
iOS for ERREST - alternative versioniOS for ERREST - alternative version
iOS for ERREST - alternative version
WO Community
 
Models, controllers and views
Models, controllers and viewsModels, controllers and views
Models, controllers and views
priestc
 

Ähnlich wie Formacion en movilidad: Conceptos de desarrollo en iOS (IV) (20)

Beginning icloud development - Cesare Rocchi - WhyMCA
Beginning icloud development - Cesare Rocchi - WhyMCABeginning icloud development - Cesare Rocchi - WhyMCA
Beginning icloud development - Cesare Rocchi - WhyMCA
 
Formacion en movilidad: Conceptos de desarrollo en iOS (III)
Formacion en movilidad: Conceptos de desarrollo en iOS (III) Formacion en movilidad: Conceptos de desarrollo en iOS (III)
Formacion en movilidad: Conceptos de desarrollo en iOS (III)
 
Core Data with multiple managed object contexts
Core Data with multiple managed object contextsCore Data with multiple managed object contexts
Core Data with multiple managed object contexts
 
MobileCity:Core Data
MobileCity:Core DataMobileCity:Core Data
MobileCity:Core Data
 
I os 11
I os 11I os 11
I os 11
 
iOS
iOSiOS
iOS
 
Core Data with Swift 3.0
Core Data with Swift 3.0Core Data with Swift 3.0
Core Data with Swift 3.0
 
I os 04
I os 04I os 04
I os 04
 
Google App Engine Developer - Day3
Google App Engine Developer - Day3Google App Engine Developer - Day3
Google App Engine Developer - Day3
 
Codeigniter : Two Step View - Concept Implementation
Codeigniter : Two Step View - Concept ImplementationCodeigniter : Two Step View - Concept Implementation
Codeigniter : Two Step View - Concept Implementation
 
Taking a Test Drive
Taking a Test DriveTaking a Test Drive
Taking a Test Drive
 
Get the Most Out of iOS 11 with Visual Studio Tools for Xamarin
Get the Most Out of iOS 11 with Visual Studio Tools for XamarinGet the Most Out of iOS 11 with Visual Studio Tools for Xamarin
Get the Most Out of iOS 11 with Visual Studio Tools for Xamarin
 
Apple Templates Considered Harmful
Apple Templates Considered HarmfulApple Templates Considered Harmful
Apple Templates Considered Harmful
 
Writing Maintainable JavaScript
Writing Maintainable JavaScriptWriting Maintainable JavaScript
Writing Maintainable JavaScript
 
iOS for ERREST - alternative version
iOS for ERREST - alternative versioniOS for ERREST - alternative version
iOS for ERREST - alternative version
 
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
 
Protractor framework – how to make stable e2e tests for Angular applications
Protractor framework – how to make stable e2e tests for Angular applicationsProtractor framework – how to make stable e2e tests for Angular applications
Protractor framework – how to make stable e2e tests for Angular applications
 
10 tips for a reusable architecture
10 tips for a reusable architecture10 tips for a reusable architecture
10 tips for a reusable architecture
 
Models, controllers and views
Models, controllers and viewsModels, controllers and views
Models, controllers and views
 
Optimize CollectionView Scrolling
Optimize CollectionView ScrollingOptimize CollectionView Scrolling
Optimize CollectionView Scrolling
 

Mehr von Mobivery

Hoy es Marketing 2013: El móvil en la cadena de distribución
Hoy es Marketing 2013: El móvil en la cadena de distribuciónHoy es Marketing 2013: El móvil en la cadena de distribución
Hoy es Marketing 2013: El móvil en la cadena de distribución
Mobivery
 
Mobile Health - Nuevos retos y oportunidades. El caso de Ă©xito de iPediatric
Mobile Health - Nuevos retos y oportunidades. El caso de Ă©xito de iPediatricMobile Health - Nuevos retos y oportunidades. El caso de Ă©xito de iPediatric
Mobile Health - Nuevos retos y oportunidades. El caso de Ă©xito de iPediatric
Mobivery
 

Mehr von Mobivery (20)

Jornada Empresa UOC - Desarrollo de Aplicaciones para FormaciĂłn en RRHH
Jornada Empresa UOC - Desarrollo de Aplicaciones para FormaciĂłn en RRHHJornada Empresa UOC - Desarrollo de Aplicaciones para FormaciĂłn en RRHH
Jornada Empresa UOC - Desarrollo de Aplicaciones para FormaciĂłn en RRHH
 
Modelo start up: Una forma de encontrar trabajo
Modelo start up: Una forma de encontrar trabajo Modelo start up: Una forma de encontrar trabajo
Modelo start up: Una forma de encontrar trabajo
 
ÂżPersona o empleado? Algo estĂĄ cambiando en nuestras empresas
ÂżPersona o empleado? Algo estĂĄ cambiando en nuestras empresasÂżPersona o empleado? Algo estĂĄ cambiando en nuestras empresas
ÂżPersona o empleado? Algo estĂĄ cambiando en nuestras empresas
 
Móvil y Retail: Cambiando las reglas del juego
Móvil y Retail: Cambiando las reglas del juegoMóvil y Retail: Cambiando las reglas del juego
Móvil y Retail: Cambiando las reglas del juego
 
PresentaciĂłn de Mobivery en el FET2013
PresentaciĂłn de Mobivery en el FET2013PresentaciĂłn de Mobivery en el FET2013
PresentaciĂłn de Mobivery en el FET2013
 
Curso de formaciĂłn en Movilidad (Parte III) - TecnologĂ­a de Servidor
Curso de formaciĂłn en Movilidad (Parte III) - TecnologĂ­a de ServidorCurso de formaciĂłn en Movilidad (Parte III) - TecnologĂ­a de Servidor
Curso de formaciĂłn en Movilidad (Parte III) - TecnologĂ­a de Servidor
 
Curso de formaciĂłn en Movilidad (Parte II) - PersonalizaciĂłn
Curso de formaciĂłn en Movilidad (Parte II) - PersonalizaciĂłn Curso de formaciĂłn en Movilidad (Parte II) - PersonalizaciĂłn
Curso de formaciĂłn en Movilidad (Parte II) - PersonalizaciĂłn
 
Curso de formaciĂłn en Movilidad (Parte I) - Mobile Device Management
Curso de formaciĂłn en Movilidad (Parte I) - Mobile Device ManagementCurso de formaciĂłn en Movilidad (Parte I) - Mobile Device Management
Curso de formaciĂłn en Movilidad (Parte I) - Mobile Device Management
 
Formacion en movilidad: Conceptos de desarrollo en iOS (II)
Formacion en movilidad: Conceptos de desarrollo en iOS (II) Formacion en movilidad: Conceptos de desarrollo en iOS (II)
Formacion en movilidad: Conceptos de desarrollo en iOS (II)
 
Formacion en movilidad: Conceptos de desarrollo en iOS (I)
Formacion en movilidad: Conceptos de desarrollo en iOS (I) Formacion en movilidad: Conceptos de desarrollo en iOS (I)
Formacion en movilidad: Conceptos de desarrollo en iOS (I)
 
FormaciĂłn en movilidad: Conceptos de desarrollo en iOS (V)
FormaciĂłn en movilidad: Conceptos de desarrollo en iOS (V) FormaciĂłn en movilidad: Conceptos de desarrollo en iOS (V)
FormaciĂłn en movilidad: Conceptos de desarrollo en iOS (V)
 
Hoy es Marketing 2013: El móvil en la cadena de distribución
Hoy es Marketing 2013: El móvil en la cadena de distribuciónHoy es Marketing 2013: El móvil en la cadena de distribución
Hoy es Marketing 2013: El móvil en la cadena de distribución
 
IntroducciĂłn Mobile Apps
IntroducciĂłn Mobile AppsIntroducciĂłn Mobile Apps
IntroducciĂłn Mobile Apps
 
UrbanSensing - Escuchando la ciudad digital
UrbanSensing  - Escuchando la ciudad digitalUrbanSensing  - Escuchando la ciudad digital
UrbanSensing - Escuchando la ciudad digital
 
Mobile Health - Nuevos retos y oportunidades. El caso de Ă©xito de iPediatric
Mobile Health - Nuevos retos y oportunidades. El caso de Ă©xito de iPediatricMobile Health - Nuevos retos y oportunidades. El caso de Ă©xito de iPediatric
Mobile Health - Nuevos retos y oportunidades. El caso de Ă©xito de iPediatric
 
Marketing de Apps
Marketing de Apps Marketing de Apps
Marketing de Apps
 
Aplicaciones mĂłviles: Usabilidad y Experiencia de Usuario
Aplicaciones mĂłviles: Usabilidad y Experiencia de UsuarioAplicaciones mĂłviles: Usabilidad y Experiencia de Usuario
Aplicaciones mĂłviles: Usabilidad y Experiencia de Usuario
 
Workshop I: Diseño de aplicaciones para iPhone
Workshop I: Diseño de aplicaciones para iPhoneWorkshop I: Diseño de aplicaciones para iPhone
Workshop I: Diseño de aplicaciones para iPhone
 
CĂłmo monetizar tus apps: Cantidad y Calidad
CĂłmo monetizar tus apps: Cantidad y CalidadCĂłmo monetizar tus apps: Cantidad y Calidad
CĂłmo monetizar tus apps: Cantidad y Calidad
 
FormaciĂłn Aecomo Academy - MonetizaciĂłn de aplicaciones
FormaciĂłn Aecomo Academy - MonetizaciĂłn de aplicacionesFormaciĂłn Aecomo Academy - MonetizaciĂłn de aplicaciones
FormaciĂłn Aecomo Academy - MonetizaciĂłn de aplicaciones
 

KĂŒrzlich hochgeladen

Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 
Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..
Christopher Logan Kennedy
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎+971_581248768%)**%*]'#abortion pills for sale in dubai@
 

KĂŒrzlich hochgeladen (20)

Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
 
Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with Milvus
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Mcleodganj Call Girls đŸ„° 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls đŸ„° 8617370543 Service Offer VIP Hot ModelMcleodganj Call Girls đŸ„° 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls đŸ„° 8617370543 Service Offer VIP Hot Model
 
Six Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal OntologySix Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal Ontology
 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistan
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 

Formacion en movilidad: Conceptos de desarrollo en iOS (IV)

  • 1. TelevisiĂł de Catalunya FormaciĂłn en movilidad Conceptos de desarrollo en iOS 4ÂȘ sesiĂłn mayo 2013 1
  • 2. QuĂ© veremos hoy Alert Search Bar Action Sheet Activity Customizing Testing 2
  • 3. Alert UIAlertView “Use the UIAlertView class to display an alert message to the user” 3
  • 4. Alert UIAlertView // MasterViewController.m - (void)insertNewObject:(NSDictionary *)values { // ... if (![context save:&error]) { // ... } else { UIAlertView *notice = [[UIAlertView alloc] initWithTitle:@"Nuevo vĂ­deo" message:@"Se a creado un nuevo vĂ­deo correctamente" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]; [notice show]; } 4
  • 5. Alert UIAlertView // MasterViewController.m - (void)insertNewObject:(NSDictionary *)values { // ... if (![context save:&error]) { // ... } else { UIAlertView *notice = [[UIAlertView alloc] initWithTitle:@"Nuevo vĂ­deo" message:@"Se a creado un nuevo vĂ­deo correctamente" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]; [notice show]; } 5
  • 6. Alert UIAlertView // MasterViewController.m - (void)insertNewObject:(NSDictionary *)values { // ... if (![context save:&error]) { // ... } else { UIAlertView *notice = [[UIAlertView alloc] initWithTitle:@"Nuevo vĂ­deo" message:@"Se a creado un nuevo vĂ­deo correctamente" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:@"Deshacer", nil]; [notice show]; } 6
  • 7. Alert UIAlertView // MasterViewController.m - (void)insertNewObject:(NSDictionary *)values { // ... if (![context save:&error]) { // ... } else { UIAlertView *notice = [[UIAlertView alloc] initWithTitle:@"Nuevo vĂ­deo" message:@"Se a creado un nuevo vĂ­deo correctamente" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:@"Deshacer", nil]; [notice show]; } 7
  • 8. “The UISearchBar object does not actually perform any searches.You use the UISearchBarDelegate protocol to implement the actions when text is entered and buttons are clicked” Search Bar 8
  • 10. Search Bar UISearchBar Placeholder ‘Buscar por tĂ­tulo o autor’ Marcar ‘Shows Cancel Button’ Seleccionar ‘Correction: No’ Conectar ‘Search Bar’ delegate con ‘MasterView Controller’ 10
  • 11. Search Bar UISearchBarDelegate // MasterViewController.m @interface MasterViewController () - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath; - (void)performSearch:(NSString *)searchText; @end 11
  • 12. Search Bar UISearchBarDelegate // MasterViewController.m @interface MasterViewController () - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath; - (void)performSearch:(NSString *)searchText; @end Declarar mĂ©todo privado performSearch: 12
  • 13. Search Bar UISearchBarDelegate // MasterViewController.m @interface MasterViewController () - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath; - (void)performSearch:(NSString *)searchText; @end - (NSFetchedResultsController *)fetchedResultsController { // ... NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil]; // ... } 13
  • 14. Search Bar UISearchBarDelegate // MasterViewController.m @interface MasterViewController () - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath; - (void)performSearch:(NSString *)searchText; @end - (NSFetchedResultsController *)fetchedResultsController { // ... NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil]; // ... } Anular uso de cachĂ© en initWithFetchRequest: 14
  • 15. Search Bar UISearchBarDelegate // MasterViewController.m #pragma mark - UISearchBarDelegate - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar { } - (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { } - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { } Acciones para botones ‘Search’ y ‘Cancel’ e introducciĂłn de texto 15
  • 16. Search Bar UISearchBarDelegate // MasterViewController.m #pragma mark - UISearchBarDelegate - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar { [self performSearch:searchBar.text]; [searchBar resignFirstResponder]; [self.tableView reloadData]; } - (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { } - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { } searchBarSearchButtonClicked: 16
  • 17. Search Bar UISearchBarDelegate // MasterViewController.m #pragma mark - UISearchBarDelegate - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar { [self performSearch:searchBar.text]; [searchBar resignFirstResponder]; [self.tableView reloadData]; } - (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { [searchBar resignFirstResponder]; } - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { } searchBarCancelButtonClicked: 17
  • 18. Search Bar UISearchBarDelegate // MasterViewController.m #pragma mark - UISearchBarDelegate - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar { [self performSearch:searchBar.text]; [searchBar resignFirstResponder]; [self.tableView reloadData]; } - (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { [searchBar resignFirstResponder]; } - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { [self performSearch:searchText]; } searchBar:textDidChange: 18
  • 19. Search Bar UISearchBarDelegate + Core Data // MasterViewController.m #pragma mark - Private - (void)performSearch:(NSString *)searchText { NSPredicate *predicate; NSError *error = nil; if(searchText && searchText.length > 0) { predicate = [NSPredicate predicateWithFormat: @"title contains[cd] %@ or author contains[cd] %@", searchText, searchText]; [self.fetchedResultsController.fetchRequest setPredicate:predicate]; } else { [self.fetchedResultsController.fetchRequest setPredicate:nil]; } if(![self.fetchedResultsController performFetch:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } [self.tableView reloadData]; } Implementar mĂ©todo privado performSearch: 19
  • 20. Search Bar UISearchBarDelegate + Core Data // MasterViewController.m #pragma mark - Private - (void)performSearch:(NSString *)searchText { NSPredicate *predicate; NSError *error = nil; if(searchText && searchText.length > 0) { predicate = [NSPredicate predicateWithFormat: @"title contains[cd] %@ or author contains[cd] %@", searchText, searchText]; [self.fetchedResultsController.fetchRequest setPredicate:predicate]; } else { [self.fetchedResultsController.fetchRequest setPredicate:nil]; } if(![self.fetchedResultsController performFetch:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } [self.tableView reloadData]; } contains[cd] is case and diacritic insensitive 20
  • 21. Search Bar UISearchBarDelegate + Core Data // MasterViewController.m #pragma mark - Private - (void)performSearch:(NSString *)searchText { NSPredicate *predicate; NSError *error = nil; if(searchText && searchText.length > 0) { predicate = [NSPredicate predicateWithFormat: @"title contains[cd] %@ or author contains[cd] %@", searchText, searchText]; [self.fetchedResultsController.fetchRequest setPredicate:predicate]; } else { [self.fetchedResultsController.fetchRequest setPredicate:nil]; } if(![self.fetchedResultsController performFetch:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } [self.tableView reloadData]; } Anular criterios = Buscar todos 21
  • 23. “Use the UIActionSheet class to present the user with a set of alternatives for how to proceed with a given task” Action Sheet 23
  • 26. Action Sheet Seleccionar ‘IdentiïŹer:Action’ Conectar ‘Bar Button Item - Action’ con DetailView Controller Connection:Action Name: shareByEmail Type: id 26
  • 27. Action Sheet TARGETS : MyVideos : Build Phases : Link With Binary Libraries MessageUI.framework 27
  • 28. Action Sheet UIActionSheet // DetailViewController.h #import <UIKit/UIKit.h> #import <MessageUI/MessageUI.h> #import <MessageUI/MFMailComposeViewController.h> @interface DetailViewController : UIViewController < UISplitViewControllerDelegate, UIWebViewDelegate, UIActionSheetDelegate, MFMailComposeViewControllerDelegate> // ... @end Importar MessageUI.h y MFMailComposeViewController.h 28
  • 29. Action Sheet UIActionSheet // DetailViewController.h #import <UIKit/UIKit.h> #import <MessageUI/MessageUI.h> #import <MessageUI/MFMailComposeViewController.h> @interface DetailViewController : UIViewController < UISplitViewControllerDelegate, UIWebViewDelegate, UIActionSheetDelegate, MFMailComposeViewControllerDelegate> // ... @end UIActionSheetDelegate 29
  • 30. // DetailViewController.h #import <UIKit/UIKit.h> #import <MessageUI/MessageUI.h> #import <MessageUI/MFMailComposeViewController.h> @interface DetailViewController : UIViewController < UISplitViewControllerDelegate, UIWebViewDelegate, UIActionSheetDelegate, MFMailComposeViewControllerDelegate> // ... @end Action Sheet UIActionSheet MFMailComposeViewControllerDelegate 30
  • 31. Action Sheet UIActionSheet // DetailViewController.m - (IBAction)shareByEmail:(id)sender { UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"Compartir" delegate:self cancelButtonTitle:@"Cancelar" destructiveButtonTitle:nil otherButtonTitles:@"Email", nil]; [actionSheet showInView:self.view]; } initWithTitle: 31
  • 32. Action Sheet UIActionSheet showInView: // DetailViewController.m - (IBAction)shareByEmail:(id)sender { UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"Compartir" delegate:self cancelButtonTitle:@"Cancelar" destructiveButtonTitle:nil otherButtonTitles:@"Email", nil]; [actionSheet showInView:self.view]; } 32
  • 33. Action Sheet UIActionSheetDelegate // DetailViewController.m #pragma mark - UIActionSheetDelegate - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { } @end Implementar actionSheet:clickedButtonAtIndex: 33
  • 34. Action Sheet UIActionSheetDelegate // DetailViewController.m #pragma mark - UIActionSheetDelegate - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { if(buttonIndex == 0 && [MFMailComposeViewController canSendMail]) { NSString *subject = @"Mira este vĂ­deo!"; NSArray *recipients = @[ @"sergi.hernando@mobivery.com" ]; NSString *url = self.webView.request.URL.absoluteString; NSString *body = [NSString stringWithFormat:@"Échale un ojo: %@", url]; } } @end ParĂĄmetros del email 34
  • 35. Action Sheet UIActionSheetDelegate // DetailViewController.m #pragma mark - UIActionSheetDelegate - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { if(buttonIndex == 0 && [MFMailComposeViewController canSendMail]) { NSString *subject = @"Mira este vĂ­deo!"; NSArray *recipients = @[ @"sergi.hernando@mobivery.com" ]; NSString *url = self.webView.request.URL.absoluteString; NSString *body = [NSString stringWithFormat:@"Échale un ojo: %@", url]; MFMailComposeViewController *mailComposer = [[MFMailComposeViewController alloc] init]; } } @end Instanciar mail compose view controller 35
  • 36. Action Sheet UIActionSheetDelegate // DetailViewController.m #pragma mark - UIActionSheetDelegate - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { if(buttonIndex == 0 && [MFMailComposeViewController canSendMail]) { NSString *subject = @"Mira este vĂ­deo!"; NSArray *recipients = @[ @"sergi.hernando@mobivery.com" ]; NSString *url = self.webView.request.URL.absoluteString; NSString *body = [NSString stringWithFormat:@"Échale un ojo: %@", url]; MFMailComposeViewController *mailComposer = [[MFMailComposeViewController alloc] init]; [mailComposer setSubject:subject]; [mailComposer setToRecipients:recipients]; [mailComposer setMessageBody:body isHTML:YES]; [mailComposer setMailComposeDelegate:self]; } } @end Asignar parĂĄmetros a mail compose view controller 36
  • 37. Action Sheet UIActionSheetDelegate // DetailViewController.m #pragma mark - UIActionSheetDelegate - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { if(buttonIndex == 0 && [MFMailComposeViewController canSendMail]) { NSString *subject = @"Mira este vĂ­deo!"; NSArray *recipients = @[ @"sergi.hernando@mobivery.com" ]; NSString *url = self.webView.request.URL.absoluteString; NSString *body = [NSString stringWithFormat:@"Échale un ojo: %@", url]; MFMailComposeViewController *mailComposer = [[MFMailComposeViewController alloc] init]; [mailComposer setSubject:subject]; [mailComposer setToRecipients:recipients]; [mailComposer setMessageBody:body isHTML:YES]; [mailComposer setMailComposeDelegate:self]; [self presentViewController:mailComposer animated:YES completion:nil]; } } @end Mostrar mail compose view controller 37
  • 38. Action Sheet MFMailComposeViewControllerDelegate Implementar mailComposeController:didFinishWithResult:error: #pragma mark - MFMailComposeViewControllerDelegate - (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error { } 38
  • 39. Action Sheet MFMailComposeViewControllerDelegate Cerrar mail compose view controller * [mailComposer setMailComposeDelegate:self]; #pragma mark - MFMailComposeViewControllerDelegate - (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error { ! [self dismissViewControllerAnimated:YES completion:nil]; } 39
  • 41. Activity UIActivityViewController // DetailViewController.m - (IBAction)shareByEmail:(id)sender { NSString *url = self.webView.request.URL.absoluteString; NSString *body = [NSString stringWithFormat:@"Échale un ojo a este vĂ­deo: %@", url]; } 41
  • 42. Activity UIActivityViewController // DetailViewController.m - (IBAction)shareByEmail:(id)sender { NSString *url = self.webView.request.URL.absoluteString; NSString *body = [NSString stringWithFormat:@"Échale un ojo a este vĂ­deo: %@", url]; UIActivityViewController *activity; activity = [[UIActivityViewController alloc] initWithActivityItems:@[body] applicationActivities:nil]; } 42
  • 43. Activity UIActivityViewController // DetailViewController.m - (IBAction)shareByEmail:(id)sender { NSString *url = self.webView.request.URL.absoluteString; NSString *body = [NSString stringWithFormat:@"Échale un ojo a este vĂ­deo: %@", url]; UIActivityViewController *activity; activity = [[UIActivityViewController alloc] initWithActivityItems:@[body] applicationActivities:nil]; activity.excludedActivityTypes = @[UIActivityTypeMessage, UIActivityTypeMail, UIActivityTypePostToWeibo]; } 43
  • 44. Activity UIActivityViewController // DetailViewController.m - (IBAction)shareByEmail:(id)sender { NSString *url = self.webView.request.URL.absoluteString; NSString *body = [NSString stringWithFormat:@"Échale un ojo a este vĂ­deo: %@", url]; UIActivityViewController *activity; activity = [[UIActivityViewController alloc] initWithActivityItems:@[body] applicationActivities:nil]; activity.excludedActivityTypes = @[UIActivityTypeMessage, UIActivityTypeMail, UIActivityTypePostToWeibo]; [self presentViewController:activity animated:YES completion:nil]; } 44
  • 46. Customizing UIAppearance // AppDelegate.m - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // ... return YES; } application:didFinishLaunchingWithOptions: 46
  • 47. Customizing UIAppearance // AppDelegate.m - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // ... [[UINavigationBar appearance] setTintColor:[UIColor blackColor]]; return YES; } UINavigationBar 47
  • 48. Customizing UIAppearance // AppDelegate.m - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // ... [[UINavigationBar appearance] setTintColor:[UIColor blackColor]]; [[UIBarButtonItem appearance] setTintColor:[UIColor blackColor]]; return YES; } UIBarButtonItem 48
  • 49. Customizing UIAppearance // AppDelegate.m - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // ... [[UINavigationBar appearance] setTintColor:[UIColor blackColor]]; [[UIBarButtonItem appearance] setTintColor:[UIColor blackColor]]; [[UISearchBar appearance] setTintColor:[UIColor blackColor]]; return YES; } UISearchBar 49
  • 55. Customizing // VideoCell.h #import <UIKit/UIKit.h> @interface VideoCell : UITableViewCell @property (nonatomic, strong) IBOutlet UILabel *titleLabel; @property (nonatomic, strong) IBOutlet UILabel *authorLabel; @end 55
  • 56. Customizing TableView Cell - Style: Custom Custom Class - Class:VideoCell 56
  • 58. Customizing Conectar labels con titleLabel y authorLabel 58
  • 59. Customizing // MasterViewController.m #import "VideoCell.h" // ... - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath]; cell.textLabel.text = [object valueForKey:@"title"]; } 59
  • 60. Customizing // MasterViewController.m #import "VideoCell.h" // ... - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath]; if([cell isKindOfClass:[VideoCell class]]) { VideoCell *videoCell = (VideoCell *)cell; videoCell.titleLabel.text = [object valueForKey:@"title"]; videoCell.authorLabel.text = [object valueForKey:@"author"]; } else { cell.textLabel.text = [object valueForKey:@"title"]; } } 60
  • 62. Testing OCUnit validate: “Devuelve ‘cierto’ si tĂ­tulo, autor y URL estĂĄn informados y URL tiene el formato correcto. Devuelve ‘falso’ en caso contrario.” 62
  • 64. Testing OCUnit // MyVideosTests.m - (void)testValidateMandatoryFields { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"http://vimeo.com/m/31158841" }; } 64
  • 65. Testing OCUnit // MyVideosTests.m - (void)testValidateMandatoryFields { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"http://vimeo.com/m/31158841" }; BOOL result = [viewController validate:values]; } 65
  • 66. Testing OCUnit // MyVideosTests.m - (void)testValidateMandatoryFields { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"http://vimeo.com/m/31158841" }; BOOL result = [viewController validate:values]; ! STAssertTrue(result, @"validate: returned false"); } 66
  • 67. Testing OCUnit // MyVideosTests.m - (void)testValidateMandatoryFields { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"http://vimeo.com/m/31158841" }; BOOL result = [viewController validate:values]; ! STAssertTrue(result, @"validate: returned false"); } “result” should be true. validate: returned false 67
  • 68. Testing OCUnit // MyVideosTests.m - (void)testValidateMandatoryFields { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"http://vimeo.com/m/31158841" }; BOOL result = [viewController validate:values]; ! STAssertTrue(result, @"validate: returned false"); } // MasterViewController.m - (BOOL)validate:(NSDictionary *)values { ! return NO; } “result” should be true. validate: returned false 68
  • 69. Testing OCUnit // MyVideosTests.m - (void)testValidateMandatoryFields { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"http://vimeo.com/m/31158841" }; BOOL result = [viewController validate:values]; ! STAssertTrue(result, @"validate: returned false"); } // MasterViewController.m - (BOOL)validate:(NSDictionary *)values { return([values objectForKey:@"title"] && [values objectForKey:@"author"] && [values objectForKey:@"url"]); } 69
  • 70. Testing OCUnit // MyVideosTests.m - (void)testValidateMandatoryFields { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"http://vimeo.com/m/31158841" }; BOOL result = [viewController validate:values]; ! STAssertTrue(result, @"validate: returned false"); } // MasterViewController.m - (BOOL)validate:(NSDictionary *)values { return([values objectForKey:@"title"] && [values objectForKey:@"author"] && [values objectForKey:@"url"]); } Test Case ‘-[MyVideosTests testValidateMandatoryFields]’ passed 70
  • 71. Testing OCUnit // MyVideosTests.m - (void)testValidatetMalformedURL { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"not an url" }; BOOL result = [viewController validate:values]; ! STAssertFalse(result, @"validate: returned true"); } 71
  • 72. Testing OCUnit // MyVideosTests.m - (void)testValidatetMalformedURL { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"not an url" }; BOOL result = [viewController validate:values]; ! STAssertFalse(result, @"validate: returned true"); } “result” should be false. validate: returned true 72
  • 73. Testing OCUnit // MyVideosTests.m - (void)testValidatetMalformedURL { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"not an url" }; BOOL result = [viewController validate:values]; ! STAssertFalse(result, @"validate: returned true"); } // MasterViewController.m - (BOOL)validate:(NSDictionary *)values { return([values objectForKey:@"title"] && [values objectForKey:@"author"] && [values objectForKey:@"url"] && [NSURL URLWithString:[values objectForKey:@"url"]]); } 73
  • 74. Testing OCUnit // MyVideosTests.m - (void)testValidatetMalformedURL { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"not an url" }; BOOL result = [viewController validate:values]; ! STAssertFalse(result, @"validate: returned true"); } // MasterViewController.m - (BOOL)validate:(NSDictionary *)values { return([values objectForKey:@"title"] && [values objectForKey:@"author"] && [values objectForKey:@"url"] && [NSURL URLWithString:[values objectForKey:@"url"]]); } Test Case ‘-[MyVideosTests testValidateMalformedURL]’ passed 74