- Create a custom class with a
readonly
property. - Write a private "helper" method that assembles data held in other properties to show publicly in a
readonly
property. - Write private "helper" methods to assess the validity of input through public methods.
- Write public methods that allow controlled access to the private properties while utilizing private validation methods to protect the class from incorrect use.
Putting a together a proper sentence can be difficult at times.
In this lab, we're going to write a custom class to handle the logic of how to assemble the individual components (words and punctuation) of a sentence into a single string. We'll then present that string publicly as a read-only property and provide public methods that offer a controlled interface to the data that is used to form that sentence.
In our implementation, we're going to hold the individual words as separate strings in an array called words, and the punctuation character in a separate string property called punctuation
.
NSArray *words = @[ @"King", @"illegal", @"forest", @"to", @"pig", @"kill", @"in", @"it", @"a", @"is" ];
NSString *punctuation = @".";
Once we've collected the data held in these variables (which we'll save as properties), we can assemble their contents in a specified way to present to the public:
NSString *sentence = @"King illegal forest to pig kill in it a is.";
That sounds easy enough!
Well, maybe not for the Sheriff.
Open the objc-fissentence.xcworkspace
file.
Create a custom class called FISSentence
that inherits from NSObject
.
In the FISSentence.h
header file, declare three public properties:
- a
readonly
property that is anNSString
calledsentence
, - an
NSMutableArray
calledwords
, and - an
NSString
calledpunctuation
.
In the FISSentence.m
implementation file, create a private @interface
section between the #import
lines and the @implementation
section with the correct syntax:
@interface FISSentence ()
@end
Within this private @interface
:
- redeclare the
sentence
property asreadwrite
,
In the FISSentence.h
header file, declare the following six (6) public methods, none of which provide a return (i.e. are return-type void
):
-
addWord:
which takes one argument, anNSString
calledword
, -
addWords:withPunctuation:
which takes two arguments, anNSArray
calledwords
and anNSString
called punctuation, -
removeWordAtIndex:
which takes one argument, anNSUInteger
calledindex
, -
insertWord:atIndex:
which takes two arguments, anNSString
calledword
and anNSUInteger
calledindex
, -
replacePunctuationWithPunctuation:
which takes one argument, anNSString
calledpunctuation
, and -
replaceWordAtIndex:withWord:
which takes two argument, anNSUInteger
calledindex
and anNSString
calledword
.
Declare a private method called assembleSentence
with return-type void
. Leave the implementation empty for now.
Declare the following three (3) private methods that will be used internally to verify the argument values passed into the public methods. All of them should be return-type BOOL
. Write their implementations to simply return NO;
for now:
validWord:
which takes oneNSString
argument calledword
,validPunctuation:
which takes oneNSString
argument calledpunctuation
, andvalidIndex:
which takes oneNSUInteger
argument calledindex
.
Below the private methods, use autocomplete to define the six (6) public method implementations to do nothing.
At this point, your project should be complete enough to successfully build. Run the tests (⌘
U
) now to see that they fail.
In the course of writing the implementation for the public method addWord:
, you'll need to write the implementations for some of the private methods, particularly assembleSentence
.
-
In
addWord:
, use theself
keyword to add theword
arguments towords
property array (Hint: Use theaddObject:
method.). End the method implementation with a call toself
of the privateassembleSentence
method. -
The
assembleSentence
method should read thewords
property array and thepunctuation
property string and assemble their contents into a properly formatted sentence (i.e., spaces between each of the words with thepunctuation
string at the end). Save the assembled string to thereadonly
string property calledsentence
.
Hint: Use thecomponentsJoinedByString:
method to concatenate all of the individual "word" strings to the body of the sentence. -
Now, add logic to the
addWord:
method that validates that theword
argument string meets certain expectations: it cannot benil
, an empty string (@""
), or a string with only a space in it (@" "
). Make sure that theassembleSentence
method still gets called every time (i.e. don't wrap the protections around the call ofassembleSentence
; instead, protect changing the data thatassembleSentence
relies upon). Get all of the tests for this method to pass. -
Finally, move the validation logic for this check into the private
validWord:
methods. Refactor theaddWord:
method to call this validation method instead of doing the check itself. This will allow other methods to use the same uniform validation logic without having to copy/paste the code. Verify thataddWord:
still passes all of its tests before moving on.
Continue writing the implementations for the other five (5) public methods. All of the public methods should end with a call of the assembleSentence
method to update the sentence
property string with the new information.
- The
addWords:withPunctuation:
method should add the strings in thewords
argument array to the end of thewords
property array, and it should overwrite the string in thepunctuation
property with thepunctuation
argument string.- Add checks that the
words
argument array is neithernil
nor anempty
array. If it is, then the method should do nothing.
Hint: Use areturn;
statement to escape the method implementation if either case is true.
- Add checks that the
-
Write validation logic that checks that the
punctuation
argument string is one of these seven (7) characters:.?!,;:—
(period, question mark, exclamation point, comma, semicolon, colon, and long-dash).
Hint: It's not in the reference documentation, butNSString
has acontainsString:
method that may be useful.
If thepunctuation
argument is invalid, then this method should do nothing.
Hint: Use anotherreturn;
statement to escape the method implementation if the check fails. -
Use the
validWord:
method inside a loop to avoid adding any strings to thewords
property array that are empty string or spaces. At this point, all of the tests for this method should pass. -
Move the validation logic of the
punctuation
argument string into thevalidPunctuation:
method. Refactor theaddWords:withPunctuation:
method to use this method to perform its check. Verify that the tests foraddWord:withPunctuation:
still pass before moving on.
- The
removeWordAtIndex:
method should remove the string from thewords
property array at the index position specified by theindex
argument.
- Add in a check the makes sure that the
index
argument is not beyond the bounds of thewords
property array. Verify that all of the tests pass for this method. - Move the validation logic of the
index
argument into thevalidIndex:
method. RefactorremoveWordAtIndex:
to usevalidIndex:
to perform the check. Verify that all of the tests forremoveWordAtIndex:
continue to pass before moving on.
-
The
insertWord:atIndex:
method should insert the submittedword
string argument into thewords
property array at the position specified bye theindex
argument. Use the private validation methodsvalidWord:
andvalidIndex:
to perform checks on the method arguments. TheinsertWord:atIndex:
method should only attempt to change the properties if both arguments pass validation. -
The
replacePunctuationWithPunctuation:
method should overwrite the string in thepunctuation
property string with the newpunctuation
argument string. This method should only attempt to make the change if the newpunctuation
argument string passes validation. -
The
replaceWordAtIndex:withWord:
method should overwrite the string in thewords
property array at the position specified by theindex
argument with the newword
argument string. This method should not attempt to make an overwrite unless theword
argument passes thevalidWord:
check and theindex
argument passes thevalidIndex:
argument.
Now give yourself a high five; you deserve it!
Did you notice, that the words
and punctuation
properties that we stored the data in were public? Ideally these would be private and not even visible to the public, but since we needed to give them starting values in order to run the tests, we needed to make them public so the test file could set the initial values.
In order to initialize private properties so they can be used internally by method implementations, a special method called an "initializer" can be written to set private properties to default values without giving access to an outside class. In the case of our words
mutable array, it could be initialized to and empty mutable array so that it's prepared to receive method calls that changes its data.