Televisió de CatalunyaFormación en movilidad
Conceptos de desarrollo en iOS4ª sesión mayo 2013
1
Qué veremos hoy
AlertSearch Bar
Action SheetActivity
Customizing
Testing
2
AlertUIAlertView
“Use the UIAlertView class to display an alert message to the user”
3
AlertUIAlertView
// 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
AlertUIAlertView
// 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
AlertUIAlertView
// 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
AlertUIAlertView
// 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 BarUISearchBar
9
Search BarUISearchBar
Placeholder ‘Buscar por título o autor’Marcar ‘Shows Cancel Button’Seleccionar ‘Correction: No’
Conectar ‘Search Bar’ delegate con ‘Master View Controller’
10
Search BarUISearchBarDelegate
// MasterViewController.m
@interface MasterViewController ()
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;- (void)performSearch:(NSString *)searchText;
@end
11
Search BarUISearchBarDelegate
// MasterViewController.m
@interface MasterViewController ()
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;- (void)performSearch:(NSString *)searchText;
@end
Declarar método privado performSearch:
12
Search BarUISearchBarDelegate
// 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 BarUISearchBarDelegate
// 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 BarUISearchBarDelegate
// MasterViewController.m
#pragma mark - UISearchBarDelegate
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
}
Acciones parabotones ‘Search’ y ‘Cancel’ e introducción de texto
15
Search BarUISearchBarDelegate
// 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 BarUISearchBarDelegate
// 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 BarUISearchBarDelegate
// 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 BarUISearchBarDelegate + 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 BarUISearchBarDelegate + 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 BarUISearchBarDelegate + 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 BarUISearchBarDelegate
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 ‘Identifier : Action’
25
Action Sheet
Seleccionar ‘Identifier : Action’
Conectar ‘Bar Button Item - Action’ con Detail View ControllerConnection: ActionName: shareByEmail
Type: id
26
Action Sheet
TARGETS : MyVideos : Build Phases : Link With Binary LibrariesMessageUI.framework
27
Action SheetUIActionSheet
// 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 SheetUIActionSheet
// 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 SheetUIActionSheet
MFMailComposeViewControllerDelegate
30
Action SheetUIActionSheet
// 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 SheetUIActionSheet
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 SheetUIActionSheetDelegate
// DetailViewController.m
#pragma mark - UIActionSheetDelegate
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {}
@end
Implementar actionSheet:clickedButtonAtIndex:
33
Action SheetUIActionSheetDelegate
// 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 = @[ @"[email protected]" ]; NSString *url = self.webView.request.URL.absoluteString; NSString *body = [NSString stringWithFormat:@"Échale un ojo: %@", url]; }}
@end
Parámetros del email
34
Action SheetUIActionSheetDelegate
// 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 = @[ @"[email protected]" ]; 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 SheetUIActionSheetDelegate
// 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 = @[ @"[email protected]" ]; 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 SheetUIActionSheetDelegate
// 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 = @[ @"[email protected]" ]; 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 SheetMFMailComposeViewControllerDelegate
Implementar mailComposeController :didFinishWithResult:error :
#pragma mark - MFMailComposeViewControllerDelegate
- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error{
}
38
Action SheetMFMailComposeViewControllerDelegate
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
ActivityUIActivityViewController
// DetailViewController.m
- (IBAction)shareByEmail:(id)sender {
}
40
ActivityUIActivityViewController
// 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
ActivityUIActivityViewController
// 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
ActivityUIActivityViewController
// 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
ActivityUIActivityViewController
// 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
CustomizingUIAppearance
// AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // ...
return YES;}
application:didFinishLaunchingWithOptions:
46
CustomizingUIAppearance
// AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // ...
[[UINavigationBar appearance] setTintColor:[UIColor blackColor]];
return YES;}
UINavigationBar
47
CustomizingUIAppearance
// AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // ...
[[UINavigationBar appearance] setTintColor:[UIColor blackColor]]; [[UIBarButtonItem appearance] setTintColor:[UIColor blackColor]];
return YES;}
UIBarButtonItem
48
CustomizingUIAppearance
// 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: CustomTitle: 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
Table View Cell - Style: CustomCustom 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
TestingOCUnit
validate:
“Devuelve ‘cierto’ si título, autor y URL están informados
y URL tiene el formato correcto.Devuelve ‘falso’ en caso contrario.”
62
TestingOCUnit
// MyVideosTests.m
- (void)testValidateMandatoryFields {}
63
TestingOCUnit
// MyVideosTests.m
- (void)testValidateMandatoryFields { MasterViewController *viewController = [MasterViewController new]; NSDictionary *values = @{ @"title": @"Murmuration", @"author": @"Islands & Rivers", @"url": @"http://vimeo.com/m/31158841" };}
64
TestingOCUnit
// 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
TestingOCUnit
// 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
TestingOCUnit
// 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
TestingOCUnit
// 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
TestingOCUnit
// 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
TestingOCUnit
// 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
TestingOCUnit
// 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
TestingOCUnit
// 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
TestingOCUnit
// 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
TestingOCUnit
// 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