MetPetDB IPhone Application Documentation

May, 2010

Version 1.1

Below each of the views of the iphone application are described in more or less the order a user would see them as they navigated through the application. Each function in each class is described in detail.

MainViewController?.m

viewDidLoad: This function loads the view MainView?.xib. The tableView object is populated with strings from the rows array and this table represents the user's options for geographic search. If the user has previously signed into the app and has not signed out, their username will be displayed in the table row at the bottom and they will not have to log in to use the app. If the user has never signed in, or has signed out since the last time they signed in, the last row of the table will indicate that the user is not logged in. The user can log in by clicking on the table row and going to the login page. If the user is logged in, they can also logout by clicking on the table row.

login: loads an instance of the LoginViewController? and the LoginView?.xib.

logout: The logout alert function above the logout function will display an alert asking the user if they are sure they want to log out. Pressing cancel will result in the user not being logged out. Since the username is stored in the keychain as a dictionary-pair value, this dictionary needs to be cleared for the user to be logged out. This deletes the username from the app and the rest of the app can be run without using a username.

useMyLocation: This function is called if the user chooses the table option of using their current location as the basis of their search. The CLLocationManager function in the file MyCLController is called to obtain a text string with location update information as well as a CLLocation object that is the location coordinate of the user. From the MyCLController class, the newLocationUpdate delegate in the MainViewController? is passed the CLLocation coordinate.

The CLLocationManager will display an alert which asks the user's permissiong to find their location coordinates. The user MUST select “Allow” for the app to use the CLLocationManager features. If the user does not do this, the locationError delegate in this file will be called and an alert will be displayed. This indicates to the user that if they wish to search using their location, they must choose allow, or the phone settings will be permamently changed to disallow this application from locating the user.

enterCoorinate: This function is called if the user selected that they wish to enter a coordinate as the basis of the geographic search. This function loads the CoordinateInputView?.xib which is controlled by an instance of CoordInputController?.

chooseRegion: This function is called if the user wants to view a list of pre-defined regions within the database and then view the samples associated with that particular region.

locationError: This function keeps track of the number of times the user selected “Don't Allow” when asked if the app should be allowed to use the current location. Appropriate messages are displayed.

newLocationUpdate: This is the delegate function for the CLLocationManager described above. SearchView?.xib, which is controlled by an instance of Radius Controller is loaded in this function. This instance of the RadiusController? will prompt the user for the radius around their current location they wish to search.

LoginViewController?.m

viewDidLoad: A table view is built with a row where the user can enter their username and another for their password. The details of the text view embedded in the table view are covered in the tableView cellForRowAtIndexPath function. The MetPetDB logo is placed in the UIImageView logoView at the bottom of the view.

textFieldShouldReturn: This function is called when the user pressed the done button on the keyboard. This action signals that the user is done entering either their username or password, so the username and password variables are set accordingly. [theTextField resignFirstResponder] will hide the keyboard.

numberOfSectionsInTableView: There is only one section in the table view. numberOfRowsInSection: There are two rows in the only section of the table view, one for the username and another for the password. cellForRowAtIndexPath: Each row of the table view is made up of a label and a text box. If indexPath.row is 0, the usernametext text label is added to the cell's content view. An email keyboard will pop up when the user touches this text box. If indexPath.row is 1, the passwordtext text lable is added to the cell's content view. This text box has a secure text entry attribute enabled since users will be entering their passwords into this field. Finally the titleLabel for the row is set to be the string at index indexPath.row of the rowTitles label.

login: This is the action linked to the login button. First, the username and password fields are checked and an alert is displayed if either is null. If there are values in both, they must be sent to the server for authentication. This is done with an http post request with the username and password in the postString which then becomes the body of the request. If the username and password combination are not approved by the server, an alert will be shown and the text fields will be cleared. If the username and password are correct, then they will be stored in the keychain of the iphone.

storeInKeychain: This function stores the username and password for this application in the keychain of the iphone so that when the user opens the application they are automatically logged in. The NSUserDefaults stores the username and password and associated with the service name metpetdb.

loadMainViewController: This function loads a copy of the xib file MainView?, controlled by an instance of MainViewController?.

InfoViewController?.m

This view is loaded when the info button in the navigation bar of the main view is pressed. This view controller corresponds to the InfoView?.xib file which has the text written in interface builder.

goToWiki: This is an action that is the target action of the “Visit our wiki” button on this view. This function opens a target URL in Safari and closes the MetPetDB app.

RadiusController?.m

viewDidLoad: This function lays out the user interface for the view. The scroll wheel is an instance of UIPickerView called radiusPicker, whose values are contained in the radii array. When the view is loaded, the 7th row is selected to suggest a 10km radius.

numberOfComponentsInPickerView: Since there is only one section in the picker view, the number of components is set to 1.

number ofRowsInComponent: This specifies the number of rows in the pickerView, which is equal to the number of strings in the radii array.

titleForRow: This specifies that the title for each row in the picker view corresponds with a string in the radii array.

didSelectRow: When a row in the picker view is selected, the radius string is set to the string corresponding to that row.

showMap: This is an interface builder action that loads the map based on the user's selection. The radius must be converted from meters to degress, to determine the north, south, east, and west coordinates of the bounding search box. Once these have been established, a query is run to get the search criteria for all the samples that lie in this box. This is accomplished by creating a PostRequest? object. The exact syntax of these post requests can be read about in the section describing PostRequest? objects. When a PostRequest? is created, the setData function is called to pass all the attributes of the current search. The last value passes (“true” in this case) is important because it indicates that the return value of the post request should be a the search criteria of all the samples returned from the searhc, not the samples themselves. The return value of the PostRequest? will be a NSData object that is then parsed by an XML parser and inserted into a SearchCriteria? object. These objects contain all the attributes of all the samples from a search so that the search can be refined later in the application. The getSamples function (described below) is then called to get all the samples matching this search.

After the search criteria has been obtained, MapView?.xib controlled by an instance of MapController? is loaded. There are many values that must be passed to the MapController?, and they are specified in the two “set” functions. The setData function passes an array of all the samples returned from the search as well as a criteriaSummary object that holds all the search criteria attributes of all the returned samples. The setCurrentSearchData function is passed a CurrentSearchData? object, which in this case contains the 4 coordinates that are being used in the search.

getSamples: This function is creating another PostRequest? object, but the last argument this time is “false” since all the data for all the matching samples is desired instead of just the search criteria summary. The NSData value that is returned from the setData function of the PostRequest? object is passed to the xmlParser and an array of all the samples returned from the search (locations) is returned. These will be displayed in the map view. setData: This function was called when this view was loaded from the mainViewController. It sets the currentSearchData object which contains all the search information the user has entered. See later documentation for a more complete explanation of the currentSearchData object.

CoordinateInputController?.m

viewDidLoad: This view contains text fields that allow the user to input a latitude and longitude that they wish to use as the center coordinate for their search. These text fields are created in the interface builder. In the viewDidLoad function, the toolbar is built and it contains only a continue button which calls the chooseRadius function when selected. chooseRadius: Since this button should be pressed after the user has input both a latitude and longitude, this function first checks to make sure the values entered by the user represent a valid coordinate. If not, an alert is displayed. If the coordinate is valid, the SearchView?.xib file is loaded which is controlled by an instance of RadiusController?. The setData function in the RadiusController? is called to set the coordinate that has been chosen, the owner category, and a boolean value to indicate whether the user's location should be displayed on the map. Since the user's current location is not being used, their location is not visible on the map and the boolean value is false. textFieldShouldReturn: This function hides the keyboard when the user presses the done button on the keyboard. The latitude and longitude strings are set to the values of the text field. setData: This function is called by the MainViewController? when loading this view to set the currentSearchData object. loadMap: This is the target function for the “Select Coordinate Using Map” button. It loads the dropMapView.xib file controlled by an instance of DropMapViewController?. This controller will allow the user to drop a pin on the map in the event they do not know the latitude and longitude of the location they would like to search around.

DropMapViewController?.m

viewDidLoad: In order to make the map displayed in this view, an instance of MKMapView must be added as a subview using the function [self.view addSubview:mapView]; The “Drop Pin” button in the navigation bar can be selected by the user to drop the pin on the map. The toolbar button “Select Coordinate” is disabled until the user has dropped a pin on the map because no coordinate can be passed to the next view controller if one has not yet been chosen. dropPin: This function is called when the user pressed the “Drop Pin” button on the navigation bar. Calls the viewForAnnotation function described below, which creates the pin. The button to drop a pin is now disabled since only one pin should exist on the map at a given time. The select button in the toolbar is enabled because the user can select a coordinate after moving the pin to their desired locaiton.

loadMap: This function loads the searchView.xib file controlled by an instance of RadiusController?. The setData function in RadiusController? is called to set the currentSearchData value.

mapView: viewForAnnotation: This function creates the pin from the DDAnnotationView class. This new class that extends MKAnnotationView is necessary for this functionality because unlike a normal pin, touches for this pin need to be intercepted. The pin is initiallly set to be in the center of the view. The selectedCoordinate of the pin is set by converting the point in the view to a coordinate in the mapView. The convertPoint function handles this.

DDAnnotationView.m

touchesBegan: This function sets the starting position in the view when a touch starts. This information is necessary to calculate the distance the pin moves after a touch.

touchesMoved: The newLocation represents the position of the point in the view after the pin moved. The view is adjusted if the user drags the pin very far.

touchesEnded: This function is called when the pin is no longer being dragged. The newCenter variable represents the new location of the pin (the self object here represents the DDAnnotationView,) and the newCoordinate represents the newCenter converted from a view position to a map coordinate. The annotation's coordinate is now set to be the new coordinate. touchesCancelled: This function does not work, and there is currently no way to cancel a touch after it has begun.

DDAnnotation.m

This class extends the MKAnnotation class. changeCoordinate: This is the only function that is utilized in this class. It is responsible for changing the current center coordinate of the annotation to the _coordinate argument passed into the function. The remainder of the functions are not used because there is no accessory view being displayed for these pins.

RegionViewController?.m

viewDidLoad: This view allows the user to select a region from the list of pre-defined regions in the database. The function getLocationInfo makes the server calls to populate the region array with all the regions that are currently listed in the databse. A dictionary is used in this view so that the user can simply select a letter from the right hand side of the view to see the regions beginning with that letter. In order to create this dictionary, a list of the all the letters that have a region needs to be created. For each letter in the array of all letters, there should be an array of all the regions beginning with that letter. The dictionary tableObjects is then set to contain the list of regions as the object corresponding to the letter which is the key. This function also checks the keychain for a username value and sets the variable Uname if there is one.

numberOfSectionsInTableView: There is a unique section of the table for every letter of the alphabet that has at least one region beginning with that letter.

numberOfRowsInSection: The number of rows in each section of the table view is the number of regions beginning with each particular letter.

cellForRowAtIndexPath: This function controls setting the text (region name) for each row of the dictionary.

sectionIndexTitlesForTableView: The index will be the letters that have regions begging with that letter. These letters are in the letters array and are displayed vertically on the right side of the view.

titleForHeaderInSection: The view is divided into sections with each section containing the regions beginning with the letter that represents the section. The title headers for the section, therefore, will simply be the letters.

didSelectRowAtIndexPath: This function is called when the user selects a region. The selectedRegion is set and the loadMap function is called.

loadMap:

getSamples: A PostRequest? object is used to to get the search criteria summary of all the samples that will be returned from this search. The final argument sent to the post request is the value “true”, which indicates that the NSData object returned will contain summary information about the samples and not all the information about all the samples. The NSData object (myReturn) is then parsed with an xmlParser to get the desired criteriaSummary object. The final argument is set to false to get all the information about all the samples and this is returned in an NSData object. This object is then parsed by an xmlParser and the samples that will be displayed on the map are stored in the sampleLocations object.

getRegions: This function returns all the region names in the database that have samples for them. If a user is logged in, they will see any regions in which they only have private samples in addition to all the regions where there are public samples. The post string that must be passed if there is a user logged in is “username= insertUsername\nregions= true\n” and simply “regions= true” if there is no user logged in.

setData: This function is called from the MainViewController? with the purpose of setting the currentSearchData object.

MapController?.m

viewDidLoad: If a region has been selected as the geographic search criteria, we need to make a navigation bar, otherwise, the makeNavBar was already called in the previous view. The line [MyCLController sharedInstance].delegate= self; is necessary so the mapView knows where to place the pin for the user's current location. It is in this function that the check is made to see if locationVisible==TRUE. If if is, the user's location is displayed on the map as a red pin. The MKMapView object (mapView) represents the acutal map, so in order to make it visible to the user, it must be added to the subview ([self.view addSubview:mapView]). If user did not select a region as their geographic search, make a search box out of 4 purple boundary pins by calling the function makeSearchBox. The type of the map is indicated by the string mapType member variable of the currentSearchData object, although the actual property is changed with mapView.mapType= MKMapTypeHybrid, or whatever type the user has chosen. In order to maintain the same map type after the user navigates away from the map (refines search criteria or views individual samples) the currentSearchData object is passed to subsequent views.

makeNavBar: This function creates the navigation bar at the top of the map view. Since the number of samples currently being displayed has to be shown in the label, this number of samples is counted and displayed in the label. The button at the right side of the navigation bar which controlls the pagination of samples must also be set with the target function viewMoreSamples so that when it is pressed more samples are loaded.

setSpan: This function controls the span of the coordinates of the map that are displayed when the map is initially loaded. The span of the longitude and latitude is always the difference between the minimum and maximum longitude or latitude. The small value .01 should always be added to these span values in case there is only one sample or there are a number of samples at the max and min values of the span. These values are obtained from the Criteria Summary object searchCriteria. The mapRegion and mapSpan properties of mapView can then be set repectively.

makeSearchBox: This function creates four boundary pins if the user is not searching using a region. These points are at the corners of the search box defined by the minimum and maximum latitudes and longitudes. Since we do not want the boundary pins to hide any of the sample pins, we add .001 to the coordinate of each pin. Each pin is of type uniqueSample.

viewSamplesAsList: This function is called when the user presses the button in the toolbar to view the samples as a list. This function simply loads the view SampleTableView?.xib which is controlled by an instance of SampleTableController?. The setData and setSamples functions set the CriteriaSummary? object (called searchCriteria here) and CurrentSearchData? object respectively in the sample table view. The sections on CriteriaSummary?.m and CurrentSearchData?.m explain the difference between these two object types.

touchesBegan: This function is not used, but is included because it enables the mapView to intercept the user's touches. If the functionality of grouping samples based on the user's zoom level were to be incorporated into the project, this function would be necessary.

makeToolar: This function builds the toolbar at the bottom of the mapView. There is a button to view samples as a list (allSamples button) and a button to navigate to the view where additional search criteria would be provided (narrowSearch button.) The info button on the right side of the toolbar loads a view that changes the map type.

returnHome: This function is called when the home button on the navigation bar is selected and it just loads MainView?.xib which is controlled by an instance of MainViewController?.

infoButtonPressed: This button loads MapTypeView?.xib which is controlled by an instance of MapTypeController?. The currentSearchData object has several values that need to be updated here. The center of the map at its current zoom level in addition to the span should be set here. The currentSearchData object is then passed to the mapTypeController (in the setCurrentSearchData function) and then back to the map so that when the user navigates back to the map they are at the same zoom level.

makeAnnotations: This function creates pins on the map out of array of uniqueSample objects and is called when the viewMoreSamples function returns samples and needs to add more samples to the map. In the radiusController, the thread that is created also calls this function to make map annotations out of the samples that were obtained in the thread. These new samples also get added to mySamples which is the array of all samples currently being displayed on the map.

showSampleInfo: This function is called when the user selects the callout button on one of the pins. buttonArray represents all of the uniqueSample objects that are single samples (all others are multiple samples that are located at the same coordinate.) The buttonArray must be looped through in order to determine which pin was selected. After this has been determined, it will be passed to the instance of SampleInfoController? controlling the SampleInfo?.xib view. The currentSearchData object has several values that need to be updated here. The center of the map at its current zoom level in addition to the span should be set here. The currentSearchData object is then passed to the mapTypeController (in the setCurrentSearchData function) and then back to the map so that when the user navigates back to the map they are at the same zoom level. The setCurrentSearchData function then passes the new currentSearchData object to the sample info view.The setSamples function sets an array of the samples being displayed called originalData, the uniqueSample object that is the chosen sample, and the Criteria Summary object representing all the search criteria.

sampleTable: This function is called when the callout button of a pin representing multiple samples is selected. Pressing the callout button will cause all the samples represented by that annotation to be displayed in the same table view as the viewSamplesAsList function. All of the uniqueSamples objects that represent multiple samples are stored in the multipleButtons array. This array, therefore, needs to be looped through to determine which uniqueSamples object was selected. Once this has been determined, the SampleTableView?.xib is loaded and controlled by an instance of SampleTableController?.

The setData function passes the array of samples from the selected uniqueSamples object that will be displayed in the table and a boolean variable that indicates whether the samples that will be displayed in the table representing all the samples in the search, or just the samples from one uniqueSamples object. This instance of the sample table is obviously only the samples from a single uniqueSamples object. The setSamples function passes the samples that will be displayed in the table and the searchCriteria object (instance of CriteriaSummary?). The setCurrentSearchData passes the currentSearchData object. The differences between CriteriaSummary? objects and CurrentSearchData? objects can be found in the sections about them later in the documentation.

mapView: viewForAnnotation: annotation This function is very important because it controls the actual creation of the annotation views (pins) on the map. The annotation that is passed as the final argument to this function is simply an instance of the uniqueSamples class since this class extends the MKAnnotation class. The samples attribute of any uniqueSamples object is an array of AnnotationObjects? which are simply map pins. A new MKPinAnnotationView called pin is created and given the same title as the argument annotation. If the pin annotation is the user's current location, the pin color is set to red, and if the pin annotation is a boundary point, the pin color is set to purple. All sample pins are green. The uniqueSamples object has an id attribute which identifies whether that particular instance represents a single or multiple samples (AnnotationObjects?.) If it represents multiple samples, it will be added to the multiplePoints arrary (multipleButtons array corresponds to the associated details button for the annotation.) If it is a single sample, the sample itself is added to the singlePoints array and its associated button is added to the button array. The details button is set with an appropriate target function and the rightCalloutAccessoryView of the pin is then set to be the details button.

viewMoreSamples: This function is the target for the arrow button in the navigation bar. This function represents the core functionality of the pagination capabilities of the app. If more than 10 samples (this number may be adjusted as the database grows) are returned from a search, only the first 10 samples will initially be displayed. Each time the user presses the arrow button on the navigation bar, 10 more samples will be added to the map, until all samples from the search are being displayed. If search criteria have been specified, that criteria is applied to ALL samples, not just the fraction that are being displayed.

A PostRequest? object is created that will build the http post string that is sent to the server. The currentSampleCount must be sent with this request because that is the number that the pagination will begin at. The maximum number of samples returned is set on the server side. Several fields of the currentSearchData object must also be passed to the PostRequest? in the setData function. The “false” boolean value passed as the last argument indicates that we want all of the information about the samples being returned, not just a criteria summary. The response that is returned is then parsed by an xml parser to obtain the array of samples representing the next 10 (or however many remain) samples. The new samples are added to the map by adding them to the mySamples array, which represents all the samples being displayed on the map. The viewDidLoad function is then called again since the navigation bar, map span, and search box may all need to be re-created.

setData: This function is called by other views to set the samples to be displayed on the map (locations array) and the current CriteriaSummary? object. The CriteriaSummary? object contains information about the samples returned from a search, such as a list of all the rock types, minerals, metamorphic grades, owners. See the description of CriteriaSummary?.m for further information.

setCurrentSearchData: This function passes the CurrentSearchData? object from various views. The CurrentSearchData? object holds data that the user has provided to narrow/change the search. If the user has provided any rock types, minerals, a region, a map type, etc, all of that information would be contained in the CurrentSearchData? object. See the description of CurrentSearchData?.m for additional information.

SampleTableController?.m

viewDidLoad: The allSamples boolean indicates whether all the samples on the map are being displayed in the sample table or whether it is just the samples that are represented by a single pin. This information is necessary because the if all samples are being displayed, then an array of uniqueSamples has been passed in and we want to display the samples attribute of each uniqueSamples object. If the samples at a particular pin are being viewed, then an array of AnnotationObjects? (this is the type of the samples attribute of a uniqueSamples object) has already been passed into the function. After the array of AnnotationObjects? has been obtained, a string consisting of the sample name and sample onwer is constructed to be displayed at each row of the table.

The toolbar of this view contains a button which allows the user to email the list of samples.

numberOfSectionsInTableView: There are no divisions between the data in this view, so there is only one section.

numberOfRowsInSection: This sets the number of samples being displayed in the table which correspons to the count of the rows array.

cellForRowAtIndexPath: This function sets the text to appear on the row of the table.

didSelectRowAtIndexPath: Selecting a row from this table will bring the user to a view of that individual sample. If the table is displaying the samples from a single map pin, it is easy to determine which sample was selected because it has the same index in the mySamples array as indexPath.row. If, however, the table is displaying all samples on the map, we must compare the owner and name from the arrays we built (see the viewDidLoad function) to the name and owner of the selected row to determine the selected sample.

When the selected sample has been determined, the SampleInfo? view which is controlled by an instance of SampleInfoController? is loaded. This view contains information about just the specific sample that was selected. The setSamples function passes the array of all samples currently being displayed on the map, the sample that was selected, and the CriteriaSummary? instance (searchCriteria) to the sample info view. The setCurrentSearchData just passes the CurrentSearchData? object.

sendEmail: This function builds the body of the email (a simple string object) so that it contains a link to each sample on the MetPetDB website. Open the iphone mail application with the [UIApplication sharedApplication] function call. If the user is logged in, their email address should be used as the address to send the email to.

setData: This function is called by the previous view to set the samples that will be displayed in the table. The sampleFlag boolean indicates whether the user navigated to this view by clicking the “View Samples As List” button or by clicking on a pin that represented multiple samples.

setSamples: The array of samples passed in this function represents all the samples currently being displayed on the map. The CriteriaSummary? object is also passed here.

setCurrentSearchData: The previous view calls this function to set the CurrentSearchData? for this view.

SampleInfoController?.m

viewDidLoad: The back button in this view re-loads the map view instead of simply navigating to the previous view by calling the backToMap function. If the sample is from a publication, the sample name and publication number must be separated. The sample name is to the left of the : and its publication number is to the right. The sample name, publication number, and rock type are displayed in the title label which is above the rest of the text. The remaining text in this view is displayed in a scrollable, non-editable text view. The bulk of the displayed text is stored in the outputText string which is basically just concatenations of all the attributes of the sample.

makeToolbar: The toolbar of this view contains a button to view the comments made about the sample and another button to view a summary of the subsample information about the sample. In order to display the number of comments and subsamples before the user clicks the button. This is accomplished by creating instances of the CommentDisplayController? and AnalysisSummary? and then calling functions within those controllers to get comment and subsample counts, respectively. makeImageButton: A similar procedure is followed for the image button as for the comment and subsample buttons. The number of images for the particular sample must be obtained so the button can display this number. The get_thumbnails function (described below) sets imageCount to be the number of images for the sample and this number appears in the image button.

get_thumbnails: Given a sample id, this function creates a query that returns all the thumbnail checksums for the image. The http post query string that must be passed is “thumbnails= sampleId\n”. If a user has signed in to the application, their username should also be passed in the post request. The syntax for that would be “thumbnails= sampleId\nusername= insertUsername\n”. The actual values of sample id and username obviously need to be substituted. The NSData Object myReturn contains the xml from the server containing the thumbnail checksums. This xml is parsed by the subsampleParser which is defined in this file.

didStartElement, foundCharacters, and didEndElement: These functions are all part of the xml parser for the thumbnail xml. imagePaths is an array of the checksums of the images which are appended to a server url to obtain the actual image. The image ids, which are needed to obtain larger versions of the images, are stored in the imageIds array.

viewSubsamples: This function loads the Chemical Analysis.xib file controlled by an instance of AnalysisSummary?. The only data that needs to be set in the controller is the sampleID and sampleName, which are set by calling the setData function in the AnalysisSummary? controller.

viewComments: This function loads the CommentDisplay?.xib file controlled by and instance of CommentDisplayController?. The only data that needs to be set in the controller is the sampleID and sampleName, which are set by calling the setData function in the AnalysisSummary? controller.

imagesDisplay: This function is called when the user presses the image button in the upper right of the view. The ImageView?.xib file will be loaded and is controlled by an instance of ImageViewController?. There are many more values that need to be set in this view than in the subsample and comments view because the user does not return to the sample info view by the back button from the image display. The setVars function call sets the variables that are relevant to the image display and is described in more detail in the description of the ImageViewController? class. The setSamples function passes the sample that is currently being displayed, an array of all samples that were being displayed on the map, and the CriteriaSummary? instance (searchCriteria). The setCurrentSearchData is called by the previous view to set the CurrentSearchData? object.

backToMap: This function returns the user to the map in the same state it was when they navigated to information about this sample. The map type is set in the setType function, and the setData function provides many variables that are crucial to restoring the previous state of the map. As always, the setCurrentSearchData sets the arrays containing any search criteria provided by the user.

AnalysisSummary? .m

viewDidLoad: This view is loaded from the sampleInfo controller and contains a summary of information about the subsamples and associated chemical analyses for this sample. The body of text in the view is contained in the scrollable, non-editable textView object. The title label at the top of the view contains the sample's name. The getSubsampleInfo function (described below) gets the subsample information for the sample from the server. It is formatted in the output string object and displayed in the textView.

getSubsampleInfo: This function creates a query using the sample's id to obtain subsample information. The syntax of the http post string is “subsampleInfo= sampleID\n”. If a user is logged in to the application the username should also be passed to the server in the event a private sample is being viewed. The syntax for this request would be “subsampleInfo= sampleID\nusername= user\n”. The actual values of sample id and username obviously need to be substituted. The return value myReturn is a NSData object, which is then parsed using the subsampleParser functions that are described below.

didStartElement, foundCharacters, and didEndElement: These functions parse the xml returned in the subsampleResponse object and populate several variables. The minerals array contains the minerals from the chemical analyses, imageCount indicates the number of images available for this particular sample, subsampleCount indicates the number of subsamples available for the sample, and analysisCount indicates the number of chemical analyses for the sample. Bulk rock is a boolean to indicate whether or not the chemical analyses were done on a bulk rock. All of these values are used in the display for this view.

getSubsampleCount: This function is called from the sampleInfo controller to obtain the number of subsamples available for the sample.

setData: This function is called when the view is loaded to set the sampleid and sample name for this controller.

CommentDisplayController?.m

viewDidLoad: This function loads the comment display view. There is a label at the top of the view that displays the sample name and then calls the function getComments to get the comments for this sample from the server. getComments: This function gets an xml formatted output of all the comments for this sample. The syntax of the http post request is “comments= sampleId\n”. If a user has signed into the application, the username must also be passed in the query in the event the sample is private. The syntax for this is “comments= sampleId\nusername= user\n”. The actual values of the sample id and username obviously need to be substituted. The return value (myReturn) is parsed using the commentParser, whose functions are described below.

didStartElement, foundCharacters, and didEndElement: These functions parse the commentResponse and put each comment in the comments array.

showComments: The comments for the sample are displayed in a scrollable, non-editable text view. The string that is displayed is called output and is simply the result of combining and formatting all the comment strings in the comments array.

addComment: This function loads the AddComment?.xib view which is controlled by an instance of AddCommentController?. Although this functionality of adding a comment does not yet exist, it is not extremely difficult to implement.

getCommentCount: This function is called by the sampleInfoController to obtain a count of the comments for the particular sample.

setData: This function sets the sampleName and sampleID attributes of the controller when it is first created.

AddCommentController?.m

This view is not functional and was removed from the version of the app that was submitted to the Apple App store. It was intended for a user to be able to add comments to be uploaded to the server, but there were server problems that resulted in this not being possible.

ImageViewController?.m

viewDidLoad: The back button for this view is overwritten with a button that reloads the sampleInfo view. For each ImageView? view, there are 9 thumbnails displayed. The total number of pages of images is calculated and indicated by the pageControl at the bottom of the view. The white dot in the page control advances to indicate which page of images is currently being displayed. Navigating forwards and backwards through the pages of images is done with a horizontal swiping gesture. If the user is on any page but the first page of images, the back button is not displayed. Each thumbnail is also a button and when selected, will display a view with an enlargened version of the selected image.

touchesBegan: This function intercepts the starting position of a swiping gesture.

touchesMoved: This function registers the amount the touch moved from the start position. If the distance it moved is great enough to be considered a horizontal swipe, the previous or next pages are loaded depending on whether the direction of the swipe.

turnPage: This function allows the user to move from page to page by simply touching the page control at the bottom of the page. If the page control is touched ahead of the current position, the next pages is displayed and if it is touched before the current position, the previous page is displayed.

viewNextPage: Each time the next page of images is requested, a new ImageView? is created and the same values that were passed to the original instance are passed to all subsequent ones.

previousPage: Since the each page of images is simply a view on the navigation controller, that view only needs to be popped off the stack to move to the previous page.

backToSample: This function loads the SampleInfo?.xib view controlled by an instance of SampleInfoController?. The setCurrentSearchData function then passes the new currentSearchData object to the sample info view.The setSamples function sets an array of the samples being displayed called originalData, the uniqueSample object that is the chosen sample, and the Criteria Summary object representing all the search criteria.

enlarge: This function is called if a user touches one of the thumbnail buttons. Set the selected image to be the image from imageIDs that was selected and then pass that value to the instance of SingleImageView? that will be loaded.

setVars: This function is called each time the next page of images is loaded. The pagesDisplayed value increases each time another page is displayed and decreases every time the previous page is displayed. The remaining count indicates how many sample images have not yet been shown. ImageCount?, imagePaths, and imageIDs do not change as new pages of images are displayed for the same sample.

setSamples: This function is called by views that load this image view. The first argument is the sample that the images belong to, the second is an array of all the samples returned from the search, and the third is the CriteriaSummary? object.

setCurrentSearchData: This function is called by views that load this image view. It sets the value of the CurrentSearchData? object.

SingleImageView?.m

viewDidLoad: This function first calls the get_large_image function which is described below. Based on the checksum that is returned from the server for this image, the imageView is set with the appropriate image. The imageView is set on top of a scrollView that allows the user to zoom and scroll the image. The navigation bar at the top of this view contains a next button that allows the user to page to the next image (enlarged version,) for the sample.

viewForZoomingInScrollView: This function specifies that the imageView is what is being zoomed. shouldAutorotateToInterfaceOrientation: This makes it so that when the user rotates the phone horizontally, the view also changes to horizontal.

nextImage: This function is called when the user pushes the next button in the navigation bar. The position variable indicates the position of the current image in the images array. This position gets incremented and another instance of SingleImageView? is created and passed the new position and the array of images.

previousImage: The position in the array of images (as described above,) is decremented. A new instance of SingleImageView? is created and passed the position and the array of images.

get_large_image: A post request must be made with the image id to get the mobile-sized image. The http post string syntax for this request is “largeImage= imageID\n”. If a user is logged into the application, the username must also be passed to the server. The syntax for that would be “largeImage= imageID\nusername= user\n”. The actual values of the image id and username obviously need to be substituted in these examples. The return value (myReturn) is parsed using the subsample parser and the functions described below.

didStartElement, foundCharacters, didEndElement: The filename is obtained and stored in the filename variable, and the mobile image path is stored in the imagePath variable.

setData: This function is called from the controller that loads this view. The position in the array that contains the images for the sample, that array of images, and the id of the current selected image all need to be set.

SearchCriteriaController?.m

viewDidLoad: This view is loaded when the user selects the option to refine the search criteria in the map view. The table view that is constructed has a title and a row title for each criteria that is specified. Since geographic location must already have been specified to arrive at the map, a row is added with the title “Geographic Location” and the subtitle indicating the region or center coordinate that was searched upon.

In order to create the second row of the table, the keychain is checked to see if the user has logged in. If so, then the second row of the table will be clickable and will take the user to a view where they can choose to change the public/private status of samples they are viewing. If the user is not logged in, the row will display “Public samples only” and it will not be clickable. Next, the members of the currentSearchData object (publicPrivateStatus, rockTypes, minerals, metamorhpicGrades, and owners) are checked. If any of these have values, a search criteria of that type has been specified. This will cause a row in the table containing that search criteria to be created. Adding row titles to the rowTitles array and subtitles to the rowSubtitles array will cause the table to have the appropriate rows when created. The subtitles for any of the search criteria indicate the actual rock type, mineral, etc. that was selected. The table view is added to the view and the toolbar is created.

calculateCriteria: Since this file handles narrowing the search, a query must be run before the user returns to the map to obtain the samples that match any search criteria that has been specified. A PostRequest? object is created to make the query for the attributes of all samples returned from the search. All the search criteria that has been specified up to this point by the user is sent to the PostRequest?, as well as the pagination number (always 0 at this point). The final argument is set to “true” because only the search criteria is desired, not all of the sample information from the search. An instance of CriteriaSummary? called searchCriteria is returned. This is used to populate the subsequent views with the available search criteria the user can select.

makeToolbar: The toolbar displays three buttons, the “Search” button, the “Add Criteria” button, and the “Edit Criteria” button. The first two are always visible, but the edit button is only visible if at least one search criteria has been specified by the user. The toolbar is built every time this function is called.

edit: This function is the target function of the edit button on the toolbar. When the edit button is pressed, a done button appears in the navigation bar that the user can press when they are done editing criteria. By setting the bool removing to true, and reloading the table view, the table will create a “Delete” button on the side of every row in the table. Pressing this delete button will remove all the criteria of that type from the search criteria. The entire row will also be removed from the table. It is very important that the [self.view addSubview:tableView] function is called here because the table view must be re-created.

done: This is the target function for the “Done” button in the navigation bar that appears when the user is editing criteria. This function sets the bool removing=false and then recreates the table. This will make the table reappear without all of the done buttons that were added during editing.

backToMap: This function is called when the user selects the “Search” button in the toolbar. mapView.xib controlled by an instance of MapViewController? is loaded and the user will see a map with a group of samples representing the samples that are returned from the new search. The getSamples function described below interacts with the server to get the new samples. getSamples: This function creates the PostRequest? that will get the samples matching the search. All of the criteria that the user has provided, which is stored in the currentSearchData object is passed to the PostRequest? along with a pagination number of 0. The final argument is “false” indicating that we want all the information about all the samples, not just the search criteria summary. An array of UniqueSamples? is returned from the post request representing the samples remaining after the user has modified the search criteria.

deleteRow: This function is called when the user presses one of the delete buttons that appears when the user is in edit mode (edit button has been pressed.) This function makes the “Done” button appear on the navigation bar, which the user can press to exit edit mode.

addCriteria: This is the target function for the “Add Criteria” button. The TableView?.xib file is loaded and controlled by an instance of TableController?.

numberOfSectionsInTableView: This table has only one section.

numberOfRowsInSection: There are as many rows in this table as there are items in the rowTitles array. There will always be at least two rows, one representing the geographic criteria and the other representing the public/private status of samples being viewed (this depends on whether or not the user is logged in.) There can be at most one table row for each different type of criteria (rock type, mineral, etc.)

cellForRowAtIndexPath: This function is a bit complicated because each row in the table view had to be constructed from a title label and a subtitle table. The background color of the row changes depending on the indexPath.row number. If the bool removing= true, this indicates the user has pressed the edit button and a delete button should now appear on each row. The delete button is placed in the content view of each cell and the deleteRow function is the target function of each delete button. The row of the table containing the public/private status of the samples is constructed a bit differently because it needs to take the user to a different view when pressed. The cell accessory type is UITableViewCellAccessoryDisclosureIndicator which makes the arrow appear on the right side of the cell. This only appears if the user logs into the application.

didSelectRowAtIndexPath: This function responds to the user clicking on the second row of the table which takes them to the view where they can edit the public/private status of the app. Since only one row of the table should respond to a user's click, the indexPath.row must equal 1 for the user to be taken to the view. If the user is not logged into the application, this row will not respond to the user's selection and there will not be an arrow. If the selected row is row 1, the PublicPrivateView? is loaded.

setData: This function is called when the MapController? loads this view. It passes the samples that are currently loaded on the map (sampleLocations) as well as the CriteriaSummary? object.

setCurrentSearchData: This function is also called by the MapContoller? when this view is loaded. All the search criteria that the user has specified is passed to this view in the currentSearchData object.

TableController?.m

viewDidLoad: There are a fixed number of rows in this table, one representing each type of search criteria (rock type, mineral, metamorphic grade, and owner). Each of these is an element in the rows array, which becomes the text for the cells.

numberOfSectionsInTableView: There is only one section in this table view.

numberOfRowsInSection: The number of rows in the table is the count of the rows array (always 4).

cellForRowAtIndexPath: This function just sets the text for the rows and the detail disclosure for the row accessory view.

didSelectRowAtIndexPath: This function registers the number of the row (indexPath.row) that has been selected by the user and calls the appropriate function. Any of the functions that are called will result in a new view being loaded where the user can choose from a list of the type of criteria they selected.

loadMineralsView: The myMinerals array is set to searchCriteria.minerals which contains a list of all the minerals in all the samples returned by this search. If this array is empty and there are no minerals, an alert is displayed and the MineralView?.xib is not loaded. If there are minerals in this search, the MineralView?.xib controlled by an instance of MineralsController?. The setData function passes the array of all samples on the map in the previous search, an array of all the minerals in all the samples from the previous search, and searchCriteria (instance of CriteriaSummary?). The setCurrentSearchData function passes the currentSearchData object to the next view.

loadMetamorphicGradeView: The myMetamorphicGrades array is set to searchCriteria.metamorphicGrades which contains a list of all the metamorphic grades in all the samples returned by this search. If this array is empty and there are no metamorphic grades, an alert is displayed and the MetamorphicGradeView?.xib is not loaded. If there are metamorphic grades in this search, the MetamorphicGradeView?.xib controlled by an instance of MetamorphicGradeController?. The setData function passes the array of all samples on the map in the previous search, an array of all the metamorphic grades in all the samples from the previous search, and searchCriteria (instance of CriteriaSummary?). The setCurrentSearchData function passes the currentSearchData object to the next view.

loadRockTypeView: The myRockTypes array is set to searchCriteria.rockTypes which contains a list of all the rock types in all the samples returned by this search. If this array is empty and there are no rock types, an alert is displayed and the RockTypeView?.xib is not loaded. If there are rock types in this search, the RockTypeView?.xib is loaded and controlled by an instance of RockTypesController?. The setData function passes the array of all samples on the map in the previous search, an array of all the rock types in all the samples from the previous search, and searchCriteria (instance of CriteriaSummary?). The setCurrentSearchData function passes the currentSearchData object to the next view.

loadOwnerView: The owners array is set to searchCriteria.owners which contains a list of all the owners in all the samples returned by this search. If this array is empty and there are no owners, an alert is displayed and the OwnerView?.xib is not loaded. If there are owners in this search, the OwnerView?.xib controlled by an instance of OwnerViewController?. The setData function passes the array of all samples on the map in the previous search, an array of all the owners of all the samples from the previous search, and searchCriteria (instance of CriteriaSummary?). The setCurrentSearchData function passes the currentSearchData object to the next view.

setData: This function was called by the searchCriteriaViewController to pass all of the samples from the search and the current CriteriaSummary? object.

setCurrentSearchData: This function is called by the searchCriteriaViewController to pass the current CurrentSearchData? object to this view.

RockTypeController?.m

viewDidLoad: The rockSelector object is an instance of UIPickerView that contains all the distinct rock types from all the samples that have been returned in this search. These rock types were passed into the controller and are stored in the myRockTypes array. The toolbar is built and only contains one button, which is an “Add” button to add the selected rock type.

numberOfComponentsInPickerView: There is only one component in this picker view.

numberOfRowsInComponent: There are the same number of rows as there are distinct rock types in all samples.

titleForRow:inComponent: The rows are given the same titles as the rock types in the myRockTypes array.

didSelectRow:inComponent: This function is called when the scroll wheel is rotated to select a specific item. The string variable rockName represents the rock type the user selected, so this variable is set when the user moves the scroll wheel to highlight a particular row.

refineSearch: This function is the target function for the “Add” button in the target bar. When this function is called, the rock type that is currently highlighted in the scroll wheel will be added to the rockTypes array that is a member variable of the currentSearchData object, provided that rock type has not already been added to the array. After the selected rock type is added to the currentSearchData.rockTypes array, the searchCriteriaSummaryView is loaded and controlled by an instance of SearchCriteriaController?.

setData: This function is called by the SearchCriteriaViewController? to set all of the samples returned from the previous search, the rock types that will be displayed on the wheel in this search, and the current CriteriaSummary? object.

setCurrentSearchData: This function is called by the SearchCriteriaController? to pass the currentSearchData object to this view.

MineralsController?.m

viewDidLoad: The sampleSelector object is an instance of UIPickerView that contains all the distinct minerals from all the samples that have been returned in this search. These minerals were passed into the controller and are stored in the myMinerals array. The toolbar is built and only contains one button, which is an “Add” button to add the selected mineral.

numberOfComponentsInPickerView: There is only one component in this picker view.

numberOfRowsInComponent: There are the same number of rows as there are distinct minerals in all samples.

titleForRow:inComponent: The rows are given the same titles as the minerals in the myMinerals array.

didSelectRow:inComponent: This function is called when the scroll wheel is rotated to select a specific item. The string variable mineralName represents the mineral the user selected, so this variable is set when the user moves the scroll wheel to highlight a particular row.

backToCriteria: This function is the target function for the “Add” button in the target bar. When this function is called, the mineral that is currently highlighted in the scroll wheel will be added to the minerals array that is a member variable of the currentSearchData object, provided that mineral has not already been added to the array. After the selected mineral is added to the currentSearchData.minerals array, the searchCriteriaSummaryView is loaded and controlled by an instance of SearchCriteriaController?.

setData: This function is called by the SearchCriteriaViewController? to set all of the samples returned from the previous search, the minerals that will be displayed on the wheel in this search, and the current CriteriaSummary? object.

setCurrentSearchData: This function is called by the SearchCriteriaController? to pass the currentSearchData object to this view.

Metamorphic Grade Controller.m

viewDidLoad: The sampleSelector object is an instance of UIPickerView that contains all the distinct metamorphic grades from all the samples that have been returned in this search. These metamorphic grades were passed into the controller and are stored in the myMetamorphicGrades array. The toolbar is built and only contains one button, which is an “Add” button to add the selected metamorphic grade.

numberOfComponentsInPickerView: There is only one component in this picker view.

numberOfRowsInComponent: There are the same number of rows as there are distinct metamorphic grades in all samples.

titleForRow:inComponent: The rows are given the same titles as the metamorphic grades in the myMetamorphicGrades array.

didSelectRow:inComponent: This function is called when the scroll wheel is rotated to select a specific item. The string variable gradeName represents the mineral the user selected, so this variable is set when the user moves the scroll wheel to highlight a particular row.

backToCriteria: This function is the target function for the “Add” button in the target bar. When this function is called, the metamorphic grade that is currently highlighted in the scroll wheel will be added to the metamorphicGrades array that is a member variable of the currentSearchData object, provided that mineral has not already been added to the array. After the selected metamorphic grade is added to the currentSearchData.metamorphicGrades array, the searchCriteriaSummaryView is loaded and controlled by an instance of SearchCriteriaController?.

setData: This function is called by the SearchCriteriaViewController? to set all of the samples returned from the previous search, the metamorphic grades that will be displayed on the wheel in this search, and the current CriteriaSummary? object.

setCurrentSearchData: This function is called by the SearchCriteriaController? to pass the currentSearchData object to this view.

Owner View Controller.m

viewDidLoad: The sampleSelector object is an instance of UIPickerView that contains all the distinct owners from all the samples that have been returned in this search. These owners were passed into the controller and are stored in the owners array. The toolbar is built and only contains one button, which is an “Add” button to add the selected owner. numberOfComponentsInPickerView: There is only one component in this picker view.

numberOfRowsInComponent: There are the same number of rows as there are distinct owners in all samples.

titleForRow:inComponent: The rows are given the same titles as the owners in the owners array.

didSelectRow:inComponent: This function is called when the scroll wheel is rotated to select a specific item. The string variable ownerName represents the owner the user selected, so this variable is set when the user moves the scroll wheel to highlight a particular row.

backToCriteria: This function is the target function for the “Add” button in the target bar. When this function is called, the owner that is currently highlighted in the scroll wheel will be added to the owners array that is a member of the currentSearchData object, provided that mineral has not already been added to the array. After the selected owner is added to the currentSearchData.owners array, the searchCriteriaSummaryView is loaded and controlled by an instance of SearchCriteriaController?.

setData: This function is called by the SearchCriteriaViewController? to set all of the samples returned from the previous search, the owners that will be displayed on the wheel in this search, and the current CriteriaSummary? object.

setCurrentSearchData: This function is called by the SearchCriteriaController? to pass the currentSearchData object to this view.

MapTypeController?.m

viewDidLoad: The segControl object is a three way button with the highlighted segment indicating the user's selection. The three types of map that can be selected are Map, Hybrid, and Satellite and are stored in the items array.

changeType: This function is the selector function for the segControl object and it sets the mapType string object to the user's selection. The loadMap function is then called which changes the display back to the map.

loadMap: This function is called from the changeType function to re-load MapView?.xib controlled by an instance of MapController?. In the viewDidLoad function of the new view, the string mapType, which is being passed by this function to the map view, will be examined and the type of the map will be set accordingly.

PublicPrivateViewController?.m

viewDidLoad: This view features a UISegmentedControl with options of public, private, and both, representing the types of samples that will be shown. When the view is being loaded, the initial selected value is set to be the same as the currentPublicStatus member variable of currentSearchData object.

changeStatus: This is the target function of the ok button and it updates currentSearchData.currentPublicStatus depending on which segment the user selects.

backToCriteria: This function takes the user back to the SearchCriteria? view.

setData: This function is called by the searchCriteriaViewController when this view is loaded. It passes the array of all samples returned in the previous search as well as the current CriteriaSummary? object.

setCurrentSearchData: This function is called by the searchCriteriaViewController and will set the currentSearchData in this view.

AnnotationObjects?.h

This is essentially a struct that represents a sample on the map or an MKAnnotation object. This is essentially map pin, but because multiple samples can be at the same geographic location, AnnotationObjects? themselves are not represented on the map. Instead, they make up UniqueSample? objects. A uniqueSample object contains an array of Annotation objects representing all the samples at that particular latitude and longitude point. If, for example, there are 10 samples at the same geographic location, they will each be represented as an AnnotaionObject?, but all of them will be in the a the array member of a uniqueSample object. Essentially there is an AnnotationObject? for each sample returned from a search and these are grouped in groups of one or more UniqueSample? objects.

The member variables of the AnnotationObject? class include title and subtitle strings, which are displayed in the information bubble that appears when a user clicks on a pin on the map. For AnnotationObjects?, the title field is always the name of the sample. Information about the sample that each AnnotationObject? represents is also stored in the AnnotationObject?. The unique id of the sample, rock type, owner, etc. are all fields of AnnotationObject?.

uniqueSample.h

This class exists for the sole purpose of grouping AnnotationObjects? by geographic location. Since there are often multiple samples in the same geographic location, this class resolves the issue of viewing these samples on the map. Each sample is represented by an AnnotationObject? and these are then grouped in a UniqueSample? object if multiple samples have the same location. The sample array member variable of uniqueSample class holds the Annotation objects. The coordinate variable is the coordinate location of all the samples and this is where the pin will appear on the map. The title field will be the name of the sample if the sample is the only sample at a location. If there are multiple samples in the same geographic location, the number of samples will be the title. The subtitle field will always be “Click for more info.” These uniqueSample objects are displayed as pins on the map.

xmlParser.m

This file contains some functions that are called in various places throughout the application. The functions are passed NSData objects that generally contain xml that has been sent from the server. This file contains functions that convert this NSData to objects within the scope of the application. The parseSamples, parseRegions, and parseSearchCriteria functions are the functions that would be called from other files.

parseSamples: This function will convert an NSData object containing xml from the server into an array of uniqueSample objects called mySamples. The mySamples object is the return value that is returned to the function that called this function. In order to parse the xml, an NSXMLParser object called myParser is created and initialized with the NSData input object. When the parse function is called, the functions parser didStartElement, foundCharacters, and didEndElement functions are called. These functions are described in more detail below.

parseRegions: This function will convert an NSData object containing xml from the server into an array of strings that represents all of the regions in the database. Similar to the previous function, an NSXMLParser object is created to parse the xml.

parseSearchCriteria: This function will convert an NSData object containing xml from the server into a CriteriaSummary? object. This object will contain a summary of all the criteria of all the samples returned in the current search. Similar to the two functions above, an NSXMLParser object is created and this object will call the didStartElement, foundCharacters, and didEndElement functions.

parser didStartElement: This function checks the opening tags of the xml in the NSData object. The string object currentStringValue always represents the text between two tags. In all of the If statements in this function, currentStringValue is set to nil since an opening tag is being considered and we do not want text from the previous tags to remain. The first If statement checks to see if the first tag is an “html” tag. If this is the case, there has been an apache error and a warning should be displayed. If there is a “set” tag, then a set of samples is being output in the xml and the mySamples array should be allocated.

For several tags such as <sample>, <rockType>, <owner>, <mineral>, <metamorphicGrades>, and <description>, the actual value of this tag is not within these tags, but rather in a <string> </string> set of tags. We therefore need to set several boolean values to indicate which value to set if the <string> tag is encountered. The booleans are set to true (rockFlag is opposite) when the start tag is encountered for this element and they are later set to false when the end tag is encountered. This way, the currentStringValue can be appropriately set even though there are many tags whose actual value is within <string> tags.

parser foundCharacters: This function appends the characters of whatever value is found within a set of tags to the variable currentStringValue. The appropriate variables are set to the currentStringValue, as shown in the following funciton.

parser didEndElement: This function checks the end tag of xml elements and sets the interior currentStringValue of those tags to various variables in the context of whichever parse function was called.

The only value surrounded by <int> tags is the total number of samples returned from a query. Since the total count is only returned when we are parsing a criteriaSummary object, the totalCount member of the criteria object is set to be the integer value of currentStringValue. The <string> tag is the most confusing because there are many elements whose value is surrounded by these tags within a more descriptive set of tags. citeriaMinFlag, criteriaMetFlag, and criteriaOwnerFlag are all true only if the parseSearchCriteria function was called. If any are true, the currentStringValue is added to the appropriate array member variable of the criteriaSummary object. locationFlag is true if the parse regions function was called. This means that all of the xml returned from the server corresponds to the regions in the database. Since all of these are simply strings, each new string sis added to the regions array, and the parser will never encounter any tags besides <string>. descriptionFlag, nameFlag, ownerFlag, mineralFlag, and metamorphicFlag are all set to true at various point during the parsing called by the parseSamples function. The currentStringValue objects are added to the appropriate arrays. The <x> and <y> flags correspond to the longitude and latitude values, respectively of the the sample. The <long> flag contains the id of the sample. When the </sample> tag is encountered, all of the information pertaining to a particular sample has been stored and that sample now needs to be added to the mySamples array, which is a collection of uniqueSamples. First, an AnnotationObject? is created, which will be the map representation of each sample. The simple attributes of this object are set such as the title, sample id, rock types, minerals, etc. After the individual sample annotation has been created, the sample now needs to be added to a uniqueSamples object. If there is only one sample occurring at a particular coordinate, then the uniqueSamples object representing that sample will only have a single AnnotationObject? in the samples array attribute of uniqueSamples. If however, there are many samples (represented by AnnotationObjects?) at the same geographic coordinate, then all of those samples will be represented by the same instance of uniqueSamples. They will all be placed in the samples array attribute, and the title attribute will be set to indicate the number of samples. In order to assign a newly created AnnotationObjects? to a uniqueSamples object, we have to compare the latitude and longitude of the new sample with the coordinates of all the uniqueSamples objects already contained in the mySamples array. If a match is found, we add the new AnnotationObjects? instance to the samples array of the uniqueSamples object that is located at the same coordinate as the sample being added. The count is incremented and the title is changed to reflect the additional sample. If a uniqueSamples object represents multiple samples, its id is set to be “multiple samples,” which becomes important when that sample is displayed on the map. If there is only one AnnotationObject? in the samples array of the uniqueSamples object, the id of the uniqueSamples is set to be the id of the that sample. If the parseSearchCriteria function was called, the minimum and maximum latitudes and longitudes of all the samples returned are contained within the tags <minLat>, <minLong>, <maxLat>, <maxLong>. The corresponding values of the CriteriaSummary? are set. When the </minerals>, </metamorphicGrades>, etc tags are encountered, we want to set their booleans to false since the currentStringValue will no longer correspond to one of their elements.

CriteriaSummary?.h

This file defines the class criteriaSummary, which is basically just a struct that contains objects representing all the attributes of all the samples returned from a search. Instances of CriteriaSummary? are used to narrow a search even if all samples have not yet been loaded into memory (pagination.) rockTypes represents all the rock types returned from the search, minerals represents the minerals, metamorphicGrades represents the metamorphic grades, and owners represents the owners. totalCount is the total number of samples returned by the search. This is NOT necessarily the number of annotations displayed on the map, since only 100 are displayed at a time initially. totalCount represents the maximum number of samples that could be displayed on the map at a time. maxLat represents the greatest latitude of any sample returned from the search, minLat the smallest, with the same logic for maxLong and minLong. These variables are needed because if the query for the samples is sent in a separate thread, the span of the map needs to be set before the samples have actually been loaded.

CurrentSearchData?.h

This class is basically just a struct that contains all the search criteria a user has provided to narrow down the search. All of the member variables therefore have no value when the app starts because the user has not yet provided any search criteria. Since the first narrowing of the search is always done with geographic criteria, these member variables will be the first to be given values. If the user selects a region, the region variable will be updated accordingly. If the user chooses to either user their current location or provide a search coordinate, the four coordinates of the search box will be calculated and stored in the originalCoordinates array. If the user chooses to search using their current location, the locationVisible boolean will be set to true. When this boolean is true, the user will be able to view their current location as a red pin on the map. After the user navigates to the map, they can press the info button in the lower right corner of the toolbar to change the map type to hybrid, satellite, or map. If this value is changed, the mapType member variable is updated. When the user navigates around the map, the zoom level must be preserved so that if the user goes to a different view and then goes back to the map it will be at the same zoom level. To indicate that a user has changed the zoom level the zoomed boolean is set to true.

From the map view the user can also press the “Refine Search” button to refine the search by more than just the geographic area. If the user chooses to add rock types, minerals, metamorphic grades, or owners to the search, the values will be stored in the respective arrays as part of the currentSearchData object. If the user is logged in, they can also choose to narrow their search to only public samples, only private samples, or both types of samples. When this is changed, the currentPublicStatus member variable is updated. The default for a user that is not logged in is “public” and the default for a user that is logged in is “both.”

PostRequest?.m

This class is passed information any time data is needed from the server. This view builds a string in the proper format to be sent to the server. The format of data being passed to the server is very important. All information must be passed in the format “parameter= value\n”. There must be no space between the parameter and the equal sign and one space between the equal sign and the value. A newline character must be placed at the end of the value. If multiple parameters are being specified in the same request (this is almost always the case) the format is as follows: “parameter= value\nparameter=value”. There should be NO SPACE between the newline character and the next parameter.

buildPostString: This function builds the post string. It first checks the iphone keychain to see if the user is logged in. If so, the username will be included in the post string so the user's private samples can be included in the search. Any information that has been passed to this view is then included in the post string. This can include a region, coordinates, a sample owner, rock types, etc. The criteriaSummary variable is true if the server should return all the information about all the samples and false if it should only return a summary of the attributes of the samples. The pagination variable indicates how many samples are currently being displayed. This number is 0 at the beginning of a search and it increases as the user presses the button to view the next set of samples. There is a variable on the server side that dictates the maximum number of samples returned in a search. The complete post string is returned from this function to whichever view called this function.

setData: This function is called from whichever view is creating the post request. All of the data that should be included in the post request is passed to this view in this function.

KeychainWrapper?.m

This class only has functions that operate on the iphone keychain. This application interacts with the keychain as follows: When the user signs in on the login page, their username and password as passed to the server in a secure connection. If the user is successfully authenticated, their username is stored in the keychain. If the user chooses to sign out, their username is deleted from the keychain. Within the keychain, data is stored in a name/value dictionary format, so the username is stored in a pair with name “Username” and a value of whatever the username is. In other views, the application checks to see if a username is being stored in the keychain. If one is, this indicates that the user has signed into the application, as there is no other way that the username would be stored there. If the user has signed in, their username is passed to the application in many instances to get private samples, images of private samples, comments of private samples, etc.

Functions of the KeychainWrapper? class: searchKeychainCopyMatching: Data is stored in the keychain in a dictionary of name-value pairs, and the MetPetDB username is stored in the keychain with “Username” as the name and the value will be that of whichever user is using the application. In many of the views this function is called to see whether or not the user has logged in. The string “Username” is passed as the argument to this function and the return value will be an NSData object representing the username of the logged in user. In the view that is calling this function, the NSData object can be converted to a string and that string will then be passed to the server to get the user's private data.

newSearchDictionary: This function is only called by the loginViewController after a user has been authenticated successfully. It creates a new dictionary that will have “Username” as the identifier.

deleteKeychainValue: This function is called by the logout function of the mainViewController. It deletes the value of any username being stored in the “Username” search dictionary.

createKeychainValue: This function is called by the loginViewController after a user has been authenticated successfully. It assigns the value of the username that the user provided with the “Username” search dictionary.

MetPetAppDelegate?.h

This class controls the main window of the application, and there is only one instance of this. There is also an instance of the MainViewController?, which is the first view displayed after the application launches. The one instance of the navigation controller is also in the metpetdbappdelegate.

MetPetDBAppDelegate.m

applicationDidFinishLaunching: This function is called when the application has been launched. An instance of MainView?.xib controlled by MainViewController? is displayed, and the root instance of the navigationController is set.

SetNetworkActivityIndicatorVisible:(Bool)setVisible: This function can be called to display or hide the network activity indicator in the bar status bar. The function is called throughout the application when the application may run slowly due to network activity.

Attachments