mastering xamarin ui developmentsd.blackball.lv/library/mastering_xamarin_ui_development... ·...

Post on 20-May-2020

9 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

MasteringXamarinUIDevelopment

TableofContents

MasteringXamarinUIDevelopmentCreditsAbouttheAuthorAcknowledgmentsAbouttheReviewerswww.PacktPub.com

Whysubscribe?CustomerFeedbackDedicationPreface

WhatthisbookcoversWhatyouneedforthisbookWhothisbookisforConventionsReaderfeedbackCustomersupport

DownloadingtheexamplecodeErrataPiracyQuestions

1.CreatingtheTrackMyWalksNativeAppCreatingtheTrackMyWalkssolution

UpdatingtheTrackMyWalkssolutionpackagesCreatingtheTrackMyWalksmodelCreatingthewalksmainpageCreatingthenewwalkentrycontentpageCreatingthewalktrailcontentpage

AddingtheXamarin.Forms.MapsNuGetpackageCreatingtheDistanceTravelledPagecontentpageCreatingtheSplashscreencontentpage

UpdatingtheXamarin.FormsAppclassDifferencesbetweenXamarinStudioandVisualStudioRunningtheTrackMyWalksappusingthesimulatorSummary

2.MVVMandDataBindingUnderstandingtheMVVMpatternarchitectureImplementingtheMVVMViewModelswithinyourappCreatingtheWalkBaseViewModelfortheTrackMyWalksappImplementingtheWalksPageViewModel

UpdatingthewalksmainpagetousetheMVVMmodelImplementingthewalksentrypageViewModel

UpdatingtheWalksEntryPagetousetheMVVMmodelImplementingthewalktrailpageViewModel

UpdatingtheWalksTrailPagetousetheMVVMmodelImplementingtheDistanceTravelledViewModel

UpdatingtheDistanceTravelledPagetousetheMVVMmodelSummary

3.NavigatingwithintheMVVMModel-TheXamarin.FormsWayUnderstandingtheXamarin.FormsNavigationAPIDifferencesbetweenthenavigationandViewModelapproachesImplementingthenavigationservicewithinyourapp

CreatingthenavigationserviceinterfacefortheTrackMyWalksappCreatinganavigationservicetonavigatewithinourViewModelsUpdatingtheWalkBaseViewModeltouseournavigationserviceUpdatingthewalksmainpageViewModelandnavigationserviceUpdatingthewalksmainpagetousetheupdatedViewModelUpdatingthewalksentrypageViewModelandnavigationserviceUpdatingtheWalksEntryPagetousetheupdatedViewModelUpdatingthewalkstrailpageViewModelandnavigationserviceUpdatingtheWalksTrailPagetousetheupdatedViewModelUpdatingthedistancetravelledViewModelandnavigationserviceUpdatingtheDistanceTravelledPagetousetheupdatedViewModelUpdatingtheXamarin.Forms.Appclasstousethenavigationservice

Summary4.AddingLocation-BasedFeatureswithinYourApp

Creatingandusingplatform-specificservicesCreatingtheLocationServiceInterfacefortheTrackMyWalksappCreatingtheLocationServiceclassfortheAndroidplatformCreatingtheLocationServiceclassfortheiOSplatformEnablingbackgroundupdatesandgettingtheuser'scurrentlocationUpdatingtheWalkEntryViewModeltousethelocationserviceUpdatingtheDistanceTravelledViewModeltousethelocationserviceUpdatingtheSplashPagetoregisterourViewModelsUpdatingtheMainActivityclasstouseXamarin.Forms.MapsUpdatingtheXamarin.FormsAppclasstouseplatformspecifics

Summary5.CustomizingtheUserInterface

CreatingtheDataTemplateclassfortheTrackMyWalksappUpdatingthewalksmainpagetousethedatatemplate

CreatingaTableViewEntryCellcustompickerfortheiOSplatformCreatingthecustompickerrendererclassfortheiOSplatform

UpdatingtheWalksEntryPagetousethecustompickerrendererCreatingPlatformEffectsusingtheEffectsAPIfortheiOSplatformCreatingPlatformEffectsusingtheEffectsAPIfortheAndroidplatformImplementingvalueconverterswithintheTrackMyWalksapp

UpdatingtheWalkBaseViewModeltouseourBooleanconverterUpdatingtheWalksPageViewModeltouseourBooleanconverterUpdatingthewalksmainpagetousetheupdatedViewModelUpdatingtheWalksTrailPagetousetheupdatedViewModelUpdatingtheDistanceTravelledPagetousetheupdatedViewModelUpdatingtheWalkCellDataTemplateclasstousePlatformEffects

Summary6.WorkingwithRazorTemplates

UnderstandingtheRazortemplateengineCreatingandimplementingRazortemplateswithinXamarinStudio

AddingtheSQLite.NetpackagetotheBookLibrarysolutionCreatingandimplementingthebooklibrarydatabasemodelCreatingandimplementingthebookdatabasewrapperCreatingandimplementingtheBookLibrarydatabasewrapperCreatingandimplementingthebooklistingmainpageCreatingandimplementingtheBookLibraryAddRazortemplateCreatingandimplementingtheBookLibraryEditRazortemplateCreatingandimplementingtheWebViewControllerclass

UpdatingthebooklibraryCascadingStyleSheet(CSS)Summary

7.IncorporatingAPIDataAccessUsingMicrosoftAzureAppServicesSettingupourTrackMyWalksappusingMicrosoftAzure

AddingtheJson.NetNuGetpackagetotheTrackMyWalksappAddingtheHttpClientNuGetpackagetotheTrackMyWalksappUpdatingtheWalkEntriesmodeltousetheJson.Netframework

CreatingtheHTTPwebserviceclassfortheTrackMyWalksappCreatingtheDataServiceAPIfortheTrackMyWalksappCreatingtheDataServiceAPIclassfortheTrackMyWalksapp

UpdatingtheWalkBaseViewModeltouseourDataServiceAPIUpdatingtheWalkEntryViewModeltouseourDataServiceAPIUpdatingtheWalksPageViewModeltouseourDataServiceAPIUpdatingtheWalksPagetousetheupdatedViewModelUpdatingthecustompickerrendererclassfortheiOSplatformUpdatingtheWalksEntryPagetousetheupdatedcustompicker

Summary8.MakingOurAppSocial-UsingtheFacebookAPI

SettingupandregisteringtheTrackMyWalksappwithFacebookAddingtheXamarin.AuthNuGetpackagetotheTrackMyWalksappAddingtheFaceBookSDKlibrarytotheTrackMyWalksapp

CreatingaFacebookusermodelfortheTrackMyWalksappCreatingaFacebookCredentialsclassfortheTrackMyWalksappCreatingtheFacebookSignIntousewithinourTrackMyWalksappCreatingtheFacebookSignInClassforTrackMyWalks(iOS)app

UpdatingtheNavigationServiceInterfacefortheTrackMyWalksapp

UpdatingtheNavigationServiceclassfortheTrackMyWalksappUpdatingtheWalksPagetoproperlyhandleFacebookSignIn

UpdatingtheWalksPageViewModeltouseourFaceBookApiUserUpdatingtheDistanceTravelledPagefortheTrackMyWalksappUpdatingtheXamarin.FormsAppclasstohandleFacebookSignInEnablingFacebookfunctionalitywithintheTrackMyWalksapp

Summary9.UnitTestingYourXamarin.FormsAppsUsingtheNUnitandUITestFrameworks

CreatingaunittestsolutionfolderusingXamarinStudioCreatingaunittestprojectusingXamarinStudio

AddingtheMoqNuGetpackagetotheunittestprojectAddingtheTrackMyWalksprojecttoTrackMyWalks.UnitTestsCreatingandimplementingtheWalksTrailViewModelNUnittestclassCreatingandimplementingtheWalkEntryViewModelNUnittestclassRunningtheTrackMyWalks.UnitTestsusingXamarinStudio

CreatingaUItestprojectusingXamarinStudioUnderstandingthecommonlyusedUITestmethods

SettingupandinitializingourTrackMyWalksappforUITestImplementingtheCreateNewWalkEntryusingtheUITest.Framework

AddingtheXamarinTestCloudAgenttotheiOSprojectUpdatingtheTrackMyWalksAppDelegateclasstohandleXamarinTestCloudAgent

RunningtheTrackMyWalksUITestsusingXamarinStudioSummary

10.PackagingandDeployingYourXamarin.FormsApplicationsCreatingandsettingupyouriOSdevelopmentteamCreatingtheTrackMyWalksiOSdevelopmentcertificate

ObtainingtheiOSdevelopmentcertificatefromAppleCreatingtheAppIDfortheTrackMyWalks(iOS)application

CreatingtheTrackMyWalksdevelopmentprovisioningprofilePreparingtheTrackMyWalks(iOS)appforsubmission

SubmittingtheTrackMyWalks(iOS)apptoiTunesConnectusingXamarinStudioSummary

MasteringXamarinUIDevelopment

MasteringXamarinUIDevelopmentCopyright©2017PacktPublishing

Allrightsreserved.Nopartofthisbookmaybereproduced,storedinaretrievalsystem,ortransmittedinanyformorbyanymeans,withoutthepriorwrittenpermissionofthepublisher,exceptinthecaseofbriefquotationsembeddedincriticalarticlesorreviews.

Everyefforthasbeenmadeinthepreparationofthisbooktoensuretheaccuracyoftheinformationpresented.However,theinformationcontainedinthisbookissoldwithoutwarranty,eitherexpressorimplied.Neithertheauthor,norPacktPublishing,anditsdealersanddistributorswillbeheldliableforanydamagescausedorallegedtobecauseddirectlyorindirectlybythisbook.

PacktPublishinghasendeavoredtoprovidetrademarkinformationaboutallofthecompaniesandproductsmentionedinthisbookbytheappropriateuseofcapitals.However,PacktPublishingcannotguaranteetheaccuracyofthisinformation.

Firstpublished:January2017

Productionreference:1130117

PublishedbyPacktPublishingLtd.

LiveryPlace

35LiveryStreet

Birmingham

B32PB,UK.

ISBN978-1-78646-200-8

www.packtpub.com

Credits

Author

StevenF.Daniel

CopyEditor

SafisEditing

Reviewers

LanceMcCarthy

EnginPolat

ProjectCoordinator

IzzatContractor

CommissioningEditor

AmarabhaBanerjee

Proofreader

SafisEditing

AcquisitionEditor

ShwetaPant

Indexer

TejalDaruwaleSoni

ContentDevelopmentEditor

PriyankaMehta

Graphics

AbhinashSahu

TechnicalEditor

AbhishekSharma

ProductionCoordinator

DeepikaNaik

AbouttheAuthorStevenF.DanielistheCEOandfounderofGENIESOFTSTUDIOS,asoftwaredevelopmentcompanybasedinMelbourne,Victoria,thatfocusesprimarilyondevelopinggamesandbusinessapplicationsfortheiOS,Android,andMacOSXplatforms.Heisanexperiencedsoftwaredeveloperwithmorethan17yearsofexperienceindevelopingdesktopandweb-basedapplicationsforseveralcompaniesandstartups.

StevenisextremelypassionateaboutmakingpeopleemployablebyhelpingthembridgethegapbetweenusingtheirexistingskillsiniOS,Android,andXamarintogetthejobdone.Toachievethis,hewritesbookstohelpnoviceandadvancedprogrammerssucceedwithintheindustry.Stevenisextremelypassionateabout,andlovesbeingattheforefrontof,technology.HeisamemberoftheSQLServerSpecialInterestGroup(SQLSIG),MelbourneCocoaHeads,andtheJavaCommunity.HewasthecofounderandChiefTechnologyOfficer(CTO)atSoftMpirePtyLtd.,acompanythatisfocusedprimarilyondevelopingbusinessapplicationsfortheiOSandAndroidplatforms.

Stevenistheauthorofvariousbooktitles,someofwhichareasfollows:

AppleWatchAppDevelopmentAndroidWearableProgrammingXcode4CookbookiPadEnterpriseApplicationDevelopmentBlueprintsiOS5EssentialsXcode4iOSDevelopmentBeginner’sGuide

Checkouthisblogathttp://www.geniesoftstudios.com/blog/,orfollowhimontwitterathttp://twitter.com/GenieSoftStudio.

AcknowledgmentsNobookistheproductofjusttheauthor;hejusthappenstobetheonewithhisnameonthecover.Severalpeoplecontributedtothesuccessofthisbook,anditwouldtakemorespacethanthankingeachoneindividually.

Iwouldpersonallyliketothankthreespecialpeoplewhohavebeenaninspirationandwhohaveprovidedmewithsomuchsupportduringthewritingofthisbook,ReshmaRaman,mySeniorAcquisitionEditor,whoisthereasonthatthisbookexists;ShwetaPant,myAcquisitionEditor;andPriyankaMehtaforherunderstandingandsupport,aswellasherbrilliantsuggestiveapproachesduringthechapterrewrites.Iwouldliketothankeachofyouforeverything,andmakingthewritingprocessenjoyable.

Lastly,tomyreviewers,thankyousomuchforyourvaluedsuggestionsandimprovementstomakethisbookwhatitis;Iamtrulygratefultoeachoneofyou.

ThankyoualsototheentirePACKTPublishingteamforworkingsodiligentlytohelpbringoutahigh-qualityproduct.Finally,abigshoutouttotheengineersatXamarin,Inc.forcreatingXamarinStudioandtheMonoPlatformtoprovidedeveloperswiththetoolstocreatefunandsophisticatedapplicationswiththepowerofXamarin.Forms.

Finally,Iwouldliketothankallmyfriendsfortheirsupport,understanding,andencouragementduringthebookwritingprocess.Iamextremelygratefultohaveyouasmyfriends,anditisaprivilegetoknoweachoneofyou.

AbouttheReviewersLanceMcCarthyisanexceptionalcommunityleaderwithanacuteexpertiseforallthings.NETandC#,especiallyontheXAMLstack,includingWPF,Silverlight,WindowsPhone,andWindowsstoreapps.Heisveryhelpfulonline,guidingandansweringquestionsfromMicrosoftdevelopersonTwitteras@lancewmccarthy;heblogsonhisowntimeaswell,withastrongfocusonWindowsUniversalapps,atWinPlatform.wordpress.com.HeorganizesandhostseventsintheBostonarea,suchasusergroupnights,mini-codecamps,andfullhackathons.

Duringtheday,LanceisaseniortechnicalsupportengineeratTelerik,wherehesupportsdeveloperswiththeirClassicWindows,UniversalWindows,webandmobile(Xamarin,AndroidandiOSnative)applicationdevelopment.

Ontheside,Lancewritesblogpostsforblogs.windows.com/buildingapps/,createsresourcesfordevelopers(tutorials,samplesourcecode,tipsoftheweek,andsoon),andhelpsthedevelopercommunityinanywaypossible.

Previously,LanceworkedforNokiaandMicrosoftasaDeveloperAmbassador,wherehesoughtoutandengageddevelopersthroughoutreachprogramsandprovidesthemwithtechnicalsupportandresourcestomakethemsuccessfulontheWindowsplatforms.

LancewasalsoanassistantprofessoratHarvardUniversity,helpingstudentsbuild,market,andpublishsuccessfulWindowsPhoneapps.Hehasalsoappearedonpodcasts,suchastheWindowsDeveloperShow,hasbeenatechnicaleditorforpublicationsandbooks,haswonseveralappbuildingcontestsandhackathons(includingfirstplaceintheMicrosoftBuild2013hackathon),andisapublisheddeveloperwithoveramilliondownloadsintheWindowsStore.

Someofthebooksthathehasreviewedareasfollows:

NetdunioHomeAutomationProjectsbyMattCavanaughMasteringCross-PlatformDevelopmentwithXamarinbyCanBilginBegintoCodewithC#byRobMiles

EnginPolathasbeeninvolvedinmanylargeandmedium-scaleprojectson.NETtechnologiesasadeveloper,architect,andconsulting,andhehaswonmanyawardssince1999.Since2008,hehasbeentrainingmanylargeenterprisesinTurkeyonWindowsdevelopment,webdevelopment,distributedapplicationdevelopment,softwarearchitecture,mobiledevelopment,clouddevelopment,andsoon.Apartfromthis,heorganizesseminarsandeventsinmanyuniversitiesinTurkeyabout.NETtechnologies,Windowsplatformdevelopment,clouddevelopment,webdevelopment,gamedevelopment,andsoon.Heshareshisexperiencesonhispersonalblog(http://www.enginpolat.com).HehasMCP,MCAD,MCSD,MCDBA,andMCTcertifications.Since2012,heisrecognizedasaWindowsDevelopmentMVPbyMicrosoft;since2017,heisrecognizedasaVisualStudioandDevelopmentTechnologiesMVPtoo.Between2013and2015,

hewasrecognizedasaNokiaDeveloperChampion-veryfewpeopleintheworldaregiventhisaward.Since2015,hehasbeenrecognizedasaRegionalDirectorbyMicrosoft.

HehasreviewedafewbooksforPackt,someofwhichareasfollows:

MasteringCross-PlatformDevelopmentwithXamarinXamarinBlueprintsXamarin4byExample

I'dliketothankmydearwife,Yeliz,andmybeautifuldaughter,MelisAda,forallthesupporttheygavemewhileIwasworkingonthisbookproject.

Ialsowanttoextendawarmwelcometothenewestmemberofmyfamily,mydearson,UtkuEge.

www.PacktPub.comForsupportfilesanddownloadsrelatedtoyourbook,pleasevisitwww.PacktPub.com.

DidyouknowthatPacktofferseBookversionsofeverybookpublished,withPDFandePubfilesavailable?YoucanupgradetotheeBookversionatwww.PacktPub.comandasaprintbookcustomer,youareentitledtoadiscountontheeBookcopy.Getintouchwithusatservice@packtpub.comformoredetails.

Atwww.PacktPub.com,youcanalsoreadacollectionoffreetechnicalarticles,signupforarangeoffreenewslettersandreceiveexclusivediscountsandoffersonPacktbooksandeBooks.

https://www.packtpub.com/mapt

Getthemostin-demandsoftwareskillswithMapt.MaptgivesyoufullaccesstoallPacktbooksandvideocourses,aswellasindustry-leadingtoolstohelpyouplanyourpersonaldevelopmentandadvanceyourcareer.

Whysubscribe?FullysearchableacrosseverybookpublishedbyPacktCopyandpaste,print,andbookmarkcontentOndemandandaccessibleviaawebbrowser

CustomerFeedbackThankyouforpurchasingthisPacktbook.Wetakeourcommitmenttoimprovingourcontentandproductstomeetyourneedsseriously-that'swhyyourfeedbackissovaluable.Whateveryourfeelingsaboutyourpurchase,pleaseconsiderleavingareviewonthisbook'sAmazonpage.Notonlywillthishelpus,moreimportantlyitwillalsohelpothersinthecommunitytomakeaninformeddecisionabouttheresourcesthattheyinvestintolearn.Youcanalsoreviewforusonaregularbasisbyjoiningourreviewers'club.Ifyou'reinterestedinjoining,orwouldliketolearnmoreaboutthebenefitsweoffer,pleasecontactus:customerreviews@packtpub.com.

DedicationTomyfavoriteuncle,BenjaminJacobDaniel,thankyouforalwaysmakingmesmileandforinspiringmetoworkhardandachievemydreams;youareatrueinspirationandIcouldn’thavedonethiswithoutyourlove,support,andguidance.Thankyou.

Asalways,toChanBanGuan,forthecontinuedpatience,encouragement,andsupport,andmostofallforbelievinginmeduringthewritingofthisbook.Iwouldliketothankmyfamilyfortheircontinuedloveandsupport,andforalwaysbelievinginmethroughoutthewritingofthisbook.

ThisbookwouldnothavebeenpossiblewithoutyourloveandunderstandingandIwouldliketothankyoufromthebottomofmyheart.

PrefaceXamarinisthemostpowerfulcross-platformmobiledevelopmentframework.IfyouareinterestedincreatingstunninguserinterfacesfortheiOSandAndroidmobileplatformsusingthepowerofXamarinandXamarin.Forms,thenthisisyourticket.

Thisbookwillprovideyouwiththepracticalskillsrequiredtodevelopreal-worldXamarinapplications.Youwilllearnhowtoimplementuserinterfacestructuresandlayouts,createcustomizedelements,andwriteC#scriptstocustomizelayouts.You’lllearnhowtocreateUserInterfacelayoutsfromscratchandcustomizetheselayoutstosuityourneedsbyusingDataTemplatesandCustomRenderers.

You’llbeintroducedtothearchitecturebehindtheModel-View-ViewModel(MVVM)pattern,andhowtoimplementthiswithinyourapplicationsothatyoucannavigatebetweeneachofyourViewModelsandContentPages.

Wewillthenmoveontodiscussmoreadvancedtopics,suchashowtoincorporateplatform-specificfeatureswithinyourappsthataredependentonthemobileplatformbeingrun,andyouwilllearnhowtoproperlyperformlocationupdates,whethertheapplication'sstateisintheforegroundorbackground,byregisteringtheappasabackground-necessaryapplication.

Wediscussmoreadvancedtopics,suchasworkingwithMicrosoftAzureAppservicestocreateyourveryfirstcloud-basedbackendHTTPwebservicetohandlecommunicationbetweenthecloudandtheapp,bycreatingaDataServiceAPIthatwillallowourapptoconsumetheAPIsothatitcanretrieve,store,anddeletewalktrailinformationfromthecloud.

WewillalsocoverhowyoucanworkwiththeFacebookSDKtoincorporatesocialnetworkingfeaturestoobtaininformationaboutaFacebookuser,aswellaspostinformationtotheirFacebookwallandusetheOpenGraphAPItoretrievecertaininformationabouttheuser.

Movingon,youwilllearnhowtousethird-partylibraries,suchastheRazortemplateengine,whichallowsyoutocreateyourownHTML5templates,withintheXamarinStudioenvironmenttobuildabooklibraryHybridsolutionthatusestheSQLite.Netlibrarytostore,update,retrieve,anddeleteinformationwithinaSQLitelocaldatabase.You’llalsoimplementkeydatabindingtechniquesthatwillmakeyouruserinterfacesdynamicandcreatepersonalizedanimationsandvisualeffectswithinyouruserinterfacesusingcustomrenderersandthePlatformEffectsAPItocustomizeandchangetheappearanceofcontrolelements.

Attheendofthisbook,youwilllearnhowtocreateandrununittestsusingtheNUnitandUITesttestingframeworksrightwithintheXamarinStudioIDE.You'lllearnhowtowriteunittestsforyourViewModelsthatwillessentiallytestthebusinesslogictovalidatethateverythingisworkingcorrectly,beforemovingontotesttheuserinterfaceportionusingautomatedUItesting.

Inthisbook,Ihavetriedmybesttokeepthecodesimpleandeasytounderstandbyprovidinga

step-by-stepapproach,withlotsofscreenshotsateachsteptomakeiteasiertofollow.YouwillsoonmasterthedifferentaspectsofXamarin.Forms,andthetechnologyandskillsneededtocreateyourownapplicationsfortheXamarin.Formsplatform.

Feelfreetocontactmeatsupport@geniesoftstudios.comwithanyqueries,orjustdropmeane-mailtosayafriendly"Hello".

WhatthisbookcoversChapter1,CreatingtheTrackMyWalksNativeApp,focusesonhowtosetupabasiccross-platformnativeappstructureusingXamarin.Formsbeforeproceedingwithaddingnew,andupdatingexisting,packageswithinyoursolution.

You’lllearnhowtocreateC#classesthatwillactasthemodelforourapp,andwillcreatecontentpagesthatwillformtheuserinterface.WewillalsocoverthedifferencesbetweendevelopingappsusingXamarinStudioandMicrosoftVisualStudio.

Chapter2,MVVMandDataBinding,introducesyoutothearchitecturebehindtheMVVMpattern,andhowyoucanimplementthiswithinyourapplicationbyaddingnewViewsandtheassociatedModels.

You’lllearnhowtocreatetheunderlyingC#classfilesthatwillactastheViewModelsforyourapp,andupdateexistingcontentpagestodata-bindwiththeViewModelstorepresenttheinformationthatwillbedisplayedwithintheuserinterfaceforourapplication.

Chapter3,NavigatingwithintheMVVMModel-TheXamarin.FormsWay,buildsuponyourworkingknowledgeoftheMVVMdesignpatternarchitecturetoshowyouhowyoucannavigatethroughtheViewModelsbycreatingaC#classthatactsasthenavigationserviceforourapp,andupdatesourexistingWalkBaseViewModelclasstoincludeadditionalabstractclassmethodseachofourViewModelswillinherit;inturn,you'llupdatecontentpagestobindwiththeViewModelstoallownavigationbetweentheseViewstohappen.

Chapter4,AddingLocation-BasedFeatureswithinYourApp,focusesonhowyoucanincorporateplatform-specificfeatureswithintheTrackMyWalksapp,whichisdependentonthemobileplatform,bycreatingalocationserviceC#classthatwillincludeseveralclassmethodsforboththeiOSandAndroidplatforms.

You’lllearnhowtoproperlyperformlocationupdateswhethertheapplication'sstateisintheforegroundorbackgroundbyregisteringtheappasabackground-necessaryapplication.

Chapter5,CustomizingtheUserInterface,showsyouhowyoucanworkwithDataTemplatestolayoutyourviewsneatlywithinyourapplicationsuserinterfacebycreatingaC#class.You’llgetaccustomedtoworkingwithplatform-specificAPIstoextendthedefaultbehaviorofXamarin.Formscontrolsusingcustomrendererstocreateacustompicker,beforemovingontolearnhowtousetheXamarin.FormsEffectsAPItocustomizetheappearanceandstylingofnativecontrolelementsforeachplatformbyimplementingaCustomRendererclass.

Finally,youwilllearnhowtomanipulatethevisualappearanceofdataboundusingvalueandimageconverters.

Chapter6,WorkingwithRazorTemplates,introducesyoutotheRazorHTMLtemplatingengine,

andhowyoucanuseittocreateahybridmobilesolution.You'lllearnhowtobuildabooklibrarymobilesolutionusingthepowerofRazortemplates,howtocreateandusemodelswithinyourapplication,andhowtoconnectthisuptoaSQLitedatabasetostore,retrieve,update,anddeletebookdetails.

Chapter7,IncorporatingAPIDataAccessusingMicrosoftAzureAppServices,showsyouhowyoucanuseMicrosoftAzureAppservicestocreateyourveryfirstlive,cloud-basedbackendHTTPwebservicetohandleallthecommunicationbetweenthecloudandtheapp.You’lllearnhowtocreateaDataServiceAPIthatwillallowtheapptoconsumetheAPIsothatitcanretrieve,store,anddeletewalktrailinformationfromthecloud,allfromwithintheTrackMyWalksapp.

Chapter8,MakingourAppSocial–UsingtheFacebookAPI,showsyouhowyoucanusebothXamarin.AuthandtheFacebookSDKtoincorporatesocialnetworkingfeatureswithintheTrackMyWalksapptoobtaininformationaboutaFacebookuser,aswellaspostinformationtotheirFacebookwall.

You’lllearnhowtocreateasign-inpagethatallowsuserstologintoyourappusingtheirFacebookcredentials,andhowtocreateaFacebookApiUserclassthatwillbeusedtostoreinformationaboutthelogged-inuser,andusetheOpenGraphAPItoretrievecertaininformationabouttheuser.

Finally,youwillseehowyoucanleveragetheFacebooklibrarytopostwalkdatatoyourFacebookprofilepage,soyoucanshowoffyourWalkTrailprogresstoyourfriendsand/orworkcolleagues.

Chapter9,UnitTestingyourXamarin.FormsAppusingtheNUnitandUITestFrameworks,focusesonshowingyouhowtocreateandrununittestsusingtheNUnitandUITesttestingframeworksrightwithintheXamarinStudioIDE.You'lllearnhowtowriteunittestsforourViewModelsthatwillessentiallytestthebusinesslogictovalidatethateverythingisworkingcorrectlybeforetestingtheuserinterface'sportionusingautomatedUItesting.

Chapter10,PackagingandDeployingyourXamarin.FormsApplications,focusesonhowtosubmityourTrackMyWalksiOSapptotheAppleAppStore,andshareyourcreationswiththerestofthecommunity.You'lllearnthestepsrequiredtosetupyouriOSdevelopmentteam,aswellascertificatesforbothdevelopmentanddistribution,andyouwilllearnhowtocreatethenecessaryprovisioningprofilesforbothyourdevelopmentanddistributionbuildsandcreatethenecessaryappIDsforyourapplication.

Finally,youwilllearnhowtoregisteryouriOSdevicessothatyouruserscandownloadandtestyourappsontheiriOSdevicesandlearnhowtoprepareyourTrackMyWalksiOSappforsubmissiontoiTunesConnectusingtheXamarinStudioIDE.

WhatyouneedforthisbookTheminimumrequirementforthisbookisanIntel-basedMacintoshcomputerrunningOSXElCapitan10.11.WewillbeusingXamarinStudio6.1.2,whichistheIntegratedDevelopmentEnvironment(IDE)usedforcreatingXamarin.FormsapplicationsusingC#,aswellasXcode8.2.1tocompileouriOSappandrunthiswithinthesimulator.

AlmostalltheprojectsthatyoucreatewiththehelpofthisbookwillworkandrunontheiOSsimulator.However,someprojectswillrequireaniOSorAndroiddevicetoworkcorrectly.YoucandownloadthelatestversionsofXamarinStudioandXcodeat:

XamarinStudio:http://xamarin.com/download

Xcode:https://itunes.apple.com/au/app/xcode/id497799835?mt=12

WhothisbookisforThisbookisintendedfordeveloperswhohaveaworkingexperienceofapplicationdevelopmentprinciples,aswellasabasicknowledgeofXamarinandC#codingandwishtoexpandtheirknowledgeanddevelopapplicationsusingXamarin.Forms.ItisassumedthatyouarefamiliarwithObject-OrientedProgramming(OOP),andhavesomeexperiencedevelopingC#applicationsusingXamarinStudio.

ConventionsInthisbook,youwillfindanumberoftextstylesthatdistinguishbetweendifferentkindsofinformation.Herearesomeexamplesofthesestylesandanexplanationoftheirmeaning.

Codewordsintext,databasetablenames,foldernames,filenames,fileextensions,pathnames,dummyURLs,userinput,andTwitterhandlesareshownasfollows:"OnethingyouwillnoticeisthatoursolutioncontainsafilecalledTrackMyWalks.cswhichispartoftheTrackMyWalksPortableClassLibrary."

Ablockofcodeissetasfollows:

//

//WalkEntryViewModel.cs

//TrackMyWalksViewModels

//

//CreatedbyStevenF.Danielon22/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//usingTrackMyWalks.Models;

usingTrackMyWalks.ViewModels;

usingXamarin.Forms;

namespaceTrackMyWalks

{

publicclassWalkEntryViewModel:WalkBaseViewModel

{

Whenwewishtodrawyourattentiontoaparticularpartofacodeblock,therelevantlinesoritemsaresetinbold:

//

//WalksPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem.Collections.Generic;

usingXamarin.Forms;

usingTrackMyWalks.Models;

usingTrackMyWalks.ViewModels;

namespaceTrackMyWalks

{

publicclassWalksPage:ContentPage

{

publicWalksPage()

{

Anycommand-lineinputoroutputiswrittenasfollows:

Lastlogin:SunNov610:48:41onconsole

GENIESOFT-MAC-Mini:~stevendaniel$curl

https://trackmywalks.azurewebsites.net/tables/walkentries

--header"ZUMO-API-VERSION:2.0.0"

Newtermsandimportantwordsareshowninbold.Wordsthatyouseeonthescreen,forexample,inmenusordialogboxes,appearinthetextlikethis:"ifyouclickontheProceedWith...button,itwillnavigatetothewalksTrailDetailspagewhereyoucanbeginyourtrail,byclickingontheBeginthisTrailbutton."

Note

Warningsorimportantnotesappearinaboxlikethis.

Tip

Tipsandtricksappearlikethis.

ReaderfeedbackFeedbackfromourreadersisalwayswelcome.Letusknowwhatyouthinkaboutthisbook-whatyoulikedordisliked.Readerfeedbackisimportantforusasithelpsusdeveloptitlesthatyouwillreallygetthemostoutof.Tosendusgeneralfeedback,simplye-mailfeedback@packtpub.com,andmentionthebook'stitleinthesubjectofyourmessage.Ifthereisatopicthatyouhaveexpertiseinandyouareinterestedineitherwritingorcontributingtoabook,seeourauthorguideatwww.packtpub.com/authors.

CustomersupportNowthatyouaretheproudownerofaPacktbook,wehaveanumberofthingstohelpyoutogetthemostfromyourpurchase.

DownloadingtheexamplecodeYoucandownloadtheexamplecodefilesforthisbookfromyouraccountathttp://www.packtpub.com.Ifyoupurchasedthisbookelsewhere,youcanvisithttp://www.packtpub.com/supportandregistertohavethefilese-maileddirectlytoyou.

Youcandownloadthecodefilesbyfollowingthesesteps:

1. Loginorregistertoourwebsiteusingyoure-mailaddressandpassword.2. HoverthemousepointerontheSUPPORTtabatthetop.3. ClickonCodeDownloads&Errata.4. EnterthenameofthebookintheSearchbox.5. Selectthebookforwhichyou'relookingtodownloadthecodefiles.6. Choosefromthedrop-downmenuwhereyoupurchasedthisbookfrom.7. ClickonCodeDownload.

Oncethefileisdownloaded,pleasemakesurethatyouunziporextractthefolderusingthelatestversionof:

WinRAR/7-ZipforWindowsZipeg/iZip/UnRarXforMac7-Zip/PeaZipforLinux

ThecodebundleforthebookisalsohostedonGitHubathttps://github.com/PacktPublishing/Mastering-Xamarin-UI-Development.Wealsohaveothercodebundlesfromourrichcatalogofbooksandvideosavailableathttps://github.com/PacktPublishing/.Checkthemout!

ErrataAlthoughwehavetakeneverycaretoensuretheaccuracyofourcontent,mistakesdohappen.Ifyoufindamistakeinoneofourbooks-maybeamistakeinthetextorthecode-wewouldbegratefulifyoucouldreportthistous.Bydoingso,youcansaveotherreadersfromfrustrationandhelpusimprovesubsequentversionsofthisbook.Ifyoufindanyerrata,pleasereportthembyvisitinghttp://www.packtpub.com/submit-errata,selectingyourbook,clickingontheErrataSubmissionFormlink,andenteringthedetailsofyourerrata.Onceyourerrataareverified,yoursubmissionwillbeacceptedandtheerratawillbeuploadedtoourwebsiteoraddedtoanylistofexistingerrataundertheErratasectionofthattitle.

Toviewthepreviouslysubmittederrata,gotohttps://www.packtpub.com/books/content/supportandenterthenameofthebookinthesearchfield.TherequiredinformationwillappearundertheErratasection.

PiracyPiracyofcopyrightedmaterialontheInternetisanongoingproblemacrossallmedia.AtPackt,wetaketheprotectionofourcopyrightandlicensesveryseriously.IfyoucomeacrossanyillegalcopiesofourworksinanyformontheInternet,pleaseprovideuswiththelocationaddressorwebsitenameimmediatelysothatwecanpursuearemedy.

Pleasecontactusatcopyright@packtpub.comwithalinktothesuspectedpiratedmaterial.

Weappreciateyourhelpinprotectingourauthorsandourabilitytobringyouvaluablecontent.

QuestionsIfyouhaveaproblemwithanyaspectofthisbook,youcancontactusatquestions@packtpub.com,andwewilldoourbesttoaddresstheproblem.

Chapter1.CreatingtheTrackMyWalksNativeAppSinceXamarinmadeitsappearanceseveralyearsago,developershavebeendelightedwithbeingabletocreatenativemobileapplicationsthattargetnon-Microsoftplatforms,andwithhavingtheoptionofdevelopingappsusingeitherC#orF#programminglanguages,whichenablesdeveloperstodistributetheirappideasoniOSandAndroidplatforms.

Asyouprogressthroughthisbook,youwilllearnhowtoapplybestpracticeprincipleswhendevelopingcross-platformmobileapplicationsanddesignpatternsusingtheXamarin.Formsplatform,whichallowsdeveloperstobuildcross-platformuserinterfacelayoutsthatcanbesharedacrossAndroid,iOS,andWindowsPhonemobileplatforms.

Sinceeachoftheseappscanbewrittenusingasingleprogramminglanguage,itmakessensetowriteasinglecodebasethatwouldcompileandbuildseparateappsforeachofthesedifferentplatforms.

ThischapterwillbeginbysettingupabasicstructureofanappbuiltusingXamarin.Forms,whichwillbethefoundationforthesubsequentchapters,wherewewillcontinuallybuilduponthis,applyingnewconcepts.Inthischapter,youwillseehowtocreateaninitialcross-platformnativeappusingXamarin.Formsandhowtogoaboutaddingnew,andupdatingexisting,packageswithinyoursolution.

You'lllearnhowtocreateC#classesthatwillactasthemodelforourapp,aswellascreatingcontentpagesthatwillformtheuserinterface.Toendthechapter,youwilllearnaboutthedifferencesbetweendevelopingappsusingXamarinStudioand/orMicrosoftVisualStudio.

Thischapterwillcoverthefollowingpoints:

CreatingtheXamarin.FormsTrackMyWalksmobileappsolutionUpdatingtheTrackMyWalkssolutionpackagesusingtheNuGetpackagemanagerCreatingtheTrackMyWalksdatamodelCreatingtheContentPagesfortheTrackMyWalkssolutionUnderstandingthedifferencesbetweenXamarinStudioandVisualStudio

CreatingtheTrackMyWalkssolutionInthissection,wewilltakealookathowwecangoaboutcreatinganewXamarin.Formssolutionforthefirsttime.Wewillbeginbydevelopingthebasicstructureforourapplication,aswellasbyaddingthenecessaryentitymodelsanddesigningtheuserinterfacefiles.

Beforewecanproceed,weneedtocreateourTrackMyWalksproject.ItisverysimpletocreatethisusingXamarinStudio.Simplyfollowthestepslistedbelow:

1. LaunchtheXamarinStudioapplication.Youwillbepresentedwiththefollowingscreen:

2. Next,clickontheNewSolution...button,oralternativelychoosetheFile|New|Solution...orsimplypressShift+Command+N.

3. Next,choosetheFormsAppoptionwhichislocatedundertheMultiplatform|Appsection.EnsureyouhaveselectedC#astheprogramminglanguagetouse:

4. Next,enterTrackMyWalkstouseasthenameforyourappintheAppNamefield.5. Then,specifyanamefortheOrganizationIdentifierfield.6. Next,ensurethatboththeAndroidandiOScheckboxeshavebeenselectedintheTarget

Platformsfields.7. Then,ensurethattheUsePortableClassLibraryoptionhasbeenselectedintheShared

Codesection,asshowninthefollowingscreenshot.

Tip

ThedifferencebetweenusingaPortableClassLibraryversusaSharedLibraryisessentiallythataPortableClassLibraryenablesdeveloperstowritecodeonce,sothatitcanbeusedwithindifferentplatformprojectssuchasWebsites,Android,andiOS.ASharedLibraryenablesdeveloperstocopyallthefileswithinthelibraryprojecttoalltheprojectsthatarecontainedwithinthesolutionduringcompilationforthevariousplatformsthatwilluseit.

Note

TheOrganizationIdentifieroptionforyourappneedstobeunique.XamarinStudiorecommendsthatyouusethereversedomainstyle(forexample,com.domainName.appName).

8. Next,ensurethattheUseXAMLforuserinterfacefilesoptionhasnotbeenselected.9. Then,clickontheNextbuttontoproceedtothenextstepinthewizard.

10. Next,ensurethattheCreateaprojectdirectorywithinthesolutiondirectory.checkboxhasbeenselected.

11. Then,clickontheCreatebuttontosaveyourprojecttothespecifiedlocation.

Onceyourprojecthasbeencreated,youwillbepresentedwiththeXamarindevelopmentenvironmentalongwithseveralprojectfilesthatthetemplatehascreatedforyou,asshowninthefollowingscreenshot:

Asyoucanseefromtheprecedingscreenshot,theTrackMyWalkssolutionhasbeendividedintothreemainareas.Thefollowingtableprovidesabriefdescriptionofwhateachareaisusedfor:

Platformspecificproject Description

TrackMyWalks

ThisisthePortableClassLibrary(PCL)projectthatwillberesponsibleforactingasthemainarchitecturallayerfortheTrackMyWalkssolution.

Thisprojectcontainsallofthebusinesslogic,dataobjects,Xamarin.FormsPages,views,andothernon-platformspecificcode.

Anycodethatyoucreatewithinthisprojectcanbesharedacrossmultipleplatform-specificprojects.

TrackMyWalks.Droid

ThisprojectisanAndroidspecificprojectthatcontainsallofthecodeandassetsrequiredtobuildanddeploytheAndroidappcontainedwithinthesolution.

Bydefault,thisprojectcontainsareferencetotheTrackMyWalksPortableClassLibrary.

ThisprojectisaniOSspecificprojectthatcontainsallofthecodeandassetsrequiredtobuildanddeploytheiOSappcontainedwithinthesolution.

TrackMyWalks.iOS Bydefault,thisprojectcontainsareferencetotheTrackMyWalksPortableClassLibrary.

OnethingyouwillnoticeisthatoursolutioncontainsafilecalledTrackMyWalks.cswhichispartoftheTrackMyWalksPortableClassLibrary.TheTrackMyWalks.csfilecontainsaclassnamedAppthatinheritsfromtheXamarin.Forms.Applicationclasshierarchy,ascanbeseeninthefollowingcodesnippet:

//

//TrackMyWalks.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms;

namespaceTrackMyWalks

{

publicclassApp:Application

{

publicApp()

{

//Therootpageofyourapplication

varcontent=newContentPage

{

Title="TrackMyWalks",

Content=newStackLayout{

VerticalOptions=LayoutOptions.Center,

Children={newLabel{

HorizontalTextAlignment=

TextAlignment.Center,

Text="WelcometoXamarinForms!"

}

}

}

};

MainPage=newNavigationPage(content);

}

protectedoverridevoidOnStart()

{

//Handlewhenyourappstarts

}

protectedoverridevoidOnSleep()

{

//Handlewhenyourappsleeps

}

protectedoverridevoidOnResume()

{

//Handlewhenyourappresumes

}

}

}

TheAppconstructormethodsetsuptheMainPagepropertytoanewinstanceoftheContentPagethatwillsimplydisplaysomedefaulttextascreatedbytheprojectwizard.Throughoutthischapter,wewillbebuildingtheinitialuserinterfacepageviewsandthenmodifyingtheMainPagepropertyforourAppclass,containedwithintheTrackMyWalks.csfile.

UpdatingtheTrackMyWalkssolutionpackagesInthissection,wewilltakealookathowtoupdatetheXamarin.FormspackagescontainedwithinourTrackMyWalkssolution.Basically,youwillnoticethateachprojectcontainedwithinoursolutioncontainsaPackagesfolder.

TheXamarin.FormspackageisessentiallyaNuGetpackagethatgetsautomaticallyincludedinoursolutionwheneverwespecifythatwewanttocreateaXamarin.FormsAppprojecttemplate.

Fromtimetotime,youwillnoticethatXamarinwillnotifyyouwheneverapackageisoutofdateandneedstobeupdatedtoensurethatyouarerunningthelatestversion.

Note

ANuGetpackage,isessentiallythepackagemanagerfortheMicrosoftDevelopmentPlatformthatcontainstheclienttoolsthatprovidethecapabilityforproducingandconsuming.NETpackages.

Let'stakealookathowtogoaboutupdatingtheNuGetpackageswithinourTrackMyWalkssolutiontoensurethatwearerunningthelatestXamarin.Formspackages.Performthefollowingsteps:

1. Right-clickontheTrackMyWalkssolutionandchoosetheUpdateNuGetPackagesmenuoption,asshowninthefollowingscreenshot:

Onceyouhaveselectedthisoption,XamarinStudiowillproceedtoupdateeachpackagethatiscontainedwithintheTrackMyWalkssolutionforeachoftheplatform-specificprojects,andwilldisplayaprogressindicatorsimilartotheoneshowninthefollowingscreenshot:

Duringthepackageupdateprocess,somepackagesthatarecontainedaspartofeachplatform-specificprojectrequireyoutoaccepttheirlicensetermspriortoinstalling,whichisshowninthefollowingscreenshot:

2. ClickontheAcceptbuttontoacceptthelicensetermsandconditionsforthepackagesdisplayedwithinthedialogboxandtoinstallthepackages,asshownintheprecedingscreenshot.

NowthatyouhavesuccessfullyupdatedtheXamarin.Formspackageswithinyoursolution,wecannowproceedwithbuildingtheuserinterfacefilesfortheTrackMyWalkssolution.

CreatingtheTrackMyWalksmodelInthissection,wewillproceedtocreateourTrackMyWalksmodelthatwillrepresentourwalkentries.Asweprogressthroughoutthischapter,wewillseehowwecanusethismodeltosetupandinitializesomewalkentriesforourmainWalksPageusingaListViewcontrolsothatwecandisplaywalkentryforeachrowwithintheListView.

Let'stakealookathowwecanachievethis,byfollowingthestepsbelow:

1. CreateanewfolderwithintheTrackMyWalksPortableClassLibraryprojectsolution,calledModelsasshowninthefollowingscreenshot:

2. Next,createanewclassfilewithintheModelsfolder,asshowninthefollowingscreenshot:

3. Then,choosetheEmptyClassoptionundertheGeneralsectionandenterinWalkEntriesforthenameofthenewclassfiletobecreatedasshowninthefollowingscreenshot:

4. Next,clickontheNewbuttontoallowthewizardtoproceedandcreatethenewfile,asshownintheprecedingscreenshot.

Congratulations,youhavecreatedyourfirstfolderandC#classfileforoursolution.Wecannowproceedwithaddingthepropertydescriptorsthatwillbeusedtodefineourmodel.

5. EnsurethattheWalkEntries.csfileisdisplayed,thenlocatetheWalkEntriesclassconstructorandenterthefollowinghighlightedcodesections:

//

//WalkEntries.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

namespaceTrackMyWalks.Models

{

publicclassWalkEntries

{

publicstringTitle{get;set;}

publicstringNotes{get;set;}

publicdoubleLongitude{get;set;}

publicdoubleLatitude{get;set;}

publicdoubleKilometers{get;set;}

publicstringDifficulty{get;set;}

publicdoubleDistance{get;set;}

publicstringImageUrl{get;set;}

}

}

Intheprecedingcodesnippet,wehavesuccessfullydefinedthemodelthatwillbeusedtorepresentourwalkentries.Inthenextsection,wewillusethismodeltosetupandinitializesomewalkentriesforourmainWalksPageusingaListViewcontrol,thenuseaDataTemplatetodescribehowthemodeldatashouldbedisplayedforeachrowwithintheListView.

CreatingthewalksmainpageAsmentionedintheprevioussection,theWalksPagewillessentiallyserveasthemainentrypointforourapplication.WewilluseourWalkEntriesmodeltopopulatesomestaticwalksinformationdata,thendisplaythisinformationwithinaListViewcontrolusingaDataTemplate.Solet'sgetstartedbyfollowingthesesteps:

1. Firstly,createanewfolderwithintheTrackMyWalksPortableClassLibraryprojectsolutioncalledPages,asyoudidintheprevioussection.

2. Next,fromtheNewFilescreen,selecttheFormssectionwithintheleftsectionpane.3. Then,selecttheFormsContentPageoptionintherightpane.4. Next,enterWalksPageforthenameofthenewclasstobecreated.5. Finally,clickontheNewbutton,asshowninthefollowingscreenshot:

6. Next,ensurethattheWalksPage.csfileisdisplayedwithinthecodeeditorandenterinthefollowinghighlightedcodesections.

//

//WalksPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem.Collections.Generic;

usingXamarin.Forms;

usingTrackMyWalks.Models;

namespaceTrackMyWalks

{

publicclassWalksPage:ContentPage

{

publicWalksPage()

{

varnewWalkItem=newToolbarItem

{

Text="AddWalk"

};

newWalkItem.Clicked+=(sender,e)=>

{

Navigation.PushAsync(newWalkEntryPage());

};

ToolbarItems.Add(newWalkItem);

varwalkItems=newList<WalkEntries>

{

newWalkEntries{

Title="10MileBrookTrail,MargaretRiver",

Notes="The10MileBrookTrailstartsintheRotaryPark

nearOldKate,apreservedsteam"+

"engineatthenorthernedgeofMargaretRiver.",

Latitude=-33.9727604,

Longitude=115.0861599,

Kilometers=7.5,

Distance=0,

Difficulty="Medium",

ImageUrl="http://trailswa.com.au/media/cache/media/

images/trails/_mid/FullSizeRender1_600_480_c1.jpg"

},

newWalkEntries{

Title="AncientEmpireWalk,ValleyoftheGiants",

Notes="TheAncientEmpireisa450metrewalktrail

thattakesyouaroundandthroughsomeof"+

"thegianttingletreesincludingthemostpopular

ofthegnarledveterans,knownasGrandmaTingle.",

Latitude=-34.9749188,

Longitude=117.3560796,

Kilometers=450,

Distance=0,

Difficulty="Hard",

ImageUrl="http://trailswa.com.au/media/cache/media/

images/trails/_mid/Ancient_Empire_534_480_c1.jpg"

},

};

varitemTemplate=newDataTemplate(typeof(ImageCell));

itemTemplate.SetBinding(TextCell.TextProperty,"Title");

itemTemplate.SetBinding(TextCell.DetailProperty,"Notes");

itemTemplate.SetBinding(ImageCell.ImageSourceProperty,

"ImageUrl");

varwalksList=newListView{

HasUnevenRows=true,

ItemTemplate=itemTemplate,

ItemsSource=walkItems,

SeparatorColor=Color.FromHex("#ddd"),

};

//Setupoureventhandler

walksList.ItemTapped+=(objectsender,

ItemTappedEventArgse)=>

{

varitem=(WalkEntries)e.Item;

if(item==null)return;

Navigation.PushAsync(newWalkTrailPage(item));

item=null;

};

Content=walksList;

}

}

}

Intheprecedingcodesnippet,webeganbydeclaringournewWalkItemvariablethatinstantiatesfromtheToolbarItemclasswhichwillbeusedtoattachanewAddWalkbuttontothemaintoolbarofthebaseContentPage.ToolbarItemscollectiontoprovideawayforuserstoaddnewwalktrailinformationwithintheapp.

Next,wecreateaneventforournewWalkItemusingtheClickedeventoftheToolbarItemclass,whichwillbeusedtonavigatetothenewWalksEntryPage.

Inournextstep,wedeclareanewvariablewalkItemsthatisacollectionoflistitemstostoreeachofourwalkentrieswithinourmodelandthenusetheDataTemplateclasstodescribehowwewantourmodeldatatobedisplayedwithineachoftherowsdeclaredwithintheListView.

Finally,wesetupaneventhandlerforourListViewthatwillbeusedtomovetotheWalksTrailPagetodisplayinformationabouttheitemselected.

CreatingthenewwalkentrycontentpageInthissection,wewillbeginbuildingtheuserinterfaceforournewWalkEntryPage.ThispageiscalledwhentheuserclicksontheAddWalkbuttonfromthemainpageandwillbeusedtoallowtheuserameansofaddingnewwalkinformationtobeusedwithintheapplication.

Thereareanumberofwaysyoucangoaboutpresentingthisinformationtocollectdata.Forthepurposeofthisapp,wewillbeusingaTableView,butyoucouldquiteeasilyuseaStackLayoutandpresentthisinformationasaseriesofLabelsandEntryCells.

Let'sbeginbycreatingthenewWalkEntryPagebyperformingthefollowingsteps:

1. CreateanewContentPagecalledWalkEntryPage,asyoudidinthesectionentitledCreatingthewalksmainpage,locatedwithinthischapter.

2. Next,ensurethattheWalkEntryPage.csfileisdisplayedwithinthecodeeditorandenterinthefollowinghighlightedcodesections:

//

//WalkEntryPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms;

usingTrackMyWalks.Models;

usingSystem.Collections.Generic;

namespaceTrackMyWalks

{

publicclassWalkEntryPage:ContentPage

{

publicWalkEntryPage()

{

//SettheContentPageTitle

Title="NewWalkEntry";

//DefineourNewWalkEntryfields

varwalkTitle=newEntryCell

{

Label="Title:",

Placeholder="TrailTitle"

};

varwalkNotes=newEntryCell

{

Label="Notes:",

Placeholder="Description"

};

varwalkLatitude=newEntryCell

{

Label="Latitude:",

Placeholder="Latitude",

Keyboard=Keyboard.Numeric

};

varwalkLongitude=newEntryCell

{

Label="Longitude:",

Placeholder="Longitude",

Keyboard=Keyboard.Numeric

};

varwalkKilometers=newEntryCell

{

Label="Kilometers:",

Placeholder="Kilometers",

Keyboard=Keyboard.Numeric

};

varwalkDifficulty=newEntryCell

{

Label="DifficultyLevel:",

Placeholder="WalkDifficulty"

};

varwalkImageUrl=newEntryCell

{

Label="ImageUrl:",

Placeholder="ImageURL"

};

//DefineourTableView

Content=newTableView

{

Intent=TableIntent.Form,

Root=newTableRoot

{

newTableSection()

{

walkTitle,

walkNotes,

walkLatitude,

walkLongitude,

walkKilometers,

walkDifficulty,

walkImageUrl

}

}

};

varsaveWalkItem=newToolbarItem{

Text="Save"

};

saveWalkItem.Clicked+=(sender,e)=>{

Navigation.PopToRootAsync(true);

};

ToolbarItems.Add(saveWalkItem);

}

}

}

Intheprecedingcodesnippet,webeganbydeclaringanumberofEntryCelllabelsforouruserinterfacetocaptureinformationenteredbytheuserfor-Title,Notes,Latitude,Longitude,Kilometers,DifficultyandImageURL.Asyouprogressthroughthisbook,youwilllearnhowtocustomizethelookandfeeloftheEntryCellsbycreatingacustomizedplatform-specificpickerfortheWalkDifficultyandKilometers.

Next,wedefineourTableViewandaddeachofourEntryCellfieldstotheTableSectionpropertyoftheTableViewcontrol.EachTableSectionthatisdefinedwithinaTableViewconsistsofaheadingandoneormoreViewCells,which,inourcase,aretheEntryCellfields.

Finally,wedeclareandaddaToolbarItemcalledsaveWalkItemtoourContentPageToolbarItemscollection,thencreateaneventthat,whenclicked,willsavethewalkinformationenteredtothemainwalkspage.Obviously,wewillberefactoringthenewWalkEntryPagethroughoutthisbook,which,whentheSavebuttonispressed,willactuallysendthisinformationtotheserverusingaRESTfulAPIandrefreshthemainTrackMyWalkspage.

CreatingthewalktrailcontentpageInthissection,wewillbeginbuildingtheuserinterfaceforourWalksTrailPage.ThispageiscalledwhentheuserclicksonanentrywithintheListViewonourTrackMyWalksmaincontentpageandwillbeusedtodisplayinformationassociatedwiththechosentrail:

1. CreateanewContentPagecalledWalkTrailPageasyoudidinthesectionentitledCreatingthewalksmainpage,locatedwithinthischapter.

2. Next,ensurethattheWalkTrailPage.csfileisdisplayedwithinthecodeeditorandenterthefollowinghighlightedcodesections:

//

//WalkTrailPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms;

usingTrackMyWalks.Models;

namespaceTrackMyWalks

{

publicclassWalkTrailPage:ContentPage

{

publicWalkTrailPage(WalkEntrieswalkItem)

{

Title="WalksTrail";

varbeginTrailWalk=newButton

{

BackgroundColor=Color.FromHex("#008080"),

TextColor=Color.White,

Text="BeginthisTrail"

};

//Setupoureventhandler

beginTrailWalk.Clicked+=(sender,e)=>

{

if(walkItem==null)return;

Navigation.PushAsync(newDistanceTravelledPage(walkItem));

Navigation.RemovePage(this);

walkItem=null;

};

varwalkTrailImage=newImage()

{

Aspect=Aspect.AspectFill,

Source=walkItem.ImageUrl

};

vartrailNameLabel=newLabel()

{

FontSize=28,

FontAttributes=FontAttributes.Bold,

TextColor=Color.Black,

Text=walkItem.Title

};

vartrailKilometersLabel=newLabel()

{

FontAttributes=FontAttributes.Bold,

FontSize=12,

TextColor=Color.Black,

Text=$"Length:{walkItem.Kilometers}km"

};

vartrailDifficultyLabel=newLabel()

{

FontAttributes=FontAttributes.Bold,

FontSize=12,

TextColor=Color.Black,

Text=$"Difficulty:{walkItem.Difficulty}"

};

vartrailFullDescription=newLabel()

{

FontSize=11,

TextColor=Color.Black,

Text=$"{walkItem.Notes}",

HorizontalOptions=LayoutOptions.FillAndExpand

};

this.Content=newScrollView

{

Padding=10,

Content=newStackLayout

{

Orientation=StackOrientation.Vertical,

HorizontalOptions=LayoutOptions.FillAndExpand,

Children=

{

walkTrailImage,

trailNameLabel,

trailKilometersLabel,

trailDifficultyLabel,

trailFullDescription,

beginTrailWalk

}

}

};

}

}

}

Intheprecedingcodesnippet,webeganbyimportingourTrackMyWalks.ModelsclassaswewillbeusingthistoextracttheinformationpassedinfromourWalksPage.

Next,wedeclareourbeginTrailWalkvariablethatinheritsfromtheButtonclass;thenwesetuptheClickedeventoftheButtonclass,whichwillbeusedtonavigatetotheDistanceTravelledPagecontentpagewhenclickedtodisplayinformationaboutourtrailafterremovingourwalkstrailcontentpagefromtheNavigationPagehierarchy.

Inthenextstep,wedeclareanimagevariablewalkTrailImageandsettheSourcepropertyoftheimagetobetheimageoftheselectedwalkItemfromtheListView.WethendeclareandinitializeanumberoflabelobjectsthatwillcontainthewalkIteminformationthathasbeenpassedfromtheWalksPagecontentpageListViewcontrolanddisplayed.

Next,wedefineaScrollViewcontrolthatispartoftheXamarin.Forms.Corebaseclass,thenaddeachofourformImageandLabelfieldstotheStackLayoutcontrol.TheScrollViewcontrolisafantasticcontrolthatallowsourContentPagetoscrollitscontentsshouldtheinformationbetoobigtofitwithintheactualdevice'sscreenrealestate.

AddingtheXamarin.Forms.MapsNuGetpackageInthissection,wewillneedtoaddtheXamarin.Forms.MapsNuGetpackagetoourcoreproject,aswellasforeachoftheplatform-specificprojectsforbothiOSandAndroidplatforms.ThispackageisrequiredinordertousetheXamarin.FormsMapcontrolintheDistanceTravelledcontentpagethatwewillbebuildinginthenextsection.

1. Right-clickontheTrackMyWalkssolutionandchoosetheAddPackages...menuoption,asshowninthefollowingscreenshot:

2. ThiswilldisplaytheAddPackagesdialog.EnterinmapswithintheSearchdialogandthenselectandclicktheXamarin.Forms.Mapsoptionwithinthelist,asshowninthefollowingscreenshot:

3. Finally,clickontheAddPackagebuttontoaddtheXamarin.Forms.MapsNuGetpackagetotheTrackMyWalkscoresolution.

4. RepeatthesameprocesstoaddtheXamarin.Forms.MapsNuGetpackageforboththeiOSandAndroidprojectsthatarecontainedwithintheTrackMyWalkssolution.

NowthatyouhaveaddedtheNuGetPackagefortheXamarin.FormsMap,wecanbegintoutilizethiscontrolwithintheDistanceTravelledcontentpagethatwewillbecoveringinthenextsection.

CreatingtheDistanceTravelledPagecontentpageInthissection,wewillbeginbuildingtheuserinterfaceforourDistanceTravelledPagecontentpage.ThispageiscalledwhentheuserclicksontheBeginthisTrailbuttonfromtheWalksTrailPagecontentpage,whichwillbeusedtodisplayinformationaboutthechosentrail,aswellasplacingapinplaceholderwithintheXamarin.Forms.Mapscontrolandcalculatingthedistancetravelled,andtimetaken:

1. CreateanewContentPagecalledDistanceTravelledPageasyoudidintheprevioussection.

2. Next,ensurethattheDistanceTravelledPage.csfileisdisplayedwithinthecodeeditorandenterthefollowinghighlightedcodesections:

//

//DistanceTravelledPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms;

usingXamarin.Forms.Maps;

usingTrackMyWalks.Models;

namespaceTrackMyWalks

{

publicclassDistanceTravelledPage:ContentPage

{

3. Then,updatetheDistanceTravelledPagemethodconstructortoincludethewalkItemparameterforthechosenwalk,asshownbythefollowinghighlightedcodesections:

publicDistanceTravelledPage(WalkEntrieswalkItem)

{

Title="DistanceTravelled";

4. Next,wedeclareatrailMapvariablethatwillpointtoaninstanceoftheXamarin.Forms.Mapscontroltocreateaplaceholderpinmarkerwithinthemapcontrol.Usingthelatitudeandlongitudecoordinates,enterthefollowinghighlightedcodesections:

//Instantiateourmapobject

vartrailMap=newMap();

//Placeapinonthemapforthechosenwalktype

trailMap.Pins.Add(newPin

{

Type=PinType.Place,

Label=walkItem.Title,

Position=newPosition(walkItem.Latitude,walkItem.Longitude)

});

//Centerthemaparoundthelistofwalksentry'slocation

trailMap.MoveToRegion(MapSpan.FromCenterAndRadius(new

Position(walkItem.Latitude,walkItem.Longitude),

Distance.FromKilometers(1.0)));

5. Then,wedeclareanumberofLabelobjectsthatcontainourwalkIteminformation,whichhasbeenpassedfromtheWalkTrailPagecontentpagesothatwecantrailrelatedinformation.Enterinthefollowinghighlightedcodesections:

vartrailNameLabel=newLabel()

{

FontSize=18,

FontAttributes=FontAttributes.Bold,

TextColor=Color.Black,

Text=walkItem.Title

};

vartrailDistanceTravelledLabel=newLabel()

{

FontAttributes=FontAttributes.Bold,

FontSize=20,

TextColor=Color.Black,

Text="DistanceTravelled",

HorizontalTextAlignment=TextAlignment.Center

};

vartotalDistanceTaken=newLabel()

{

FontAttributes=FontAttributes.Bold,

FontSize=20,

TextColor=Color.Black,

Text=$"{walkItem.Distance}km",

HorizontalTextAlignment=TextAlignment.Center

};

vartotalTimeTakenLabel=newLabel()

{

FontAttributes=FontAttributes.Bold,

FontSize=20,

TextColor=Color.Black,

Text="TimeTaken:",

HorizontalTextAlignment=TextAlignment.Center

};

vartotalTimeTaken=newLabel()

{

FontAttributes=FontAttributes.Bold,

FontSize=20,

TextColor=Color.Black,

Text="0h0m0s",

HorizontalTextAlignment=TextAlignment.Center

};

6. Next,wedeclareourwalksHomeButtonvariablethatinheritsfromtheButtonclassandproceedtosetupourclickhandler,whichwillbeusedtonavigateourapptothemainWalksPagecontentpagewhenclicked.Enterthefollowinghighlightedcodesections:

varwalksHomeButton=newButton

{

BackgroundColor=Color.FromHex("#008080"),

TextColor=Color.White,

Text="EndthisTrail"

};

//Setupoureventhandler

walksHomeButton.Clicked+=(sender,e)=>

{

if(walkItem==null)return;

Navigation.PopToRootAsync(true);

walkItem=null;

};

7. Then,wedefineaScrollViewcontrolthatispartoftheXamarin.Forms.CorebaseclassandproceedtoaddeachofourformButtonandLabelfieldstotheStackLayoutcontrol.Enterthefollowinghighlightedcodesections:

this.Content=newScrollView

{

Padding=10,

Content=newStackLayout

{

Orientation=StackOrientation.Vertical,

HorizontalOptions=LayoutOptions.FillAndExpand,

Children={

trailMap,

trailNameLabel,

trailDistanceTravelledLabel,

totalDistanceTaken,

totalTimeTakenLabel,

totalTimeTaken,

walksHomeButton

}

}

};

}

}

}

Intheprecedingcodesnippet,webeganbyimportingourTrackMyWalks.ModelsclassaswewillbeusingthistoextracttheinformationpassedinfromourWalksPage.TheXamarin.Forms.MapsNuGetpackageisacross-platformlibrarythatallowsdeveloperstodisplayandannotateinformationwithinthemap.Wewillbeusingthiscontroltocreateapinplaceholderwithinthemapcontrol,alongwithadditionaldetailsassociatedwiththetrail.

Next,wedeclareourtrailMapvariablethatpointstoaninstanceoftheXamarin.Forms.Mapscontrolandcreateaplaceholderpinmarkerwithinthemapcontainingthedetailsforthechosentrail,thencenterinonthemaptoshowtheareaforourwalkslocation,derivedbythelatitudeandlongitudecoordinates.WethendeclareandinitializeanumberofLabelobjectsthatcontainthewalkIteminformationthathasbeenpassedfromtheWalkTrailPagecontentpageand

declareourwalksHomeButtonvariablethatinheritsfromtheButtonclass,thensetuptheClickedeventfortheButtonclass,whichwillbeusedtonavigatebacktotheTrackMyWalkswhenclicked.

Finally,wedefineaScrollViewcontrolthatispartoftheXamarin.Forms.Corebaseclass,thenaddeachofourformButtonandLabelfieldstotheStackLayoutcontrol.

Inournextsection,wewillneedtoinitializetheXamarin.Forms.MapsNuGetpackagelibrarywithineachofourplatform-specificstartupclasses(forexample,AppDelegate.csforiOSandMainActivity.csforAndroid).Let'stakealookathowwecanachievethisbyfollowingthestepsbelow.

1. EnsurethattheAppDelegate.csfileisdisplayedwithinthecodeeditorandenterthefollowinghighlightedcodesections:

//

//AppDelegate.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingFoundation;

usingUIKit;

namespaceTrackMyWalks.iOS

{

[Register("AppDelegate")]

publicpartialclassAppDelegate:

global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate

{

publicoverrideboolFinishedLaunching(UIApplicationapp,

NSDictionaryoptions)

{

global::Xamarin.Forms.Forms.Init();

Xamarin.FormsMaps.Init();

LoadApplication(newApp());

returnbase.FinishedLaunching(app,options);

}

}

}

Intheprecedingcodesnippet,webeganbyinitializingourAppDelegateclasstousetheXamarin.Forms.Mapslibrary,byaddingtheXamarin.FormsMaps.InitwhichinitializestheXamarin.Formsplatform,sothatourTrackMyWalkssolutioncanusethemaps.Ifthisisomittedfromthisclass,theDistanceTravelledPagecontentpagewillnotdisplaythemapandwillnotworkasexpected.

Note

FormoreinformationonXamarin.Forms.Mapslibrary,aswellasthevarioustypesofdifferent

classesavailable,pleaserefertotheXamarindeveloperdocumentationathttps://developer.xamarin.com/api/namespace/Xamarin.Forms.Maps/.

CreatingtheSplashscreencontentpageInthissection,wewillbeginbuildingtheuserinterfaceforourSplashpagewhichwillonlybeusedfortheAndroidplatform,sinceiOSalreadycontainsamethodofachievingthis.ThesplashpagewillsimplydisplaythedefaultXamarinicon,butasweprogressthroughoutthisbook,wewillberefactoringthispagetoincludeamoresuitableimageforourapp:

1. CreateanewContentPagecalledSplashPageasyoudidintheprevioussection.2. Next,ensurethattheSplashPage.csfileisdisplayedwithinthecodeeditorandenterthe

followinghighlightedcodesections:

//

//SplashPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingSystem.Threading.Tasks;

usingXamarin.Forms;

namespaceTrackMyWalks

{

publicclassSplashPage:ContentPage

{

3. Then,locatetheSplashPageconstructormethodandenterthefollowinghighlightedcodesections:

publicSplashPage()

{

AbsoluteLayoutsplashLayout=newAbsoluteLayout

{

HeightRequest=600

};

varimage=newImage()

{

Source=ImageSource.FromFile("icon.png"),

Aspect=Aspect.AspectFill,

};

AbsoluteLayout.SetLayoutFlags(image,AbsoluteLayoutFlags.All);

AbsoluteLayout.SetLayoutBounds(image,newRectangle(0f,0f,

1f,1f));

splashLayout.Children.Add(image);

Content=newStackLayout()

{

Children={splashLayout}

};

}

4. Next,locatetheOnAppearingmethodandenterthefollowinghighlightedcodesections:

protectedoverrideasyncvoidOnAppearing()

{

base.OnAppearing();

//Delayforafewsecondsonthesplashscreen

awaitTask.Delay(3000);

//InstantiateaNavigationPagewiththeMainPage

varnavPage=newNavigationPage(newWalksPage()

{

Title="TrackMyWalks"

});

Application.Current.MainPage=navPage;

}

}

}

Intheprecedingcodesnippet,webeganbyimportingourSystem.Threading.Tasksclass.Thisclasswillbeusedtoperformaslightdelaytoallowourusertoseethesplashscreen,asdefinedwithintheOnAppearingclassmethod.

WethencreateasplashLayoutvariableoftypeAbsoluteLayoutthatwillbeusedtopositionandsizeeachofthechildelementsproportionallywithinourview,andthensettheHeightRequestproperty.

Next,wedeclareanimagevariablethatinheritsfromtheImageclass;thenassigntheSourcepropertytotheimagethatwewouldliketouseandsettheimagesAspectpropertysothattheimagefillstheview.

Inourfinalsteps,wedefinevaluesforbothoftheLayoutFlagsandLayoutBoundspropertiesontheAbsoluteLayoutclasssothattheimageresizeswithintheview.ThenweaddoursplashLayouttothecontentpageusingtheStackLayoutcontrol.Finally,weoverridetheOnAppearingclassmethodoftheXamarin.Forms.Pagepagelifecycle,whichgetscalledimmediatelypriortothepagebecomingvisibleonthescreen,andthenspecifyadelayofthreesecondspriortoinstantiatinganewinstanceoftheNavigationPage,whichwillcallourWalksPagetobethemaincontentrootpage.

UpdatingtheXamarin.FormsAppclassInthissection,weneedtoinitializetheAppclassofXamarin.FormslibrarytocallourSplashPageandsettherootpageforourapplicationifthedetectedOSdeviceisAndroid.Let'stakealookathowwecanachievethis:

1. OpentheTrackMyWalks.cslocatedwithintheTrackMyWalksgroupandensurethatitisdisplayedwithinthecodeeditor.

2. Next,locatetheAppmethodandenterthefollowinghighlightedcodesections,asshowninthefollowingcodesnippet:

//

//TrackMyWalks.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms;

namespaceTrackMyWalks

{

publicclassApp:Application

{

publicApp()

{

//ChecktheTargetOSPlatform

if(Device.OS==TargetPlatform.Android)

{

MainPage=newSplashPage();

}

else

{

//Therootpageofyourapplication

varnavPage=newNavigationPage(newTrackMyWalks.

WalksPage()

{

Title="TrackMyWalks"

});

MainPage=navPage;

}

}

}

}

Intheprecedingcodesnippet,webegancheckingtheDevice.OSclasstodeterminewhatOSXamarin.Formsisrunningon,thenusedtheTargetPlatformclasstodeterminewhetherourappisbeingrunonGoogle'sAndroidOSplatform.IfourappisrunningonAndroid,wesettheAppconstructormethodsMainPagetoanewinstanceoftheContentPagethatwillsimplyusetheSplashPageastherootpage.Alternatively,wecreateanewinstanceoftheNavigationPageclassandsetthistoourWalksPagetobethemaincontentrootpageforourContentPage.

DifferencesbetweenXamarinStudioandVisualStudioWhendevelopingcross-platformmobileapps,youcurrentlyhaveachoiceofusingeitherXamarinStudioorMicrosoft'sVisualStudiodevelopmentenvironments.Itisworthnotingthat,althoughthescreenshotsandexamplecodeusedthroughoutthisbookhavebeendevelopedusingXamarinStudiorunningonanAppleMacintoshcomputer,thecodeshouldcompilefineonaWindowsmachinerunningMicrosoftVisualStudio2015.

However,therearesomedifferencesthatyouneedtobeawareofbeforeembarkingonthejourneyofbuildingyourmobiledevelopmentsolutions.IfyouarerunningXamarinStudioonaWindowsmachine,youwillgetanAndroidprojectsolutionwheneveryoucreateanewXamarin.Formssolution.

IfyouwanttointegrateanddevelopappsforWindowsPhone,youwillneedtoensurethatyouarerunningMicrosoftVisualStudioonaWindowsmachine.WhendevelopingappsforiOSapplications,youwillneedtoprepareyourMactobetheXamarinbuildhostbyfirstlyenablingRemoteLoginonyourMacwithintheSystemPreferencessection,andthenselectingtheMactobethebuildhostfromwithintheMicrosoftVisualStudioenvironmentonyourWindowsmachine.

Note

FormoreinformationonhowtoprepareyourMactobetheXamarinbuildhost,refertotheXamarindeveloperdocumentationathttps://developer.xamarin.com/guides/ios/getting_started/installation/windows/connecting-to-mac/.

NowthatyouhaveanunderstandingofthedifferencesbetweenXamarinStudioandMicrosoftVisualStudio,ournextstepistocompile,buildandruntheTrackMyWalksapplicationwithintheiOSsimulator.

RunningtheTrackMyWalksappusingthesimulatorInthissection,wewilltakealookathowtocompileandruntheTrackMyWalksapplication.Youhavetheoptionofchoosingtorunyourapplicationusinganactualdevice,orchoosingfromalistofsimulatorsavailableforaniOSdevice.

TheversionnumberofthesimulatorisdependentontheversionoftheiOSSDKthatisinstalledonyourcomputer.Performthefollowingsteps:

1. Toruntheapp,chooseyourpreferreddevicefromthelistofavailableiOSsimulatorsandselecttheRunmenuoption,asshowninthefollowingscreenshot:

2. Next,choosetheRunWithsub-menuitem,andthenchoosetheCustomConfiguration...thenclickontheRunbuttonfromtheCustomParametersdialog,asshowninthefollowingscreenshot:

3. Alternatively,youcanalsobuildandruntheTrackMyWalksapplicationbypressingCommand+Returnkeycombinations.

Whenthecompilationiscomplete,theiOSsimulatorwillappearautomaticallyandtheTrackMyWalksapplicationwillbedisplayed,asshowninthefollowingscreenshot:

Asyoucanseefromtheprecedingscreenshot,thiscurrentlydisplaysalistofstaticwalktrailentries,thataredisplayedwithinourListView.WhentheuserclicksontheAddWalkbuttonlink,thiswilldisplaythenewWalkEntrycontentpage,sotheycanbeginenteringnewWalktrailinformation.

Currently,thispagedoesn'tsaveenteredinformation,butasweprogressthroughoutthisbook,wewillberefactoringthesepagestoallowforthistohappen.UponclickingtheSavebutton,theuserwillberedirectedbacktothemainTrackMyWalkspage.

Theprecedingscreenshotshowsyouthenavigationflowbetweeneachofthepageswhenatrailhasbeenselectedfromthelist,withthefinalscreenshowingthedistancetravelledpagealongwiththeplaceholderpinmarkershowingthetraillocationwithinthemapview.

Congratulations,youhavesuccessfullybuiltthefoundationfortheTrackMyWalksapplication,aswellastheuserinterfaceforeachofthecontentpagesthatwillbeusedbyourapp.Asweprogressthroughthisbook,wewillbeenhancingourapptoincludebetterarchitecturaldesignpatterns,nicerlookinguserinterfaceelements,aswellasreal-timedatabeingsynchronizedthroughtheuseofRESTfulwebserviceAPIs.

SummaryInthischapterweexploredhowtogoaboutcreatingaXamarin.Formscross-platformapplicationforbothiOSandAndroidplatforms.Wethenmovedontobuildingaseriesofcontentpageswithstaticdata.

Next,welookedathowtousethedefaultXamarin.FormsnavigationAPIstohelpmovebetweeneachofthecontentpages,whichwewillrefactorquiteabitwhenwecoverthisinChapter3,NavigatingwithintheMVVMModel-TheXamarin.FormsWaytouseamoreflexible,customizednavigationservice.Finally,wetalkedaboutsomeofthedifferencesbetweenusingXamarinStudioandMicrosoftVisualStudiofordevelopment,beforerunningourTrackMyWalksappwithinthesimulator.

Inthenextchapter,youwilllearnabouttheconceptsbehindtheModel-View-View-Model(MVVM)patternarchitecture,howtoimplementtheMVVMmodelwithinyourapplication,andtheprocessofhowtoaddnewViewModelstoyourXamarinsolution.

Chapter2.MVVMandDataBindingInthepreviouschapter,weexploredhowtogoaboutcreatinganativeXamarin.Formscross-platformapplicationforboththeiOSandAndroidplatforms,andlearnedhowtoaddnewpackagestoyoursolutionusingtheNuGetpackagemanager.WealsolookedathowtogoaboutaddingandcreatingseveralContentPagestoyoursolution,aswellashowtorunandtestyourappwithinthesimulator.

TheModel-View-ViewModel(MVVM)architecturalpatternwasinventedwiththeExtensibleApplicationMarkupLanguage(XAML)inmindthatwascreatedbyMicrosoftbackin2008andisparticularlywellsuitedforusewiththeMVVMapplicationarchitecturalpattern,becauseitenforcesaseparationoftheXAMLuserinterfacefromtheunderlyingdatamodelthroughaclassthatwillactasaconnectionbetweenboththeViewandtheModel.TheViewandtheViewModelcanthenbeconnectedthroughdatabindingsthathavebeendefinedwithintheXAMLfile.

XAMLhasalsobeenintegratedintotheXamarin.Formsplatformthatallowsforthecreationofcross-platform,natively-basedprogramminginterfacesforiOS,Android,andWindowsPhonemobiledevices.ThisallowsdeveloperstodefineuserinterfacesusingalltheXamarin.Formsviews,layouts,andpages,aswellascustomclasses.

XAMLenforcestheseparationbetweentheapplication'suserinterfacefromtheunderlyingdata,throughaclassthatwillactasthecommunicationlayerbetweentheViewandtheViewModel,whichareconnectedthroughdata-bindingsthataredefinedineithertheXAMLorunderlyingcodefile,alongwiththebindingcontextfortheView,thatpointstoaninstanceoftheViewModel.

ThischapterwillbeginbyintroducingyoutothearchitecturebehindtheMVVMpatternarchitecture,andhowyoucanimplementthesewithinyourapplication,byaddingnewViewsandtheassociatedModels.

You'lllearnhowtocreatetheunderlyingC#classfilesthatwillactastheViewModelsforourapp,aswellasupdatingtheexistingcontentpagestodata-bindwiththeViewModelstorepresenttheinformationthatwillbedisplayedwithintheuser-interfaceforourapplication.

Thischapterwillcoverthefollowingpoints:

UnderstandingtheMVVMpatternarchitectureanddata-bindingImplementingtheMVVMbasemodelwithintheTrackMyWalkssolutionImplementingtheMVVMViewModelswithintheappImplementingtheMVVMdata-bindingstoouruserinterfacepages

UnderstandingtheMVVMpatternarchitectureInthissectionwewillbetakingalookattheMVVMpatternarchitectureandthecommunicationbetweenthecomponentsthatmakeupthearchitecture.

TheMVVMdesignpatternisdesignedtocontroltheseparationbetweentheuserinterfaces(views),theViewModelsthatcontaintheactualbindingtotheModel,andthemodelsthatcontaintheactualstructureoftheentitiesrepresentinginformationstoredonadatabaseorfromawebservice.

ThefollowingscreenshotshowsthecommunicationbetweeneachofthecomponentscontainedwithintheMVVMdesignpatternarchitecture:

TheMVVMdesignpatternisdividedintothreemainareas,asyoucanseefromtheprecedingscreenshot,andtheseareexplainedinthefollowingtable:

MVVMtype Description

ModelTheModelisbasicallyarepresentationofbusinessrelatedentitiesusedbyanapplication,andisresponsibleforfetchingdatafromeitheradatabase,orwebservice,andthende-serializedtotheentitiescontainedwithintheModel.

View

TheViewcomponentoftheMVVMmodelbasicallyrepresentstheactualscreensthatmakeuptheapplication,alongwithanycustomcontrolcomponents,andcontrolelements,suchasbuttons,labels,andtextfields.

TheviewscontainedwithintheMVVMpatternareplatform-specificandaredependentontheplatformAPIsthatareusedtorendertheinformationthatis

containedwithintheapplication'suserinterface.

ViewModel

TheViewModelessentiallycontrols,andmanipulatestheviewsbyactingastheirmaindatacontext.TheViewModelcontainsaseriesofpropertiesthatareboundtotheinformationcontainedwithineachModel.Thosepropertiesarethenboundtoeachoftheviewstorepresentthisinformationwithintheuserinterface.

ViewModelscanalsocontaincommandobjectsthatprovideaction-basedeventsthatcantriggertheexecutionofeventmethodsthatoccurwithintheView.Forexample,whentheusertapsonatoolbaritem,orabutton.

ViewModelsgenerallyimplementtheINotifyPropertyChangedandINotifyCollectionChangedinterfaces.SuchaclassfiresaPropertyChangedandINotifyCollectionChangedeventwheneveracollectionhaschanged(suchas,addinganitem,removinganitem,orwhenachangeoccurstooneoftheitems,properties)changes.ThedatabindingmechanisminXamarin.FormsattachesahandlertothisPropertyChangedandCollectionChangedeventssoitcanbenotifiedwhenapropertychangesandkeepthetargetupdatedwiththenewvalue.

NowthatyouhaveagoodunderstandingofthecomponentsthatarecontainedwithinMVVMdesignpatternarchitecture,wecanbegintocreateourentitymodelsandupdateouruserinterfacefiles.

Note

InXamarin.Forms,thetermViewisusedtodescribeformcontrols,suchasbuttonsandlabels,andusesthetermPagetodescribetheuserinterfaceorscreen.Whereas,inMVVM,Viewsareusedtodescribetheuserinterface,orscreen.

ImplementingtheMVVMViewModelswithinyourappInthissection,wewillbeginbysettingupthebasicstructureforourTrackMyWalkssolutiontoincludethefolderthatwillbeusedtorepresentourViewModels.Let'stakealookatthefollowingstepstoachievethis:

1. LaunchtheXamarinStudioapplicationandensurethattheTrackMyWalkssolutionisloadedwithintheXamarinStudioIDE.

2. Next,createanewfolderwithintheTrackMyWalksPCLproject,calledViewModelsasshowninthefollowingscreenshot:

CreatingtheWalkBaseViewModelfortheTrackMyWalksappInthissection,wewillbeginbycreatingabaseMVVMViewModelthatwillbeusedbyeachofourViewModelswhenwecreatethese,theViews(pages)willthenimplementthoseViewModelsandusethemastheirBindingContext.

Tip

WewillstartbycreatingabaseViewModelclassthatwillessentiallybeanabstractclass,containingbasicfunctionalitythateachofourViewModelswillinheritfrom,andwillimplementtheINotifyPropertyChangedInterface.

Let'stakealookathowwecanachievethis,byfollowingthesesteps:

1. CreateanemptyclasswithintheViewModelsfolder,showninthefollowingscreenshot:

2. Next,choosetheEmptyClassoptionlocatedwithintheGeneralsection,andenterinWalkBaseViewModelforthenameofthenewclassfiletobecreated,asshowninthefollowingscreenshot:

3. Next,clickontheNewbuttontoallowthewizardtoproceedandcreatethenewemptyclassfile,asshownintheprecedingscreenshot.

Upuntilthispoint,allwehavedoneiscreateourWalkBaseViewModelclassfile.ThisabstractclasswillactasthebaseViewModelclassthatwillcontainthebasicfunctionalitythateachofourViewModelswillinheritfrom.

Aswestarttobuildthebaseclass,youwillseethatitcontainsacoupleofmembersanditwillimplementtheINotifyPropertyChangedInterface.Asweprogressthroughthisbook,wewillbuildtothisclass,whichwillbeusedbytheTrackMyWalksapplication.ToproceedwithcreatingthebaseViewModelclass.

EnsurethattheWalkBaseViewModel.csfileisdisplayedwithinthecodeeditor,andenterinthefollowingcodesnippet:

//

//WalkBaseViewModel.cs

//TrackMyWalksBaseViewModel

//

//CreatedbyStevenF.Danielon22/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem.ComponentModel;

usingSystem.Runtime.CompilerServices;

namespaceTrackMyWalks.ViewModels

{

publicabstractclassWalkBaseViewModel:

INotifyPropertyChanged

{

protectedWalkBaseViewModel()

{

}

publiceventPropertyChangedEventHandlerPropertyChanged;

protectedvirtualvoidOnPropertyChanged

([CallerMemberName]stringpropertyName=null)

{

varhandler=PropertyChanged;

if(handler!=null)

{

handler(this,newPropertyChangedEventArgs(propertyName));

}

}

}

}

Intheprecedingcodesnippet,webeginbycreatinganewabstractclassforourWalkBaseViewModelthatimplementsfromtheINotifyPropertyChangedinterfaceclass,whichallowstheVieworpagetobenotifiedwheneverpropertiescontainedwithintheViewModelhavechanged.Next,wedeclareavariablePropertyChangedthatinheritsfromthePropertyChangedEventHandlerthatwillbeusedtoindicatewheneverpropertiesontheobjecthavechanged.Finally,withintheOnPropertyChangedmethod,thiswillbecalledwhenithasdeterminedthatachangehasoccurredonapropertywithintheViewModelfromachildclass.

Note

TheINotifyPropertyChangedinterfaceisusedtonotifyclients,typicallybindingclients,whenthevalueofapropertyhaschanged.

ImplementingtheWalksPageViewModelIntheprevioussection,webuiltourbaseclassViewModelforourTrackMyWalksapplication.ThiswillactasthemainclassthatwillallowourVieworpagestobenotifiedwheneverchangestopropertieswithintheViewModelhavebeenmade.

Inthissection,wewillneedtobeginbuildingtheViewModelforourWalksPage.ThismodelwillbeusedtostoretheWalkEntries,whichwilllaterbeusedanddisplayedwithintheListViewontheWalksPagecontentpage.

Let'stakealookathowwecanachievethisbyfollowingthesesteps:

1. First,createanewclassfilewithintheViewModelsfoldercalledWalksPageViewModel,asyoudidintheprevioussection,entitledCreatingtheWalkBaseViewModellocatedwithinthischapter.

2. Next,ensurethattheWalksPageViewModel.csfileisdisplayedwithinthecodeeditor,andenterinthefollowingcodesnippet:

//

//WalksPageViewModel.cs

//TrackMyWalksViewModels

//

//CreatedbyStevenF.Danielon22/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem.Collections.ObjectModel;

usingTrackMyWalks.Models;

namespaceTrackMyWalks.ViewModels

{

publicclassWalksPageViewModel:WalkBaseViewModel

{

ObservableCollection<WalkEntries>_walkEntries;

publicObservableCollection<WalkEntries>walkEntries

{

get{return_walkEntries;}

set{_walkEntries=value;

OnPropertyChanged();

}

}

Intheprecedingcodesnippet,webeginbyensuringthatourViewModelinheritsfromtheWalkBaseViewModelclass.Next,wecreateanObservableCollectionvariable_walkEntrieswhichisveryusefulwhenyouwanttoknowwhenthecollectionhaschanged,andaneventistriggeredthatwilltelltheuserwhatentrieshavebeenaddedorremovedfromtheWalkEntriesmodel.

Inournextstep,wecreatetheObservableCollectionconstructorWalkEntries,thatisdefinedwithintheSystem.Collections.ObjectModelclass,andacceptsaList

parametercontainingourWalkEntriesmodel.TheWalkEntriespropertywillbeusedtobindtotheItemSourcepropertyoftheListViewwithintheWalksMainPage.Finally,wedefinethegetter(get)andsetter(set)methodsthatwillreturnandsetthecontentofour_walkEntrieswhenithasbeendeterminedwhetherapropertyhasbeenmodifiedornot.

3. Next,locatetheWalksPageViewModelclassconstructor,andenterthefollowinghighlightedcodesections:

publicWalksPageViewModel()

{

walkEntries=newObservableCollection<WalkEntries>(){

newWalkEntries{

Title="10MileBrookTrail,MargaretRiver",

Notes="The10MileBrookTrailstartsinthe

RotaryParknearOldKate,apreservedsteam"+

"engineatthenorthernedgeofMargaretRiver.",

Latitude=-33.9727604,

Longitude=115.0861599,

Kilometers=7.5,

Distance=0,

Difficulty="Medium",

ImageUrl="http://trailswa.com.au/media/

cache/media/images/trails/_mid/"+

"FullSizeRender1_600_480_c1.jpg"

},

newWalkEntries{

Title="AncientEmpireWalk,ValleyoftheGiants",

Notes="TheAncientEmpireisa450metrewalktrail

thattakesyouaroundandthroughsomeof"+

"thegianttingletreesincludingthemostpopularof

thegnarledveterans,knownas"+"GrandmaTingle.",

Latitude=-34.9749188,

Longitude=117.3560796,

Kilometers=450,

Distance=0,

Difficulty="Hard",

ImageUrl="http://trailswa.com.au/media/cache/media/

images/trails/_mid/"+"Ancient_Empire_534_480_c1.jpg"

},

};

}

}

}

Intheprecedingcodesnippet,webeganbycreatinganewObservableCollectionforourwalkEntriesmethodandthenaddedeachofthewalklistitemsthatwewouldliketostorewithinourModel.Aseachitemisadded,theObservableCollection,constructoriscalled,andthesetter(set)methodisinvokedtoaddtheitem,thentheINotifyPropertyChangedeventwillbetriggeredtonotifythatachangehasoccurred.

UpdatingthewalksmainpagetousetheMVVMmodelNowthatwehavecreatedtheMVVMViewModelthatwillbeusedforourmainWalksPage,weneedtomodifytheWalksmainpage.Inthissection,wewillbetakingalookathowtobindtheWalksPageBindingContexttotheWalksPageViewModelsothatthewalkentrydetailscanbedisplayed.

Let'stakealookathowwecanachievethis,byfollowingthesesteps:

1. EnsurethattheWalksPage.csfileisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesections:

//

//WalksPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem.Collections.Generic;

usingXamarin.Forms;

usingTrackMyWalks.Models;

usingTrackMyWalks.ViewModels;

namespaceTrackMyWalks

{

publicclassWalksPage:ContentPage

{

publicWalksPage()

{

varnewWalkItem=newToolbarItem

{

Text="AddWalk"

};

//Setupourclickeventhandler

newWalkItem.Clicked+=(sender,e)=>

{

Navigation.PushAsync(newWalkEntryPage());

};

//AddtheToolBaritemtoourToolBar

ToolbarItems.Add(newWalkItem);

2. Next,weneedtodeclareandcreateanewBindingContextinstancefortheWalksPage,andsetthistoanewinstanceoftheWalksPageViewModelsothatitknowswheretogettheWalkEntriessothatwecanpopulatethesewithintheListView.Proceedandenterinthefollowinghighlightedcodesections:

//DeclareandinitializeourModelBindingContext

BindingContext=newWalksPageViewModel();

//DefineourItemTemplate

varitemTemplate=newDataTemplate(typeof(ImageCell));

itemTemplate.SetBinding(TextCell.TextProperty,"Title");

itemTemplate.SetBinding(TextCell.DetailProperty,"Notes");

itemTemplate.SetBinding(ImageCell.ImageSourceProperty,

"ImageUrl");

3. Then,changethewayinwhichourListViewgetstheWalkEntriesvariableitems,byupdatingtheListViewclassandsettingthebindingpropertyforthewalkentries,byinitializingtheItemsView<Cell>.ItemsSourcePropertypropertyandusingtheSetBindingmethodtobindthecontents.Proceedandenterinthefollowinghighlightedcodesections:

varwalksList=newListView

{

HasUnevenRows=true,

ItemTemplate=itemTemplate,

SeparatorColor=Color.FromHex("#ddd"),

};

//SettheBindingpropertyforourwalksEntries

walksList.SetBinding(ItemsView<Cell>.ItemsSourceProperty,

"walkEntries");

//InitializeoureventHandlertousewhentheitemistapped

walksList.ItemTapped+=(objectsender,ItemTappedEventArgse)=>

{

varitem=(WalkEntries)e.Item;

if(item==null)return;

Navigation.PushAsync(newWalkTrailPage(item));

item=null;

};

Content=walksList;

}

}

}

Inthissection,welookedatthestepsinvolvedinmodifyingourWalksPagesothatitcantakeadvantageofourWalksPageViewModel.WelookedathowtosetourcontentpagetoaninstanceofourWalksPageViewModelsothatitknowswheretogetthelistofwalkentriestobeusedanddisplayedwithintheListViewcontrol.TheSetBindingpropertycreatesandappliesabindingtoaspecificproperty.Asyoucansee,byusingViewModelswithinyourapplication,youcanseethattheresultisbothcleanandelegant,andmakesyourcodealotmorereadablewhensupportingcodemodifications.

ImplementingthewalksentrypageViewModelWehavecreatedtheViewModelthatwillbeusedbyourWalksPagesothatthewalkentriescanbedisplayedwithintheListViewcontrol.ThenextstepistobuildtheViewModelthatwillbeusedtocreatenewwalkentriesandhavethisinformationsavedbacktoourWalkBaseViewModel,whichwewillbecoveringinalaterchapterasweprogressthroughthisbook.

Inthissection,wewillbetakingalookatthestepsrequiredtocreatetheViewModelforourWalksEntryViewModelsothatwecaninitialize,andcaptureinformationenteredwithinthisscreen.

Let'stakealookathowwecanachievethis,byfollowingthesesteps:

1. CreateanewclassfilewithintheViewModelsfoldercalledWalksEntryViewModel,asyoudidintheprevioussection,entitledCreatingtheWalkBaseViewModellocatedwithinthischapter.

2. Next,ensurethattheWalksEntryViewModel.csfileisdisplayedwithinthecodeeditor,andenterinthefollowingcodesnippet.

//

//WalkEntryViewModel.cs

//TrackMyWalksViewModels

//

//CreatedbyStevenF.Danielon22/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//usingTrackMyWalks.Models;

usingTrackMyWalks.ViewModels;

usingXamarin.Forms;

namespaceTrackMyWalks.ViewModels

{

publicclassWalkEntryViewModel:WalkBaseViewModel

{

3. Then,createthefollowingTitlepropertyanditsassociatedgettersandsetterqualifiers.TheOnPropertyChangedmethod,aswementionedpreviously,willbecalledwhenourpropertydeterminesthatthecontentshavebeenchanged.AcallismadetotheSaveCommand.ChangeCanExecutemethodthatwillvalidatetheformfields,andthendeterminewhethertheSaveToolBarItemshouldbeenabledornottoallowtheusertosavethewalkentrydetails.Proceedandenterinthefollowingcodesnippet:

string_title;

publicstringTitle

{

get{return_title;}

set

{

_title=value;

OnPropertyChanged();

SaveCommand.ChangeCanExecute();

}

}

4. Next,createtheremainingViewModelpropertiesandtheassociatedgettersandsetterqualifiersthatwillbeusedtobindthevaluesenteredontheWalkEntryPage,asshowninthefollowingcodesnippets:

string_notes;

publicstringNotes

{

get{return_notes;}

set

{

_notes=value;

OnPropertyChanged();

}

}

double_latitude;

publicdoubleLatitude

{

get{return_latitude;}

set

{

_latitude=value;

OnPropertyChanged();

}

}

double_longitude;

publicdoubleLongitude

{

get{return_longitude;}

set

{

_longitude=value;

OnPropertyChanged();

}

}

double_kilometers;

publicdoubleKilometers

{

get{return_kilometers;}

set

{

_kilometers=value;

OnPropertyChanged();

}

}

string_difficulty;

publicstringDifficulty

{

get{return_difficulty;}

set

{

_difficulty=value;

OnPropertyChanged();

}

}

double_distance;

publicdoubleDistance

{

get{return_distance;}

set

{

_distance=value;

OnPropertyChanged();

}

}

string_imageUrl;

publicstringImageUrl

{

get{return_imageUrl;}

set

{

_imageUrl=value;

OnPropertyChanged();

}

}

5. Next,weneedtoinitializeourViewModelclassconstructorwithdefaultvaluesforourTitle,DifficultyandDistanceproperties.LocatetheWalkEntryViewModelclassconstructor,andenterthefollowinghighlightedcodesections:

publicWalkEntryViewModel()

{

Title="NewWalk";

Difficulty="Easy";

Distance=1.0;

}

6. Then,weneedtocreateaCommandpropertyforourclass.ThiswillbeusedwithinourWalkEntryPageandwillbeusedtobindtotheSaveToolBarItem.TheCommandpropertywillrunanactionuponbeingpressed,thenexecuteaclassinstancemethodtodeterminewhetherthecommandcanbeexecuted.Proceedandenterthefollowinghighlightedcodesections:

Command_saveCommand;

publicCommandSaveCommand

{

get

{

return_saveCommand??(_saveCommand=new

Command(ExecuteSaveCommand,ValidateFormDetails));

}

}

7. Next,weneedtocreatetheExecuteSaveCommandinstancemethod.Thiswillbeusedtostorethewalkinformationthatweenter,whichwillbewrittentoeachofthepropertiesasdefinedwithintheWalkEntriesmodel.Thesavingportionwillnotbecoveredinthischapter,asthiswillbecoveredinalaterchapter.Fornow,proceedandenterinthefollowinghighlightedcodesections:

voidExecuteSaveCommand()

{

varnewWalkItem=newWalkEntries

{

Title=this.Title,

Notes=this.Notes,

Latitude=this.Latitude,

Longitude=this.Longitude,

Kilometers=this.Kilometers,

Difficulty=this.Difficulty,

Distance=this.Distance,

ImageUrl=this.ImageUrl

};

//Here,wewillsavethedetailsenteredinalaterchapter.

}

8. Finally,createtheValidateFormDetailsinstancemethod.Thiswillbeusedtodeterminewhetherornotwecansaveournewwalkinformation.Thismethodisprettybasic,butyoucouldaddadditionalchecksdependingonyourneeds.Forthisexample,we'llusetheIsNullOrWhiteSpacemethodonthestringclass,andpassintheTitleproperty,whichcheckstoseeiftheTitlepropertycontainsanyblankspaces,orisanemptyfield.Toproceed,enterinthefollowinghighlightedcodesection:

//methodtocheckforanyformerrors

boolValidateFormDetails()

{

return!string.IsNullOrWhiteSpace(Title);

}

}

}

Inthissection,webeganbyensuringthatourViewModelinheritsfromtheWalkBaseViewModelclassandthenmovedontocreateaTitlepropertyanditsassociatedgettersandsetterqualifiers.WealsocreatedtheOnPropertyChangedmethod,aswedefinedpreviouslysothatitwillbecalledwhenthepropertydeterminesthatthecontentshavebeenchanged.

Next,weaddedareferencetothemethodSaveCommand.ChangeCanExecutethatwillvalidatetheformfieldstodetermineiftheSaveToolBarItemshouldbeenabledtoallowtheusertosavethewalkentrydetails.WethencreatedtheremainingpropertiesforourViewModelwiththeirassociatedgettersandsetters,whichwillbeusedtobindthevaluesenteredontheWalkEntryPage.

Inthenextsteps,weinitializedtheclassconstructorwithdefaultvaluesfortheTitle,DifficultyandDistancepropertiesandthencreatedaCommandpropertytoourclasssothatitcanbeusedwithintheWalkEntryPageandwillbeusedtobindtotheSaveToolBarItem.

Finally,weneededtocreatetheExecuteSaveCommandinstancemethodsothatitcanstoreourwalkinformationtoeachofthepropertiesasdefinedwithintheWalkEntriesmodel.

UpdatingtheWalksEntryPagetousetheMVVMmodelInthissection,weneedtobindourmodelbindingcontextBindingContexttotheWalkEntryViewModelsothatthenewwalkinformationthatwillbeenteredwithinthispagecanbestoredwithintheWalkEntriesmodel.Let'stakealookathowwecanachievethis,byfollowingthesesteps:

1. EnsurethattheWalkEntryPage.csfileisdisplayedwithinthecodeeditor.

//

//WalkEntryPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms;

usingTrackMyWalks.Models;

usingSystem.Collections.Generic;

namespaceTrackMyWalks

{

publicclassWalkEntryPage:ContentPage

{

publicWalkEntryPage()

{

//SettheContentPageTitle

Title="NewWalkEntry";

2. Next,weneedtodeclareandcreateanewBindingContextinstancefortheWalksEntryPage,andsetthistoanewinstanceoftheWalkEntryViewModelsothatitknowswheretogettheWalkEntriessothatwecanbindittotheassociatedpropertiescontainedwithinourmodel.Proceedandenterinthefollowinghighlightedcodesections:

//DeclareandinitializeourModelBindingContext

BindingContext=newWalkEntryViewModel();

3. Next,createtheremainingEntryCellobjects,aswellastheSetBindingpropertiestotheirmatchedpropertynameascontainedwithintheViewModel,asshowninthefollowingcodesnippets:

//DefineourNewWalkEntryfields

varwalkTitle=newEntryCell

{

Label="Title:",

Placeholder="TrailTitle"

};

walkTitle.SetBinding(EntryCell.TextProperty,"Title",

BindingMode.TwoWay);

varwalkNotes=newEntryCell

{

Label="Notes:",

Placeholder="Description"

};

walkNotes.SetBinding(EntryCell.TextProperty,"Notes",

BindingMode.TwoWay);

varwalkLatitude=newEntryCell

{

Label="Latitude:",

Placeholder="Latitude",

Keyboard=Keyboard.Numeric

};

walkLatitude.SetBinding(EntryCell.TextProperty,

"Latitude",BindingMode.TwoWay);

varwalkLongitude=newEntryCell

{

Label="Longitude:",

Placeholder="Longitude",

Keyboard=Keyboard.Numeric

};

walkLongitude.SetBinding(EntryCell.TextProperty,

"Longitude",BindingMode.TwoWay);

varwalkKilometers=newEntryCell

{

Label="Kilometers:",

Placeholder="Kilometers",

Keyboard=Keyboard.Numeric

};

walkKilometers.SetBinding(EntryCell.TextProperty,

"Kilometers",BindingMode.TwoWay);

varwalkDifficulty=newEntryCell

{

Label="DifficultyLevel:",

Placeholder="WalkDifficulty"

};

walkDifficulty.SetBinding(EntryCell.TextProperty,

"Difficulty",BindingMode.TwoWay);

varwalkImageUrl=newEntryCell

Label="ImageUrl:",

Placeholder="ImageURL"

};

walkImageUrl.SetBinding(EntryCell.TextProperty,

"ImageUrl",BindingMode.TwoWay);

//DefineourTableView

Content=newTableView

{

Intent=TableIntent.Form,

Root=newTableRoot

{

newTableSection()

{

walkTitle,

walkNotes,

walkLatitude,

walkLongitude,

walkKilometers,

walkDifficulty,

walkImageUrl

}

}

};

varsaveWalkItem=newToolbarItem

{

Text="Save"

};

saveWalkItem.SetBinding(MenuItem.CommandProperty,

"SaveCommand");

ToolbarItems.Add(saveWalkItem);

saveWalkItem.Clicked+=(sender,e)=>

{

Navigation.PopToRootAsync(true);

};

}

}

}

Inthissection,welookedatthestepsinvolvedinmodifyingourWalksEntryPagesothatitcantakeadvantageofourWalksEntryViewModel.WelookedathowtosetthecontentpagetoaninstanceoftheWalksEntryViewModelsothatitpointstotheWalkEntriesmodel.WethenusedtheSetBindingmethodoneachofourEntryCellssothatitcanbindtotheappropriateViewModelproperty.Finally,weupdatedtheSaveToolbarItemontheWalkEntryPagetobindtotheWalksEntryViewModelSaveCommandproperty.

Thefollowingtableprovidesabriefdescriptionofthedifferentbindingtypes,andwhentheseshouldbeusedwithinyourapplications:

Bindingmode Description

OneWayThistypeofbindingindicatesthatthebindingshouldonlypropagatechangesfromsource(usuallytheViewModel)totarget(theBindableObject).ThisisthedefaultmodeformostBindablePropertyvalues.

OneWayToSourceThistypeofbindingindicatesthatthebindingonlypropagateschangesfromthetargetBindableObjecttotheViewModelandismainlyusedforread-onlyBindablePropertyvalues.

TwoWayThistypeofbindingindicatesthatthebindingshouldpropagatethechangesfromtheViewModeltothetargetBindableObjectinbothdirections.

Onethingtonoticeisthat,ifyoudon'tspecifyavaluefortheBindingModeproperty,itwillusethedefaultBindingMode.OneWaywhichisdefinedintheBindableProperty.DefaultBindingModeproperty.

ImplementingthewalktrailpageViewModelInthissection,wewillbetakingalookatthestepsrequiredtocreatetheViewModelforourWalksTrailViewModelsothatwecanobtainthewalkentryinformationassociatedwiththechosenwalkthathasbeenselectedfromthemainWalksPage.

Let'stakealookathowwecanachievethis,byfollowingthesesteps:

1. CreateanewclassfilewithintheViewModelsfoldercalledWalksTrailViewModel,asyoudidintheprevioussectionentitledCreatingtheWalkBaseViewModel,locatedwithinthischapter.

2. Next,ensurethattheWalksTrailViewModel.csfileisdisplayedwithinthecodeeditor,andenterthefollowingcodesnippet:

//

//WalksTrailViewModel.cs

//TrackMyWalksViewModels

//

//CreatedbyStevenF.Danielon22/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingTrackMyWalks.Models;

namespaceTrackMyWalks.ViewModels

{

publicclassWalksTrailViewModel:WalkBaseViewModel

{

WalkEntries_walkEntry;

publicWalkEntriesWalkEntry

{

get{return_walkEntry;}

set

{

_walkEntry=value;

OnPropertyChanged();

}

}

publicWalksTrailViewModel(WalkEntrieswalkEntry)

{

WalkEntry=walkEntry;

}

}

}

Inthissection,webeganbyensuringthatourViewModelinheritsfromtheWalkBaseViewModelclassandthenwecreatedanWalkEntriesvariable_walkEntriesthatwillbeusedtostoreourWalkEntries.Next,wecreatedaWalkEntrypropertyanditsassociatedgettersandsetter

qualifiersandtheOnPropertyChangedmethod,sothatitwillbecalledwhenourpropertydeterminesthatthecontentshavebeenchanged.Inthefinalstep,wecreatedtheWalksTrailViewModelconstructor,thatacceptsaListparametercontainingourWalkEntriesmodel.

UpdatingtheWalksTrailPagetousetheMVVMmodelInthissectionweneedtobindourmodelbindingcontextBindingContexttotheWalksTrailViewModelsothatthewalkinformationdetailswillbedisplayedfromtheWalkEntriesmodelwhenawalkhasbeenclickedonwithinthemainWalksPage.Let'stakealookathowwecanachievethis,byfollowingthesesteps:

1. EnsurethattheWalkTrailPage.csfileisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesections:

//

//WalkTrailPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms;

usingTrackMyWalks.Models;

usingTrackMyWalks.ViewModels;

namespaceTrackMyWalks

{

publicclassWalkTrailPage:ContentPage

{

publicWalkTrailPage(WalkEntrieswalkItem)

{

Title="WalksTrail";

2. Next,weneedtodeclareandcreateanewBindingContextinstancefortheWalksTrailPage,andsetthistoanewinstanceoftheWalksTrailViewModelsothatitknowswheretogettheWalkEntriessothatwecanbindittotheassociatedpropertiescontainedwithinourModelanddisplaythiswithintheView.Proceedandenterthefollowinghighlightedcodesections:

//DeclareandinitializeourModelBindingContext

BindingContext=newWalksTrailViewModel(walkItem);

varbeginTrailWalk=newButton

{

BackgroundColor=Color.FromHex("#008080"),

TextColor=Color.White,

Text="BeginthisTrail"

};

//DeclareandinitializeourEventHandler

beginTrailWalk.Clicked+=(sender,e)=>

{

if(walkItem==null)return;

Navigation.PushAsync(newDistanceTravelled(walkItem));

Navigation.RemovePage(this);

walkItem=null;

};

3. Next,createtheremainingImageandLabelcontrolobjects,aswellastheSetBindingpropertiestotheirmatchedpropertynameascontainedwithintheViewModel,asshowninthefollowingcodesnippets:

varwalkTrailImage=newImage()

{

Aspect=Aspect.AspectFill

};

walkTrailImage.SetBinding(Image.SourceProperty,

"WalkEntry.ImageUrl");

vartrailNameLabel=newLabel()

{

FontSize=28,

FontAttributes=FontAttributes.Bold,

TextColor=Color.Black

};

trailNameLabel.SetBinding(Label.TextProperty,

"WalkEntry.Title");

vartrailKilometersLabel=newLabel()

{

FontAttributes=FontAttributes.Bold,

FontSize=12,

TextColor=Color.Black,

};

trailKilometersLabel.SetBinding(Label.TextProperty,

"WalkEntry.Kilometers",stringFormat:"Length:{0}km");

vartrailDifficultyLabel=newLabel()

{

FontAttributes=FontAttributes.Bold,

FontSize=12,

TextColor=Color.Black

};

trailDifficultyLabel.SetBinding(Label.TextProperty,

"WalkEntry.Difficulty",stringFormat:"Difficulty:{0}");

vartrailFullDescription=newLabel()

{

FontSize=11,

TextColor=Color.Black,

HorizontalOptions=LayoutOptions.FillAndExpand

};

trailFullDescription.SetBinding(Label.TextProperty,

"WalkEntry.Notes");

this.Content=newScrollView

{

Padding=10,

Content=newStackLayout

{

Orientation=StackOrientation.Vertical,

HorizontalOptions=LayoutOptions.FillAndExpand,

Children=

{

walkTrailImage,

trailNameLabel,

trailKilometersLabel,

trailDifficultyLabel,

trailFullDescription,

beginTrailWalk

}

}

};

}

}

}

Inthissection,welookedatthestepsinvolvedinmodifyingourWalksTrailPagesothatitcantakeadvantageoftheWalksTrailViewModel.WelookedathowtosetthecontentpagetoaninstanceofourWalksTrailViewModelsothatitknowswheretogetthelistofwalkentriestobeusedanddisplayedwithinourStackLayoutcontrol.WeusedtheSetBindingpropertytocreateandbindeachofourmodelvaluestoaspecificproperty.

Finally,wedefinedaScrollViewcontrol,thenaddedeachofourformImageandLabelfieldstotheStackLayoutcontrol.

ImplementingtheDistanceTravelledViewModelInthissection,wewillbetakingalookatthestepsrequiredtocreatetheViewModelforourDistTravelledViewModelsothatwecancalculatehowlongthechosenwalkfromthemainWalksPagetooktocomplete.

Let'stakealookathowwecanachievethis,byfollowingthesesteps:

1. CreateanewclassfilewithintheViewModelsfoldercalledDistTravelledViewModel,asyoudidintheprevioussectionentitledCreatingtheWalkBaseViewModel,locatedwithinthischapter.

2. Next,ensurethattheDistTravelledViewModel.csfileisdisplayedwithinthecodeeditor,andenterthefollowingcodesnippet:

//

//DistTravelledViewModel.cs

//TrackMyWalksViewModels

//

//CreatedbyStevenF.Danielon22/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingTrackMyWalks.Models;

usingTrackMyWalks.ViewModels;

namespaceTrackMyWalks.ViewModels

{

publicclassDistTravelledViewModel:WalkBaseViewModel

{

WalkEntries_walkEntry;

3. Then,createthefollowingWalkEntrypropertyanditsassociatedgettersandsetterqualifiers.TheOnPropertyChangedmethod,aswementionedpreviously,willbecalledwhenourpropertydeterminesthatthecontentshavebeenchanged.Proceedandenterinthefollowingcodesnippet:

publicWalkEntriesWalkEntry

{

get{return_walkEntry;}

set

{

_walkEntry=value;

OnPropertyChanged();

}

}

4. Next,createtheremainingViewModelpropertiesandtheassociatedgettersandsetterqualifiersthatwillbeusedtobindthevaluesenteredontheDistanceTravelledPage,asshowninthefollowingcodesnippets:

double_travelled;

publicdoubleTravelled

{

get{return_travelled;}

set

{

_travelled=value;

OnPropertyChanged();

}

}

double_hours;

publicdoubleHours

{

get{return_hours;}

set

{

_hours=value;

OnPropertyChanged();

}

}

double_minutes;

publicdoubleMinutes

{

get{return_minutes;}

set

{

_minutes=value;

OnPropertyChanged();

}

}

double_seconds;

publicdoubleSeconds

{

get{return_seconds;}

set

{

_seconds=value;

OnPropertyChanged();

}

}

publicstringTimeTaken

{

get

{

returnstring.Format("{0:00}:{1:00}:{2:00}",this.Hours,

this.Minutes,this.Seconds);

}

}

publicDistTravelledViewModel(WalkEntrieswalkEntry)

{

this.Hours=0;

this.Minutes=0;

this.Seconds=0;

this.Travelled=100;

WalkEntry=walkEntry;

}

}

}

Inthissection,webeganbyensuringthatourViewModelinheritsfromtheWalkBaseViewModelclassandthenwecreatedanWalkEntriesvariable_walkEntriesthatwillbeusedtostoreourWalkEntries.

Next,wecreatedaWalkEntrypropertyanditsassociatedgettersandsetterqualifiersandtheOnPropertyChangedmethod,sothatitwillbecalledwhenourpropertydeterminesthatthecontentshavebeenchanged.

Inourfinalstep,wecreatedtheDistTravelledViewModelconstructor,thatacceptsaListparametercontainingourWalkEntriesmodel,priortoinitializingourclassconstructorwithdefaultvaluesfortheHours,Minutes,Seconds,andTravelledproperties.

UpdatingtheDistanceTravelledPagetousetheMVVMmodelInthissectionweneedtobindourmodelbindingcontextBindingContexttotheDistTravelledViewModelsothatthewalkinformationdetailsandthecalculationsofdistancetravelledwillbedisplayedfromtheWalkEntriesmodel.

Let'stakealookathowwecanachievethis,byfollowingthesesteps:

1. EnsurethattheDistanceTravelledPage.csfileisdisplayedwithinthecodeeditor.

//

//DistanceTravelledPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms;

usingXamarin.Forms.Maps;

usingTrackMyWalks.Models;

namespaceTrackMyWalks

{

publicclassDistanceTravelledPage:ContentPage

{

2. Next,weneedtoupdatethemaptogetitsvaluesfromtheDistTravelledViewModelsothatitcorrectlyplotsthesewithinthemapview.WeneedtodothisbecausetheXamarin.Forms.Mapcontroldoesn'tprovidesupportforbindingdirectlytothemap,sowehavetosetthesedirectlywithinourViewModelinstead.Therefore,weneedtocreateaprivate_viewModelvariablewithinthecontentpagetoreturnthevaluefromthepage'sBindingContext.

3. Proceedandenterthefollowinghighlightedcodesections:

DistTravelledViewModel_viewModel

{

get{returnBindingContextasDistTravelledViewModel;}

}

publicDistanceTravelledPage(WalkEntrieswalkItem)

{

Title="DistanceTravelled";

4. Next,createanewBindingContextinstancefortheDistanceTravelledPage,andsetthistoanewinstanceoftheDistTravelledViewModelsothatitknowswheretogettheWalkEntriestobindittotheassociatedpropertiescontainedwithinourmodelanddisplaythiswithintheView.Proceedandenterinthefollowinghighlightedcodesections:

//DeclareandinitializeourModelBindingContext

BindingContext=newDistTravelledViewModel(walkItem);

//Instantiateourmapobject

vartrailMap=newMap();

5. Next,updatethemaptograbthenameofthechosenwalkandtheLatitudeandLongitudevaluesfromtheDistTravelledViewModel.Proceedandenterthefollowinghighlightedcodesections:

//Placeapinonthemapforthechosenwalktype

trailMap.Pins.Add(newPin

{

Type=PinType.Place,

Label=_viewModel.WalkEntry.Title,

Position=newPosition(_viewModel.WalkEntry.Latitude,

_viewModel.WalkEntry.Longitude)

});

//Centerthemaparoundthelistofwalksentry'slocation

trailMap.MoveToRegion(MapSpan.FromCenterAndRadius(new

Position(_viewModel.WalkEntry.Latitude,

_viewModel.WalkEntry.Longitude),

Distance.FromKilometers(1.0)));

6. Next,createtheremainingLabelcontrolobjects,aswellastheSetBindingpropertiestotheirmatchedpropertynameascontainedwithintheViewModel,asshowninthefollowingcodesnippets:

vartrailNameLabel=newLabel()

{

FontSize=18,

FontAttributes=FontAttributes.Bold,

TextColor=Color.Black,

HorizontalTextAlignment=TextAlignment.Center

};

trailNameLabel.SetBinding(Label.TextProperty,

"WalkEntry.Title");

vartrailDistanceTravelledLabel=newLabel()

{

FontAttributes=FontAttributes.Bold,

FontSize=20,

TextColor=Color.Black,

HorizontalTextAlignment=TextAlignment.Center

};

trailDistanceTravelledLabel.SetBinding(Label.TextProperty,

"Travelled",stringFormat:"DistanceTravelled:{0}km");

vartotalTimeTakenLabel=newLabel()

{

FontAttributes=FontAttributes.Bold,

FontSize=20,

TextColor=Color.Black,

HorizontalTextAlignment=TextAlignment.Center

};

totalTimeTakenLabel.SetBinding(Label.TextProperty,"TimeTaken",

stringFormat:"TimeTaken:{0}");

varwalksHomeButton=newButton

{

BackgroundColor=Color.FromHex("#008080"),

TextColor=Color.White,

Text="EndthisTrail"

};

//Setupoureventhandler

walksHomeButton.Clicked+=(sender,e)=>

{

if(walkItem==null)return;

Navigation.PopToRootAsync(true);

walkItem=null;

};

this.Content=newScrollView

{

Padding=10,

Content=newStackLayout

{

Orientation=StackOrientation.Vertical,

HorizontalOptions=LayoutOptions.FillAndExpand,

Children={

trailMap,

trailNameLabel,

trailDistanceTravelledLabel,

totalTimeTakenLabel,

walksHomeButton

}

}

};

}

}

}

Inthissection,welookedatthestepsinvolvedinmodifyingtheDistanceTraveledPagesothatitcantakeadvantageoftheDistTravelledViewModel.WelookedathowtosetthecontentpagetoaninstanceofourDistTravelledViewModel.WeusedtheSetBindingpropertytocreateandbindeachofourmodelvaluestoaspecificproperty.Finally,wedefinedaScrollViewcontrol,thenaddedeachoftheformImageandLabelfieldstotheStackLayoutcontrol.

NowthatyouhavecreatedalloftheMVVMViewModelsandhaveupdatedtheassociatedcontentpages,ournextstepistofinallybuildandruntheTrackMyWalksapplicationwithintheiOSsimulator.

Whencompilationcompletes,theiOSSimulatorwillappearautomaticallyandtheTrackMyWalksapplicationwillbedisplayed,asshowninthefollowingscreenshot:

Asyoucanseefromtheprecedingscreenshot,thiscurrentlydisplaysalistofstatictrailentries,thataredisplayedwithintheListView.WhentheuserclicksontheAddWalkbuttonlink,thiswilldisplaytheNewWalkEntrycontentpage.

AsyoucanseefromtheNewWalkEntryscreen,wehavesuccessfullybindedeachoftheEntryCellpropertiestoourWalkEntryViewModelandhavedisplayedthisinformationbesideeachofthecontentpageproperties.IfyouclearouttheinformationassociatedwiththeTitleproperty,youwillnoticethattheSavebuttonwilldim.ThisisbecausetheValidateFormDetailsinstancemethodperformsacheck,thentheSaveCommandcommandeventistriggered,andhandlesitaccordingly.

Theprecedingscreenshotshowsyouthenavigationflowbetweeneachofthepageswhenatrailhasbeenselectedfromthelist,withthefinalscreenshowingtheDistanceTravelledpagealongwiththeplaceholderpinmarkershowingthetraillocationwithinthemapview.

SummaryInthischapter,weupdatedourTrackMyWalksapplicationandcreatedanumberofViewModels.Wethenremovedandmigratedourdataandlogicfromeachofourpages,thenaddedbindingtothosepagessothattheypointdirectlytotheViewModelsassociatedwiththepage.

Inthenextchapter,youwilllearnaboutthedifferentnavigationtechniquesthatcanbeusedwithintheMVVMmodelarchitecture,tonavigatebetweenViewModelsbycreatingandimplementinganavigationservicebetweeneachoftheViews,sothatyoucaneasilynavigatebetweenthem.

Chapter3.NavigatingwithintheMVVMModel-TheXamarin.FormsWayUptothispoint,youhaveseenhowtoincorporatetheMVVMarchitecturalpatternintoyourapplications,sothatitenforcestheseparationbetweentheapplication'suserinterface,orpresentationlayer,fromtheunderlyingdata.ThisisdonebyusingaclassthatactsasthecommunicationlayerbetweenboththeViewandtheViewModel,andisconnectedthroughdatabindingsalongwiththebindingcontextfortheView,pointingtoaninstanceoftheViewModel.

Inthischapter,youwillseehowyoucanleveragewhatyoualreadyknowabouttheMVVMdesignpattern,andwewilllearnhowtomovenavigationintotheViewModels.You'lllearnhowtocreateaC#classthatwillactasthenavigationserviceforourapp,aswellashowtoupdateourexistingWalkBaseViewModelclassfile.ThiswillincludeanumberofabstractclassmethodsthateachofourViewModelswillinherit,andinturnupdatethecontentpagestobindwiththeViewModelstoallownavigationbetweentheseViewstohappen.

Thischapterwillcoverthefollowingtopics:

UnderstandingtheXamarin.FormsNavigationAPIpatternarchitectureCreatinganavigationserviceusingtheXamarin.Forms.NavigationclassUpdatingtheTrackMyWalksapplicationtousethenavigationserviceUpdatingtheMVVMViewModelstousethenavigationserviceinterfaceUpdatingtheuserinterfacecontentpagestousetheupdatedMVVMViewModels

UnderstandingtheXamarin.FormsNavigationAPIInthissection,wewilltakealookattheXamarin.FormsNavigationAPIpatternarchitectureandgainanunderstandingintothedifferenttypesofnavigationpatternsthatareavailable.

TheXamarin.FormsNavigationAPIisexposedthroughtheXamarin.Forms.INavigationinterface,andisimplementedviatheNavigationproperty.TheNavigationpropertycanbecalledfromanyXamarin.Formsobject,typicallytheXamarin.Forms.PagethatinheritsfromtheContentPageclassthatispartoftheXamarin.Forms.Coreassembly.

TheXamarin.FormsNavigationAPIsupportstwodifferenttypesofnavigation-hierarchicalandmodal,andtheseareexplainedinthefollowingtable:

Navigationpage Description

Hierarchical

Thehierarchicalnavigationtypeisessentiallyastack-basednavigationpatternthatenablesuserstomoveiterativelythrougheachofthescreenswithinthehierarchy,andthennavigatebackoutagain,onescreenatatime,removingthemfromthenavigationstack.

ModalThemodalnavigationtypeisasinglepop-uporscreenthatinterruptsthehierarchicalnavigationbyrequiringtheusertorespondtoanaction,priortothescreenorpopupfrombeingdismissed.

Thehierarchicalnavigationpatternprovidesameansofnavigatingthroughthenavigationalstructureandistypicallythemostused.Thisinvolvestheusertappingtheirwayforwardthroughaseriesofpages,andthennavigatingbackwardsthroughthestackusingthenavigationmethodsonAndroidoriOSdevices.

Thefollowingscreenshotshowstheprocesswhenmovingfromonepagetoanotherwithinthehierarchicalnavigationmodel,andpoppingpagesfromtheNavigationStack.Wheneveranewpageispushedontothenavigationstack,thiswillbecometheactivepage.

Alternatively,whenyouwanttoreturnbacktothepreviouspage,theapplicationwillstartbypoppingthecurrentpagefromthenavigationstack,andthenewtopmostpagewillthenbecometheactivepage.Themodalnavigationpatterndisplaysapageontopofthecurrentpagethatpreventstheuserfromanyinteractionfromthepageunderneathit,andprovidestheuserwithchoicesforwhattheywanttodobeforethemodalpagecanbeclosed.

TheINavigationinterface,whichispartoftheXamarin.Forms.NavigationPage,implementsandexposestwoseparateread-onlyproperties-NavigationStackandModalStack.Thiswillallowyoutoviewboththehierarchicalandmodalnavigationstacks.

TheXamarin.FormsINavigationinterfaceprovidesyouwithseveralmethodsthatwillallowyoutoasynchronouslypush(add)andpop(remove)pagesontothenavigationandmodalstacks,andtheseareexplainedinthetablebelow:

Navigationmethods Description

PushAsync(Page

page)

ThismethodaddsanewpageatthetopoftheNavigationStackthatenablesuserstomovedeeperwithinthescreenhierarchy.

PopAsync()

ThismethodallowsyoutonavigatebackthroughtheNavigationStacktothepreviouspage,ifonehasbeenpreviouslyaddedtotheNavigationStack.

Thismethodallowsyoutodisplayapagemodallywhenyouneedtoeitherdisplaysomeinformationalinformationorrequestinformation

PushModalAsync(Page

page)fromtheuser.Agoodexampleofamodalpagewouldbeasign-onpage,whereyouneedtogetusercredentials.

PopModalAsync()Thismethodwilldismissthecurrentlydisplayedmodalpageandreturnyoutothepagedisplayedunderneath.

Aswellastheabovementionednavigationmethods,theXamarin.Forms.INavigationinterfaceprovidesyouwithanumberofadditionalmethodsthatwillhelpyoumanipulatetheNavigationStack,andtheseareexplainedinthefollowingtable:

Navigationmethods Description

InsertPageBefore(Page

page,Pagebefore)

ThismethodallowsyoutoinsertapagebeforeaspecificpagethathasalreadybeenaddedtotheNavigationStack.

RemovePage(Pagepage)ThismethodallowsyoutoremoveaspecificpagewithintheNavigationStack.

PopToRootAsync()

ThismethodnavigatesyoubacktothefirstpagethatiscontainedwithintheNavigationStackwhilstremovingalloftheotherpagesthatarecontainedwithintheNavigationStack.

NowthatyouhaveagoodunderstandingofthecomponentsthatarecontainedwithintheNavigationAPIpatternarchitecture,wecanbegintotakealookatsomeofthedifferentapproachestonavigatingbetweenpagesandViewModels.

DifferencesbetweenthenavigationandViewModelapproachesInthissection,wewilltakealookattheapproacheswhenperformingnavigationwithinViewModelscontainedwithinanXamarin.Formssolution.WhenperformingnavigationwithinyourViewModels,thereareacoupleofapproachesthatyoushouldconsiderbeforegoingdownthispath.Anapproachwouldbetousethepagenavigationapproach,whichinvolvesnavigatingtoanotherpageusingadirectreferencetothatpage.

ThepagenavigationapproachcanbeaccomplishedinXamarin.FormsbyessentiallypassingthecurrentINavigationinstanceintoaViewModel'sobjectconstructor,whichwillforcetheViewModeltousetheXamarin.Formsdefaultnavigationmechanismtonavigatetootherpages.

IfyouwantedtousetheViewModelapproachtonavigatetoapageusingtheassociatedpagesViewModel,youwouldneedtoformsomesortofmappingbetweeneachofthepages,aswellastheirassociatedViewModels.Thiswouldbedonebycreatingadictionaryorkey-valuetypepropertyinthenavigationservicethatwillmaintainaone-to-onemappingforeachofthepagesandtheirtype.

InMVVM,actionstakenbytheuseronaparticularpageareboundtocommandsthatarepartofthepages,ViewModel,andsothisprocessneedstobethoughtthroughdifferentlywhennavigatingtoanotherpage,oreventhepreviouspage,whenperformingtaskssuchassavingdataorupdatingamapslocation.Assuch,weneedtorethinkhowtoachievenavigationthatleveragestheMVVMdesignpatternwithinourapp,sothatitcanbecontrolledbytheViewModelsandnotbytheunderlyingpages.

WhenusingtheViewModelstohandlethenavigation,thisalleviatestheneedforaViewModeltohaveanydependenciesonthespecificimplementationofapage,andbecausetheViewModeldoesn'tnavigatedirectlytoapageviatheContentPage'sViewModel,thismeansthatwhenyouimplementthisapproach,thereisaneedforarelationshipormappingtobedonebetweeneachofthepagesandtheirassociatedViewModels.

Inthenextsection,wewillbetakingalookathowtonavigatethroughourTrackMyWalksappbycreatinganavigationservicethatwillincludeViewModelandpagetypemappings.

ImplementingthenavigationservicewithinyourappInthissection,wewillbeginbysettingupthebasicstructureforourTrackMyWalkssolutiontoincludethefolderthatwillbeusedtorepresentourServices.Let'stakealookathowwecanachievethis,byperformingfollowingthesteps:

1. LaunchtheXamarinStudioapplicationandensurethattheTrackMyWalkssolutionisloadedwithintheXamarinStudioIDE.

2. Next,createanewfolderwithintheTrackMyWalksPortableClassLibraryproject,calledServices,asshowninthefollowingscreenshot:

Nowthatwehavecreatedthefolderstructurethatwillbeusedtostoreournavigationservices,wecanbegintostartbuildingtheNavigationServiceInterfaceclassthatwillbeusedbyourNavigationServiceclassandinturnusedbyourViewModels.

CreatingthenavigationserviceinterfacefortheTrackMyWalksappInthissection,wewillbeginbycreatinganavigationserviceinterfaceclassthatwillextendfromtheXamarin.Formsnavigationabstractionlayer.ThisissothatwecanperformViewModeltoViewModelnavigationwithinourMVVMdesignpattern,andinturnbindwithourcontentpagestoallownavigationbetweentheseViewstohappen.

Wewillfirstneedtodefinetheinterfaceforournavigationservice,asthiswillcontainanddefineitsmethods,andwillmakeitaloteasierifweeverwantedtoaddnewmethodimplementationsforourservice,withouttheneedtochangeeachofourViewModels.

Let'stakealookathowwecanachievethis,byperformingthefollowingsteps:

1. CreateanemptyclasswithintheServicesfolder,asshowninthefollowingscreenshot:

2. Next,choosetheEmptyInterfaceoptionlocatedwithintheGeneralsection,andenterinIWalkNavServiceforthenameofthenewinterfacefiletocreate,asshowninthefollowingscreenshot:

3. Next,clickontheNewbuttontoallowthewizardtoproceedandcreatethenewemptyclassfile,asshownintheprecedingscreenshot.

Upuntilthispoint,allwehavedoneiscreateourIWalkNavServiceclassfile.ThisInterfaceclasswillbeusedandwillactasthebaseNavigationServiceclassthateachofourViewModelswillinheritfrom.AswestarttobuildtheNavigationServiceInterfaceclass,youwillseethatitcontainsacoupleofclassmembersthatwillbeusedbyourcontentpagesandViewModels,aswewillbeusingthisasourbaseclasswithinourViewModelsusedbytheTrackMyWalksapplication.

ToproceedwithcreatingthebaseIWalkNavServiceinterface,ensurethattheIWalkNavService.csfileisdisplayedwithinthecodeeditor,andenterinthefollowingcodesnippet:

//

//IWalkNavService.cs

//TrackMyWalksNavigationServiceInterface

//

//CreatedbyStevenF.Danielon03/09/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem.Threading.Tasks;

usingTrackMyWalks.ViewModels;

namespaceTrackMyWalks.Services

{

publicinterfaceIWalkNavService

{

//NavigatebacktothePreviouspagein

theNavigationStack

TaskPreviousPage();

//Navigatetothefirstpagewithin

theNavigationStack

TaskBackToMainPage();

//NavigatetoaparticularViewModelwithin

ourMVVMModel,

//andpassaparameter

TaskNavigateToViewModel<ViewModel,

TParameter>(TParameterparameter)

whereViewModel:WalkBaseViewModel;

}

}

Intheprecedingcodesnippet,westartedbycreatinganewInterfaceclassforourIWalkNavServicethatallowstheabilitytonavigatetoeachofourViewModels,aswellasnavigatingbacktothePreviousPagemethod,andbacktothefirstpagewithinourhierarchicalmodel,asdeterminedbytheBackToMainPagemethod.

Note

Aninterfacecontainsonlythemethods,properties,andeventsignaturedefinitions.Anyclassthatimplementstheinterfacemustimplementallmembersoftheinterfacethatarespecifiedintheinterfacedefinition.

TheNavigateToViewModelmethoddeclaresagenerictypewhichisusedtorestricttheViewModeltoitsusetoobjectsoftheWalkBaseViewModelbaseclass,andastrongly-typedTParameterparametertobepassedalongwiththenavigation.

Note

Thetermstrongly-typedmeansthat,ifavariablehasbeendeclaredofaspecifictype(string,integer,ordefinedasauser-definedtype),itcannotbeassignedavalueofadifferenttypelateron,asthiswillresultinthecompilernotifyingyouofanerror.Anexamplewouldbe:inti=10;i="Ten"

TheTaskclassisessentiallyusedtohandleasynchronousoperations.Thisisdonebyensuringthattheasynchronousmethodyouinitiatedwilleventuallyfinish,thuscompletingthetask.The

TaskobjectisusedtoreturnbackinformationonceithasfinishedbyreturningbackaTaskobjectalmostinstantaneously,althoughtheunderlyingworkwithinthemethodwouldlikelyfinishlater.

Tohandlethis,youcanusetheawaitkeywordtowaitforthetasktocompletewhichwillblockthecurrentthreadandwaituntiltheasynchronousmethodhascompleted.

CreatinganavigationservicetonavigatewithinourViewModelsIntheprevioussection,wecreatedourbaseinterfaceclassforournavigationserviceanddefinedanumberofdifferentmethods,whichwillbeusedtonavigatewithinourMVVMViewModel.

ThesewillbeusedbyeachofourViewModels,andtheViews(pages)willimplementtheseViewModelsandusethemastheirBindingContext.

Let'stakealookathowwecanachievethis,byperformingthefollowingthesteps:

1. CreateanemptyclasswithintheServicesfolder,asshowninthenextscreenshot.2. Next,choosetheEmptyClassoptionlocatedwithintheGeneralsection,andenterin

WalkNavServiceforthenameofthenewclassfiletocreate,asshowninthefollowingscreenshot:

3. Next,clickontheNewbuttontoallowthewizardtoproceedandcreatethenewemptyclassfile,asshownintheprecedingscreenshot.

4. Upuntilthispoint,allwehavedoneiscreateourWalkNavServiceclassfile.ThisclasswillbeusedandwillactasthebaseNavigationServiceclassthatwillcontainthefunctionalityrequiredthateachofourViewModelswillinheritfrom,inordertonavigatebetweeneachoftheViewModelswithinourMVVMmodel.

5. AswestarttobuildourNavigationclass,youwillseethatitcontainsanumberofmethodmembersthatwillbeusedtoenablenavigationbetweeneachofourViewModelsanditwillimplementtheIWalkNavServiceInterface.ToproceedwithcreatingthebaseWalkNavServiceclass,performthefollowingsteps:

6. EnsurethattheWalkNavService.csfileisdisplayedwithinthecodeeditor,andenterinthefollowingcodesnippet:

//

//WalkNavService.cs

//TrackMyWalksNavigationServiceClass

//

//CreatedbyStevenF.Danielon03/09/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingXamarin.Forms;

usingSystem.Collections.Generic;

usingSystem.Threading.Tasks;

usingSystem.Reflection;

usingSystem.Linq;

usingTrackMyWalks.ViewModels;

usingTrackMyWalks.Services;

First,weneedtoinitializeournavigationclasstobemarkedasadependency,byaddingtheDependencymetadataattributesothatitcanberesolvedbytheXamarin.FormsDependencyServiceclass.Thiswillenablesothatittofindanduseourmethodimplementationasdefinedbyourinterface.

7. Next,wealsoneedtoneedtoensurethatourWalkNavServiceclassinheritsfromtheIWalkNavServicenavigationinterfaceclass,sothatitcanaccessthemethodgettersandsetters.Proceedandenterinthefollowingcodesnippetasshownhere:

[assembly:Dependency(typeof(WalkNavService))]

namespaceTrackMyWalks.Services

{

publicclassWalkNavService:IWalkNavService

{

8. Next,weneedtocreateapublicINavigationpropertynamednavigation.ThenavigationpropertywillprovideourclasswithareferencetothecurrentXamarin.Forms.INavigationinstance,andthiswillneedtobesetwhenthenavigationserviceisfirstinitialized.WewillseehowthisisdoneasweprogressthroughupdatingourTrackMyWalksapp.Proceedandenterinthefollowingcodesnippet:

publicINavigationnavigation{get;set;}

9. ThenweneedtoregisterthenavigationservicetohandleContentPagetoViewModelmappingsbydeclaringadictionary_viewMappingpropertyvariablethatinheritsfromthe

IDictionaryinterface.Proceedandenterinthefollowingcodesnippet:

readonlyIDictionary<Type,Type>_viewMapping=

newDictionary<Type,Type>();

10. Next,weneedtodeclareanewmethodcalledRegisterViewMappingwhichwillbeusedtopopulateourViewModelandContentPage(View)withinthe_viewMappingdictionarypropertyobject.Proceedandenterinthefollowingcodesections:

//RegisterourViewModelandViewwithinourDictionary

publicvoidRegisterViewMapping(TypeviewModel,Typeview)

{

_viewMapping.Add(viewModel,view);

}

11. Then,weneedtocreatethePreviousPageinstancemethodforourWalkNavServiceclass.ThiswillbeusedtonavigatebacktothepreviouspagecontainedwithinourNavigationStack,byfirstcheckingtheNavigationStackpropertyofthenavigationpropertyINavigationinterfacetoensurethatitisnotnullandthatwehavemorethanoneViewModelcontainedwithinourNavigationStacktonavigatebackto.Ifwedon'tperformthischeck,itcouldresultinourapplicationcrashing.Finally,weusethePopAsyncmethodtoremovethelastviewaddedtoourNavigationStack,thusreturningbacktothepreviousViewModel.Proceedandenterinthefollowingcodesections:

//Instancemethodthatallowsustomovebacktothe

//previouspage.

publicasyncTaskPreviousPage()

{

//Checktoseeifwecanmovebacktothepreviouspage

if(navigation.NavigationStack!=null&&

navigation.NavigationStack.Count>0)

{

awaitnavigation.PopAsync(true);

}

}

12. Next,weneedtocreatetheBackToMainPageinstancemethodforourWalkNavServiceclass.ThiswillbeusedtotakeusbacktothefirstContentPagecontainedwithinourNavigationStack.WeusethePopToRootAsyncmethodofournavigationpropertyandusetheawaitoperatortowaituntilthetaskcompletes,beforeremovingallViewModelscontainedwithinourNavigationStackandreturningbackaTaskobject.Proceedandenterthefollowingcode:

//Instancemethodthattakesusbacktothemain

//RootWalksPage

publicasyncTaskBackToMainPage()

{

awaitnavigation.PopToRootAsync(true);

}

13. Then,weneedtocreatetheNavigateToViewModelinstancemethodforourWalkNavServiceclass.ThiswillbeusedtonavigatetoaspecificViewModelthatiscontainedwithinour_viewMappingdictionaryobject.Next,weusetheTryGetValue

methodofthe_viewMappingdictionaryobjecttochecktoseeifourViewModeldoesindeedexistwithinourdictionary,andreturnthenameoftheViewModel.

14. ThenameofthereturnedviewwillbestoredwithintheviewTypeobject,andwethenusethePushAsyncmethodtonavigatetothatview.Finally,wesettheBindingContextforthelastpushedviewthatiscontainedwithinourNavigationStack,andthennavigatetotheview,passinginanyparametersrequired.Proceedandenterinthefollowingcodesections:

//InstancemethodthatnavigatestoaspecificViewModel

//withinourdictionaryviewMapping

publicasyncTaskNavigateToViewModel<ViewModel,

WalkParam>(WalkParamparameter)

whereViewModel:WalkBaseViewModel

{

TypeviewType;

if(_viewMapping.TryGetValue(typeof(ViewModel),out

viewType))

{

varconstructor=viewType.GetTypeInfo()

.DeclaredConstructors

.FirstOrDefault(dc=>dc.GetParameters()

.Count()<=0);

varview=constructor.Invoke(null)asPage;

awaitnavigation.PushAsync(view,true);

}

if(navigation.NavigationStack.Last().BindingContextis

WalkBaseViewModel<WalkParam>)

await((WalkBaseViewModel<WalkParam>)(

navigation.NavigationStack.Last().BindingContext)).

Init(parameter);

}

}

}

Intheprecedingcodesnippet,webeganbyensuringthatourWalkNavServiceclassinheritsfromourIWalkNavServiceclass,andthenmovedontocreateanavigationpropertythatinheritsfromourINavigationclass.Wethencreateditsassociatedgetterandsetterqualifiers.

Finally,wecreatedtheinstancemethodsrequiredforourWalkNavServiceclass.

UpdatingtheWalkBaseViewModeltouseournavigationserviceInthissectionwewillproceedtoupdateourWalkBaseViewModelclasstoincludereferencestoourIWalkNavService.SinceourWalkBaseViewModelinheritsandisusedbyeachofourViewModels,itmakessensetoplaceitwithinthisclass.Thatway,ifweneedtoaddadditionalmethods,wecanjustaddthemwithinthisclass.Toproceed,performthefollowingsteps:

1. EnsurethattheWalkBaseViewModel.csfileisdisplayedwithinthecodeeditor,andenterinthefollowingcodesnippet:

//

//WalkBaseViewModel.cs

//TrackMyWalksBaseViewModel

//

//CreatedbyStevenF.Danielon22/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem.ComponentModel;

usingSystem.Runtime.CompilerServices;

usingSystem.Threading.Tasks;usingTrackMyWalks.Services;

namespaceTrackMyWalks.ViewModels

{

publicabstractclassWalkBaseViewModel:

INotifyPropertyChanged

{

2. Next,weneedtocreateaprotectedIWalkNavServicepropertynamedNavService.TheNavServicepropertywillprovideourclasswithareferencetothecurrentnavigationinstancethatiscontainedwithinourIWalkNavServiceinterfaceclass.Proceedandenterinthefollowingcodesection:

protectedIWalkNavServiceNavService{get;privateset;}

3. Then,weneedtomodifytheclassconstructoranddeclareanavServiceparameterthatinheritsfromourIWalkNavServiceinterfaceclass.Next,wesettheNavServicepropertyforourWalkBaseViewModelbaseclass,toaninstanceofthenavServiceparameter.Proceedandenterinthefollowinghighlightedcodesections:

protectedWalkBaseViewModel(IWalkNavServicenavService)

{

NavService=navService;

}

4. Next,weneedtocreatetheInitabstractmethodforourWalkBaseViewModelclassthatreturnsbackanasynchronousTaskobject.ThiswillbeusedtoinitializeourWalkBaseViewModel.Proceedandenterinthefollowinghighlightedcodesections:

publicabstractTaskInit();

publiceventPropertyChangedEventHandlerPropertyChanged;

protectedvirtualvoidOnPropertyChanged(

[CallerMemberName]stringpropertyName=null)

{

varhandler=PropertyChanged;

if(handler!=null)

{

handler(this,newPropertyChangedEventArgs(propertyName));

}

}

}

5. Then,weneedtocreateasecondaryabstractclassforourWalkBaseViewModelthatinheritsfromtheWalkBaseViewModelanddefinesageneric-typedTParameterobject.WethenproceedtooverloadtheWalkBaseViewModelclassconstructor,andsetthisclasstoinheritfromournavServicebaseclass.Proceedandenterinthefollowinghighlightedcodesections:

publicabstractclassWalkBaseViewModel<WalkParam>:

WalkBaseViewModel

{

protectedWalkBaseViewModel(IWalkNavServicenavService):

base(navService)

{

}

6. Next,weneedtooverridetheInitmethodforourWalkBaseViewModel<WalkParam>thatacceptsadefaultWalkParamvalueforourwalkDetailsmodel.Proceedandenterinthefollowinghighlightedcodesections:

publicoverrideasyncTaskInit()

{

awaitInit(default(WalkParam));

}

publicabstractTaskInit(WalkParamwalkDetails);

}

}

Intheprecedingcodesnippet,webeganbycreatingaNavServicepropertythatinheritsfromourIWalkNavServiceclass,andthencreateditsassociatedgetterandsetterqualifiers.

Next,weupdatetheWalkBaseViewModelclassconstructortosettheNavServicepropertytoaninstanceofournavService,beforecreatingourInitabstractionmethodthatwillbeusedtoinitializeourclass.

Inthenextstep,wecreateanewabstractclassforourWalkBaseViewModelthatimplementsfromtheWalkBaseViewModelclass,andthenoverloadsourclassconstructorsothatitinheritsfromournavServiceclass.

Next,we'lloverridetheInitmethodforourWalkBaseViewModel<WalkParam>thatacceptsadefaultWalkParamvalueforourwalkDetailsmodel.

UpdatingthewalksmainpageViewModelandnavigationserviceWehavecreatedourIWalkNavServiceInterfaceclassandupdatedtheNavServiceclasstoincludeallofthenecessaryclassinstancemethods.WealsomadesomechangestoourWalkBaseViewModelclasstoinheritfromourIWalkNavServicenavigationservice.WehavealsoincludedanadditionalabstractionclassthatwillbeusedtoinitializeourWalkBaseViewModelwhennavigatingbetweenViewModelswithinourMVVMmodel.

Ournextstepistomodifythewalksmainpage.Inthissection,wewillbetakingalookathowtoupdateourWalksPageViewModelsothatitcantakeadvantageofournavigationservice.

Let'stakealookathowwecanachievethis,byperformingthefollowingsteps:

1. EnsurethattheWalksPageViewModel.csfileisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesections:

//

//WalksPageViewModel.cs

//TrackMyWalksViewModels

//

//CreatedbyStevenF.Danielon22/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingSystem.Collections.ObjectModel;

usingSystem.Threading.Tasks;

usingTrackMyWalks.Models;

usingTrackMyWalks.Services;

usingXamarin.Forms;

namespaceTrackMyWalks.ViewModels

{

publicclassWalksPageViewModel:WalkBaseViewModel

{

ObservableCollection<WalkEntries>_walkEntries;

publicObservableCollection<WalkEntries>walkEntries

{

get{return_walkEntries;}

set

{

_walkEntries=value;

OnPropertyChanged();

}

}

2. NowweneedtomodifytheWalksPageViewModelclassconstructor,whichwillneedtoincludeaparameternavServicethatisincludedwithinourIWalkNavServiceinterfaceclass.ThenweneedtosettheViewModel'sclassconstructortoaccessallinstanceclassmemberscontainedwithinthenavServicewithintheWalksPageViewModelbyusingthe

basekeyword.WethensetupanObservableCollectioncalledwalkEntries.ThisacceptsalistparametercontainingourWalkEntriesmodel,whichwillbeusedtodeterminewheneverthecollectionhaschangedwithintheWalkEntriesmodel.

3. Next,wecreatetheInitmethodwithinourWalksPageViewmodel,andamethodcalledLoadWalkDetailstopopulatetheWalkDetails.Proceedandenterinthefollowinghighlightedcodesections:

publicWalksPageViewModel(IWalkNavServicenavService):

base(navService)

{

walkEntries=newObservableCollection<WalkEntries>();

}

publicoverrideasyncTaskInit()

{

awaitLoadWalkDetails();

}

4. Next,weneedtocreateanewasyncmethodcalledLoadWalkDetailsthatwillbeusedtoaddeachofthewalkentrieswithinourmodel.WeusetheTask.Factory.StartNewtostartandexecuteourtask,andthenproceedtopopulateeachofourlistsofWalkEntriesasynchronously.WethenuseandspecifytheawaitkeywordtowaituntilourTaskcompletes.Proceedandenterinthefollowinghighlightedcodesections:

publicasyncTaskLoadWalkDetails()

{

awaitTask.Factory.StartNew(()=>

{

walkEntries=newObservableCollection<WalkEntries>(){

newWalkEntries{

Title="10MileBrookTrail,MargaretRiver",

Notes="The10MileBrookTrailstartsinthe

RotaryParknearOldKate,apreservedsteam"+

"engineatthenorthernedgeofMargaretRiver.",

Latitude=-33.9727604,

Longitude=115.0861599,

Kilometers=7.5,

Distance=0,

Difficulty="Medium",

ImageUrl=

"http://trailswa.com.au/media/cache/media/images/

trails/_mid/"+

"FullSizeRender1_600_480_c1.jpg"

},

newWalkEntries

{

Title="AncientEmpireWalk,ValleyoftheGiants",

Notes="TheAncientEmpireisa450metrewalktrail

thattakesyouaroundandthroughsomeof"+

"thegianttingletreesincludingthemostpopular

ofthegnarledveterans,knownas"+

"GrandmaTingle.",

Latitude=-34.9749188,

Longitude=117.3560796,

Kilometers=450,

Distance=0,

Difficulty="Hard",

ImageUrl="http://trailswa.com.au/media/cache

/media/images/trails/_mid/"+

"Ancient_Empire_534_480_c1.jpg"

},

};

});

}

5. Next,weneedtocreateaCommandpropertyforourclass.ThiswillbeusedwithinourWalksPageandwillbeusedtobindtotheAddWalkToolBarItem.TheCommandpropertywillrunanactionuponbeingpressed,andthenexecuteaclassinstancemethod,todeterminewhetherthecommandcanbeexecuted.Proceedandenterinthefollowinghighlightedcodesections:

Command_createNewWalk;

publicCommandCreateNewWalk

{

get

{

return_createNewWalk

??(_createNewWalk=

newCommand(async()=>

awaitNavService.NavigateToViewModel<WalkEntryViewMod

el,WalkEntries>(null)));

}

}

6. Then,createaCommandpropertytoourclass.ThiswillbeusedwithintheWalksPageandwillbeusedtohandleclicksonawalkitemwithintheListView.TheCommandpropertywillrunanactionuponbeingpressed,andthenexecuteaclassinstancemethodtodeterminewhetherthecommandcanbeexecutedornot,priortonavigatingtotheWalksTrailViewModelandpassinginthetrailDetailsforthechosenwalkwithintheListView.Proceedandenterinthefollowinghighlightedcodesections:

Command<WalkEntries>_trailDetails;

publicCommand<WalkEntries>WalkTrailDetails

{

get

{

return_trailDetails

??(_trailDetails=

newCommand<WalkEntries>(async(trailDetails)=>

awaitNavService.NavigateToViewModel

<WalksTrailVi

ewModel,WalkEntries>(trailDetails)));

}

}

}

}

NowthatwehavemodifiedourWalksPageViewModeltoincludethenavigationserviceclass,whichwillbeusedbyourmainWalksPage,ournextstepistomodifyourwalksmainpagesothatitpointstoareferenceofourWalksPageViewModel,andensuresthatallofthenecessaryCommandbindingsandBindingContextshavebeensetupcorrectly.

UpdatingthewalksmainpagetousetheupdatedViewModelNowthatwehavemodifiedourMVVMViewModeltotakeadvantageofthenavigationservice,weneedtomodifyourwalksmainpagetobindtheWalksPageBindingContexttotheWalksPageViewModelsothatthewalkentrydetailscanbedisplayedandallofthenavigationalaspectsareworkingasexpected.

Let'stakealookathowwecanachievethis,byperformingthefollowingsteps:

1. EnsurethattheWalksPage.csfileisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesections:

//

//WalksPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem.Collections.Generic;

usingXamarin.Forms;

usingTrackMyWalks.Models;

usingTrackMyWalks.ViewModels;

usingTrackMyWalks.Services;

namespaceTrackMyWalks

{

publicclassWalksPage:ContentPage

{

2. Next,weneedtocreateanewprivatepropertynamed_viewModelwithinourWalksPageclass.ThisisoftheWalksPageViewModeltype,andwillessentiallyprovideuswithaccesstotheContentPage'sBindingContextobject.Proceedandenterinthefollowinghighlightedcodesections:

WalksPageViewModel_viewModel

{

get{returnBindingContextasWalksPageViewModel;

}

}

publicWalksPage()

{

varnewWalkItem=newToolbarItem

{

Text="AddWalk"

};

3. Then,weneedtosetupaBindingtotheCommandpropertythatwedefinedwithinourWalksPageViewModelclass.ThiswillbecalledwhentheuserchoosestheAddWalk

button.Proceedandenterinthefollowinghighlightedcodesections:

//SetupourBindingclickeventhandler

newWalkItem.SetBinding(ToolbarItem.CommandProperty,

"CreateNewWalk");

//AddtheToolBaritemtoourToolBar

ToolbarItems.Add(newWalkItem);

4. Next,weneedtodeclareandinitializeourWalksPageViewModelBindingContexttoincludeourIWalkNavServiceconstructor,whichisusedbytheWalkBaseViewModelclass,andisretrievedfromtheXamarin.FormsDependencyServiceclass.Proceedandenterinthefollowinghighlightedcodesections:

//DeclareandinitializeourModelBindingContext

BindingContext=newWalksPageViewModel(DependencyService.Get

<IWalkNavS

ervice>());

//DefineourItemTemplate

varitemTemplate=newDataTemplate(typeof(ImageCell));

itemTemplate.SetBinding(TextCell.TextProperty,"Title");

itemTemplate.SetBinding(TextCell.DetailProperty,"Notes");

itemTemplate.SetBinding(ImageCell.ImageSourceProperty,

"ImageUrl");

varwalksList=newListView

{

HasUnevenRows=true,

ItemTemplate=itemTemplate,

SeparatorColor=Color.FromHex("#ddd"),

};

//SettheBindingpropertyforourwalksEntries

walksList.SetBinding(ItemsView<Cell>.ItemsSourceProperty,

"walkEntries");

5. Then,weneedtochangethewayinwhichanitemgetsselectedfromtheListView.WeneedtomakeacalltotheWalksTrailDetailscommandthatisincludedwithinourWalksPageViewModelclass,sothatitcannavigatetotheWalksTrailViewModel,whilstpassinginthechosenitemfromwithintheListView.Proceedandenterthefollowinghighlightedcodesections:

//InitializeoureventHandlertousewhen

theitemistapped

walksList.ItemTapped+=(objectsender,

ItemTappedEventArgse)=>

{

varitem=(WalkEntries)e.Item;

if(item==null)return;

_viewModel.WalkTrailDetails.Execute(item);

item=null;

};

Content=walksList;

}

6. Finally,weneedtocreateanOnAppearinginstancemethodofthenavigationhierarchythatwillbeusedtodisplayourWalksEntriespriortotheViewModelappearingonscreen.

7. WeneedtoensurethatourViewModelhasbeenproperlyinitializedbycheckingtoseethatitisn'tnull,priortocallingtheInitmethodofourWalksPageViewModel.Proceedandenterthefollowinghighlightedcodesections:

protectedoverrideasyncvoidOnAppearing()

{

base.OnAppearing();

//InitializeourWalksPageViewModel

if(_viewModel!=null)

await_viewModel.Init();

}

}

}

Inthissection,welookedatthestepsinvolvedinmodifyingtheWalksPagesothatitcantakeadvantageofourupdatedWalksPageViewModel.WelookedathowtosetthecontentpagetoaninstanceoftheWalksPageViewModelsothatitknowswheretogetthelistofwalkentries.ThelistwillbeusedanddisplayedwithintheListViewcontrol,andwillthenupdatetheBindingContextpropertyfortheWalksPagetopointtoaninstanceoftheIWalkNavServiceinterface.Asyoucansee,byusinganavigationservicewithinyourViewModels,itmakesnavigatingbetweeneachoftheViewModelsquiteeasy.

UpdatingthewalksentrypageViewModelandnavigationserviceNowthatwehavemodifiedtheMVVMViewModelthatwillbeusedforthemainWalksPage,ournextstepistobeginmodifyingtheWalkEntryViewModeltotakeadvantageofthenavigationservice,whichwillbeusedtocreatenewwalkentries,andsavethisinformationbacktotheWalkBaseViewModel.Thiswillbecoveredinalaterchapterasweprogressthroughoutthisbook.

Let'stakealookathowwecanachievethis,byperformingthefollowingsteps:

1. EnsurethattheWalkEntryViewModel.csfileisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesections:

//

//WalkEntryViewModel.cs

//TrackMyWalksViewModels

//

//CreatedbyStevenF.Danielon22/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingSystem.Diagnostics.Contracts;

usingSystem.Threading.Tasks;

usingTrackMyWalks.Models;

usingTrackMyWalks.Services;

usingTrackMyWalks.ViewModels;

usingXamarin.Forms;

namespaceTrackMyWalks.ViewModels

{

publicclassWalkEntryViewModel:WalkBaseViewModel

{

string_title;

publicstringTitle

{

get{return_title;}

set

{

_title=value;

OnPropertyChanged();

SaveCommand.ChangeCanExecute();

}

}

string_notes;

publicstringNotes

{

get{return_notes;}

set

{

_notes=value;

OnPropertyChanged();

}

}

double_latitude;

publicdoubleLatitude

{

get{return_latitude;}

set

{

_latitude=value;

OnPropertyChanged();

}

}

double_longitude;

publicdoubleLongitude

{

get{return_longitude;}

set

{

_longitude=value;

OnPropertyChanged();

}

}

double_kilometers;

publicdoubleKilometers

{

get{return_kilometers;}

set

{

_kilometers=value;

OnPropertyChanged();

}

}

string_difficulty;

publicstringDifficulty

{

get{return_difficulty;}

set

{

_difficulty=value;

OnPropertyChanged();

}

}

double_distance;

publicdoubleDistance

{

get{return_distance;}

set

{

_distance=value;

OnPropertyChanged();

}

}

string_imageUrl;

publicstringImageUrl

{

get{return_imageUrl;}

set

{

_imageUrl=value;

OnPropertyChanged();

}

}

2. Inthenextstep,weneedtomodifytheWalksEntryViewModelclassconstructorwhichwillnowneedtoincludeaparameternavServicethatisincludedwithintheIWalkNavServiceinterfaceclass.Thenwe'llsettheViewModel'sclassconstructortoaccessallinstanceclassmemberscontainedwithinthenavServicewithintheWalksEntryViewModel,byusingthebasekeyword.Next,we'llinitializetheconstructorwithdefaultvaluesforourTitle,DifficultyandDistanceproperties.

3. LocatetheWalkEntryViewModelclassconstructor,andenterthefollowinghighlightedcodesections:

publicWalkEntryViewModel(IWalkNavServicenavService):

base(navService)

{

Title="NewWalk";

Difficulty="Easy";

Distance=1.0;

}

4. Next,weneedtomodifytheSaveCommandcommandpropertytoincludetheasyncandawaitkeywords.ThiscommandpropertywillbeusedtobindtotheSaveToolBarItemandwillrunanactionuponbeingpressed.Itwillthenexecuteaclassinstancemethodtodeterminewhetherthecommandcanbeexecuted.Proceedandenterinthefollowinghighlightedcodesections:

Command_saveCommand;

publicCommandSaveCommand

{

get

{

return_saveCommand??(_saveCommand=

newCommand(async()=>awaitExecuteSaveCommand(),

ValidateFormDetails));

}

}

5. Next,welocateandmodifytheExecuteSaveCommandinstancemethodtoincludetheasyncTaskkeywordstothemethoddefinition,andthenincludeareferencetoourPreviousPagemethodthatisdefinedwithinourIWalkNavServiceinterfacetoallowourWalkEntryPage

tobedismissedupontheuserclickingontheSavebutton.Proceedandenterinthefollowinghighlightedcodesections:

asyncTaskExecuteSaveCommand()

{

varnewWalkItem=newWalkEntries

{

Title=this.Title,

Notes=this.Notes,

Latitude=this.Latitude,

Longitude=this.Longitude,

Kilometers=this.Kilometers,

Difficulty=this.Difficulty,

Distance=this.Distance,

ImageUrl=this.ImageUrl

};

//Here,wewillsavethedetailsenteredinalaterchapter.

awaitNavService.PreviousPage();

}

//methodtocheckforanyformerrors

boolValidateFormDetails()

{

return!string.IsNullOrWhiteSpace(Title);

}

6. Finally,createtheInitmethodwithintheWalkEntryViewModel.ThiswillbeusedtoinitializetheWalkEntryPagewhenitiscalled.WeusetheTask.Factory.StartNewmethodtogivetheViewModelenoughtimetodisplaythepageonscreen,priortoinitializingtheContentPagecontents.Proceedandenterinthefollowinghighlightedcodesections:

publicoverrideasyncTaskInit()

{

awaitTask.Factory.StartNew(()=>

{

Title="NewWalk";

Difficulty="Easy";

Distance=1.0;

});

}

}

}

Inthissection,webeganbyensuringthatourViewModelinheritsfromtheWalkBaseViewModelclassandthenmodifiestheWalksEntryViewModelclassconstructortoincludetheparameternavServicewhichisincludedwithintheIWalkNavServiceinterfaceclass.Inournextstep,we'llinitializetheclassconstructorwithdefaultvaluesfortheTitle,Difficulty,andDistancepropertiesandthenmodifytheSaveCommandcommandmethodtoincludeareference

totheNavService.PreviousPagemethod.ThisisdeclaredwithintheIWalkNavServiceinterfaceclasstoallowourWalkEntryPagetonavigatebacktothepreviouscallingpagewhentheSavebuttonisclicked.

UpdatingtheWalksEntryPagetousetheupdatedViewModelInthissection,weneedtobindourmodelbindingcontext,BindingContext,totheWalkEntryViewModelsothatthenewwalkinformation,whichwillbeenteredwithinthispage,canbestoredwithintheWalkEntriesmodel.Let'stakealookathowwecanachievethis,byperformingthefollowingsteps:

1. EnsurethattheWalkEntryPage.csfileisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesections:

//

//WalkEntryPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms;

usingTrackMyWalks.Models;

usingSystem.Collections.Generic;

namespaceTrackMyWalks

{

publicclassWalkEntryPage:ContentPage

{

2. Next,weneedtocreateanewprivatepropertynamed_viewModelwithintheWalkEntryPageclassthatisoftheWalksEntryViewModeltype,andwhichwillessentiallyprovideuswithaccesstotheContentPage'sBindingContextobject.Proceedandenterinthefollowinghighlightedcodesections:

WalkEntryViewModel_viewModel

{

get

{returnBindingContextasWalkEntryViewModel;

}

}

publicWalkEntryPage()

{

//SettheContentPageTitle

Title="NewWalkEntry";

3. Next,weneedtodeclareandinitializeourWalkEntryViewModelBindingContexttoincludetheIWalkNavServiceconstructor,whichisusedbytheWalkBaseViewModelclass,andisretrievedfromtheXamarin.FormsDependencyServiceclass.Proceedandenterinthefollowinghighlightedcodesections:

//DeclareandinitializeourModelBindingContext

BindingContext=newWalkEntryViewModel(

DependencyService.

Get<IWalkNavService>());

//DefineourNewWalkEntryfields

varwalkTitle=newEntryCell

{

Label="Title:",

Placeholder="TrailTitle"

};

walkTitle.SetBinding(EntryCell.TextProperty,

"Title",BindingMode.TwoWay);

varwalkNotes=newEntryCell

{

Label="Notes:",

Placeholder="Description"

};

walkNotes.SetBinding(EntryCell.TextProperty,

"Notes",BindingMode.TwoWay);

varwalkLatitude=newEntryCell

{

Label="Latitude:",

Placeholder="Latitude",

Keyboard=Keyboard.Numeric

};

walkLatitude.SetBinding(EntryCell.TextProperty,

"Latitude",BindingMode.TwoWay);

varwalkLongitude=newEntryCell

{

Label="Longitude:",

Placeholder="Longitude",

Keyboard=Keyboard.Numeric

};

walkLongitude.SetBinding(EntryCell.TextProperty,

"Longitude",BindingMode.TwoWay);

varwalkKilometers=newEntryCell

{

Label="Kilometers:",

Placeholder="Kilometers",

Keyboard=Keyboard.Numeric

};

walkKilometers.SetBinding(EntryCell.TextProperty,

"Kilometers",BindingMode.TwoWay);

varwalkDifficulty=newEntryCell

{

Label="DifficultyLevel:",

Placeholder="WalkDifficulty"

};

walkDifficulty.SetBinding(EntryCell.TextProperty,

"Difficulty",BindingMode.TwoWay);

varwalkImageUrl=newEntryCell

{

Label="ImageUrl:",

Placeholder="ImageURL"

};

walkImageUrl.SetBinding(EntryCell.TextProperty,

"ImageUrl",BindingMode.TwoWay);

//DefineourTableView

Content=newTableView

{

Intent=TableIntent.Form,

Root=newTableRoot

{

newTableSection()

{

walkTitle,

walkNotes,

walkLatitude,

walkLongitude,

walkKilometers,

walkDifficulty,

walkImageUrl

}

}

};

varsaveWalkItem=newToolbarItem

{

Text="Save"

};

saveWalkItem.SetBinding(MenuItem.CommandProperty,

"SaveCommand");

ToolbarItems.Add(saveWalkItem);}

}

}

Inthissection,welookedatthestepsinvolvedinmodifyingtheWalkEntryPagesothatitcantakeadvantageofourupdatedWalkEntryViewModel.WelookedathowtosetthecontentpagetoaninstanceoftheWalkEntryViewModelsothattheBindingContextpropertyfortheWalkEntryPagewillnowpointtoaninstanceoftheIWalkNavServiceinterface.

UpdatingthewalkstrailpageViewModelandnavigationserviceNowthatwehavemodifiedtheMVVMViewModelthatwillbeusedforourWalkEntrypage,ournextstepistobeginmodifyingtheWalksTrailViewModeltotakeadvantageofthenavigationservice,sothatitwillbeusedtodisplaythewalkentryinformationthathasbeenassociatedwiththechosenwalk.

Let'stakealookathowwecanachievethis,byperformingthefollowingthesteps:

1. EnsurethattheWalksTrailViewModel.csfileisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesections:

//

//WalksTrailViewModel.cs

//TrackMyWalksViewModels

//

//CreatedbyStevenF.Danielon22/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem.Threading.Tasks;

usingTrackMyWalks.Models;

usingTrackMyWalks.Services;

usingXamarin.Forms;

namespaceTrackMyWalks.ViewModels

{

publicclassWalksTrailViewModel:

WalkBaseViewModel<WalkEntries>

{

WalkEntries_walkEntry;

publicWalkEntriesWalkEntry

{

get{return_walkEntry;}

set

{

_walkEntry=value;

OnPropertyChanged();

}

}

2. Next,weneedtocreateaCommandpropertyforourclass.ThiswillbeusedwithinourWalkTrailPageandwillbeusedtohandlewhentheuserclicksontheBeginThisTrialbutton.TheCommandpropertywillrunanactionuponbeingpressed,andthenexecuteaclassinstancemethodtodeterminewhetherthecommandcanbeexecutedornot,priortonavigatingtotheDistTravelledViewModel,andpassinginthetrailDetailsforthechosenwalkfromtheWalksPage.Proceedandenterinthefollowinghighlightedcodesections:

Command<WalkEntries>_command;

publicCommand<WalkEntries>DistanceTravelled

{

get

{

return_command

??(_command=

newCommand<WalkEntries>(async(trailDetails)=>

awaitNavService.NavigateToViewModel

<DistTravelledViewModel,WalkEntries>(trailDetails)));

}

}

3. Next,weneedtodeclareandinitializeourWalksTrailViewModelBindingContexttoincludetheIWalkNavServiceconstructor,whichisusedbytheWalkBaseViewModelclass,andisretrievedfromtheXamarin.FormsDependencyServiceclass.Proceedandenterinthefollowinghighlightedcodesections:

publicWalksTrailViewModel(IWalkNavServicenavService):

base(navService)

{

}

4. Finally,createtheInitmethodwithintheWalksTrailViewModel.ThiswillbeusedtoinitializetheWalkTrailPagewhenitiscalled.WeusetheTask.Factory.StartNewmethodtogivetheViewModelenoughtimetodisplaythepageonscreen,priortoinitializingtheContentPagecontents,usingthepassedinwalkDetailsforourmodel.Proceedandenterinthefollowinghighlightedcodesections:

publicoverrideasyncTaskInit(WalkEntrieswalkDetails)

{

awaitTask.Factory.StartNew(()=>

{

WalkEntry=walkDetails;

});

}

}

}

Inthissection,webeginbyensuringthatourViewModelinheritsfromtheWalkBaseViewModelclass,andthatitacceptstheWalkEntriesdictionaryasitsparameter.Inournextstep,we'llcreateaDistanceTravelledCommandmethodthatwillnavigatetotheDistanceTravelledPagecontentpagewithinourNavigationStackthatpassestheWalkEntrydictionarytotheDistTravelledViewModelViewModelandpassaparametercontainingthetrailDetailsofthechosenwalk.

UpdatingtheWalksTrailPagetousetheupdatedViewModelInthissection,weneedtobindourmodelbindingcontext,BindingContext,totheWalksTrailViewModelsothatthewalkinformationdetailswillbedisplayedfromtheWalkEntriesmodelwhenawalkhasbeenclickedonwithinthemainWalksPage.Let'stakealookathowwecanachievethis,byperformingthefollowingsteps:

1. EnsurethattheWalkTrailPage.csfileisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesections:

//

//WalkTrailPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms;

usingTrackMyWalks.Models;

usingTrackMyWalks.ViewModels;

usingTrackMyWalks.Services;

namespaceTrackMyWalks

{

publicclassWalkTrailPage:ContentPage

{

publicWalkTrailPage(WalkEntrieswalkItem)

{

Title="WalksTrail";

2. Next,weneedtodeclareandinitializeourWalkEntryViewModelBindingContexttoincludetheIWalkNavServiceconstructor,whichisusedbytheWalkBaseViewModelclass,andisretrievedfromtheXamarin.FormsDependencyServiceclass.Proceedandenterinthefollowinghighlightedcodesections:

//DeclareandinitializeourModelBindingContext

BindingContext=newWalksTrailViewModel(DependencyService.

Get<IWalkNavService>());

varbeginTrailWalk=newButton

{

BackgroundColor=Color.FromHex("#008080"),

TextColor=Color.White,

Text="BeginthisTrail"

};

3. Next,weneedtomodifythebeginTrailWalk.Clickedhandlerforourbutton,sothatuponbeingclicked,itwillnavigatetotheDistTravelledViewModelandpassintheWalkEntrydictionaryforthechosenwalkfromtheWalksPage.Proceedandenterinthefollowinghighlightedcodesections:

//DeclareandinitializeourEventHandler

beginTrailWalk.Clicked+=(sender,e)=>

{

if(_viewModel.WalkEntry==null)return;

_viewModel.DistanceTravelled.Execute(_viewModel.WalkEntry);

};

varwalkTrailImage=newImage()

{

Aspect=Aspect.AspectFill

};

walkTrailImage.SetBinding(Image.SourceProperty,

"WalkEntry.ImageUrl");

vartrailNameLabel=newLabel()

{

FontSize=28,

FontAttributes=FontAttributes.Bold,

TextColor=Color.Black

};

trailNameLabel.SetBinding(Label.TextProperty,

"WalkEntry.Title");

vartrailKilometersLabel=newLabel()

{

FontAttributes=FontAttributes.Bold,

FontSize=12,

TextColor=Color.Black,

};

trailKilometersLabel.SetBinding(Label.TextProperty,

"WalkEntry.Kilometers",

stringFormat:"Length:{0}km");

vartrailDifficultyLabel=newLabel()

{

FontAttributes=FontAttributes.Bold,

FontSize=12,

TextColor=Color.Black

};

trailDifficultyLabel.SetBinding(Label.TextProperty,

"WalkEntry.Difficulty",stringFormat:"Difficulty:{0}");

vartrailFullDescription=newLabel()

{

FontSize=11,

TextColor=Color.Black,

HorizontalOptions=LayoutOptions.FillAndExpand

};

trailFullDescription.SetBinding(Label.TextProperty,

"WalkEntry.Notes");

this.Content=newScrollView

{

Padding=10,

Content=newStackLayout

{

Orientation=StackOrientation.Vertical,

HorizontalOptions=LayoutOptions.FillAndExpand,

Children=

{

walkTrailImage,

trailNameLabel,

trailKilometersLabel,

trailDifficultyLabel,

trailFullDescription,

beginTrailWalk

}

}

};

}

}

}

Inthissection,welookedatthestepsinvolvedinmodifyingtheWalksTrailPagesothatitcantakeadvantageoftheWalksTrailViewModel.WelookedathowtosetthecontentpagetoaninstanceoftheWalksTrailViewModelsothattheBindingContextpropertyfortheWalkTrailPagewillnowpointtoaninstanceoftheIWalkNavServiceinterface.

WealsoslightlymodifiedourClickedhandlerforthebeginTrailWalkbuttonsothatitwillnownavigatetotheDistanceTravelledPagecontentpagewithintheNavigationStack,andpassintheWalkEntrydictionaryobjecttotheDistTravelledViewModelViewModel.

UpdatingthedistancetravelledViewModelandnavigationserviceNowthatwehavemodifiedtheMVVMViewModelthatwillbeusedforourWalkTrailPage,ournextstepistoupdatetheDistTravelledViewModeltotakeadvantageofthenavigationservice,sothatitcandisplaythewalkentryinformationthathasbeenassociatedwiththechosenwalk.

Let'stakealookathowwecanachievethis,byperformingthefollowingsteps:

1. EnsurethattheDistTravelledViewModel.csfileisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesections:

//

//DistTravelledViewModel.cs

//TrackMyWalksViewModels

//

//CreatedbyStevenF.Danielon22/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingSystem.Threading.Tasks;

usingTrackMyWalks.Models;

usingTrackMyWalks.Services;

usingTrackMyWalks.ViewModels;

usingXamarin.Forms;

namespaceTrackMyWalks.ViewModels

{

publicclassDistTravelledViewModel:

WalkBaseViewModel<WalkEntries>

{

WalkEntries_walkEntry;

publicWalkEntriesWalkEntry

{

get{return_walkEntry;}

set

{

_walkEntry=value;

OnPropertyChanged();

}

}

double_travelled;

publicdoubleTravelled

{

get{return_travelled;}

set

{

_travelled=value;

OnPropertyChanged();

}

}

double_hours;

publicdoubleHours

{

get{return_hours;}

set

{

_hours=value;

OnPropertyChanged();

}

}

double_minutes;

publicdoubleMinutes

{

get{return_minutes;}

set

{

_minutes=value;

OnPropertyChanged();

}

}

double_seconds;

publicdoubleSeconds

{

get{return_seconds;}

set

{

_seconds=value;

OnPropertyChanged();

}

}

publicstringTimeTaken

{

get

{

returnstring.Format("{0:00}:{1:00}:{2:00}",

this.Hours,this.Minutes,this.Seconds);

}

}

2. Next,weneedtomodifytheDistTravelledViewModelclassconstructor,whichwillnowneedtoincludeanavServiceparameterthatisincludedwithintheIWalkNavServiceinterfaceclass.WethensettheViewModel'sclassconstructortoaccessallinstanceclassmemberscontainedwithinthenavServicebyusingthebasekeywordandinitializetheconstructorwithdefaultvaluesfortheHours,Minutes,Seconds,andTravelledproperties.

3. LocatetheDistTravelledViewModelclassconstructor,andenterthefollowinghighlightedcode:

publicDistTravelledViewModel(IWalkNavServicenavService):

base(navService)

{

this.Hours=0;

this.Minutes=0;

this.Seconds=0;

this.Travelled=100;

}

4. Then,createtheInitmethodwithintheDistTravelledViewModel,whichwillbeusedtoinitializetheDistanceTravelledPagecontentpagewhenitiscalled.WeneedtospecifyandusetheTask.Factory.StartNewmethodtogivetheViewModelenoughtimetodisplaythepageonscreen,priortoinitializingtheContentPagecontents,usingthepassedinwalkDetailsforourmodel.Proceedandenterinthefollowinghighlightedcodesections:

publicoverrideasyncTaskInit(WalkEntrieswalkDetails)

{

awaitTask.Factory.StartNew(()=>

{

WalkEntry=walkDetails;

});

}

5. Next,weneedtocreatetheBackToMainPagecommandpropertythatwillbeusedtobindtotheEndThisTrailbuttonthatwillrunanactionuponbeingpressed.Thisactionwillexecuteaclassinstancemethod,todeterminewhethertheCommandcanbeexecuted.

6. IftheCommandcanbeexecuted,acallwillbemadetotheBackToMainPagemethodontheNavServicenavigationserviceclasstotaketheuserbacktotheTrackMyWalksmainpage,byremovingallexistingViewModelswithintheNavigationStack,exceptthefirstpage.Proceedandenterinthefollowinghighlightedcodesections:

Command_mainPage;

publicCommandBackToMainPage

{

get

{

return_mainPage

??(_mainPage=new

Command(async()=>await

NavService.BackToMainPage()));

}

}

}

}

Inthissection,weupdatedtheDistanceTravelledViewModeltoinheritfromour

WalkBaseViewModelInterfaceclassandthenmodifytheDistTravelledViewModelclassconstructortopointtoaninstanceoftheIWalkNavServiceinterfaceclass.

WethencreatedtheInitmethodthatwillinitializetheDistanceTravelledViewModelwhenitiscalledandusetheTask.Factory.StartNewmethodtogivetheViewModelenoughtimetodisplaytheDistanceTravelledPagecontentpageonscreen,priortoinitializingtheContentPagecontents,usingthepassedinwalkDetailsforourmodel.

WealsocreatedtheBackToMainPagecommandpropertythatwillbeusedtobindtotheEndThisTrailbuttonthatwillrunanactiontoexecuteaclassinstancemethod,todeterminewhethertheCommandcanbeexecuted,andthenacallwillbemadetoBackToMainPagemethodontheNavServicenavigationserviceclasstotaketheuserbacktothefirstpagewithintheNavigationStack.

UpdatingtheDistanceTravelledPagetousetheupdatedViewModelNowthatwehavemodifiedtheMVVMViewModelthatwillbeusedbyourDistanceTravelledPagecontentpage,ournextstepistobeginmodifyingtheDistanceTravelledPagepagetotakeadvantageofournavigationservice,anddisplaywalkinformationdetails.ThecalculationsanddistancetravelledwillbedisplayedfromtheWalkEntriesmodel.

Let'stakealookathowwecanachievethis,byperformingthefollowingsteps:

1. EnsurethattheDistanceTravelledPage.csfileisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesections:

//

//DistanceTravelledPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms;

usingXamarin.Forms.Maps;

usingTrackMyWalks.Models;

usingTrackMyWalks.Services;

namespaceTrackMyWalks

{

publicclassDistanceTravelledPage:ContentPage

{

2. Next,weneedtocreateanewprivatepropertynamed_viewModelwithintheDistanceTravelledPageclass,whichisofourDistTravelledViewModeltype,andwillessentiallyprovideuswithaccesstotheContentPage'sBindingContextobject.Proceedandenterinthefollowinghighlightedcodesections:

DistTravelledViewModel_viewModel

{

get{returnBindingContextasDistTravelledViewModel;}

}

publicDistanceTravelledPage()

{

Title="DistanceTravelled";

3. Next,weneedtodeclareandinitializetheDistTravelledViewModelBindingContexttoincludeourIWalkNavServiceconstructor,whichisusedbytheWalkBaseViewModelclass,andisretrievedfromtheXamarin.FormsDependencyServiceclass.Proceedandenterin

thefollowinghighlightedcodesections:

//DeclareandinitializeourModelBindingContext

BindingContext=newDistTravelledViewModel

(DependencyService.

Get<IWalkNavService>());

4. Then,weneedtocreateanewmethodcalledLoadDetailswhichwillbeusedtograbthenameofthechosenwalkandtheLatitudeandLongitudevaluesfromtheDistTravelledViewModelaswellaszoomintotheuserentrylocation,usingtheMoveToRegionmethod.Proceedandenterinthefollowinghighlightedcodesections:

publicvoidLoadDetails()

{

//Instantiateourmapobject

vartrailMap=newMap();

//Placeapinonthemapforthechosen

//walktype

trailMap.Pins.Add(newPin

{

Type=PinType.Place,

Label=_viewModel.WalkEntry.Title,

Position=newPosition(_viewModel.WalkEntry.Latitude,

_viewModel.WalkEntry.Longitude)

});

//Centerthemaparoundthelistof

//walksentry'slocation

trailMap.MoveToRegion(MapSpan.FromCenterAndRadius(

newPosition(_viewModel.WalkEntry.Latitude,

_viewModel.WalkEntry.Longitude),

Distance.FromKilometers(1.0)));

vartrailNameLabel=newLabel()

{

FontSize=18,

FontAttributes=FontAttributes.Bold,

TextColor=Color.Black,

HorizontalTextAlignment=TextAlignment.Center

};

trailNameLabel.SetBinding(Label.TextProperty,

"WalkEntry.Title");

vartrailDistanceTravelledLabel=newLabel()

{

FontAttributes=FontAttributes.Bold,

FontSize=20,

TextColor=Color.Black,

HorizontalTextAlignment=TextAlignment.Center

};

trailDistanceTravelledLabel.SetBinding(Label.TextProperty,

"Travelled",stringFormat:"DistanceTravelled:{0}km");

vartotalTimeTakenLabel=newLabel()

{

FontAttributes=FontAttributes.Bold,

FontSize=20,

TextColor=Color.Black,

HorizontalTextAlignment=TextAlignment.Center

};

totalTimeTakenLabel.SetBinding(Label.TextProperty,

"TimeTaken",stringFormat:"TimeTaken:{0}");

varwalksHomeButton=newButton

{

BackgroundColor=Color.FromHex("#008080"),

TextColor=Color.White,

Text="EndthisTrail"

};

5. Next,weneedtomodifythewalksHomeButton.Clickedhandlerforourbuttonsothat,uponbeingclicked,itwillallowtheDistanceTravelledPagetonavigatebacktothefirstpagewithintheNavigationStack.Proceedandenterinthefollowinghighlightedcodesections:

//Setupoureventhandler

walksHomeButton.Clicked+=(sender,e)=>

{

if(_viewModel.WalkEntry==null)return;

_viewModel.BackToMainPage.Execute(0);

};

this.Content=newScrollView

{

Padding=10,

Content=newStackLayout

{

Orientation=StackOrientation.Vertical,

HorizontalOptions=LayoutOptions.FillAndExpand,

Children={

trailMap,

trailNameLabel,

trailDistanceTravelledLabel,

totalTimeTakenLabel,

walksHomeButton

}

}

};

}

6. Finally,weneedtocreateanOnAppearinginstancemethodofthenavigationhierarchythatwillbeusedtocorrectlyplotthewalk'sLongitudeandLatitudecoordinateswithinthemap,alongwiththewalkinformation,priortotheViewModelappearingonscreen.WeneedtoensurethattheViewModelhasproperlybeeninitializedbycheckingtoseethatitisn'tnull,priortocallingtheInitmethodoftheDistTravelledViewModel.Proceedandenterinthefollowinghighlightedcodesections:

protectedoverrideasyncvoidOnAppearing()

{

base.OnAppearing();

//InitializeourDistanceTravelledViewModel

if(_viewModel!=null)

{

await_viewModel.Init();

LoadDetails();

}

}

}

}

Inthissection,welookedatthestepsinvolvedinmodifyingtheDistanceTraveledPagesothatitcantakeadvantageoftheDistTravelledViewModel.WelookedathowtosetthecontentpagetoaninstanceoftheDistTravelledViewModelsothattheBindingContextpropertyfortheDistanceTravelledPagewillnowpointtoaninstanceoftheIWalkNavServiceinterface.

WealsoslightlymodifiedourClickedhandlerfortheWalksHomeButtonbutton,sothatitwillnownavigatetotheNavService.BackToMainPagemethod,whichisdeclaredwithintheIWalkNavServiceinterfaceclasstoallowtheDistanceTravelledPagetonavigatebacktothefirstpagewithintheNavigationStack.

UpdatingtheXamarin.Forms.AppclasstousethenavigationserviceInthissection,weneedtoupdateourXamarin.Forms.Appclass,bymodifyingtheconstructorinthemainAppclasstocreateanewinstanceofthenavigationserviceandregistertheapplication'sContentPagetoViewModelmappings.

Let'stakealookathowwecanachievethis,byperformingthefollowingsteps:

1. OpentheTrackMyWalks.csfileandensurethatitisdisplayedwithinthecodeeditor.2. Next,locatetheAppmethodandenterinthefollowinghighlightedcodesections:

//

//TrackMyWalks.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingTrackMyWalks.Services;

usingTrackMyWalks.ViewModels;

usingXamarin.Forms;

namespaceTrackMyWalks

{

publicclassApp:Application

{

publicApp()

{

//ChecktheTargetOSPlatform

if(Device.OS==TargetPlatform.Android)

{

MainPage=newSplashPage();

}

else

{

//Therootpageofyourapplication

varwalksPage=newNavigationPage(new

WalksPage()

{

Title="TrackMyWalks"

});

varnavService=DependencyService.

Get<IWalkNavService>()asWalkNavService;

navService.navigation=walksPage.Navigation;

navService.RegisterViewMapping(typeof

(WalksPageViewModel

),typeof(WalksPage));

navService.RegisterViewMapping(

typeof(WalkEntryViewModel

),

typeof(WalkEntryPage));

navService.RegisterViewMapping(

typeof(WalksTrailViewModel

),

typeof(WalkTrailPage));

navService.RegisterViewMapping(

typeof(DistTravelledViewMo

del),

typeof(DistanceTravelledPage));

MainPage=walksPage;

}

}

protectedoverridevoidOnStart()

{

//Handlewhenyourappstarts

}

protectedoverridevoidOnSleep()

{

//Handlewhenyourappsleeps

}

protectedoverridevoidOnResume()

{

//Handlewhenyourappresumes

}

}

}

Intheprecedingcodesnippet,webeginbydeclaringanavServicevariablethatpointstoaninstanceofthenavigationserviceasdefinedbyourassemblyattributefortheXamarin.FormsDependencyService,asdeclaredintheWalkNavServiceclass.

Inournextstep,wesetthenavService.navigationpropertytopointtoaninstanceoftheNavigationPageclassthatthewalksPage.navigationpropertycurrentlypointsto,andwillbeusedasthemainrootpage.

Finally,wecalltheRegisterViewMappinginstancemethodforeachoftheViewModelsandspecifytheassociatedContentPageforeach.

SummaryInthischapter,weupdatedourTrackMyWalksapplicationandcreatedanavigationserviceclassthatextendsthedefaultXamarin.Forms.NavigationAPI,whichprovidesuswithabettermethodofperformingViewModelnavigation.ThisseparatesthepresentationaspectsandbusinesslogicthatarecontainedwithintheViewModels.

Inthenextchapter,you'lllearnhowtocreatealocationservicesclassthatwillallowourTrackMyWalksapptoretrievelocation-basedinformation,anddeterminetheuser'scurrentlocation.You'llalsolearnhowtosetupourapptohandlebackgroundlocationupdates.Youwillalsolearnhowtoincorporateplatform-specificfeatureswithinyourapp,dependingontheplatformthatisbeingrun.

Chapter4.AddingLocation-BasedFeatureswithinYourAppInourpreviouschapter,welookedathowwecanapplywhatwealreadyknowabouttheMVVMdesignpattern,andhowwecannavigatebetweenourViewModels,bycreatinganavigationserviceC#classthatactsasthenavigationserviceforourapp,usingtheXamarin.FormsDependencyServiceclass.

Inthischapter,you'lllearnhowtogoaboutincorporatingplatform-specificfeatureswithintheTrackMyWalksapp,dependingonthemobileplatform.You'lllearnhowtocreateaC#class,whichwillactastheLocationServiceforourapp,aswellascreatingaIWalkLocationServiceinterfaceclassfile,whichwillincludeanumberofclassmethodsthatbothouriOSandAndroidplatformswillinherit,and,inturn,updatethecontentpagestobindwiththeViewModelstoallowlocation-basedinformationbetweentheseViewstohappen.

Wewillalsobecoveringhowtoproperlyperformlocationupdateswhiletheapplicationiseitherintheforegroundorbackground,andwewillalsobetouchingonsomekeybackgroundconcepts,whichincluderegisteringanappasabackground-necessaryapplication.

Thischapterwillcoverthefollowingtopics:

Creatingalocation-basedclassthatutilizesthenativeplatformcapabilitiesthatcomeaspartoftheiOSandAndroidplatformsEnablingbackgroundlocationupdatesaswellasgettingtheuser'scurrentlocationUpdatingtheTrackMyWalksapplicationtousetheLocationServiceUpdatingtheWalkEntryViewModeltousetheLocationServiceInterfaceUpdatingtheDistanceTravelledViewModeltousetheLocationServiceInterface

Creatingandusingplatform-specificservicesAsmentionedintheintroductiontothischapter,wecreatedacustomizednavigationservice,whichprovidedanIWalkNavServiceInterfaceclassforwhichourWalkBaseViewModelcontainedapropertyofthatinterfacetype,sothatanyimplementationsoftheIWalkNavServicecanbeprovidedtoeachoftheViewModels,asrequired.

ThebenefitofusinganInterfacetodefineplatform-specificservicesisthatitcanbeusedwithintheViewModelsandtheimplementationsoftheservicecanbeprovidedviadependencyinjection,usingtheDependencyService,withthoseimplementationsbeingactualservices,orevenmocked-upservicesforunittestingtheViewModels,whichwewillbecoveringinChapter9,UnitTestingYourXamarin.FormsAppsUsingtheNUnitandUITestFrameworks.

Inadditiontothenavigationservice,wecanuseacoupleofotherplatform-specificfeatureserviceswithinourTrackMyWalksapptoenrichitsdataanduserexperience.Inthissection,wewillbetakingalookathowtocreateaLocationServiceclassthatallowsustogetthespecificgeolocationcoordinatesfromtheactualdeviceforbothouriOSandAndroidplatforms.

CreatingtheLocationServiceInterfacefortheTrackMyWalksappBeforewecanbeginallowingourTrackMyWalksapptotakeadvantageofthedevice'sgeolocationcapabilitiesforbothouriOSandAndroidplatforms,wewillneedtocreateanInterfacewithintheTrackMyWalksPortableClassLibrary,whichcanthenbeusedbytheViewModelsforeachplatform.

Wewillneedtodefinetheinterfaceforourlocationservice,asthiswillcontainmethodimplementations,aswellasadatastructurethatwillbeusedtorepresentourlatitudeandlongitudecoordinates.

Let'stakealookathowwecanachievethisthroughthefollowingsteps:

1. LaunchtheXamarinStudioapplication,andensurethattheTrackMyWalkssolutionisloadedwithintheXamarinStudioIDE.

2. Next,createanewemptyinterfacewithintheTrackMyWalksPCLprojectsolution,undertheServicesfolder.

3. Then,choosetheEmptyInterfaceoptionlocatedwithintheGeneralsectionandenterIWalkLocationServiceforthenameofthenewinterfacefiletobecreated,asshowninthefollowingscreenshot:

4. Next,clickontheNewbuttontoallowthewizardtoproceedandcreatethenewEmptyInterfaceclassfile,asshownintheprecedingscreenshot.

5. OurwizardhascreatedourIWalkLocationServiceclassfile,whichwillbeusedbyourViewModelsandcontentpageViewstodisplaygeolocationcoordinates.AswestarttobuildtheLocationServiceInterfaceclass,youwillseethatitcontainsacoupleofclassmembersthatwillallowustogettheuser'slocationaswellasdeterminingthedistancethattheuserhastravelledfrompointAtopointB.

6. ItalsocontainsadatastructureIWalkLocationCoordsthatwillbeusedtoholdourlatitudeandlongitudegeolocationcoordinates.ToproceedwithcreatingthebaseIWalkLocationServiceInterface,performthefollowingsteps:

7. EnsurethattheIWalkLocationService.csfileisdisplayedwithinthecodeeditorandenterthefollowingcodesnippet:

//

//IWalkLocationService.cs

//TrackMyWalksLocationServiceInterface

//

//CreatedbyStevenF.Danielon16/09/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

namespaceTrackMyWalks.Services

{

//DefineourWalkLocationServiceInterface

publicinterfaceIWalkLocationService

{

//DefineourLocationServiceInstanceMethods

voidGetMyLocation();

doubleGetDistanceTravelled(doublelat,doublelon);

eventEventHandler<IWalkLocationCoords>MyLocation;

}

//WalkLocationCoordinatesObtained

publicinterfaceIWalkLocationCoords

{

doublelatitude{get;set;}

doublelongitude{get;set;}

}

}

Intheprecedingcodesnippet,westartbydefiningtheimplementationforourIWalkLocationService,whichwillprovideourTrackMyWalksappwiththeabilitytogettheuser'scurrentlocation,calculatingthedistancetravelledfromtheuser'scurrentlocationtothetrailgoal.WealsodefineanEventHandlermyLocation,whichwillbecalledwhenevertheplatformobtainsanewlocation.

TheIWalkLocationCoordsinterfacedefinesaclassthatcontainstwopropertiesthatwillbeusedbyourEventHandlertoreturnthelatitudeandlongitudevalues.

Note

AnInterfacecontainsonlythemethods,properties,andeventssignaturedefinitions.Anyclassthatimplementstheinterfacemustimplementallmembersoftheinterfacethatarespecifiedintheinterfacedefinition.

NowthatwehavedefinedthepropertyandmethodimplementationsthatwillbeusedbyourIWalkLocationService,ournextstepwillbetocreatetherequiredLocationServiceclassimplementationsforeachofourplatforms,astheyaredefinedquitedifferently.

CreatingtheLocationServiceclassfortheAndroidplatformInthissection,wewillbeginbysettingupthebasicstructureforourTrackMyWalks.DroidsolutiontoincludethefolderthatwillbeusedtorepresentourServices.Let'stakealookathowwecanachievethisthroughthefollowingsteps:

1. LaunchtheXamarinStudioapplication,andensurethattheTrackMyWalkssolutionisloadedwithintheXamarinStudioIDE.

2. Next,createanewfolderwithintheTrackMyWalks.Droidproject,calledServices,asshowninthefollowingscreenshot:

3. Next,createanemptyclasswithintheServicesfolder.Ifyoucan'trememberhowtodothis,youcanrefertothesectionentitledCreatingtheNavigationServiceInterfacefortheTrackMyWalksapp,withinChapter3,NavigatingwithintheMVVMModel-TheXamarin.FormsWay.

4. Then,choosetheEmptyClassoptionlocatedwithintheGeneralsectionandenterWalkLocationServiceforthenameofthenewclassfiletobecreated,asshowninthefollowingscreenshot:

5. Next,clickontheNewbuttontoallowthewizardtoproceedandcreatethenewemptyclassfile,asshownintheprecedingscreenshot.

Upuntilthispoint,allwehavedoneiscreateourWalkLocationServiceclassfile.ThisclasswillbeusedandwillactasthebaseLocationServiceclassthatwillcontainthefunctionalityrequiredbyourViewModels.

AswestarttobuildourLocationClass,youwillseethatitcontainsanumberofmethodmembersthatwillbeusedtohelpusgettheuser'scurrentgeolocationcoordinatesfromtheirdevice,sothatwecandisplaythiswithineachofourViewModels,anditwillimplementtheIWalkLocationServiceInterface.

ToproceedwithcreatingandimplementingthebaseWalkLocationServiceclass,performthefollowingsteps:

1. EnsurethattheWalkLocationService.csfileisdisplayedwithinthecodeeditor,andenterthehighlightedcodesectionsshowninthefollowingcodesnippet:

//

//WalkLocationService.cs

//TrackMyWalksLocationServiceClass(Android)

//

//CreatedbyStevenF.Danielon16/09/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingAndroid.Content;

usingAndroid.Locations;

usingTrackMyWalks.Droid;

usingTrackMyWalks.Services;

usingXamarin.Forms;

2. First,weinitializeourWalkLocationServiceclass,whichistobemarkedasadependency,byaddingtheDependencymetadataattributejustaswedidforournavigationservice.ThisissothatitcanberesolvedbytheXamarin.FormsDependencyServicetoallowittofindanduseourmethodimplementationsasdefinedwithinourInterface.WealsoneedtoimplementtheIWalkCoordinatesinterfaceusingtheLocationEventArgsclassthatcontainsourlatitudeandlongitudeproperties,whichwillbepopulatedwheneveranewlocationisobtained:

[assembly:Xamarin.Forms.Dependency(

typeof(WalkLocationService))]

namespaceTrackMyWalks.Droid

{

//Eventargumentscontaininglatitudeandlongitude

publicclassCoordinates:EventArgs,IWalkCoordinates

{

publicdoublelatitude{get;set;}

publicdoublelongitude{get;set;}

}

3. Next,weneedtomodifyourWalkLocationServiceclassconstructorsignature,sothatitinheritsfromtheIWalkLocationServiceInterfaceclass,aswellasimplementinganILocationListenerinterfaceclass,whichwillbeusedtoindicatewhenevertheuser'slocationchanges,byimplementingfourmethods-OnLocationChanged,OnProviderDisabled,OnProviderEnabled,andOnStatusChanged:

publicclassWalkLocationService:Java.Lang.Object,

IWalkLocationService,ILocationListener{

LocationManagerlocationManager;

LocationnewLocation;

//CreatethefourmethodsforourLocationListener

//interface.

publicvoidOnProviderDisabled(stringprovider){}

publicvoidOnProviderEnabled(stringprovider){}

publicvoidOnStatusChanged(stringprovider,

Availabilitystatus,Android.OS.Bundleextras){}

Note

WeneedtoensurethatourWalkLocationServiceclassinheritsfromtheAndroid-specificJava.Lang.Objectclass,sothatwecanprovideaccesstothesystemlocationservices,inordertoobtainperiodicupdatesonthedevice'sgeographicallocation.

WheneveryourclassesinheritfromtheILocationListenerAPI,theILocationListenerInterfacesupportsseveraldifferentmethodtypes,whichareexplainedinthefollowingtable:

Methodname Description

OnProviderDisabledThismethodisfiredupwheneverthelocationserviceproviderhasbeendisabledbytheuser.

OnProviderEnabledThismethodisfiredupwheneverthelocationserviceproviderhasbeenenabledbytheuser.

OnStatusChanged

Thismethodisfiredupwheneverthelocationserviceproviderstatushasbeenchanged,thatis,thelocationserviceshavebeendisabledbytheuser.

OnLocationChangedThismethodisfiredupwheneverachangeinlocationhasbeendetected.

4. Then,weneedtosetupanEventHandlerdelegateobjectthatwillbecalledwheneverthelocationhasbeenobtainedorchanged:

//SetupourEventHandlerdelegatethatiscalled

//wheneveralocationhasbeenobtained

publiceventEventHandler<IWalkCoordinates>MyLocation;

5. Next,wecreatetheOnLocationChangedmethodthatwillbefiredupwhenevertheuser'slocationhasbeenchangedsincethelasttime.Thismethodacceptstheuser'scurrentlocation,andweneedtoaddachecktoensurethatourlocationisnotemptypriortocreatinganinstanceofourCoordinatesclassdatastructure,andthenassigningthenewlocationdetailsforourlatitudeandlongitude,beforefinallypassingacopyoftheCoordinatestotheMyLocationEventHandler:

//Firedwheneverthereisachangeinlocation

publicvoidOnLocationChanged(Locationlocation)

{

if(location!=null)

{

//CreateaninstanceofourCoordinates

varcoords=newCoordinates();

//Assignouruser'sLatitudeandLongitude

//values

coords.latitude=location.Latitude;

coords.longitude=location.Longitude;

//Updateournewlocationtostorethe

//newdetails.

newLocation=newLocation("PointA");

newLocation.Latitude=coords.latitude;

newLocation.Longitude=coords.longitude;

//Passthenewlocationdetailstoour

//LocationServiceEventHandler.

MyLocation(this,coords);

};

}

6. Then,wecreatetheGetMyLocationmethodthatwillbeusedtostartgettingtheuser'slocation.WethensetupourlocationManagertorequestlocationupdates.Thisisbecause,whendealingwithAndroid,theseservicesrequireaContextobjectinorderforthemtowork.Xamarin.FormscomeswiththeForms.Contextobject,andweusetheNetworkProvidermethodtoobtainthelocationusingthecellularnetworkandWi-Fi.Considerthefollowingcode:

//Methodtocalltostartgettinglocation

publicvoidGetMyLocation()

{

locationManager=(LocationManager)

longminTime=0;//Timeinmilliseconds

floatminDistance=0;//Distanceinmetres

Forms.Context.GetSystemService(Context.LocationService);

locationManager.RequestLocationUpdates(

LocationManager.NetworkProvider,

minTime,

minDistance,

this);

}

7. Next,createtheGetDistanceTravelledmethod,whichacceptstwoparameterscontainingourlatitudeandlongitudevalues.Wecreateanewlocation,andsettheLatitudeandLongitudevaluesthatcontaintheendingcoordinatesforourtrail.Wethendeclareavariabledistance,whichcallstheDistanceTomethodonournewLocationobject,todetermineourcurrentdistancefromtheendgoal.Wedividethedistanceby1000toconvertthedistancetravelledtometers:

//Calculatesthedistancebetweentwopoints

publicdoubleGetDistanceTravelled(doublelat,doublelon)

{

LocationlocationB=newLocation("TrailFinish");

locationB.Latitude=lat;

locationB.Longitude=lon;

floatdistance=newLocation.DistanceTo(locationB)/1000;

returndistance;

}

8. Finally,createtheWalkLocationServiceclassfinalizer;thiswillbeusedtostopallupdatelistenereventswhenourclasshasbeensettonull.

//Stopthelocationupdatewhentheobjectissettonull

~WalkLocationService()

{

locationManager.RemoveUpdates(this);

}

}

}

NowthatwehavecreatedtheWalkLocationServiceclassfortheAndroidportionofourTrackMyWalksapp,ournextstepistocreatethesameclassfortheiOSportion,whichwillbecoveredinthenextsection.

CreatingtheLocationServiceclassfortheiOSplatformIntheprevioussection,wecreatedtheclassforourWalkLocationService.Wealsodefinedanumberofdifferentmethodsthatwillbeusedtoprovidelocation-basedinformationwithinourMVVMViewModel.

Inthissection,wewillbuildtheiOSportionforourWalkLocationService,justlikewedidforourAndroidportion.Youwillnoticethattheimplementationsforbothoftheseclassesarequitesimilar;however,theseimplementdifferentmethods,asyouwillseeoncewestartimplementingthem.

Let'stakealookathowwecanachievethisthroughthefollowingsteps:

1. CreateanemptyclasswithintheServicesfolderforourTrackMyWalks.iOSproject,andenterWalkLocationServiceforthenameofthenewclassfiletocreate.

2. OnceyouhavecreatedtheWalkLocationServiceclassfile,ensurethattheWalkLocationService.csfileisdisplayedwithinthecodeeditorandenterthehighlightedcodesectionsshowninthefollowingcodesnippet:

//

//WalkLocationService.cs

//TrackMyWalksLocationServiceClass(iOS)

//

//CreatedbyStevenF.Danielon16/09/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingCoreLocation;

usingTrackMyWalks.iOS;

usingTrackMyWalks.Services;

usingUIKit;

3. Next,weinitializeourWalkLocationServiceclass,whichistobemarkedasadependency,byaddingtheDependencymetadataattributejustaswedidforournavigationservice.ThisissothatitcanberesolvedbytheXamarin.FormsDependencyServicetoallowittofindanduseourmethodimplementationsasdefinedwithinourInterface.WealsoneedtoimplementtheIWalkCoordinatesinterfaceusingtheCoordinatesclass,whichcontainsthelatitudeandlongitudepropertiesthatwillbepopulatedwheneveranewlocationisobtained:

[assembly:Xamarin.Forms.Dependency(typeof(WalkLocationService))]

namespaceTrackMyWalks.iOS

{

//Eventargumentscontaininglatitudeandlongitude

publicclassCoordinates:EventArgs,IWalkCoordinates

{

publicdoublelatitude{get;set;}

publicdoublelongitude{get;set;}

}

4. Then,weneedtomodifyourWalkLocationServiceclassconstructorsignature,sothatitinheritsfromtheIWalkLocationServiceInterfaceclass.WealsoneedtodeclareourlocationManagerobject,whichwillbeusedtoobtaintheuser'slocation.WealsocreateanewLocationobjectoftypeCLLocation,whichwillbeusedtoconvertthelatitudeandlongitudecoordinatesfromthelocationManagerobjectintoaCLLocationobject:

//WalkLocationServiceclassthatinheritsfromour

//IWalkLocationServiceinterface

publicclassWalkLocationService:IWalkLocationService

{

//DeclareourLocationManager

CLLocationManagerlocationManager;

CLLocationnewLocation;

5. Next,weneedtosetupanEventHandlerdelegateobjectthatwillbecalledwheneverthelocationhasbeenobtainedorchanged:

//SetupourEventHandlerdelegatethatiscalled

//wheneveralocationhasbeenobtained

publiceventEventHandler<IWalkCoordinates>MyLocation;

6. Then,wecreatetheGetMyLocationmethodthatwillbeusedtostartgettingtheuser'slocation.Next,wesetupourlocationManagerusingtheiOSCLLocationManagerclasstoallowourclasstorequestlocationupdates.Wethenperformacheck,usingtheLocationServicesEnabledpropertyoftheCLLocationManagerclass,toensurethatlocationserviceshavebeenenabledontheuser'sdevice.

7. Thisisagoodchecktoenforcepriortorequestingthegettingoftheuser'slocation,andifourCLLocationManagerclassdeterminesthatlocationserviceshavebeendisabled,wedisplayamessagetotheuser,usingtheUIAlertViewclass:

//Methodtocalltostartgettinglocation

publicvoidGetMyLocation()

{

locationManager=newCLLocationManager();

//Checktoseeifwehavelocationservices

//enabled

if(CLLocationManager.LocationServicesEnabled)

{

//Setthedesiredaccuracy,inmeters

locationManager.DesiredAccuracy=1;

//CLLocationManagerDelegateMethods

8. Next,wesetupaneventhandler,LocationsUpdated,thatwillstartfiringupwheneverthereisachangeintheuser'scurrentlocation,andwecallthelocationUpdatedinstancemethod,passinginthelocationgeo-coordinates:

//Firedwheneverthereisachangein

//location

locationManager.LocationsUpdated+=(objectsender,

CLLocationsUpdatedEventArgse)=>

{

locationUpdated(e);

};

9. Then,wesetupaneventhandler,AuthorizationChanged,whichwillbecalledwheneveritdetectsachangemadetotheauthorizationoflocation-basedservices.Forexample,thiswillbecalledif,forsomereason,theuserdecidestoturnofflocation-basedservices:

//Thiseventgetsfiredwheneverit

//detectsachange,i.e.,iftheuser

//hasturnedoffordisabledlocation

//basedservices.

locationManager.AuthorizationChanged+=(object

sender,CLAuthorizationChangedEventArgse)=>

{

didAuthorizationChange(e);

//Performlocationchangeswithinthe

//foreground.

locationManager.RequestWhenInUseAuthorization();

};

}

}

10. Next,wecreatethelocationUpdatedmethodthatwillbefiredupwhenevertheuser'slocationhasbeenchangedsincethelasttime.Thismethodacceptstheuser'scurrentlocation,whichisdefinedbytheCLLocationsUpdatedEventArgsinavariablecallede.Next,wecreateaninstanceofourCoordinatesclassdatastructure,andthenassignthenewlocationdetailsforthelatitudeandlongitude,beforefinallypassingacopyoftheCoordinatestotheMyLocationEventHandler:

//Methodiscalledwheneverthereisachangein

//location

publicvoidlocationUpdated(CLLocationsUpdatedEventArgse)

{

//CreateourLocationCoordinates

varcoords=newCoordinates();

//Getalistofourlocationsfound

varlocations=e.Locations;

//ExtractourLatitudeandLongitudevalues

//fromourlocationsarray.

coords.latitude=locations[locations.Length-1].

Coordinate.Latitude;

coords.longitude=locations[locations.Length-1].

Coordinate.Longitude;

//Then,convertbothourLatitudeandLongitude

//valuestoaCLLocationobject.

newLocation=newCLLocation(coords.latitude,

coords.longitude);

MyLocation(this,coords);

}

11. Then,wecreatethedidAuthorizationChangemethod,whichwillbecalledwhenevertheCLLocationManagerdelegatedetectsachangeintheauthorizationstatus;youwillbenotifiedaboutthosechanges.Tohandleanychangesintheauthorizationstatuswhileyourappisrunning,andtopreventyourapplicationfromcrashingunexpectedly,youwillneedtoensurethattheproperauthorizationishandledaccordingly.

12. Ifwedetectthattheuserhasrestrictedordeniedaccesstolocationservicesonthedevice,wewillneedtoalerttheusertothis,anddisplayanalertdialogpopup:

publicvoiddidAuthorizationChange(

CLAuthorizationChangedEventArgsauthStatus)

{

switch(authStatus.Status){

caseCLAuthorizationStatus.AuthorizedAlways:

locationManager.RequestAlwaysAuthorization();

break;

caseCLAuthorizationStatus.AuthorizedWhenInUse:

locationManager.StartUpdatingLocation();

break;

caseCLAuthorizationStatus.Denied:

UIAlertViewalert=newUIAlertView();

alert.Title="LocationServicesDisabled";

alert.AddButton("OK");

alert.AddButton("Cancel");

alert.Message="Enablelocationsforthisapp

via\ntheSettingsapponyouriPhone";

alert.AlertViewStyle=UIAlertViewStyle.Default;

alert.Show();

alert.Clicked+=(objects,

UIButtonEventArgsev)=>

{

varButton=ev.ButtonIndex;

};

break;

default:

break;

}

}

13. ThedidAuthorizationChangemethodcontainsanumberofauthorizationstatuscodes,andtheseareexplained,alongwiththeirdescriptions,inthefollowingtable:

Authorizationstatus Description

.AuthorizedAlwaysor

.AuthorizedWhenInUse

Eitherofthesecasescanoccurwhenevertheuserhasgrantedaccessforyourapptouselocationservices.Thesestatusesarebothmutuallyexclusive,asyoucanonlyreceiveonetypeofauthorizationatatime.

.NotDetermined

Thisgenerallyhappenswhenevertheuserhasn'tmadeachoiceregardingwhetheryouriOSappcanbeginacceptinglocationupdates,andcanbecausediftheuserhasinstalledyourappforthefirsttimeandhasnotrunityet.

.Restrictedor

.Denied

Youwillgenerallyreceivethistypeofauthorizationstatusstatewhenevertheuserhasexplicitlydeniedaccesstoyourappfortheuseoflocationservices,orwhenlocationservicesarecurrentlyunavailable.

Note

IfyouareinterestedinfindingoutmoreinformationontheCLLocationManagerclass,pleaserefertotheXamarindeveloperdocumentationlocatedathttps://developer.xamarin.com/api/type/CoreLocation.CLLocationManager/.

14. Next,createtheGetDistanceTravelledmethod,whichacceptstwoparameterscontainingourlatandlonvalues,anddeclaresavariabledistance,whichcallstheDistanceFrommethodonournewLocationobject,todetermineourcurrentdistancefromtheendgoal.Wedividethedistanceby1000toconvertthedistancetravelledtometers:

//Calculatesthedistancebetweentwopoints

publicdoubleGetDistanceTravelled(doublelat,doublelon)

{

//Getthedistancetravelledfrom

//currentlocationtothepreviouslocation.

vardistance=newLocation.DistanceFrom(new

CLLocation(lat,lon))/1000;

returndistance;

}

15. Finally,createtheWalkLocationServiceclassfinalizer,whichwillbeusedtostopallupdatelistenereventsandfreethememoryusedwhenourclasshasbeensettonull:

//Stopsperforminglocationupdateswhenthe

//objecthasbeensettonull.

~WalkLocationService()

{

locationManager.StopUpdatingLocation();

}

}

}

NowthatwehavecreatedtheWalkLocationServiceclassfortheiOSportionofourTrackMyWalksapp,ournextstepistolearnhowtoprovideouriOSappwiththefunctionalitytoperformcontinuouslocationupdatesinthebackground.

Enablingbackgroundupdatesandgettingtheuser'scurrentlocationThisrelatestoworkingwithbackgroundlocationupdatestocontinuouslymonitorchangestotheuserlocationinthebackground.

Let'stakealookathowwecanachievethisthroughthefollowingsteps:

1. Double-clickontheInfo.plistfile,whichiscontainedwithintheTrackMyWalks.iOSproject,andensurethattheApplicationtabisshowing.

2. Next,scrolldowntothebottomofthepageandselectEnableBackgroundModesfromundertheBackgroundModessectiontoenablebackgroundupdates.

3. Then,ensurethattheLocationUpdatesoptionhasbeenselected,sothatXcodecanprovisionyourapptomonitorlocation-basedupdatesinthebackground:

4. NowthatwehavemodifiedourTrackMyWalks.iOSprojecttomonitorlocationupdatesinthebackground,weneedtodoonemorethingandtellXcodetohandleLocationupdates.Solet'sdothatnow.

5. EnsurethattheInfo.plistfileisdisplayedwithintheXamarinIDE,andthattheSourcetabisshowing.

6. Next,createthekeysNSLocationAlwaysUsageDescriptionandNSLocationWhenInUseUsageDescriptionbyclickingwithintheAddnewentrysection

oftheInfo.plist.

7. Then,addTrackMyWalkswouldliketoobtainyourlocationasthestringdescriptionfortheValuefield,asshownintheprecedingscreenshot.

Next,weneedtoprovideourappwiththeabilitytomonitorlocationupdatesinthebackgroundforourTrackMyWalks.iOSproject.Let'stakealookathowwecanachievethisthroughthefollowingsteps:

1. EnsurethattheWalkLocationService.csfileisdisplayedwithinthecodeeditor.2. Next,locatetheGetMyLocationmethodandenterthefollowingcodesnippet:

//Methodtocalltostartgettinglocation

publicvoidGetMyLocation()

{

locationManager=newCLLocationManager();

//Checktoseeifwehavelocationservices

//enabled

if(CLLocationManager.LocationServicesEnabled)

{

//Setthedesiredaccuracy,inmeters

locationManager.DesiredAccuracy=1;

//iOS8hasadditionalpermission

//requirements

if(UIDevice.CurrentDevice.CheckSystemVersion(8,0))

{

//Performlocationchangeswithinthe

//background

locationManager.RequestAlwaysAuthorization();

}

//iOS9,comeswithanewmethodthat

//allowsustoreceivelocationupdates

//withintheback,whentheapphas

//suspended.

if(UIDevice.CurrentDevice.CheckSystemVersion(9,0))

{

locationManager.AllowsBackgroundLocationUpdates=

true;

}

//CLLocationManagerDelegateMethods

//Firedwheneverthereisachangein

//location

locationManager.LocationsUpdated+=(objectsender,

CLLocationsUpdatedEventArgse)=>

{

locationUpdated(e);

};

//Thiseventgetsfiredwheneverit

//detectsachange,i.e.,iftheuserhas

//turnedoffordisabledLocationBased

//Services.

locationManager.AuthorizationChanged+=(object

sender,CLAuthorizationChangedEventArgse)=>

{

didAuthorizationChange(e);

//Performlocationchangeswithin

//theforeground.

locationManager.RequestWhenInUseAuthorization();

};

}

}

Intheprecedingcodesnippet,wechecktheiOSversioncurrentlyrunningontheuser'sdevice,andusetheRequestAlwaysAuthorizationmethodcallonthelocationManagerclasstorequesttheuser'spermissiontoobtaintheircurrentlocation.IniOS9,AppledecidedtoaddanewmethodcalledAllowsBackgroundLocationUpdates,whichallowsthehandlingofbackgroundlocationupdates.Next,wealsoneedtoconfigureourAndroidportionofourTrackMyWalks.DroidprojectbymodifyingtheAndroidManifest.xmlfile.

Let'stakealookathowwecanachievethisthroughthefollowingsteps:

1. Double-clickontheAndroidManifest.xmlfile,whichiscontainedwithinthe

TrackMyWalks.Droidproject,andensurethattheSourcetabisselected,asshowninthefollowingscreenshot:

2. EnsurethattheAndroidManifest.xmlfileisdisplayedwithinthecodeeditor,andenterthefollowinghighlightedcodesections:

<?xmlversion="1.0"encoding="utf-8"?>

<manifestxmlns:android="http://schemas.

android.com/apk/res/android"

android:versionCode="1"android:versionName="1.0"

package="com.geniesoftstudios.trackmywalks">

<uses-sdkandroid:minSdkVersion="15"/>

<uses-permissionandroid:name="android.

permission.ACCESS_FINE_LOCATION"/>

<uses-permissionandroid:name="android.

permission.ACCESS_COARSE_LOCATION"/>

<uses-permissionandroid:name="android.

permission.INTERNET"/>

<applicationandroid:label="TrackMyWalks">

</application>

</manifest>

Intheprecedingcodesnippet,webeginbyaddingpermissionsthatwillallowour

TrackMyWalksAndroidapptoaccesslocationinformationforlocationupdates,aswellastheInternet.Googleisprettystrictaboutwhichpermissionsareallowed,andthesemustbeapprovedpriortoyourappbeingacceptedintotheGooglePlayStore.

UpdatingtheWalkEntryViewModeltousethelocationserviceNowthatwehavecreatedourWalkLocationServiceforbothourAndroidandiOSimplementations,weneedtobeginmodifyingourViewModel,whichwillbeusedbyourWalkEntrypage,totakeadvantageofourLocationService.

Let'stakealookathowwecanachievethisthroughthefollowingsteps:

1. EnsurethattheWalkEntryViewModel.csfileisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesections:

//

//WalkEntryViewModel.cs

//TrackMyWalksViewModels

//

//CreatedbyStevenF.Danielon22/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingSystem.Diagnostics.Contracts;

usingSystem.Threading.Tasks;

usingTrackMyWalks.Models;

usingTrackMyWalks.Services;

usingTrackMyWalks.ViewModels;

usingXamarin.Forms;

namespaceTrackMyWalks.ViewModels

{

publicclassWalkEntryViewModel:WalkBaseViewModel

{

2. Next,wedeclarealocationServicevariablethatwillbeusedtoprovideareferencetoourIWalkLocationServiceandprovideourclasswithareferencetotheEventHandler,whichiscontainedwithinourIWalkLocationServiceinterfaceclass;thiswillcontainourlocationcoordinateinformationwheneverthelocationchanges.Toproceed,enterthefollowinghighlightedcodesections:

IWalkLocationServicelocationService;

string_title;

publicstringTitle

{

get{return_title;}

set

{

_title=value;

OnPropertyChanged();

SaveCommand.ChangeCanExecute();

}

}

string_notes;

publicstringNotes

{

get{return_notes;}

set

{

_notes=value;

OnPropertyChanged();

}

}

double_latitude;

publicdoubleLatitude

{

get{return_latitude;}

set

{

_latitude=value;

OnPropertyChanged();

}

}

double_longitude;

publicdoubleLongitude

{

get{return_longitude;}

set

{

_longitude=value;

OnPropertyChanged();

}

}

double_kilometers;

publicdoubleKilometers

{

get{return_kilometers;}

set

{

_kilometers=value;

OnPropertyChanged();

}

}

string_difficulty;

publicstringDifficulty

{

get{return_difficulty;}

set

{

_difficulty=value;

OnPropertyChanged();

}

}

double_distance;

publicdoubleDistance

{

get{return_distance;}

set

{

_distance=value;

OnPropertyChanged();

}

}

string_imageUrl;

publicstringImageUrl

{

get{return_imageUrl;}

set

{

_imageUrl=value;

OnPropertyChanged();

}

}

3. Inournextstep,wewillneedtomodifythecontentsofourWalksEntryViewModelclassconstructortodeclareandinitializeourlocationServicevariable,whichwillincludeourIWalkLocationServiceconstructorthatisretrievedfromtheXamarin.FormsDependencyServiceclass.WethenproceedtocalltheMyLocationmethodonourEventHandler,whichisdefinedwithintheIWalkLocationServiceinterface;thiswillreturnthegeographicallocationcoordinatesdefinedbytheirLatitudeandLongitudevalues.

4. LocatetheWalksEntryViewModelclassconstructorandenterthefollowinghighlightedcodesections:

publicWalkEntryViewModel(IWalkNavServicenavService):

base(navService)

{

Title="NewWalk";

Difficulty="Easy";

Distance=1.0;

//GetourLocationService

locationService

=DependencyService.Get<IWalkLocationService>();

//Checktoensurethatwehaveavalue

//forourobject

if(locationService!=null)

{

locationService.MyLocation+=(objectsender,

IWalkCoordinatese)=>

{

//ObtainourLatitudeandLongitude

//coordinates

Latitude=e.latitude;

Longitude=e.longitude;

};

}

//CallourServicetogetourGPSlocation

locationService.GetMyLocation();

}

Command_saveCommand;

publicCommandSaveCommand

{

get

{

return_saveCommand??(

_saveCommand=newCommand(async()=>

awaitExecuteSaveCommand(),ValidateFormDetails));

}

}

asyncTaskExecuteSaveCommand()

{

varnewWalkItem=newWalkEntries

{

Title=this.Title,

Notes=this.Notes,

Latitude=this.Latitude,

Longitude=this.Longitude,

Kilometers=this.Kilometers,

Difficulty=this.Difficulty,

Distance=this.Distance,

ImageUrl=this.ImageUrl

};

5. Then,welocateandmodifytheExecuteSaveCommandinstancemethodtofreethememoryusedbyourlocationServicevariablewhentheSavebuttonispressed.Thisisachievedbysettingthistonull,whichinturnwillcallthe~GetMyLocation()withintheiOSandAndroidclassde-constructor.Proceedtoenterthefollowinghighlightedcodesections:

//UponexitingourNewWalkEntryPage,

//weneedtostopcheckingforlocation

//updates

locationService=null;

//Here,wewillsavethedetailsentered

//inalaterchapter.

awaitNavService.PreviousPage();

}

//methodtocheckforanyformerrors

boolValidateFormDetails()

{

return!string.IsNullOrWhiteSpace(Title);

}

publicoverrideasyncTaskInit()

{

awaitTask.Factory.StartNew(()=>

{

Title="NewWalk";

Difficulty="Easy";

Distance=1.0;

});

}

}

}

Inthissection,welookedatthestepsinvolvedinmodifyingourWalkEntryViewModelsothatitcantakeadvantageofourWalkLocationService.

WethendeclaredalocationServicevariablethatwillbeusedtoprovideareferencetoourIWalkLocationServiceandprovideourclasswithareferencetotheEventHandler,whichiscontainedwithinourIWalkLocationServiceinterfaceclass;thiswillcontainourlocationcoordinateinformationwheneverthelocationchanges.

WealsomodifiedourWalkEntryViewModelclassconstructortoinitializeourlocationServicevariable,topointtotheIWalkLocationServiceconstructorthatisretrievedfromtheXamarin.FormsDependencyServiceclass,whichneedstobedonepriortocallingtheMyLocationmethodonourEventHandler,sothatitcanreturnthegeographicallocationcoordinates,definedbytheirLatitudeandLongitudevalues.

Finally,wesetourlocationServiceobjecttonulltostopcheckingforlocationupdates.

UpdatingtheDistanceTravelledViewModeltousethelocationserviceNowthatwehavemodifiedourMVVMViewModelforourWalkEntryViewModel,ournextstepistobeginmodifyingourDistTravelledViewModeltotakeadvantageofourWalkLocationServiceclass,thatwillbeusedtocalculatethedistancetravelled,andsavethisinformationbacktoourDistTravelledViewModel.

Let'stakealookathowwecanachievethisthroughthefollowingsteps:

1. EnsurethattheDistTravelledViewModel.csfileisdisplayedwithinthecodeeditor.

//

//DistTravelledViewModel.cs

//TrackMyWalksViewModels

//

//CreatedbyStevenF.Danielon22/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingSystem.Threading.Tasks;

usingTrackMyWalks.Models;

usingTrackMyWalks.Services;

usingTrackMyWalks.ViewModels;

usingXamarin.Forms;

namespaceTrackMyWalks.ViewModels

{

publicclassDistTravelledViewModel:

WalkBaseViewModel<WalkEntries>

{

WalkEntries_walkEntry;

2. Next,wedeclaredalocationServicevariablethatwillbeusedtoprovideareferencetoourIWalkLocationServiceandprovideourclasswithareferencetotheEventHandler,whichiscontainedwithinourIWalkLocationServiceinterfaceclass;thiswillcontainourlocationcoordinateinformationwheneverthelocationchanges.Toproceed,enterthefollowinghighlightedcodesections:

IWalkLocationServicelocationService;

publicWalkEntriesWalkEntry

{

get{return_walkEntry;}

set

{

_walkEntry=value;

OnPropertyChanged();

}

}

double_travelled;

publicdoubleTravelled

{

get{return_travelled;}

set

{

_travelled=value;

OnPropertyChanged();

}

}

double_hours;

publicdoubleHours

{

get{return_hours;}

set

{

_hours=value;

OnPropertyChanged();

}

}

double_minutes;

publicdoubleMinutes

{

get{return_minutes;}

set

{

_minutes=value;

OnPropertyChanged();

}

}

double_seconds;

publicdoubleSeconds

{

get{return_seconds;}

set

{

_seconds=value;

OnPropertyChanged();

}

}

publicstringTimeTaken

{

get

{

returnstring.Format("{0:00}:{1:00}:{2:00}",

this.Hours,this.Minutes,this.Seconds);

}

}

3. Next,weneedtomodifytheDistTravelledViewModelclassconstructortodeclareandinitializeourlocationServicevariable;thiswillincludeourIWalkLocationServiceconstructor,whichisretrievedfromtheXamarin.FormsDependencyServiceclass.WethenproceedtocalltheMyLocationmethodonourEventHandler,whichisdefinedwithin

theIWalkLocationServiceinterface;thiswillreturnthegeographicallocationcoordinates,definedbytheirLatitudeandLongitudevalues.

4. LocatetheDistTravelledViewModelclassconstructorandenterthefollowinghighlightedcode:

publicDistTravelledViewModel(IWalkNavServicenavService):

base(navService)

{

this.Hours=0;

this.Minutes=0;

this.Seconds=0;

this.Travelled=100;

locationService=DependencyService.Get

<IWalkLocationService>();

locationService.MyLocation+=(objectsender,

IWalkCoordinatese)=>

{

//DetermineDistanceTravelled

if(_walkEntry!=null)

{

vardistance=locationService.GetDistanceTravelled(

_walkEntry.Latitude,_walkEntry.Longitude);

this.Travelled=distance;

}

};

locationService.GetMyLocation();

}

5. Then,wecreatetheInitmethodwithinourDistTravelledViewModel,whichwillbeusedtoinitializetheDistanceTravelledwhenitiscalled.WeneedtospecifyandusetheTask.Factory.StartNewmethodtogivetheViewModelenoughtimetodisplaythepageonscreen,priortoinitializingtheContentPagecontentsandusingthepassed-inwalkDetailsforourmodel:

publicoverrideasyncTaskInit(WalkEntrieswalkDetails)

{

awaitTask.Factory.StartNew(()=>

{

WalkEntry=walkDetails;

});

}

6. Next,weneedtocreatetheBackToMainPagecommandpropertythatwillbeusedtobindtotheEndThisTrailbutton,whichwillrunanactionuponbeingpressed.Thisactionwillexecuteaclassinstancemethodtodeterminewhetherthecommandcanbeexecuted.

7. Ifthecommandcanbeexecuted,acallwillbemadetotheBackToMainPagemethodontheNavServicenavigationserviceclasstotaketheuserbacktotheTrackMyWalksmainpage;thisisdonebyremovingallexistingViewModelswithintheNavigationStack,exceptthefirstpage:

Command_mainPage;

publicCommandBackToMainPage

{

get

{

return_mainPage??(_mainPage=newCommand(

async()=>await

NavService.BackToMainPage()));

}

}

}

}

Inthissection,welookedatthestepsinvolvedinmodifyingourDistanceTravelledViewModelsothatitcantakeadvantageofourWalkLocationService.WethendeclaredalocationServicevariablethatwillbeusedtoprovideareferencetoourIWalkLocationServiceandprovideourclasswithareferencetotheEventHandler,whichiscontainedwithinourIWalkLocationServiceinterfaceclass;thiswillcontainourlocationcoordinateinformationwheneverthelocationchanges.

WealsomodifiedourDistTravelledViewModelclassconstructortoinitializeourlocationServicevariabletopointtotheIWalkLocationServiceconstructorthatisretrievedfromtheXamarin.FormsDependencyServiceclass;thisneedstobedonepriortocallingtheMyLocationmethodonourEventHandler,sothatitcanreturnthegeographicallocationcoordinates,definedbytheirLatitudeandLongitudevalues.

UpdatingtheSplashPagetoregisterourViewModelsInthissection,weneedtoupdateourSplashPagetoregisterourViewModelsforourAndroidplatform;thiswillinvolvecreatinganewinstanceofthenavigationservice,andregisteringtheapplicationContentPageandViewModelmappings:

1. EnsurethattheSplashPage.csfileisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesections:

//

//SplashPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingSystem.Threading.Tasks;

usingTrackMyWalks.Services;

usingTrackMyWalks.ViewModels;

usingXamarin.Forms;

namespaceTrackMyWalks

{

publicclassSplashPage:ContentPage

{

publicSplashPage()

{

AbsoluteLayoutsplashLayout=newAbsoluteLayout

{

HeightRequest=600

};

varimage=newImage()

{

Source=ImageSource.FromFile("icon.png"),

Aspect=Aspect.AspectFill,

};

AbsoluteLayout.SetLayoutFlags(image,

AbsoluteLayoutFlags.All);

AbsoluteLayout.SetLayoutBounds(image,

newRectangle(0f,0f,1f,1f));

splashLayout.Children.Add(image);

Content=newStackLayout()

{

Children={splashLayout}

};

}

2. Next,locatetheOnAppearingmethodandenterthefollowinghighlightedcodesections:

protectedoverrideasyncvoidOnAppearing()

{

base.OnAppearing();

//Delayforafewsecondsonthesplashscreen

awaitTask.Delay(3000);

//InstantiateaNavigationPagewiththe

//MainPage

varnavPage=newNavigationPage(newWalksPage()

{

Title="TrackMyWalks-Android"

});

navPage.BarBackgroundColor=Color.FromHex("#4C5678");

navPage.BarTextColor=Color.White;

//DeclareourDependencyServiceInterface

varnavService=DependencyService.Get<IWalkNavService>()

asWalkNavService;

navService.navigation=navPage.Navigation;

//RegisterourViewModelMappingsbetween

//ourViewModelsandViews(Pages).

navService.

RegisterViewMapping(typeof(WalksPageViewModel),

typeof(WalksPage));

navService.RegisterViewMapping(

typeof(WalkEntryViewModel),

typeof(WalkEntryPage));

navService.RegisterViewMapping(

typeof(WalksTrailViewModel),

typeof(WalkTrailPage));

navService.RegisterViewMapping(

typeof(DistTravelledViewModel)

,

typeof(DistanceTravelledPage));

//SettheMainPagetobeourWalksNavigationPage

Application.Current.MainPage=navPage;

}

}

}

Intheprecedingcodesnippet,webeginbycustomizingourNavigationBar,bysettingtheBackgroundandTextColorattributes,andthendeclaringavariablenavServicethatpointsto

aninstanceofournavigationserviceasdefinedbyourassemblyattributeforourXamarin.FormsDependencyService,whichisdeclaredinourWalkNavServiceclass.

Inournextstep,wesetthenavService.navigationpropertytopointtoaninstanceoftheNavigationPageclassthatourwalksPage.navigationpropertycurrentlypointsto,andthiswillbeusedasthemainrootpage.

Finally,wecalltheRegisterViewMappinginstancemethodforeachofourViewModelsandspecifytheassociatedContentPageforeach.

UpdatingtheMainActivityclasstouseXamarin.Forms.MapsInthissection,weneedtoupdateourMainActivityClasstointegratewiththeXamarin.Forms.MapspackageforourAndroidplatform,sothatourViewModelscanusethistodisplaymappingcapabilities:

1. OpentheMainActivity.csfileandensurethatitisdisplayedwithinthecodeeditor.2. Next,locatetheOnCreatemethodandenterthefollowinghighlightedcodesections:

//

//MainActivity.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingAndroid.App;

usingAndroid.Content.PM;

usingAndroid.OS;

namespaceTrackMyWalks.Droid

{

[Activity(Label="TrackMyWalks.Droid",

Icon="@drawable/icon",Theme="@style/MyTheme",

MainLauncher=true,ConfigurationChanges=

ConfigChanges.ScreenSize|

ConfigChanges.Orientation)]

publicclassMainActivity:

global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity

{

protectedoverridevoidOnCreate(Bundle

savedInstanceState)

{

TabLayoutResource=Resource.Layout.Tabbar;

ToolbarResource=Resource.Layout.Toolbar;

base.OnCreate(savedInstanceState);

global::Xamarin.Forms.Forms.Init(this,

savedInstanceState);

//IntegrateXamarinFormsMaps

Xamarin.FormsMaps.Init(this,savedInstanceState);

LoadApplication(newApp());

}

}

}

Intheprecedingcodesnippet,webeginbyinitializingourMainActivityclasstousetheXamarin.Forms.Mapslibrary,sothatourTrackMyWalkssolutioncanusethemaps.Ifthisisomittedfromtheclass,theDistanceTravelledPagecontentpagewillnotdisplaythemap,and

thereforewillnotworkasexpected.

UpdatingtheXamarin.FormsAppclasstouseplatformspecificsInthissection,weneedtoupdateourXamarin.Forms.AppclassbymodifyingtheconstructorinthemainAppclasstosettheMainPageinstance,dependingontheTargetPlatformthatourdeviceisrunning.ThisisextremelyeasywhenusingXamarin.Forms.

Let'stakealookathowwecanachievethisbyfollowingthesesteps:

1. OpentheTrackMyWalks.csfileandensurethatitisdisplayedwithinthecodeeditor.2. Next,locatetheAppmethodandenterthefollowinghighlightedcodesections:

//

//TrackMyWalks.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingTrackMyWalks.Services;

usingTrackMyWalks.ViewModels;

usingXamarin.Forms;

namespaceTrackMyWalks

{

publicclassApp:Application

{

publicApp()

{

//ChecktheDeviceTargetOSPlatform

if(Device.OS==TargetPlatform.Android)

{

//Therootpageofyourapplication

MainPage=newSplashPage();

}

elseif(Device.OS==TargetPlatform.iOS)

{

//Therootpageofyourapplication

varwalksPage=newNavigationPage(new

WalksPage()

{

Title="TrackMyWalks-iOS"

});

//SettheNavigationBarTextColorand

//BackgroundColor

walksPage.BarBackgroundColor=

Color.FromHex("#440099");

walksPage.BarTextColor=Color.White;

//DeclareourDependencyServiceInterface

varnavService=DependencyService.

Get<IWalkNavService>()asWalkNavService;

navService.navigation=walksPage.Navigation;

//RegisterourViewModelMappings

//betweenourViewModelsandViews(Pages)

navService.RegisterViewMapping(

typeof(WalksPageViewModel),typeof(WalksPage));

navService.RegisterViewMapping(

typeof(WalkEntryViewModel),

typeof(WalkEntryPage));

navService.RegisterViewMapping(

typeof(WalksTrailViewModel),

typeof(WalkTrailPage));

navService.RegisterViewMapping(typeof(

DistTravelledViewModel),

typeof(DistanceTravelledPage));

//SettheMainPagetobeour

//WalksNavigationPage

MainPage=walksPage;

}

}

protectedoverridevoidOnStart()

{

//Handlewhenyourappstarts

}

protectedoverridevoidOnSleep()

{

//Handlewhenyourappsleeps

}

protectedoverridevoidOnResume()

{

//Handlewhenyourappresumes

}

}

}

Intheprecedingcodesnippet,weusetheTargetPlatformclassthatcomesaspartoftheXamarin.Forms.Corelibrary,andwecheckthisagainsttheDevice.OSclassandhandleitaccordingly.

TheTargetPlatformmethodcontainsanumberplatformcodes,whichareexplainedalongwiththeirdescriptionsinthefollowingtable:

Platformname Description

Android ThisindicatesthattheXamarin.FormsplatformisrunningonadevicethatisrunningtheAndroidoperatingsystem.

iOS ThisindicatesthattheXamarin.FormsplatformisrunningonadevicethatisrunningtheAppleiOSoperatingsystem.

Windows ThisindicatesthattheXamarin.FormsplatformisrunningonadevicethatisrunningtheWindowsplatform.

WinPhone ThisindicatesthattheXamarin.FormsplatformisrunningonadevicethatisrunningtheMicrosoftWinPhoneOS.

Note

FormoreinformationontheDeviceclass,refertotheXamarindocumentationathttps://developer.xamarin.com/guides/xamarin-forms/platform-features/device/.

NowthatwehaveupdatedthenecessaryMVVMViewModelstotakeadvantageofourWalkLocationService,ournextstepistofinallybuildandruntheTrackMyWalksapplicationwithintheiOSsimulator.Whencompilationcompletes,theiOSsimulatorwillappearautomaticallyandtheTrackMyWalksapplicationwillbedisplayed,asshowninthefollowingscreenshot:

Asyoucanseefromtheprecedingscreenshot,thisdisplaysourcurrentlistofwalktrailentries,whicharedisplayedwithinourListView.WhentheuserclicksontheAddWalkbuttonlink,thiswilldisplaytheNewWalkEntrycontentpage,andwilldisplaythecurrentuser'sgeolocationcoordinatesfortheLatitudeandLongitudeEntryCellpropertiescontainedwithinourWalkEntryViewModel.Theprecedingscreenshot,thisshowsthedistancetravelledpagealongwiththeplaceholderpinmarkershowingthetraillocationwithinthemapView.YouwillnoticethattheDistanceTravelledsectionhasbeenupdatedandshowsthedistancetravelledbytheuserthatiscalculatedbytheGetDistanceTravelledmethodcontainedwithinourIWalkLocationServiceinterface.

SummaryInthischapter,weupdatedourTrackMyWalksapplication,andcreatedaLocationServiceclassthatextendedthedefaultnativecoreLocationServicesclassesforiOSandAndroid,whichprovidesuswithabettermethodofcapturinggeolocationcoordinateswithintheViewModel.

Inthenextchapter,you'lllearnaboutcustomrenderersandhowyoucanusethemtochangetheappearanceofthecontrolelementswithintheuserinterfacethattargetaspecificplatform.

YouwilllearnhowtoworkwithDataTemplatesbycreatingaC#classtolayoutyourviewsbeautifullythroughoutyourapplication,andworkwiththeplatform-specificAPIstoextendthedefaultbehaviorofXamarin.Formscontrolsthroughtheuseofcustomrenderers,bycreatingacustompickercontrolforiOS.

WewillalsobecoveringhowyoucanusetheXamarin.FormsEffectsAPItocustomizetheappearanceandstylingofnativecontrolelementsforeachplatform,byimplementingacustomrendererclass,andmanipulatethevisualappearanceofdatathatisbound,throughtheuseofValueandImageConverters.

Chapter5.CustomizingtheUserInterfaceInourpreviouschapter,welookedathowwecanincorporateplatform-specificfeatureswithintheTrackMyWalksapp,whichisdependentonthemobileplatform.YoulearnedhowtocreateaC#class,whichactedasalocationservicethatincludedanumberofclassmethodsforbothiOSandAndroidplatforms.

Wealsocoveredhowtoproperlyperformlocationupdateswhethertheapplication'sstateisintheforegroundorbackgroundbyregisteringtheappasabackground-necessaryapplication.

Inthischapter,you'lllearnhowtoworkwiththeDataTemplateCustomRendererbycreatingaC#classtolayoutyourviewsbeautifullywithinyourapplications,andyouwillalsogetaccustomedtoworkingwithplatform-specificAPIstoextendthedefaultbehaviorofXamarin.Forms'controlsthroughtheuseofcustomrenderers,bycreatingacustompicker.

WewillalsobecoveringhowtousetheXamarin.FormsEffectsAPItocustomizetheappearanceandstylingofnativecontrolelementsforeachplatform,byimplementingaCustomRendererclass.We'lllookathowtomanipulatethevisualappearanceofdatathatisbound,throughtheuseofvalueandimageconverters.

Thischapterwillcoverthefollowingpoints:

CreatingacustomDataTemplateclasswhichutilizesnativeplatformcapabilities,thatcomeaspartoftheiOSandAndroidplatformsWorkingwithcustomrendererstochangetheappearanceofcontrolelementsUsingtheplatformEffectsAPItochangetheappearanceofcontrolelementsWorkingwithBooleanandstringtoimagevalueconvertersUpdatingthewalkscontentpageapplicationtousethedatatemplateUpdatingtheWalkEntrycontentpagetousetheCustomRendererUpdatingtheDistanceTravelledcontentpagetousetheEffectsAPI

CreatingtheDataTemplateclassfortheTrackMyWalksappOneofthefeaturesoftheXamarin.Formstoolkitistheabilitytomanipulatetheuserinterfacebyleveragingthevariousplatform-specificAPIsthatareavailable,whetheritbemanipulatingtheappearanceofcontrolsandtheirelementsusingcustomrenderers,orchangingtheappearanceandstylingofnativecontrolelements.

Inthissection,wewillbeworkingwiththeXamarin.Formsdatatemplates,whichwillprovidetheabilitytodefinethepresentationofdata.Let'sbeginbycreatinganewfoldercalledDataTemplates,withinourTrackMyWalkssolution,whichwillbeusedtorepresentourDataTemplates,byfollowingthesesteps:

1. LaunchtheXamarinStudioapplication,andensurethattheTrackMyWalkssolutionisloadedwithintheXamarinStudioIDE.

2. Next,createanewfolder,withintheTrackMyWalksPortableClassLibraryproject,calledDataTemplatesasshowninthefollowingscreenshot:

3. Next,createanemptyclasswithintheDataTemplatesfolder.Ifyoucan'trememberhowtodothis,youcanrefertothesectionentitledCreatingtheNavigationServiceInterface

fortheTrackMyWalksapp,withinChapter3,NavigatingwithintheMVVMmodel-TheXamarin.FormsWay.

4. Then,choosetheEmptyClassoptionlocatedwithintheGeneralsection,andenterWalkCellDataTemplateasthenameofthenewclassfile,asshowninthefollowingscreenshot:

5. Next,clickontheNewbuttontoallowthewizardtocreatethenewemptyclassfile,asshownintheprecedingscreenshot.

6. OurnextstepistobegincreatingandimplementingthecodeforourWalkCellDataTemplateclass;performthefollowingsteps.

7. EnsurethattheWalkCellDataTemplate.csfileisdisplayedwithinthecodeeditor,andenterthefollowingcodesnippet:

//

//WalkCellDataTemplate.cs

//TrackMyWalksDataTemplateforCells

//

//CreatedbyStevenF.Danielon01/10/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingTrackMyWalks.Converters;

usingXamarin.Forms;

namespaceTrackMyWalks.Controls

{

publicclassWalkCellDataTemplate:ViewCell

{

publicWalkCellDataTemplate()

{

varwalkTrailImage=newImage

{

WidthRequest=140,

HeightRequest=140,

HorizontalOptions=LayoutOptions.FillAndExpand,

VerticalOptions=LayoutOptions.FillAndExpand,

Aspect=Aspect.Fill

};

walkTrailImage.SetBinding(Image.SourceProperty,

"ImageUrl");

varTrailNameLabel=newLabel()

{

FontAttributes=FontAttributes.Bold,

FontSize=16,

TextColor=Color.Black

};

TrailNameLabel.SetBinding(Label.TextProperty,

"Title");

vartotalKilometersLabel=newLabel()

{

FontAttributes=FontAttributes.Bold,

FontSize=12,

TextColor=Color.FromHex("#666")

};

totalKilometersLabel.SetBinding(Label.TextProperty,

"Kilometers",stringFormat:"Kilometers:{0}");

vartrailDifficultyLabel=newLabel()

{

FontAttributes=FontAttributes.Bold,

FontSize=12,

TextColor=Color.Black

};

trailDifficultyLabel.SetBinding(Label.TextProperty,

"Difficulty",stringFormat:"Difficulty:{0}");

vartrailDifficultyImage=newImage

{

HeightRequest=50,

WidthRequest=50,

Aspect=Aspect.AspectFill,

HorizontalOptions=LayoutOptions.Start

};

trailDifficultyImage.SetBinding(Image.SourceProperty,

"Difficulty",converter:new

TrailImageConverter());

varnotesLabel=newLabel()

{

FontSize=12,

TextColor=Color.Black

};

notesLabel.SetBinding(Label.TextProperty,"Notes");

varnotesStack=newStackLayout()

{

Spacing=3,

Orientation=StackOrientation.Vertical,

VerticalOptions=LayoutOptions.FillAndExpand,

Children={notesLabel}

};

varstatusLayout=newStackLayout

{

Orientation=StackOrientation.Vertical,

Children={totalKilometersLabel,

trailDifficultyLabel,

trailDifficultyImage

}

};

varDetailsLayout=newStackLayout

{

Padding=newThickness(10,0,0,0),

Spacing=0,

HorizontalOptions=LayoutOptions.FillAndExpand,

Children={TrailNameLabel,statusLayout,

notesStack

}

};

varcellLayout=newStackLayout

{

Spacing=0,

Padding=newThickness(10,5,10,5),

Orientation=StackOrientation.Horizontal,

HorizontalOptions=LayoutOptions.FillAndExpand,

Children={walkTrailImage,DetailsLayout}

};

this.View=cellLayout;

}

}

}

Intheprecedingcodesnippet,webeganbyensuringthatourclassinheritsfromtheXamarin.FormsViewCellclassrenderer,andisessentiallyacellthatcanbeaddedtoanyListVieworTableViewcontrolthatcontainsadefinedview.WhenworkingwithXamarin.Forms,andtheViewCellclass,everycellhasanaccompanyingrendererthatisassociatedwitheachplatformthatcreatesaninstanceofanativecontrol.WheneveraViewCellclassisrenderedundertheiOSplatform,theViewCellRendererclasswillinstantiatethenativeUITableViewCellcontrol.Alternatively,undertheAndroidplatform,theViewCellRendererclassinstantiatesanativeViewcontrol.

Finally,ontheWindowsPhoneplatform,theViewCellRendererclassinstantiatesanativeDataTemplatecontrol.Next,wecreatethecelllayoutinformationusingtheStackLayoutcontrol,andthenusetheSetBindingpropertytocreateandbindeachofourmodelvaluestoaspecificproperty.Finally,wedefineacellLayoutvariablethatusestheStackLayoutcontrol,toaddeachofourchildelementsandthenassigntheresultingcellLayouttotheclassView.

Note

IfyouareinterestedinfindingoutmoreinformationaboutDataTemplates,pleaserefertotheXamarindeveloperdocumentationlocatedathttps://developer.xamarin.com/guides/xamarin-forms/templates/data-templates/.

NowthatwehavecreatedourWalkCellDataTemplate,thenextstepistomodifythewalksmainpagesothatitcanmakeuseofthisclass.

UpdatingthewalksmainpagetousethedatatemplateIntheprevioussection,wecreatedtheclassforourWalkCellDataTemplate,aswellasdefiningthelayoutinformationforeachofthecontrolelementsthatwewouldliketohavedisplayedwithinourView.

Inthissection,wewilltakealookathowtoimplementthenecessarycodechangessothattheWalksPageContentPagecantakeadvantageofourWalkCellDataTemplateclass.

Let'stakealookathowwecanachievethis,byfollowingthesteps:

1. EnsurethattheWalksPage.csfileisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesectionsasshowninthefollowingcodesnippet:

//

//WalksPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms;

usingTrackMyWalks.Models;

usingTrackMyWalks.ViewModels;

usingTrackMyWalks.Services;

usingTrackMyWalks.DataTemplates;

namespaceTrackMyWalks

{

publicclassWalksPage:ContentPage

{

WalksPageViewModel_viewModel

{

get{returnBindingContextasWalksPageViewModel;}

}

publicWalksPage()

{

varnewWalkItem=newToolbarItem

{

Text="AddWalk"

};

...

...

...

//DefineourDataTemplateClass

varwalksList=newListView

{

HasUnevenRows=true,

ItemTemplate=newDataTemplate(typeof(

WalkCellDataTemplate)),

SeparatorColor=(Device.OS

==TargetPlatform.iOS)?

Color.Default:Color.Black

};

...

...

...

}

}

Intheprecedingcodesnippet,webeganbyincludingareferencetoourDataTemplatesclass,viatheusingstatement.ThenwepassedintheWalkCellDataTemplatedatatemplatetotheDataTemplateclassobject,whichwillbeassignedtotheItemTemplatepropertyoftheListViewclass.Next,dependingontheoperatingsystemwearerunningon,we'llsettheseparatorcolorforourTableView.

Asyoucanseefromtheprecedingscreenshot,thiswillshowyoualistofourcurrentwalkstrailentries,whicharenicelyrenderedusingtheDataTemplate,anddisplayedwithintheListViewcontrol.

Inournextsection,youwillseehowwecangoaboutcreatingacustompickerforourWalkEntrycontentpage,sothatwecandisplayalistofdifficultychoicesfortheusertochoosefrom.

CreatingaTableViewEntryCellcustompickerfortheiOSplatformOurTrackMyWalksappusesaTableViewwithEntryCellstopresentaformtotheusertoaddnewwalkentrieswithintheWalkEntryPage.Currently,thedifficultyfieldwithintheformisusingtheregularEntryCellcontrol,whichpresentstheuserwithaneditabletextfieldusingthedefaultkeyboard.

Asyoucanimagine,thisisnottheidealuserexperiencethatweareafter,asthiscancauseissueswhenitcomestovalidatingtheinformationentered.Ourgoalistopresenttheuserwithastandard,customplatform-specificpickerthatcontainsanumberofchoicestheusercanchoosefrom.

Inthissection,wewillbecreatingacustomrendererthatwillextendtheEntryCellRenderertodisplayanEntryCellthatwillbehavemuchlikethestandardpickercontrol.Sincewedon'twantourpickertorenderalloftheEntryCellswithintheWalkEntryPage,wewillneedtocreateacustomEntryCellcontrolthatthecustomrendererwillassociatedwith.

Let'stakealookathowwecanachievethis,byfollowingthesteps:

1. CreateanewfolderwithintheTrackMyWalksPortableClassLibraryproject,calledControlsandthencreateanemptyclasswithintheControlsfolder.

2. Next,choosetheEmptyClassoptionlocatedwithintheGeneralsection,andenterDifficultyPickerEntryCellasthenameofthenewclassfiletocreate.

3. Next,onceyouhavecreatedtheDifficultyPickerEntryCellclassfile,ensurethattheDifficultyPickerEntryCell.csfileisdisplayedwithinthecodeeditor,andenterinthefollowingcodesnippet:

//

//DifficultyPickerEntryCell.cs

//TrackMyWalksCustomRendererforDifficultyEntryCells

//

//CreatedbyStevenF.Danielon01/10/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingXamarin.Forms;

namespaceTrackMyWalks.Controls

{

4. Then,weneedtomodifytheDifficultyPickerEntryCellclassconstructorsignature,sothatitinheritsfromtheXamarin.Forms.EntryEntryCellclass,sincetheWalkEntryPagecontainsanumberofEntryCellcontrols:

publicclassDifficultyPickerEntryCell:EntryCell{

5. Next,weneedtocreateastringBindablePropertysothatthecustomcontrolcanbedata-

boundjustlikeourothercontrols:

publicstaticreadonlyBindablePropertyDifficultyProperty=

BindableProperty.Create<DifficultyPickerEntryCell,

String>(p=>p.Difficulty,"Easy",propertyChanged:

newBindableProperty.BindingPropertyChangedDelegate<String>

(DifficultyPropertyChanged));

6. Then,wecreateaDifficultypropertysothat,whenthevalueschangeswithinthecustomcontrol,itcanreturnthevaluebacktoourEntryCell:

publicStringDifficulty

{

get{return(String)GetValue(DifficultyProperty);}

set{SetValue(DifficultyProperty,value);}

}

7. Next,wecreateaCompletedEventHandlerthatwillbeusedinrelationtotheDifficultyPropertyChangedevent,sowecanrespondtotheCompletedeventsonourDifficultyPickerEntryCell:

publicneweventEventHandlerCompleted;

staticvoidDifficultyPropertyChanged(BindableObjectbindable,

StringoldValue,StringnewValue)

{

var@this=(DifficultyPickerEntryCell)bindable;

if(@this.Completed!=null)

@this.Completed(bindable,newEventArgs());

}

}

}

NowthatwehavecreatedtheDifficultyPickerEntryCellclassfortheiOSportionofourTrackMyWalksapp,ournextstepistocreatethecustompickerrendererfortheiOSplatform,whichwewillbecoveringinthenextsection.

CreatingthecustompickerrendererclassfortheiOSplatformIntheprevioussection,wecreatedaclassfortheDifficultyPickerEntryCell,aswellasdefininganumberofdifferentdata-bindablepropertymethodsthatwillbeusedtohandletheuserchoosinganitemwithinourcustompicker.

Inthissection,wewillbuildthecustompickerrenderermodelthatwillbeusedbytheiOSportionoftheDifficultyPickerEntryCell.Let'stakealookathowwecanachievethis,byfollowingthesesteps:

1. CreateanewfolderwithintheTrackMyWalks.iOSproject,calledRenderers.2. Next,createanemptyclasswithintheRenderersfolderforourTrackMyWalks.iOS

project,andenterDifficultyPickerModelasthenameofthenewclassfiletocreate.3. Then,ensurethattheDifficultyPickerModel.csfileisdisplayedwithinthecodeeditor,

andenterinthefollowingcodesnippet:

//

//DifficultyPickerModel.cs

//TrackMyWalksLevelModelforUIPickerViewModel(iOS)

//

//CreatedbyStevenF.Danielon01/10/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingUIKit;

namespaceTrackMyWalks.iOS.Renderers

{

4. Next,weneedtomodifytheDifficultyPickerModelclassconstructorsignature,sothatitinheritsfromtheUIPickerViewModelinterfaceclass,aswellasdeclaringourdifficultyStringobjectwhichcontainsavalidlistofchoicesfortheusertochoosefrom:

//DeclareourDifficultyPickerModelClass

publicclassDifficultyPickerModel:UIPickerViewModel

{

//Defineourlistofdifficultylevels

staticpublicstring[]difficulty=newstring[]

{

"Easy",

"Moderate",

"Challenging",

"Difficult",

"VeryDifficult",

"Extreme"

};

5. Then,weneedtocreatetheGetComponentCountmethod,whichacceptsapickerView

objectthattellstheUIPickerViewhowmanycomponentsweareexpectingourcustompickertocontain:

publicoverridenintGetComponentCount(UIPickerViewpickerView)

{

return1;

}

6. Next,weneedtocreatetheGetRowsInComponentmethodthatacceptsapickerViewobjectandacomponentvalue.ThismethodworksouthowmanyrowstodisplaywithinourUIPickerViewcustomcontrol,whichisderivedfromthedifficultystringarray.Thecomponentparameterdetermineswhichsectiontodisplaythosevaluesin:

publicoverridenintGetRowsInComponent(UIPickerViewpickerView,

nintcomponent)

{

returndifficulty.Length;

}

7. Finally,weneedtocreatetheGetTitlemethod,whichacceptsapickerViewobject,arowparameter,andacomponentvalue.Thismethodisusedtodisplaythetitleinformationforeachrowcontainedwithinourdifficultyarray:

publicoverridestringGetTitle(UIPickerViewpickerView,

nintrow,nintcomponent)

{

returndifficulty[row];

}

}

}

Upuntilthispoint,allwehavedoneiscreateourmodelforourDifficultyPicker,whichactsasthebasemodelneededbyourDifficultyPickerCellRendererclass.OurnextstepistocreatetheDifficultyPickerCellRendererthatwillusethemodelfortheiOSplatformtodisplayacustomlistofentriesfortheusertochoosefrom.

1. CreateanemptyclasswithintheRenderersfolderfortheTrackMyWalks.iOSproject,andenterinDifficultyPickerCellRendererasthenameofthenewclassfiletocreate.

2. Next,ensurethattheDifficultyPickerCellRenderer.csfileisdisplayedwithinthecodeeditor,andenterthefollowingcodesnippet:

//

//DifficultyPickerCellRenderer.cs

//TrackMyWalksCustomRendererforUIPickerViewEntryCells

(iOS)

//

//CreatedbyStevenF.Danielon01/10/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms.Platform.iOS;

usingUIKit;

usingTrackMyWalks.Controls;

usingXamarin.Forms;

usingTrackMyWalks.iOS.Renderers;

3. Then,weneedtoinitializeourDifficultyCellRendererclasstobemarkedasanExportRendererbyincludingtheExportRendererassemblyattributeatthetopofourclassdefinition.ThisletsourclassknowthatitinheritsfromtheViewRendererclass:

[assembly:ExportRenderer(typeof(DifficultyPickerEntryCell),

typeof(DifficultyPickerCellRenderer))]

namespaceTrackMyWalks.iOS.Renderers

{

4. Next,weneedtomodifytheDifficultyCellRendererclassconstructorsignature,sothatitcaninheritfromtheEntryCellRendererclass:

publicclassDifficultyPickerCellRenderer:EntryCellRenderer

{

5. Then,weneedtooverridetheEntryCellRendererGetCellmethodsothatitcanoverridethedefaultbehavioroftheEntryCellforiOSbysettingtheInputViewoftheUITextFieldtoaUIPickerViewclassinstance:

publicoverrideUITableViewCellGetCell(Cellitem,

UITableViewCellreusableCell,UITableViewtv)

{

varcell=base.GetCell(item,reusableCell,tv);

varentryPickerCell=(EntryCell)item;

UITextFieldtextField=null;

if(cell!=null)

textField=(UITextField)cell.ContentView.Subviews[0];

6. Next,wecreateaninstancetoouriOSUIPickerViewnativecontrol,thatpointstotheDifficultyPickerModel;andthenwecreateatoolbarthatwillcontainaDonebuttonandwillprovideuswithamechanismtoupdatetheEntryCellUITextFieldwiththechosenvaluefromthedifficultyPickerobject.Thendismissthecustompickercontrol:

//CreateouriOSUIPickerViewNativeControl

vardifficultyPicker=newUIPickerView

{

AutoresizingMask=UIViewAutoresizing.FlexibleWidth,

ShowSelectionIndicator=true,

Model=newDifficultyPickerModel(),

BackgroundColor=UIColor.White,

};

//Createatoolbarwithadonebuttonthatwill

//settheselectedvaluewhenclosed.

vardone=newUIBarButtonItem("Done",

UIBarButtonItemStyle.Done,(s,e)=>

{

//UpdatethevalueoftheUITextFieldwithin

//theCell.

if(textField!=null)

{

textField.Text=DifficultyPickerModel.difficulty

[difficultyPicker.SelectedRowInComponent(0)];

textField.ResignFirstResponder();

}

});

vartoolbar=newUIToolbar

{

BarStyle=UIBarStyle.BlackTranslucent,

Translucent=true

};

toolbar.SizeToFit();

toolbar.SetItems(new[]{done},true);

7. Then,wesettheinputviewandtoolbarandaninitialdefaultvaluefortheEntryCell'sTextFieldifnothinghasbeenchosen.ThisisdonebysettingtheInputViewoftheUITextFieldtoaUIPickerViewclassinstance:

//Settheinputview,toolbarandinitialvalue

//fortheCell'sUITextField.

if(textField!=null)

{

textField.InputView=difficultyPicker;

textField.InputAccessoryView=toolbar;

textField.Font=UIFont.FromName("Courier",16);

textField.BorderStyle=UITextBorderStyle.Bezel;

textField.TextColor=UIColor.Red;

8. Finally,ifwehaveselectedadifficultyvaluefromourUIPickerViewcontrol,wefirstneedtoensurethatwehavechosenavalue,andthenassignthisvaluetotheDifficultyEntryCellfieldwithintheentryform:

if(entryPickerCell!=null)

{

textField.Text=DifficultyPickerModel.difficulty

[difficultyPicker.SelectedRowInComponent(0)];

}

}

returncell;

}

}

}

Note

IfyouareinterestedinfindingoutmoreinformationabouttheUIPickerViewclass,pleaserefertotheXamarindeveloperdocumentationathttps://developer.xamarin.com/api/type/MonoTouch.UIKit.UIPickerView/.

NowthatwehavecreatedtheDifficultyPickerCellRendererclassfortheiOSportionofourTrackMyWalksapp,ournextstepistoimplementthiswithintheWalkEntrycontentpage.

UpdatingtheWalksEntryPagetousethecustompickerrendererIntheprevioussection,wecreatedtheclassforourDifficultyPickerCellRenderer,aswellasdefiningthevariousmethodsthatwillhandlethedisplayoftheUIPickerViewcontrolwhenanEntryCellwithintheViewModelhasbeentapped.

Inthissection,wewilltakealookathowtoimplementthecodechangesrequiredsothattheWalkEntryPagecontentpagecantakeadvantageoftheDifficultyPickerCellRendererclass.

Let'stakealookathowwecanachievethis,byfollowingthesesteps:

EnsurethattheWalkEntryPage.csfileisdisplayedwithinthecodeeditor,andenterthefollowinghighlightedcodesectionsasshowninthefollowingcodesnippet:

//

//WalkEntryPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms;

usingTrackMyWalks.Services;

usingTrackMyWalks.Controls;

namespaceTrackMyWalks

{

publicclassWalkEntryPage:ContentPage

{

WalkEntryViewModel_viewModel

{

get{returnBindingContextasWalkEntryViewModel;}

}

publicWalkEntryPage()

{

//SettheContentPageTitle

Title="NewWalkEntry";

//DeclareandinitializeourModelBindingContext

BindingContext=newWalkEntryViewModel

(DependencyService

.Get<IWalkNavService>());

...

...

...

varwalkDifficulty=newDifficultyPickerEntryCell

{

Label="DifficultyLevel:",

Placeholder="WalkDifficulty"

};

walkDifficulty.SetBinding(

DifficultyPickerEntryCell.DifficultyProperty,

"Difficulty",BindingMode.TwoWay);

...

...

...

}

}

}

Inthissection,welookedatthestepsinvolvedinmodifyingourWalkEntryPagetotakeadvantageoftheDifficultyPickerEntryCellclasscustomrenderer.WelookedatupdatingthewalkDifficultyobjectvariable,toreferencetheDifficultyPickerEntryCellclass,andupdatedthesetBindingtoreturnthevaluefromtheDifficultyPropertythatisimplementedwithintheDifficultyPickerEntryCellclass.

Asyoucanseefromtheprecedingscreenshot,thisshowsourcustomUIPickerViewcontrol,populatedwiththeentriesfromtheDifficultyPickerModel,aswellastheDonebuttondisplayedastheheader.ScrollingthroughthelistofchoicesoperatesinthesamewayasyouwouldexpectunderiOS;clickingontheDonebuttonwillpopulatetheDifficultyLevelUITextFieldwiththehighlightedchoicewithintheUIPickerViewcontrol.

Inournextsection,wewillfocusonhowwecanusetheXamarin.FormsEffectsAPItocustomizetheappearanceandstylingofnativecontrolelementsforboththeiOSandAndroidplatformsbyimplementingacustomrendererclass.Youwillnoticethattheimplementationsforbothoftheseclassesarequitesimilar.However,theseimplementdifferentmethods,asyouwillseeoncewestartimplementingthem.

CreatingPlatformEffectsusingtheEffectsAPIfortheiOSplatformInthissection,wewillbuildtheiOSportionofourPlatformEffects,whichwillallowustocustomizetheappearanceoftheXamarin.Formscontrolelements.Wewillbecreatingtwocompletelydifferentplatformeffects-LabelShadowandButtonShadow,forboththeiOSandAndroidplatforms.

Let'stakealookathowwecanachievethis,byfollowingthesesteps:

1. CreateanewfolderwithintheTrackMyWalks.iOSproject,calledPlatformEffects.2. Next,createanemptyclasswithinthePlatformEffectsfolderforourTrackMyWalks.iOS

project.3. Then,enterButtonShadowEffectasthenameofthenewclassfiletocreate,ensurethatthe

ButtonShadowEffect.csfileisdisplayedwithinthecodeeditor,andenterthefollowingcodesnippet:

//

//ButtonShadowEffect.cs

//TrackMyWalksButtonShadowEffect(iOS)

//

//CreatedbyStevenF.Danielon02/10/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingTrackMyWalks.iOS.PlatformEffects;

usingUIKit;

usingXamarin.Forms;

usingXamarin.Forms.Platform.iOS;

4. Next,weinitializetheButtonShadowEffectclassassemblytobemarkedwithtwoimportantattributesforourclasssothatitcanbeusedasaneffectwithintheTrackMyWalksapplication:

[assembly:ResolutionGroupName("com.geniesoftstudios")]

[assembly:ExportEffect(typeof(ButtonShadowEffect),

"ButtonShadowEffect")]

namespaceTrackMyWalks.iOS.PlatformEffects

{

5. Then,weneedtomodifyourButtonShadowEffectclassconstructorsothatitcaninheritfromthePlatformEffectclassandaccesseachoftheplatform-specificimplementationsofthePlatformEffectclass:

publicclassButtonShadowEffect:PlatformEffect

{

6. Next,wecreatetheOnAttachedmethodthatwillbecalledwheneveranaffectisattachedtoaXamarin.FormscontrolandthenusetheContainerpropertytoreferencetheplatform-specificcontrolthatisusedtoimplementthelayout:

protectedoverridevoidOnAttached()

{

Container.Layer.ShadowOpacity=0.5f;

Container.Layer.ShadowColor=UIColor.Black.CGColor;

Container.Layer.ShadowRadius=2;

}

7. Then,wecreatetheOnDetachedmethod,whichwillbecalledwheneveraneffectisdetachedfromaXamarin.Formscontroltoperformanyeffectclean-up.Here,inthismethod,wesettheShadowOpacityoftheContainerpropertytozero:

protectedoverridevoidOnDetached()

{

Container.Layer.ShadowOpacity=0;

}

}

}

Eachplatform-specificPlatformEffectclassexposesanumberofpropertiesandtheseareexplainedinthefollowingtable:

Platformeffect Description

ContainerThisparticulartypereferencestheplatform-specificcontrolthatisbeingusedtoimplementthelayout.

ControlThistypereferencestheplatform-specificcontrolthatisbeingusedtoimplementtheXamarin.Formscontrol.

Element ThistypereferencestheXamarin.Formscontrolthatiscurrentlybeingrendered.

WheneveryoucreateyourownPlatformEffects,theseinheritfromthePlatformEffectclass,whichisdependentontheplatformthatisbeingrun.However,theAPIforaneffectisprettymuchidenticalacrosseachoftheplatformsastheyderivefromthePlatformEffect<T,T>andcontaindifferentgenericparameters.TherearealsotwoveryimportantattributesthatyouneedtosetforeachclassthatsubclassesfromthePlatformEffectclass,andtheseareexplainedinthefollowingtable:

Attributetype Description

ResolutionGroupName

Thisattributesetsacompany-widenamespaceforeffects,preventingcollisionswithothereffectswiththesamename.Itisworthmentioning

that,ifyoucreatemultiplePlatformEffects,youcanonlyapplythisattributeonceperproject.

ExportEffect

ThisattributeregisterstheeffectusingauniqueIDandisusedbytheXamarin.Formsplatformalongwiththegroupname.Theattributetakestwoparameters:thetypenameoftheeffect,andauniquestringthatwillbeusedtolocatetheeffectpriortoapplyingittoacontrol.

Note

IfyouareinterestedinfindingoutmoreinformationaboutthePlatFormEffectclass,pleaserefertotheXamarindeveloperdocumentationathttps://developer.xamarin.com/guides/xamarin-forms/effects/.

NowthatwehavecreatedtheButtonShadowEffectclass,ournextstepistocreatetheLabelShadowEffectfortheiOSportionofourTrackMyWalksapp:

1. Next,createanemptyclasswithinthePlatformEffectsfolderfortheTrackMyWalks.iOSprojectandenterLabelShadowEffectasthenameofthenewclassfiletocreate.

2. Then,ensurethattheLabelShadowEffect.csfileisdisplayedwithinthecodeeditor,andenterthefollowingcodesnippet:

//

//LabelShadowEffect.cs

//TrackMyWalksLabelShadowEffect(iOS)

//

//CreatedbyStevenF.Danielon02/10/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingCoreGraphics;

usingTrackMyWalks.iOS.PlatformEffects;

usingXamarin.Forms;

usingXamarin.Forms.Platform.iOS;

3. Next,weinitializetheLabelShadowEffectclassassemblytobemarkedwiththeExportEffectattributesothatitcanbeusedasaneffectwithintheTrackMyWalksapplication:

[assembly:ExportEffect(typeof(LabelShadowEffect),

"LabelShadowEffect")]

namespaceTrackMyWalks.iOS.PlatformEffects

{

4. Then,weneedtomodifytheLabelShadowEffectclassconstructorsothatitcaninheritfromthePlatformEffectclass,andaccesseachoftheplatform-specificimplementationsofthePlatformEffectclass:

publicclassLabelShadowEffect:PlatformEffect{

5. Next,wecreatetheOnAttachedmethodthatwillbecalledwheneveranaffectisattachedtoaXamarin.FormscontrolandthenusetheControlpropertytoreferencetheplatform-specificcontrolthatwillbeusedtochangetheappearanceofthecontrol.

protectedoverridevoidOnAttached()

{

try

{

Control.Layer.CornerRadius=5;

Control.Layer.ShadowColor=Device.OnPlatform(

Color.Black,Color.White,Color.Black).ToCGColor();

Control.Layer.ShadowOffset=newCGSize(4,4);

Control.Layer.ShadowOpacity=0.5f;

}

catch(Exceptionex)

{

Console.WriteLine("Cannotsetpropertyonattached

control.Error:",ex.Message);

}

}

6. Then,wecreatetheOnDetachedmethod,whichwillbecalledwheneveraneffectisdetachedfromaXamarin.Formscontroltoperformanyeffectcleanup.Here,inthismethod,wedon'tneedtodoanything,butwestillneedtoimplementthistoconformwiththePlatformEffectclassprotocolimplementations:

protectedoverridevoidOnDetached()

{

}

}

}

NowthatwehavecreatedthePlatformEffectsfortheiOSplatform,weneedtoimplementthesamePlatformEffectsfortheAndroidplatform,whichwewillbecoveringinthenextsection.

CreatingPlatformEffectsusingtheEffectsAPIfortheAndroidplatformInthissection,wewillbuildtheAndroidportionofourPlatformEffectsthatwillallowustocustomizetheappearanceofXamarin.FormscontrolelementsjustlikewedidfortheiOSportion,andwewillbeimplementingthesamePlatformEffects--LabelShadowandButtonShadowtoshowyouhowtheseimplementationsdifferoneachplatform,eventhoughtheresultingrenderingisthesame.

Let'stakealookathowwecanachievethis,byfollowingthesteps:

1. CreateanewfolderwithintheTrackMyWalks.Droidproject,calledPlatformEffects.2. Next,createanemptyclasswithinthePlatformEffectsfolderforour

TrackMyWalks.Droidproject.3. Then,enterButtonShadowEffectasthenameofthenewclassfiletocreate,ensurethatthe

ButtonShadowEffect.csfileisdisplayedwithinthecodeeditor,andenterthefollowingcodesnippet:

//

//ButtonShadowEffect.cs

//TrackMyWalksButtonShadowEffect(Droid)

//

//CreatedbyStevenF.Danielon02/10/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingTrackMyWalks.Droid.PlatformEffects;

usingXamarin.Forms;

usingXamarin.Forms.Platform.Android;

usingSystem;

4. Next,weinitializetheButtonShadowEffectclassassemblytobemarkedwiththesametwoattributesforourclass,justlikewedidfortheiOSportion,sothatitcanbeusedasaneffectwithintheTrackMyWalksapplication:

[assembly:ResolutionGroupName("com.geniesoftstudios")]

[assembly:ExportEffect(typeof(ButtonShadowEffect),

"ButtonShadowEffect")]

namespaceTrackMyWalks.Droid.PlatformEffects

{

5. Then,weneedtomodifytheButtonShadowEffectclassconstructorsothatitcaninheritfromthePlatformEffectclass,andaccesseachoftheplatform-specificimplementationsofthePlatformEffectclass:

publicclassButtonShadowEffect:PlatformEffect

{

6. Next,wecreatetheOnAttachedmethodthatwillbecalledwheneveranaffectisattachedtoaXamarin.FormscontrolandthenusetheControlpropertytoreferencetheplatform-specificcontrolthatwillbeusedtochangetheappearanceofthecontrol.UnderAndroidwe

needtocreateacontrolobjectandconverttheButtonintoaControlobject,andthenapplythecustomizationstotheControl.Wewrapthiswithinatry...catch()block,tocatchanyerrorsthatmayoccurif,forsomereason,wecan'tapplythecolororshadowLayerforourcontrol:

protectedoverridevoidOnAttached()

{

try

{

varcontrol=ControlasAndroid.Widget.Button;

Android.Graphics.Colorcolor=

Android.Graphics.Color.Red;

control.SetShadowLayer(12,4,4,color);

}

catch(Exceptionex)

{

Console.WriteLine("Cannotsetpropertyonattached

control.Error:",ex.Message);

}

}

7. Then,wecreatetheOnDetachedmethod,whichwillbecalledwheneveraneffectisdetachedfromaXamarin.Formscontroltoperformanyeffectcleanup.Hereinthismethod,wedon'tneedtodoanything,butwestillneedtoimplementthistoconformwiththePlatformEffectclassprotocolimplementations:

protectedoverridevoidOnDetached()

{

thrownewNotImplementedException();

}

}

}

NowthatwehavecreatedtheButtonShadowEffectclass,ournextstepistocreatetheLabelShadowEffectfortheAndroidportionofourTrackMyWalksapp:

1. Next,createanemptyclasswithinthePlatformEffectsfolderfortheTrackMyWalks.DroidprojectandenterLabelShadowEffectasthenameofthenewclassfiletocreate.

2. Then,ensurethattheLabelShadowEffect.csfileisdisplayedwithinthecodeeditor,andenterthefollowingcodesnippet:

//

//LabelShadowEffect.cs

//TrackMyWalksLabelShadowEffect(Droid)

//

//CreatedbyStevenF.Danielon02/10/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingTrackMyWalks.Droid.PlatformEffects;

usingXamarin.Forms;

usingXamarin.Forms.Platform.Android;

3. Next,weinitializeourLabelShadowEffectclassassemblytobemarkedwiththeExportEffectattributesothatitcanbeusedasaneffectwithintheTrackMyWalksapplication:

[assembly:ExportEffect(typeof(LabelShadowEffect),

"LabelShadowEffect")]

namespaceTrackMyWalks.Droid.PlatformEffects

{

4. Then,weneedtomodifytheLabelShadowEffectclassconstructorsothatitcaninheritfromthePlatformEffectclass,andaccesseachoftheplatform-specificimplementationsofthePlatformEffectclass:

publicclassLabelShadowEffect:PlatformEffect

{

5. Next,wecreatetheOnAttachedmethodthatwillbecalledwheneveranaffectisattachedtoaXamarin.FormscontrolandthenusetheControlpropertytoreferencetheplatform-specificcontrolthatwillbeusedtochangetheappearanceofthecontrol.InAndroidweneedtocreateacontrolobject,converttheTextViewintoaControlobject,andthenapplythecustomizationstotheControl.Wewrapthiswithinatry...catch()block,tocatchanyerrorsthatmayoccurif,forsomereason,wecan'tapplythecolororshadowLayerforourcontrol:

protectedoverridevoidOnAttached()

{

try

{

varcontrol=ControlasAndroid.Widget.TextView;

floatradius=5;

floatdistanceX=4;

floatdistanceY=4;

Android.Graphics.Colorcolor=Device.OnPlatform(

Color.Black,Color.White,Color.Black).ToAndroid();

control.SetShadowLayer(

radius,distanceX,distanceY,color);

}

catch(Exceptionex)

{

Console.WriteLine("Cannotsetpropertyonattached

control.

Error:",ex.Message);

}

}

6. Then,wecreatetheOnDetachedmethodthatwillbecalledwheneveraneffectisdetachedfromaXamarin.Formscontroltoperformanyeffectcleanupinthismethod.AswhatwedidintheiOSimplementation,wedon'tneedtodoanything,butwestillneedtoimplementthistoconformwiththePlatformEffectclassprotocolimplementations:

protectedoverridevoidOnDetached()

{

}

}

}

NowthatwehavecreatedPlatformEffectsforboththeiOSandAndroidimplementations,ournextstepistobegincreatingtwovalueconvertersthatwillbeusedbyourapplication,beforewecanstartmodifyingthecontentpages,andthiswillbecoveredinthenextsection.

ImplementingvalueconverterswithintheTrackMyWalksappAsmentionedintheprevioussection,valueconvertersformanimportantconceptindatabindingastheyallowyoutocustomizetheappearanceofadatapropertyatthetimeitisbound.ThisprocessisquitesimilartoWPF(WindowsPresentationFoundation)ontheWindowsapplicationdevelopmentplatform.Xamarin.FormsprovidesyouwithanumberofvalueconverterinterfacesaspartofitsAPI.

ValueconvertersareextremelyhelpfulwhenworkingwiththeXamarin.Formsplatform,astheyallowyoutotogglethevisibilityofelements,basedonaBooleanproperty.

Inthissection,wewillcreateaBooleanConverterthatwewillusetohidecontrolsuntiltheViewModelhascompletelyfinishedloading.WewillalsocreateaconverterthatconvertsastringvalueintoaURLpropertythatwillbeusedtodisplayanimageforourdifficultyrating.

Let'stakealookathowwecanachievethis,byfollowingthesteps:

1. Createanewfolder,withintheTrackMyWalksPortableClassLibraryproject,calledValueConvertersandthencreateanemptyclasswithintheValueConvertersfolder.

2. Next,choosetheEmptyClassoptionlocatedwithintheGeneralsection,andenterBooleanConverterasthenameofthenewclassfiletocreate.

3. Next,ensurethattheBooleanConverter.csfileisdisplayedwithinthecodeeditor,andenterthefollowingcodesnippet:

//

//BooleanConverter.cs

//TrackMyWalksValueConverterforconvertingBooleanvalues

//

//CreatedbyStevenF.Danielon02/10/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingXamarin.Forms;

namespaceTrackMyWalks.ValueConverters

{

4. Then,weneedtomodifytheBooleanConverterclassconstructorsothatitcaninheritfromtheIValueConverterclass:

publicclassBooleanConverter:IValueConverter

{

5. Next,createtheConvertandConvertBackmethodsoftheIValueConverterclass,sothattheconverterwillreturntheoppositeofagivenBooleanvalue:

publicobjectConvert(objectvalue,TypetargetType,

objectparameter,System.Globalization.CultureInfoculture)

{

if(!(valueisBoolean))

returnvalue;

return!((Boolean)value);

}

publicobjectConvertBack(objectvalue,TypetargetType,

objectparameter,System.Globalization.CultureInfoculture)

{

if(!(valueisBoolean))

returnvalue;

return!((Boolean)value);

}

}

}

Intheprecedingcodesnippet,webeganbymodifyingtheBooleanConverterclassconstructorsothatitcaninheritfromtheIValueConverterclass.ThenweproceededtocreatetheConvertandConvertBackmethodsoftheIValueConverterclass.ThisissothattheconverterwillreturntheoppositeofagivenBooleanvalue;forexample,ifthevalueisTrue,itwillreturnFalse,andconvertitfromFalsebacktoTrue.

NowthatwehavecreatedtheBooleanConverterclass,ournextstepistobegincreatingtheTrailImageConverterthatwillbeusedbytheTrackMyWalksapp.ThisclasswillbeusedtoconvertastringvalueintoaURLpropertythatwillbeusedtodisplayanimageforourdifficultyrating:

1. CreateanemptyclasswithintheValueConvertersfolder,whichislocatedwithintheTrackMyWalksPortableClassLibraryproject.

2. Next,choosetheEmptyClassoptionlocatedwithintheGeneralsection,andenterTrailImageConverterasthenameofthenewclassfiletocreate.

3. Next,ensurethattheTrailImageConverter.csfileisdisplayedwithinthecodeeditor,andenterthefollowingcodesnippet:

//

//TrailImageConverter.cs

//TrackMyWalksValueConverterforconvertingdifficultyvalue

//toanimage.

//

//CreatedbyStevenF.Danielon02/10/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingXamarin.Forms;

namespaceTrackMyWalks.ValueConverters

{

4. Then,weneedtomodifyourTrailImageConverterclassconstructorsothatitcaninheritfromtheIValueConverterclass:

publicclassTrailImageConverter:IValueConverter

{

5. Next,createtheConvertandConvertBackmethodsoftheIValueConverterclass,sothattheconverterwillreturnbackanimageURLbasedonthedifficultylevel:

publicobjectConvert(objectvalue,TypetargetType,

objectparameter,System.Globalization.CultureInfoculture)

{

//Returnbacktherelevantimagebasedonthe

//difficultylevel.

switch((string)value)

{

case"Easy":

return"http://www.yourdomain.com/g1.jpeg";

case"Moderate":

return"http://www.yourdomain.com/g2.jpeg";

case"Challenging":

case"Difficult":

return"http://www.yourdomain.com/g3.jpeg";

case"VeryDifficult":

case"Extreme":

return"http://www.yourdomain.com/g5.jpeg";

default:

return"http://www.yourdomain.com/g1.jpeg";

}

}

publicobjectConvertBack(objectvalue,TypetargetType,

objectparameter,System.Globalization.CultureInfoculture)

{

thrownewNotImplementedException();

}

}

}

Intheprecedingcodesnippet,webeganbymodifyingourTrailImageConverterclassconstructorsothatitcaninheritfromtheIValueConverterclass.Inournextstep,we'llproceedtocreatetheConvertandConvertBackprotocolmethodsoftheIValueConverterclass.ThisissothattheconverterwillreturnbackanimageURLbasedonthedifficultylevelandwillbedisplayedwithinourTrackMyWalksapp.

UpdatingtheWalkBaseViewModeltouseourBooleanconverterInthissection,wewillproceedtoupdateourWalkBaseViewModelclasstoincludereferencestoourBooleanvalueconverter.SincetheWalkBaseViewModelalreadyinheritsandisusedbyeachoftheViewModels,itmakessensetoplaceitwithinthisclass.Thatway,ifweneedtoaddadditionalmethods,wecanjustaddthemwithinthisclass.Toproceed,performthefollowingsteps,asshownhere.

EnsurethattheWalkBaseViewModel.csfileisdisplayedwithinthecodeeditor,andenterthefollowinghighlightedcodesections,asshowninthefollowingcodesnippet:

//

//WalkBaseViewModel.cs

//TrackMyWalksBaseViewModel

//

//CreatedbyStevenF.Danielon22/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem.ComponentModel;

usingSystem.Runtime.CompilerServices;

usingSystem.Threading.Tasks;

usingTrackMyWalks.Services;

namespaceTrackMyWalks.ViewModels

{

publicabstractclassWalkBaseViewModel:

INotifyPropertyChanged

{

protectedIWalkNavServiceNavService

{get;privateset;}

bool_isProcessBusy;

publicboolIsProcessBusy

{

get{return_isProcessBusy;}

set

{

_isProcessBusy=value;

OnPropertyChanged();

OnIsBusyChanged();

}

}

...

...

...

protectedvirtualvoidOnIsBusyChanged()

{

//WeareprocessingourWalksTrailInformation

}

}

publicabstractclassWalkBaseViewModel<WalkParam>:

WalkBaseViewModel

{

protectedWalkBaseViewModel(

IWalkNavServicenavService):

base(navService)

{

}

publicoverrideasyncTaskInit()

{

awaitInit(default(WalkParam));

}

publicabstractTaskInit(WalkParamwalkDetails);

}

}

Intheprecedingcodesnippet,webeganbycreatingaBooleanproperty,calledisProcessBusy,whichwewillonlysettoTruewhileweareintheprocessofactuallyloadingdatawithinourListView,ordoingsomeotherprocessthattakesquitealongtime.TheIsProcessBusypropertycontainsboththegetter(get)andsetter(set)implementations.WhenwesettheIsProcessBusyproperty,weassignthisvaluetoour_isProcessBusyvariable,andthencalltheOnPropertyChangedandOnIsBusyChangedinstancemethodstotelltheViewModelsthatachangehasbeenmade.

UpdatingtheWalksPageViewModeltouseourBooleanconverterInthissection,wewillproceedtoupdatetheWalksPageViewModelViewModeltoreferenceourBooleanvalueconverter.SincetheWalksPageViewModelisusedtodisplayinformationfromtheWalkEntriesmodel,wewillneedtoupdatetheLoadWalksinstancemethodtotogglebetweentheIsProcessBusyvaluewhileitisloadingdatawithintheListView.Toproceed,performthefollowingsteps,asshownhere:

EnsurethattheWalksPageViewModel.csfileisdisplayedwithinthecodeeditor,andenterthefollowinghighlightedcodesections,asshowninthefollowingcodesnippet:

//

//WalksPageViewModel.cs

//TrackMyWalksViewModels

//

//CreatedbyStevenF.Danielon22/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem.Collections.ObjectModel;

usingSystem.Threading.Tasks;

usingTrackMyWalks.Models;

usingTrackMyWalks.Services;

usingXamarin.Forms;

namespaceTrackMyWalks.ViewModels

{

publicclassWalksPageViewModel:WalkBaseViewModel

{

ObservableCollection<WalkEntries>_walkEntries;

publicObservableCollection<WalkEntries>walkEntries

{

get{return_walkEntries;}

set

{

_walkEntries=value;

OnPropertyChanged();

}

}

publicWalksPageViewModel(IWalkNavServicenavService):

base(navService)

{

walkEntries=newObservableCollection<WalkEntries>();

}

publicoverrideasyncTaskInit()

{

awaitLoadWalkDetails();

}

publicasyncTaskLoadWalkDetails()

{

//Checktoseeifwearealreadyprocessingour

//WalkTrailItems

if(IsProcessBusy){

return;

}

//Ifwearen'tcurrentlyprocessing,weneedto

//initialiseourvariabletotrue.

IsProcessBusy=true;

//Addatemporarytimer,sothatwecansee

//ourprogressindicatorworking

awaitTask.Delay(1000);

...

...

...

//Re-initialiseourprocessbusyvaluebacktofalse

IsProcessBusy=false;

}

...

...

...

}

}

Intheprecedingcodesnippet,webeganbyupdatingtheLoadWalksmethodtotogglebetweentheBooleanpropertycalledIsProcessBusy.WesetthistoTruewhileweareintheprocessofactuallyloadingdatawithintheListView.Wethenaddedatemporarytimersowecanseethisprocessinaction,butwewillberemovingthisinChapter7,IncorporatingAPIDataAccess,whenweloadtheTrailinformationfromanAPI.Finally,were-initializedourIsProcessBusystatevaluebysettingthistoFalsetotelltheWalkBaseViewModelthatwehavecompletedprocessingofthewalktrailitems.

NowthattheViewModelisawareofwhenitisbusilyprocessingitemswithintheListView,ournextstepistomodifytheuserinterfaceforthewalkspagetoincludeanactivityindicatorthatinheritsfromtheXamarin.Forms.Coreplatform.WewillalsobemodifyingourDistanceTravelledandWalksTrailPagetoincludethePlatFormEffectsclasses.

UpdatingthewalksmainpagetousetheupdatedViewModelInthissection,wewillproceedtoupdatetheWalksPagecontentpage,whichwillincludeanactivityprocessindicatorthatwilldisplayatextstringtotheuser,lettingthemknowthatthetrailwalksarebeingpopulatedwithintheListViewcontrol.WewillalsobemakinguseofourPlatformEffectsclassesfortheLabelShadow.Toproceed,performthefollowingsteps:

1. EnsurethattheWalksPage.csfileisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesections,asshowninthefollowingcodesnippet:

//

//WalksPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms;

usingTrackMyWalks.Models;

usingTrackMyWalks.ViewModels;

usingTrackMyWalks.Services;

usingTrackMyWalks.DataTemplates;

usingTrackMyWalks.ValueConverters;

namespaceTrackMyWalks

{

publicclassWalksPage:ContentPage{

WalksPageViewModel_viewModel

{

get{

returnBindingContextasWalksPageViewModel;}

}

publicWalksPage()

{

varnewWalkItem=newToolbarItem

{

Text="AddWalk"

};

...

...

...

//DeclareandinitializeourModelBindingContext

BindingContext=newWalksPageViewModel(DependencyService

.Get<IWalkNavService>());

//DefineourItemTemplate

varwalksList=newListView

{

HasUnevenRows=true,

ItemTemplate=newDataTemplate(typeof(

WalkCellDataTemplate)),

SeparatorColor=(Device.OS==TargetPlatform.iOS)?

Color.Default:Color.Black

};

//SettheBindingpropertyforourwalksEntries

walksList.SetBinding(ItemsView<Cell>.ItemsSourceProperty,

"walkEntries");

2. Next,weneedtosetuptheBindingtotheIsVisiblePropertythatwedefinedwithintheWalkBaseViewModelclass.ThiswillbecalledwhenthispropertyhasbeensetTruetodisplayourWalkEntrieswithintheListViewandishandledbytheIsProcessBusyproperty.Proceedandenterthefollowinghighlightedcodesections,asshowninthecodesnippet:

walksList.SetBinding(ItemsView<Cell>.IsVisibleProperty,

"IsProcessBusy",converter:newBooleanConverter());

//InitializeoureventHandlertousewhentheitem

//istapped

walksList.ItemTapped+=(objectsender,

ItemTappedEventArgse)=>

{

varitem=(WalkEntries)e.Item;

if(item==null)return;

_viewModel.WalkTrailDetails.Execute(item);

item=null;

};

3. Then,wedeclareandinitializeanewprogressLabelcontrol,whichwillbeusedtodisplayinstructiveinformationtotheuserwhentheListViewisbeingpopulated.Proceedandenterinthefollowinghighlightedcodesections,asshowninthecodesnippet:

//DeclareourProgressLabel

varprogressLabel=newLabel()

{

FontSize=14,

FontAttributes=FontAttributes.Bold,

TextColor=Color.Black,

HorizontalTextAlignment=TextAlignment.Center,

Text="LoadingTrailWalks..."

};

4. Next,weapplythePlatformEffectLabelShadowEffectclasstoourprogressLabelcontrol,instantiateandinitializetheActivityIndicatorclass,andsettheIsRunningpropertytoTrue,beforecreatingaStackLayoutvariableprogressIndicatorandaddingboththeactivityIndicatorandprogressLabelitems.Finally,wesetuptheBindingforourProgressIndicatorusingtheisVisiblePropertyoftheStackLayoutcontrol.ThiswillusetheIsProcessBusypropertytodeterminewhetherornottoshowtheActivityIndicator.Proceedandenterinthefollowinghighlightedcodesections,asshowninthecodesnippet:

//ApplyPlatformEffectstoourProgressLabel

progressLabel.Effects.Add(Effect.Resolve("com.geniesoftst

udios.LabelShadowEffect"));

//InstantiateandinitialiseourActivityIndicator.

varactivityIndicator=newActivityIndicator()

{

IsRunning=true

};

varprogressIndicator=newStackLayout

{

Orientation=StackOrientation.Vertical,

HorizontalOptions=LayoutOptions.CenterAndExpand,

VerticalOptions=LayoutOptions.CenterAndExpand,

Children={

activityIndicator,

progressLabel

}

};

progressIndicator.SetBinding(StackLayout.IsVisibleProperty,

"IsProcessBusy");

varmainLayout=newStackLayout

{

Children=

{

walksList,

progressIndicator

}

};

Content=mainLayout;

}

protectedoverrideasyncvoidOnAppearing()

{

base.OnAppearing();

//InitializeourWalksPageViewModel

if(_viewModel!=null)

await_viewModel.Init();

}

}

}

Intheprecedingcodesnippet,webeganbysettinguptheBindingtoourIsVisibleProperty,whichwedefinedwithintheWalkBaseViewModelclass;thiswillbecalledwhenthepropertyhasbeensettoTruetodisplaytheWalkEntrieswithintheListView,andishandledbytheIsProcessBusyproperty.Inournextstep,wedeclaredandinitializedanewprogressLabelcontrol,whichwillbeusedtodisplayinstructiveinformationtotheuserwhentheListViewisbeingpopulated.

ThenweappliedtheLabelShadowEffectclasstotheprogressLabelcontrol,instantiatedandinitializedtheActivityIndicatorclass,andsettheIsRunningpropertytoTrue,beforecreatingaStackLayoutvariableprogressIndicatorandaddingboththeactivityIndicatorandprogressLabelitems.

Next,wesetuptheBindingfortheProgressIndicatorusingtheisVisiblePropertyofourStackLayoutcontrol,whichwillusetheIsProcessBusypropertytodeterminewhetherornottoshowtheActivityIndicator.Finally,weaddedtheProgressIndicatortoourmainLayoutaspartoftheChildrenproperty,sothatthiscanbedisplayedaspartofthemaincontentpage.

Tip

Ifyoudon'texportanEffectforaparticularplatform,theEffect.Resolvewillreturnanon-nullvaluethateffectivelydoesn'tdoanything,andyouwon'tseeanyrenderinghappenonyourcontrols.Youwillneedtoensurethat,withinyourPlatformEffectclasses,youincludetheExportEffectassemblyattributeatthetopofyourclassimplementations.

UpdatingtheWalksTrailPagetousetheupdatedViewModelInthissection,wewillproceedtoupdateourWalksTrailPagecontentpagewillmakeuseofthePlatformEffectsclassesfortheButtonShadow.Toproceed,performthefollowingsteps:

EnsurethattheWalksTrailPage.csfileisdisplayedwithinthecodeeditor,andenterthefollowinghighlightedcodesections,asshowninthecodesnippet:

//

//WalkTrailPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms;

usingTrackMyWalks.ViewModels;

usingTrackMyWalks.Services;

usingTrackMyWalks.ValueConverters;

namespaceTrackMyWalks

{

publicclassWalkTrailPage:ContentPage

{

WalksTrailViewModel_viewModel

{

get{returnBindingContext

asWalksTrailViewModel;}

}

publicWalkTrailPage()

{

Title="WalksTrail";

...

...

...

beginTrailWalk.Effects.Add(Effect.Resolve

("com.geniesofts

tudios.ButtonShadowEffect"));

...

...

...

trailDifficultyLabel.SetBinding(Label.TextProperty,

"WalkEntry.Difficulty",stringFormat:"Difficulty:{0}");

vartrailDifficultyImage=newImage

{

HeightRequest=50,

WidthRequest=50,

Aspect=Aspect.AspectFill,

HorizontalOptions=LayoutOptions.Start

};

trailDifficultyImage.SetBinding(Image.SourceProperty,

"WalkEntry.Difficulty",

converter:newTrailImageConverter());

...

...

...

}

}

}

Intheprecedingcodesnippet,webeganbyapplyingtheButtonShadowEffectclasstoourbeginTrailWalkbutton,andthenupdatedthebindingofthetrailDifficultyImagetousetheTrailImageConvertervalueconverter.Thiswillconvertthestringrepresentationforthechosendifficulty,andreturnbackanimageURLthatwillbedisplayedwithinthecontentpage.

UpdatingtheDistanceTravelledPagetousetheupdatedViewModelInthissection,wewillproceedtoupdateanotherDistanceTravelledPagecontentpagethatwillmakeuseofthePlatformEffectsclassesforourButtonShadow.Toproceed,performthefollowingsteps:

EnsurethattheDistanceTravelledPage.csfileisdisplayedwithinthecodeeditor,andenterthefollowinghighlightedcodesections,asshowninthecodesnippet:

//

//DistanceTravelledPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms;

usingXamarin.Forms.Maps;

usingTrackMyWalks.Services;

namespaceTrackMyWalks

{

publicclassDistanceTravelledPage:ContentPage

{

DistTravelledViewModel_viewModel

{

get{returnBindingContextas

DistTravelledViewModel;}

}

publicDistanceTravelledPage()

{

Title="DistanceTravelled";

...

...

...

varwalksHomeButton=newButton

{

BackgroundColor=Color.FromHex("#008080"),

TextColor=Color.White,

Text="EndthisTrail"

};

walksHomeButton.Effects.Add(Effect.Resolve

("com.geniesoft

studios.ButtonShadowEffect"));

...

...

...

}

}

Intheprecedingcodesnippet,webeginbyapplyingtheButtonShadowEffectclasstoourwalksHomeButtoncontrolsothatitcantakeadvantageoftheniceplatform-specificrenderingeffectstovisualcontrolelements.

UpdatingtheWalkCellDataTemplateclasstousePlatformEffectsInthissection,wewillproceedtoupdatetheWalkCellDataTemplateclass,whichwillmakeuseofthePlatformEffectsclassesforourLabelShadow,sothattheDataTemplatewillinheritsomeofthenicevisualrepresentationsthatyouruserswilllove.Toproceed,performthefollowingsteps,asshownhere:

EnsurethattheWalkCellDataTemplate.csfileisdisplayedwithinthecodeeditor,andenterthefollowinghighlightedcodesections,asshowninthecodesnippet:

//

//WalkCellDataTemplate.cs

//TrackMyWalksDataTemplateforCells

//

//CreatedbyStevenF.Danielon01/10/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingTrackMyWalks.ValueConverters;

usingXamarin.Forms;

namespaceTrackMyWalks.DataTemplates

{

publicclassWalkCellDataTemplate:ViewCell

{

publicWalkCellDataTemplate()

{

...

...

...

//ApplyPlatformEffectstoourTrailNameLabelControl

TrailNameLabel.Effects.Add(Effect.Resolve

("com.geniesofts

tudios.LabelShadowEffect"));

TrailNameLabel.SetBinding(Label.TextProperty,"Title");

...

...

...

trailDifficultyImage.SetBinding(Image.SourceProperty,

"Difficulty",converter:newTrailImageConverter());

...

...

...

this.View=cellLayout;

}

}

}

Intheprecedingcodesnippet,webeganbyapplyingtheLabelShadowEffectclasstoourTrailNameLabelcontrol,andthenupdatedthebindingofthetrailDifficultyImagetousethe

TrailImageConvertervalueconverter.Thiswillconvertthestringrepresentationforourchosendifficulty,andreturnbackanimageURLthatwillbedisplayedwithinthecontentpage.

NowthatyouhaveupdatedthenecessaryViewModelsandContentPagestotakeadvantageofourPlatFormEffectsandValueConverters,ournextstepistofinallybuildandruntheTrackMyWalksapplicationwithintheiOSsimulator.

Whenthecompilationiscomplete,theiOSsimulatorwillappearautomatically,andtheTrackMyWalksapplicationwillbedisplayed,asshowninthefollowingscreenshot:

Asyoucanseefromtheprecedingscreenshot,thiscurrentlydisplaysourActivityIndicatorspinnercontrol,withtheassociatedLoadingTrailWalks...text.AfterthistheListViewcontainingthelistoftrailwalksfromtheDataTemplatecontrolwilldisplay.Youwillnoticethatitdisplaysaniceimageassociatedwiththedifficultyforthetrailwalk,whichispulleddirectlyfromtheTrailImageValueConverterclass.

WhentheAddWalkbuttonispressedfromthemainTrackMyWalkscontentpage,thisdisplaystheNewWalkEntryscreen,withtheDifficultyLevelcustompickershowingthelistofchoicesasdefinedwithinourDifficultyPickerModel:

TheprecedingscreenshotshowstheupdatedViewModelandContentPagesthatmakeuseofthePlatformEffectsclassesfortheButtonShadoweffects,aswellastheValueConvertersfortheTrailImageValueConverter.Asyoucansee,byusingthepowerofXamarin.FormsPlatformEffectsandValueConverterswithinyourownapplications,youcanreallycreatesomestunninguserinterfacesthatwillshowoffyourappsandthatyouruserswilllove.

SummaryInthischapter,weupdatedtheTrackMyWalksapplicationtouseCustomRendererstochangetheappearanceofcontrolelementsthataredisplayedwithintheuserinterfaceforeachspecificplatform.Next,youlearnedhowtoworkwithDataTemplates,bycreatingacustomclasstorepresenttheinformationthatispresentedwithintheListViewclass,aswellascreatingtwoValueConverterclassesBooleanValueConverterthatareusedtodeterminewheninformationiscurrentlybeingdisplayedwithintheuserinterface.YoualsocreatedaTrailImageValueConverterthatreturnsanimageURLbasedonthestringpassedintoit.Finally,youlearnedhowtoworkwiththePlatformEffectsclass,tocreateaLabelShadowandButtonShadow,andupdatedtheViewModelandContentPagestoapplythoseeffectstocontrolelements.

Inthenextchapter,you'lllearnabouttheRazorTemplatingEngineandhowyoucanuseittocreateahybridmobilesolution.You'lllearnhowtocreate,andusemodelswithinyourapplication,aswellascallingJavaScriptcodeusingC#toexecutemethodcalls.

Chapter6.WorkingwithRazorTemplatesInourpreviouschapter,welookedathowtoworkwiththeDataTemplateCustomRendererbycreatingaC#classtolayoutyourviewsbeautifullywithinyourapplications,andhowyouwillalsogetaccustomedtoworkingwiththeplatform-specificAPIstoextendthedefaultbehaviorofXamarin.Formscontrolsusingcustomrenderers,bycreatingacustompicker.

YoualsolearnedhowtousetheXamarin.FormsEffectsAPItocustomizetheappearanceandstylingofnativecontrolelementsforeachplatform,byimplementingacustomrendererclass,andlookedathowtomanipulatethevisualappearanceofdatathatisbound,usingvalueandimageconverters.

Inthischapter,you'lllearnabouttheRazorHTMLtemplateengineandhowyoucanuseittocreateahybridmobilesolution.You'lllearnhowtobuildabooklibrarymobilesolutionusingthepowerofRazortemplates,andlearnhowtocreate,andusemodelswithinyourapplicationandconnectthisuptoanSQLitedatabasetostore,retrieve,update,anddeletebookdetails.

Thischapterwillcoverthefollowingtopics:

IntroductiontotheRazorHTMLtemplateengineHowtobuildahybridmobilesolutionusingXamarinStudioIncorporatingSQLite.NetandcreatingaSQLitedatabasewrapperCreatingthebookdatabasemodelCreatingthebooklistingmainpageCreatingthebooklistingaddpageCreatingthebooklistingeditpage

UnderstandingtheRazortemplateengineTheRazortemplatingenginewasfirstintroducedaspartoftheASP.NetMVCarchitecture,andwasoriginallydesignedtorunonawebservertogenerateHTMLfilestobeservedtowebbrowsers.

SinceRazormadeitsfirstappearanceonthedevelopmentscene,theRazortemplatingenginehascomealongwayandnowextendsthestandardHTMLsyntax,sothatyoucanuseC#toexpressthelayoutofyourHTMLfiles,andincorporateCSSstylesheetsandJavaScripteasily.

EachRazortemplatehastheabilitytoreferenceaModelclasswhichcanbeofanycustomtype,andpropertiescanbeaccesseddirectlyfromthetemplate,byhavingtheabilitytomixHTMLandC#syntaxeasily.

Asyouworkthroughthischapter,youwillseehow,byworkingwithXamarinStudio,youcanutilizetheRazorHTMLtemplatingengineandbeequippedwiththeflexibilityofbuildingcross-platformtemplatedHTMLviewsthatusebothJavaScriptandCSS,aswellashavingaccesstotheunderlyingplatformAPIsusingthepowerofC#.

Note

FormoreinformationonusingRazorsyntax(C#)withASP.NETwebprogrammingrefertothefollowingURL:https://www.asp.net/web-pages/overview/getting-started/introducing-razor-syntax-c.

CreatingandimplementingRazortemplateswithinXamarinStudioInthissection,wewilllookathowtogoaboutcreatinganewRazortemplatesolutionusingXamarinStudio.Wewillbeginbydevelopingthebasicstructureforourapplication,aswellasaddingallthenecessarydatabasemodelsanduserinterfacefiles.

Beforewecanproceed,weneedtocreatetheBookLibraryproject.ItisverysimpletocreatethisusingXamarinStudio.Simplyfollowthegivensteps:

1. LaunchtheXamarinStudioapplication,andchoosetheNewSolution...option,oralternativelychooseFile|New|Solution...orsimplypressShift+Command+N.

2. Next,choosetheWebViewAppoptionwhichislocatedundertheiOS|Appsection,ensurethatyouhaveselectedC#astheprogramminglanguagetouse,andclickNext:

3. Then,enterinBookLibraryasthenameforyourappintheAppNamefieldaswellasspecifyinganamefortheOrganizationIdentifierfield.

4. Next,ensurethatboththeiPadandiPhonecheckboxeshavebeenselectedfortheDevicesfield,aswellasensuringthatyouhavechoseniOS10.0fortheTargetfieldtosupporttheminimumiOSversionthatwewantourapptosupport:

5. ClickontheNextbuttontoproceedtothenextstepinthewizard:

6. Next,ensurethattheCreateaprojectdirectorywithinthesolutiondirectory.checkboxhasbeenselectedandclickontheCreatebuttontosaveyourprojectatthespecifiedlocation.

Onceyourprojecthasbeencreated,youwillbepresentedwiththeXamarinStudiodevelopmentenvironment,alongwithseveralprojectfilesthatthetemplatecreatedforyourRazortemplateSolution,asshowninthefollowingscreenshot:

Asyoucanseefromtheprecedingscreenshot,theBookLibrarysolutionhasbeendividedintothreeseparatefolders.Thefollowingtableprovidesabriefdescriptionofwhateachareaisusedfor:

Foldertype Description

Models

Thissectionisresponsibleforrepresentingthemodelthatourviewswilluse,andcontainsastructureoffieldsthatwillbedisplayedand/orwrittentobyourRazortemplates.

ResourcesThissectioncontainsaplaceforyoutoaddtheimagesandCSS,orJavaScriptfilesthatyourapplicationwilluse.

Views

ThissectionisresponsibleforcontainingalloftheHTML5Razortemplatesthatyourapplicationwillbereferencing,andtheyneedtocontainandbeprefixedwiththe.cshtmlextension.

Onethingyouwillnoticeisthatoursolutioncontainsafilecalledstyle.css.Thisisbecauseourapplicationisessentiallyahybridmobilesolution,andcontainsthefilesyoumightexpectfromawebsolution,ascanbeseeninthefollowingcodesnippet:

/*Thisisaminimalstylesheetintendedtodemonstrate

howtoincludestaticcontentinyourhybridapp.

Otherstaticcontent,suchasjavascriptfilesandimages,

canbeincludedinthissamefolder(ResourcesoniOSorAssets

onAndroid),withthesameBuildAction(BundleResourceoniOS

orAndroidAssetonAndroid),tobeaccessiblefromapathstarting

attherootofyourhybridapplication.*/

#page{

margin-top:10px;

}

Asyoucansee,thisfiledoesn'tcontainmuchinformation,butasweworkourwaythroughthischapter,wewillbeaddingtothisfile,andbuildingtheRazortemplateuser-interfacefiles.

AddingtheSQLite.NetpackagetotheBookLibrarysolutionNowthatwehavecreatedourBookLibrarysolution,thenextstepistoaddtheSQLite.NetNuGetpackagetooursolution,sincethisdoesn'tgetaddedautomaticallyforus.TheSQLite.NetpackagewillessentiallyprovideuswiththeabilitytohaveourapplicationwritetoanSQLitedatabase,tostoreourbookdetails.

Note

ANuGetpackageisessentiallythepackagemanagerfortheMicrosoftdevelopmentplatformthatcontainstheclienttoolsthatprovideoursolutionwiththeabilitytoproduceandconsume.NETpackages.

Let'slookathowtoaddtheSQLite.NETNuGetpackagewithinourBookLibrarysolution,byperformingthefollowingsteps:

1. Right-clickonthePackagesfolderthatiscontainedwithintheBookLibrarysolution,andchoosetheAddPackages...menuoption,asshowninthefollowingscreenshot.

2. ThiswilldisplaytheAddPackages...dialog.EnterinSQLite.Netwithinthesearchdialog,andthenclickonthesqlite-netoptionwithinthelist,asshowninthefollowingscreenshot:

3. Finally,clickontheAddPackagebuttontoaddthesqlite-netNuGetpackagetothePackagesfolder,containedwithintheBookLibrarysolution.

WhenyouclickontheAddPackagebutton,youwillnoticethatthepackagemanagerwillcreatetwonewfilesforuswithinoursolution.ThesearecalledSQLite.csandSQLiteAsync.cs,andarebasicallywrapperconvenienceclassesthatallow.NETandMonoapplicationstostoredatawithinaSQLite3database.

NowthatyouhaveaddedtheNuGetpackageforthesqlite-netlibrary,wecanbegintoutilizethislibrarywithintheBookLibrarysolutionthatwewillbecoveringinthenextsection.

CreatingandimplementingthebooklibrarydatabasemodelInthissection,wewillbeginbybuildingthedatabasemodelthatwillbeusedbyourBookLibraryprojectsolutionthatwillbeusedbyourRazortemplateswhenwecreatethese,andthentheWebViewController.csfilewillcommunicateandinteractwitheachoftheRazortemplateviewsandhandletheactionswithinthem.

Let'slookathowwecanachievethis,byperformingfollowingsteps:

1. CreateanemptyclasswithintheModelsfolder,bychoosingAdd|NewFile...,asshowninthefollowingscreenshot:

2. Next,choosetheEmptyClassoption,locatedwithintheGeneralsection,andenterinBookItem,asshowninthefollowingscreenshot:

3. Next,clickontheNewbuttontoallowthewizardtoproceedandcreatethenewemptyclassfile.

Upuntilnow,allwehavedoneiscreateourBookItemclassfile.ThisclasswillbeusedbyeachofourRazortemplateviews,aswellasourWebViewController.csfilethatwilleventuallyallowcommunicationtohappenbetweeneachoftheRazortemplateviews,aswellasbeingabletohandleactionswithinthem,whenevertheuserinteractswiththem.

Let'snowstarttoimplementthecoderequiredforourBookItemclassmodel,byperformingthefollowingsteps:

1. EnsurethattheBookItem.csfileisdisplayedwithinthecodeeditor,andenterthefollowingcodesnippet:

//

//BookItem.cs

//BookLibraryDatabaseModel

//

//CreatedbyStevenF.Danielon20/10/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSQLite;

namespaceBookLibrary{

publicclassBookItem

{

publicBookItem()

{

}

[PrimaryKey,AutoIncrement]

publicintId{get;set;}

publicstringTitle{get;set;}

publicstringAuthor{get;set;}

publicstringIsbn{get;set;}

publicstringSynopsis{get;set;}

}

}

Intheprecedingcodesnippet,wehavesuccessfullydefinedourdatabasemodelthatwillbeusedtorepresentourbookitems.YouwillnoticesomethingthatisdifferentwithinourmodeltowhatyouwouldhaveseenwithinourTrackMyWalksapp:herewehavedefineda[PrimaryKey,AutoIncrement]itemforouridfield,thiswilltellourBookLibrarydatabasetosettheidpropertytoautomaticallyincrementwheneverweaddanewitemtoourdatabase.

Ifyouhaveusedrelationaldatabasesinthepast,suchasMicrosoftSQLServer,Oracle,orMicrosoftAccess,thisshouldbequitefamiliartoyou.Inthenextsection,wewillusethismodeltosetupandinitializeourdatabase,bycreatingadatabaselibrarywrappertohandleconnectionstoourdatabase,aswellasCreation,Retrieval,Updating,andDeletion(CRUD),ofeachofourbookentries.

Note

TheAndroidversionoftheBookItemclassmodelisavailableinthecompanionsourcecodeforthisbook.

CreatingandimplementingthebookdatabasewrapperIntheprevioussection,wesuccessfullycreatedandimplementedourBookItemdatabasemodelthatwillbeusedbyourBookLibraryapplication.OurnextstepistobeginimplementingthecoderequiredforourBookItemDatabaseclassmodel,byperformingthefollowingsteps:

1. CreateanewfolderwithintheBookLibraryprojectsolution,calledDatabaseasshowninthefollowingscreenshot:

2. Next,createanemptyclasswithintheDatabasefolder,bychoosingAdd|NewFile...,asyoudidwhencreatingthemodelintheprevioussectionentitledCreatingandimplementing

thebooklibrarydatabasemodel,locatedwithinthischapter.3. Then,enterinBookDatabaseforthenameofthenewclassthatyouwanttocreate,andclick

ontheNewbuttontoallowthewizardtoproceedandcreatethenewfile.4. Next,ensurethattheBookDatabase.csfileisdisplayedwithinthecodeeditorwindow,and

thenenterinthefollowingcodesnippet:

//

//BookDatabase.cs

//BookLibraryDatabasetohandleperformingdatabase

//Creation,Retrieval,UpdatingandDeletionofBookItems.

//

//CreatedbyStevenF.Danielon20/10/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem.Collections.Generic;

usingSystem.Linq;

usingSQLite;

namespaceBookLibrary

{

publicclassBookDatabase

{

5. Next,weneedtocreatealockervariablethatwillbeusedtocreateamutually-exclusivelockonthedatabasewhileweareeithercreating,updating,retrieving,ordeletingbookitems.Wedothissothatnootheroperationcaninterferewhileweareprocessing,andthiswillpreventissuesfromarising.Wealsoneedtodeclareadatabasevariable,thatpointstoaninstanceofourSQLiteConnectionlocatedwithinourSQLitelibrary,sothatwecanperformdatabaseoperations:

staticobjectlocker=newobject();

SQLiteConnectiondatabase;

6. Then,weneedtocreatetheBookDatabase(SQLiteConnectionconn)methodthatacceptsaconnobject,whichisaninstanceofourSQLiteConnectionclass,andthisinstancemethodwillbeusedtocreatethenecessarydatabasetablestructure,basedonourBookItemmodel:

///<summary>

///InitializesanewinstanceoftheBookLibrary

///Databaseclass.

///</summary>

///<paramname="conn">Conn.</param>

publicBookDatabase(SQLiteConnectionconn)

{

database=conn;

//CreatethetableswithinourBookLibraryDatabase

database.CreateTable<BookItem>();

}

7. Next,weneedtocreatetheGetItems()methodthatwillbeusedtoextractalloftheexistingbookentriesthathavebeensavedtothedatabase.WeusetheLINQlanguagesyntax

toiterateandretrieveallitemsfromourBookItemtable,andconvertthiscollectiontoalistinstance,asdeterminedbythe.ToList()method:

///<summary>

///Getsallofthebooklibraryitemsfromourdatabase.

///</summary>

///<returns>Theitems.</returns>

publicIEnumerable<BookItem>GetItems()

{

//Setamutual-exclusivelockonourdatabase,while

//retrievingitems.

lock(locker)

{

return(fromiindatabase.Table<BookItem>()

selecti).ToList();

}

}

8. Then,weneedtocreatetheGetItem()methodthatwillextracttheselectedbookentryfromtheBookItemdatabasetable,usingtheiridasthekey.Again,weusetheLINQlanguagesyntaxtoretrievethefirstitemfromtheBookItemtablethatmatchestheidofthebookitem:

///<summary>

///Getsaspecificbookitemfromthedatabase.

///</summary>

///<returns>Theitem.</returns>

///<paramname="id">Identifier.</param>

publicBookItemGetItem(intid)

{

//Setamutual-exclusivelockonourdatabase,while

//retrievingthebookitem.

lock(locker)

{

returndatabase.Table<BookItem>().FirstOrDefault(x=>

x.Id==id);

}

}

9. Next,weneedtocreatetheSaveItem()methodthatwillsavethebookitemtotheBookItemdatabasetable.Inthisinstancemethod,wearehandlingtwocasescenarios:oneiftheitemwearesavingisanexistingitem,wechecktheidofthebookitem,andifitisanon-zerovalue,weproceedtoupdatethebookitemusingtheUpdatemethodonthedatabaseobject,andreturnthebookitemidback.

10. However,iftheitemisanewbookrecord,thatisallnewbooksthatgetcreatedwillhaveanidof0,thiswillbedirectlyinsertedintotheBookItemtable,usingtheInsertmethodonthedatabaseobject:

///<summary>

///Savesthebookitemcurrentlybeingedited.

///</summary>

///<returns>Theitem.</returns>

///<paramname="item">Item.</param>

publicintSaveItem(BookItemitem)

{

//Setamutual-exclusivelockonourdatabase,while

//saving/updatingourbookitem.

lock(locker)

{

if(item.Id!=0)

{

database.Update(item);

returnitem.Id;

}

else{

returndatabase.Insert(item);

}

}

}

11. Finally,wecreatetheDeleteItem()methodthatwill,asyoumighthaveguessed,deleteabookitemfromtheBookItemdatabasetableusingthebook'sitemidandthencallingtheDeletemethodonthedatabaseobject:

///<summary>

///Deletesaspecificbookitemfromthedatabase.

///</summary>

///<returns>Theitem.</returns>

///<paramname="id">Identifier.</param>

publicintDeleteItem(intid)

{

//Setamutual-exclusivelockonourdatabase,while

//deletingourbookitem.

lock(locker)

{

returndatabase.Delete<BookItem>(id);

}

}

}

}

NowthatwehavecreatedourBookDatabaseclass,wecanproceedandcreateourBookLibraryDBdatabasewrapperthatwilluseourBookDatabaseclasstohandlealltheoperationsforcreating,retrieving,updating,anddeletionofbookitemsfromourdatabase.ThatwillbeusedbyourWebViewController.csclasstointeractwithourRazortemplateViews.

Note

TheAndroidversionoftheBookDatabaseclassisavailableinthecompanionsourcecodeforthisbook.

CreatingandimplementingtheBookLibrarydatabasewrapperIntheprevioussection,wesuccessfullycreatedandimplementedourBookDatabasedatabaseclassthatwillbeusedbyourBookLibraryapplication.OurnextstepistobeginimplementingthecoderequiredforourBookLibraryDBclassthatwillactasawrapperforourBookDatabaseclass,toperformalldatabaseactionsforourBookLibraryapplication.

Let'sbeginbyperformingthefollowingsteps:

1. CreateanemptyclasswithintheBookLibrarysolution,bychoosingAdd|NewFile...,asyoudidwhencreatingthemodelintheprevioussectionentitledCreatingandimplementingthebooklibrarydatabasemodel,locatedwithinthischapter.

2. Then,enterinBookLibraryDBforthenameofthenewclassthatyouwanttocreate,andclickontheNewbuttontoallowthewizardtoproceedandcreatethenewfile.

3. Next,ensurethattheBookLibraryDB.csfileisdisplayedwithinthecodeeditorwindow,andthenenterinthefollowingcodesnippet:

//

//BookLibraryDB.cs

//BookLibraryDatabaseLayer

//

//CreatedbyStevenF.Danielon20/10/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSQLite;

namespaceBookLibrary

{

publicclassBookLibraryDB

{

4. Next,weneedtocreateaconnvariablethatwillbeusedtosetaconnectiontoourBookLibrarydatabase,andwealsoneedtodeclareadatabasevariable,thatpointstoaninstanceofourBookDatabasethatcontainseachoftheoperationsforhandlingandperformingtheinsertion,updating,anddeletionofbookentries:

staticSQLiteConnectionconn;

staticBookDatabasedatabase;

5. Then,weneedtocreatetheSetDatabaseConnection()methodthatwillsetaconnectiontoourBookDatabasedatabaseclass,sothatitcanaccesseachofourinstancemethodsforhandlingthecreation,updating,retrieval,anddeletionofourbookentries,withinourBookItemdatabasetable.Withinthisinstancemethod,wesetupandinitializetheconnvariablethatcontainsthevalueofourconnectionparameter,andtheninstantiatesaconnectiontoourBookDatabaseclass,passingintheconnconnectionobjectsothattheclasscanreturnourdatabaseobject:

///<summary>

///SetsaconnectiontoourBookDatabasedatabaseclass.

///</summary>

///<paramname="connection">Connection.</param>

publicstaticvoidSetDatabaseConnection(SQLiteConnection

connection)

{

conn=connection;

database=newBookDatabase(conn);

}

6. Finally,wecreatetheDatabasemethodthatgetsareferencetoourBookLibrarydatabasetouse:

///<summary>

///GetsareferencetoourBookLibrarydatabase.

///</summary>

///<value>Thedatabase.</value>

publicstaticBookDatabaseDatabase

{

get{returndatabase;}

}

}

}

NowthatwehavecreatedourBookLibraryDBclass,wecancreateourRazortemplateuserinterfacefilesthatwillconnecttoourBookLibrarydatabase,andallowtheusertointeractwiththedatabasemodeltocreate,retrieve,update,anddeletebookitemsfromthedatabase.

Note

TheAndroidversionoftheBookLibraryDBclassisavailableinthecompanionsourcecodeforthisbook.

CreatingandimplementingthebooklistingmainpageIntheprevioussection,wecreatedandimplementedourBookLibraryDBdatabasewrapperclassthatwillbeusedbyourBookLibraryapplication.ThenextstepistobegincreatingeachoftheRazortemplatesthatwillconnecttoourdatabasemodelallowingtheusertointeractwiththedatabasevisuallybyenteringinbookdetails,andhavingthisinformationpresentedbacktothem,sotheycanmakechanges,ordeletethebookitemaltogether.

Let'sbeginbyperformingthefollowingsteps:

1. CreateanemptyclasswithintheViewsfolder,bychoosingAdd|NewFile...,asyoudidwhencreatingthemodelintheprevioussectionentitledCreatingandimplementingthebooklibrarydatabasemodel,locatedwithinthischapter.

2. Next,choosethePreprocessedRazorTemplateoption,locatedwithintheTextTemplatingsection,andenterinBookLibraryListing,asshowninthefollowingscreenshot:

3. Next,clickontheNewbuttontoallowthewizardtoproceedandcreatethenewemptyRazortemplatefile.

Congratulations,youhavecreatedyourfirstRazortemplate!ThisfilewillbeusedtolistallthebookentriesthatarestoredwithinourBookItemdatabasetable,containedwithinourBookLibrarydatabase.ThenextstepistostarttoimplementthecoderequiredforourBookLibraryListingRazortemplate,byperformingthefollowingstep:

1. EnsurethattheBookLibraryListing.cshtmlfileisdisplayedwithinthecodeeditor,andenterinthefollowingcodesnippet:

@usingBookLibrary

@modelList<BookItem>

<html>

<head><linkrel="stylesheet"href="style.css"/></head>

<body>

<p></p>

<h1>BookLibraryDatabaseListing</h1>

<tableborder="1"cellpadding="8">

@foreach(varbookin@Model){

<tr>

<td>@book.Id</td>

<td>@book.Title</td>

<td>@book.Author</td>

<td>@book.Isbn</td>

<td>

<ahref="hybrid:EditBookDetails?id=@book.Id">

Edit</a>

</td>

</tr>

}

<tr>

<tdcolspan="8">

<ahref="hybrid:CreateNewBook?">

<inputtype="submit"name="Button"

value="AddNewBook"/></a>

</td>

</tr>

</table>

</body>

</html>

Intheprecedingcodesnippet,wedefinetheHTMLlayoutinformationthatwillbeusedbyourBookLibraryListingRazortemplate.WefirstlyspecifythatweareusingtheBookLibrary.iOSnamespace,sothatwecanhaveaccesstoourdatabasemodelasspecifiedbythe@modeldirective,andthismustbetheveryfirstlineprecedingthe<html>tagwithinaRazortemplatefile.Youwillnoticethatthe@modeldirectivehasthetypeofList.ThisisbecauseweareiteratingthrougheachofourbookitemswithinourModel,anddisplayingtheid,title,

author,andisbndetailsforeachbook.

Next,wesetupa<ahreftag,thatpointstoourWebViewController.csclass,andin-turnwillcalltheBookLibraryEdit.cshtmlRazortemplatetoretrieveanddisplaythebookentrydetailsfortheassociatedidentifier.Thehybridtag,usedtoidentifyiftheURLisnotourowncustomscheme,andwilljustlettheWebViewloadtheURLasusual.Finally,wesetupanother<ahreftag,thatpointstoourWebViewController.csclass,andin-turnwillcalltheBookLibraryAdd.cshtmlRazortemplatetoallowtheusertocreateanotherbook.Youwillnoticethatwedon'tneedtopassinanidforthebook,asthiswillbeautomaticallyassignedoncethebookhassuccessfullybeenwrittentothedatabase.

CreatingandimplementingtheBookLibraryAddRazortemplateIntheprevioussection,wecreatedandimplementedtheBookLibraryListingRazortemplatethatwillbeusedtodisplayalistofallbooksthathavebeenpreviouslyaddedtotheBookLibraryapplication.OurnextstepistobegincreatingtheRazortemplatethatwillallowtheusertocreateanewbookandsavethistoourdatabasemodel.

Let'sbeginbyperformingthefollowingsteps:

1. CreateanemptyclasswithintheViewsfolder,bychoosingAdd|NewFile...,asyoudidwhencreatingtheBookLibraryListingintheprevioussectionentitledCreatingandimplementingthebooklistingmainpage,locatedwithinthischapter.

2. Next,choosethePreprocessedRazorTemplateoption,locatedwithintheTextTemplatingsection,andenterinBookLibraryAdd.

3. Next,clickontheNewbuttontoallowthewizardtoproceedandcreatethenewemptyRazortemplatefile.

OurnextstepistostarttoimplementthecoderequiredforourBookLibraryAddRazortemplate,byperformingthefollowingsteps:

EnsurethattheBookLibraryAdd.cshtmlfileisdisplayedwithinthecodeeditor,andenterinthefollowingcodesnippet:

@usingBookLibrary

@modelBookItem

<html>

<head>

<linkrel="stylesheet"href="style.css"/>

</head>

<body>

<h1>AddNewBookDetails</h1>

<tableborder="1"cellpadding="8">

<formaction="hybrid:SaveBookDetails"method="GET">

<inputname="id"type="hidden"value="@Model.Id"/>

<tr>

<td>Title:

<inputname="Title"value="@Model.Title"/></td>

<tr>

<td>Author:

<inputname="Author"value="@Model.Author"/></td>

<tr>

<td>BookISBN:

<inputname="ISBN"value="@Model.Isbn"/></td>

<tr>

<td>Synopsis:

<textareaname="Synopsis"rows="5"cols="40">

@Model.Synopsis

</textarea>

</td>

<tr>

<tdcolspan="8">

<inputtype="submit"name="Button"value="Save"/>

<inputtype="submit"name="Button"value="Cancel"/>

</td>

</tr>

</form>

</table>

</body>

</html>

Intheprecedingcodesnippet,wedefinedtheHTMLlayoutinformationthatwillbeusedbyourBookLibraryAddRazortemplate.WefirstlyspecifiedthatweareusingtheBookLibrary.iOSnamespace,sothatwecanhaveaccesstoourdatabasemodelasspecifiedbythe@modeldirective.We'vealreadyexplainedthatthismustbetheveryfirstlineprecedingthe<html>tagwithinaRazortemplatefile.

Next,wesetupa<formactiontagsothatwhentheformgetssubmitted,theSaveorCancelbuttonsarepressed,ourWebViewController.csclasswillbecalled,andtheappropriateactionwilltakeplace.WespecifythehybridtaghereagaintoidentifyiftheURLisnotourowncustomscheme,andwilljustlettheWebViewloadtheURLasusual.

CreatingandimplementingtheBookLibraryEditRazortemplateIntheprevioussection,wecreatedandimplementedtheBookLibraryAddRazortemplatethatwillbeusedtoallowtheusertoaddnewbookentriestotheBookLibraryapplication.OurnextstepistobegincreatingtheRazortemplatethatwillallowtheusertoeditanexistingbookandsavethisbacktoourdatabasemodel.

Let'sbeginbyperformingthefollowingsteps:

1. CreateanemptyclasswithintheViewsfolder,bychoosingAdd|NewFile...,asyoudidwhencreatingtheBookLibraryListingintheprevioussectionentitledCreatingandimplementingthebooklistingmainpage,locatedwithinthischapter.

2. Next,choosethePreprocessedRazorTemplateoption,locatedwithintheTextTemplatingsection,andenterinBookLibraryEdit.

3. Next,clickontheNewbuttontoallowthewizardtoproceedandcreatethenewemptyRazortemplatefile.

OurnextstepistostarttoimplementthecoderequiredforourBookLibraryEditRazortemplate,byperformingthefollowingsteps:

EnsurethattheBookLibraryEdit.cshtmlfileisdisplayedwithinthecodeeditor,andenterinthefollowingcodesnippet:

@usingBookLibrary

@modelBookItem

<html>

<head>

<linkrel="stylesheet"href="style.css"/>

</head>

<body>

<h1>EditBookDetails</h1>

<tableborder="1"cellpadding="8">

<formaction="hybrid:SaveBookDetails"method="GET">

<inputname="id"type="hidden"value="@Model.Id"/>

<tr>

<td>Title:

<inputname="Title"value="@Model.Title"/></td>

<tr>

<td>Author:

<inputname="Author"value="@Model.Author"/></td>

<tr>

<td>BookISBN:

<inputname="ISBN"value="@Model.Isbn"/></td>

<tr>

<td>Synopsis:

<textareaname="Synopsis"rows="5"cols="40">

@Model.Synopsis

</textarea></td>

<tr>

<tdcolspan="8">

<inputtype="submit"name="Button"value="Save"/>

<inputtype="submit"name="Button"value="Cancel"/>

@if(Model.Id>0){

<inputtype="submit"name="Button"value="Delete"/>

}

</td>

</tr>

</form>

</table>

</body>

</html>

Intheprecedingcodesnippet,wedefinetheHTMLlayoutinformationthatwillbeusedbyourBookLibraryEditRazortemplate.WefirstlyspecifythatweareusingtheBookLibrary.iOSnamespace,sothatwecanhaveaccesstoourdatabasemodelasspecifiedbythe@modeldirective,whichandwehavesaidthatthismustbetheveryfirstlineprecedingthe<html>tagwithinaRazortemplatefile.

Next,wesetupa<formactiontag,sothatwhentheformgetssubmittedtheSaveorCancelbuttonsarepressed,ourWebViewController.csclasswillbecalled,andtheappropriateactionwilltakeplace.WespecifythehybridtaghereagaintoidentifyiftheURLisnotourowncustomscheme,andwilljustlettheWebViewloadtheURLasusual.YouwillnoticethatthisimplementationisquitesimilartothatofourBookLibraryAdd.TheonlythingthatthisRazortemplatedoesdifferently,isifwehaveavalueforourbookid,wedisplaytheDeletebuttonsothattheusercanchoosetodeletethebookentry.

Note

TheAndroidversionoftheRazortemplatesareavailableinthecompanionsourcecodeforthisbook.

CreatingandimplementingtheWebViewControllerclassIntheprevioussection,wesuccessfullycreatedandimplementedeachofourRazortemplatesthatwillbeusedbyourBookLibraryapplication.Ournextstepistobeginimplementingthecodeforourapplication,thatwillberesponsibleforinteractingwithourRazortemplates,andhandlingtheactionsassociatedwitheachmodel.ItwilluseourBookDatabaseclass,tohandletheperformanceofallthedatabaseactionsforourBookLibraryapplication.

Let'sbeginbyperformingthefollowingsteps:

1. EnsurethattheWebViewController.csfileisdisplayedwithinthecodeeditorwindow,andthenenterinthefollowinghighlightedcodesections,asshowninthefollowingcodesnippet:

//

//WebViewController.cs

//WebContainerforrepresentingRazorTemplates

withinaWebView

//

//CreatedbyStevenF.Danielon20/10/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingSystem.Linq;

usingFoundation;

usingUIKit;

usingSystem.Collections.Specialized;

namespaceBookLibrary

{

publicpartialclassWebViewController:

UIViewController

{

staticboolUserInterfaceIdiomIsPhone

{

get{return

UIDevice.CurrentDevice.UserInterfaceIdiom

==UIUserInterfaceIdiom.Phone;}

}

2. Next,wedeclareawebViewobjectthatwillpointtoourUIWebViewinstance,andthendeclareourWebViewController()classconstructormethodthatwillbeinstantiatedfromourAppDelegateclass,asyouwillseeoncewebeginimplementingthenecessarychanges.

3. Enterinthefollowinghighlightedcodesections,asshowninthefollowingcodesnippet:

UIWebViewwebView;

publicWebViewController()

{

}

protectedWebViewController(IntPtrhandle):base(handle)

{

//Note:thisconstructorshouldnotcontainany

//initializationlogic.

}

publicoverridevoidViewDidLoad()

{

base.ViewDidLoad();

4. Then,wedefineandspecifythescreendimensionsthatwewouldlikeourwebViewtocontain.Inthiscase,wesetthistotakeupthewholeregionofourscreenandthenaddthistoourexistingviewcontainer.Inthenextstep,wecalltheGetItemsinstancemethodonourBookLibraryDB.Databasetoreturnallexistingbookentrieswithinthedatabase,andassignthistoourmodel.

5. Inthenextstep,wespecifytheBookLibraryListingRazortemplate,andpassinthemodelthatwillbeusedtopopulatetheViewModel.Next,wecalltheGenerateString()methodonourtemplate,toexecutethetemplatewithinthemainapplicationbundleandreturntheoutputasastring,andthenloadthiswithinourwebView,usingtheLoadHtmlStringmethod:

webView=newUIWebView(UIScreen.MainScreen.Bounds);

View.Add(webView);

//InterceptURLloadingtohandlenativecallsfrom

//browser

webView.ShouldStartLoad+=HandleShouldStartLoad;

//RendertheviewtouseourBookList.cshtmlfile

varmodel=BookLibraryDB.Database.GetItems().ToList();

vartemplate=newBookLibraryListing()

{Model=model};

varpage=template.GenerateString();

//LoadtherenderedHTMLintotheviewwithabaseURL

//thatpointstotherootofthebundledResources

//folder

webView.LoadHtmlString(page,NSBundle.MainBundle.BundleUrl);

//Performanyadditionalsetupafterloadingtheview,

//typicallyfromanib.

}

publicoverridevoidDidReceiveMemoryWarning()

{

base.DidReceiveMemoryWarning();

//Releaseanycacheddata,images,etcthataren't

//inuse.

}

boolHandleShouldStartLoad(UIWebViewwebView,NSUrlRequest

request,UIWebViewNavigationTypenavigationType)

{

//IftheURLisnotourowncustomscheme,just

//letthewebViewloadtheURLasusual

conststringscheme="hybrid:";

if(request.Url.Scheme!=scheme.Replace(":",""))

returntrue;

//Thishandlerwilltreateverythingbetweenthe

//protocoland"?"asthemethodname.Thequerystring

//hasalloftheparameters.

varresources=request.Url.ResourceSpecifier.Split('?');

varmethod=resources[0];

varparameters=System.Web.HttpUtility.ParseQueryString(

resources[1]);

6. Next,wecreateaswitchstatementtohandlethetypeofmethodoperationthatweobtainedfromtheRazortemplatedirectlyafterthehybrid:tag,andhandleaccordingly:

switch(method)

{

case"CreateNewBook":

CreateNewBook(webView);

break;

case"EditBookDetails":

EditBookDetails(webView,parameters);

break;

case"SaveBookDetails":

SaveBookDetails(webView,parameters);

break;

default:

//Casesnotcoveredarehandledhere.

break;

}

returnfalse;

}

7. Then,weneedtocreatetheCreateNewBook()instancemethodthatwillberesponsibleforhandlingthecreationofournewbookentry.ThismethodacceptsthenameofthewebViewto

displayitscontentandwespecifytheBookLibraryAddRazortemplate,andpassinthemodelthatwillbeusedtopopulatetheViewModel.Next,wecalltheGenerateString()methodonourtemplate,toexecutethetemplatewithinthemainapplicationbundleandreturntheoutputasastring,andthenloadthiswithinourwebView,usingtheLoadHtmlStringmethod:

///<summary>

///Handlesthecreationofournewbookentry.

///</summary>

///<paramname="webView">Webview.</param>

voidCreateNewBook(UIWebViewwebView,

NameValueCollectionparameters)

{

vartemplate=newBookLibraryAdd()

{Model=newBookItem()};

varpage=template.GenerateString();

webView.LoadHtmlString(page,

NSBundle.MainBundle.BundleUrl);

}

8. Next,weneedtocreatetheEditBookDetails()instancemethodthatwillberesponsibleforhandlingtheeditingofourbookentry.ThismethodacceptsthenameofthewebViewtodisplayitscontentaswellasanyparameters.Next,wespecifytheBookLibraryEditRazortemplate,andpassinthemodelaswellastheidvaluefortheselectedbookentrytobeusedtopopulatetheViewModel.Next,wecalltheGenerateString()methodonourtemplate,toexecutethetemplatewithinthemainapplicationbundleandreturntheoutputasastring,andthenloadthiswithinourwebView,usingtheLoadHtmlStringmethod:

///<summary>

///Handlestheeditingofourbookdetails.

///</summary>

///<paramname="webView">Webview.</param>

///<paramname="parameters">Parameters.</param>

voidEditBookDetails(UIWebViewwebView,

System.Collections.Specialized.NameValueCollectionparameters)

{

varmodel=BookLibraryDB.Database.GetItem(

Convert.ToInt32(parameters["Id"]));

vartemplate=newBookLibraryEdit(){Model=model};

varpage=template.GenerateString();

webView.LoadHtmlString(page,

NSBundle.MainBundle.BundleUrl);

}

9. Then,weneedtocreatetheSaveBookDetails()instancemethodthatwillberesponsibleforhandlingthesavingofourbookentry.ThismethodacceptsthenameofthewebViewtodisplayitscontentaswellasanyparameters.Next,wegrabthenameofthebuttonthatwepressedfromtherelevantRazortemplateanduseaswitchstatementtohandlethetypeofbuttonoperationandhandleaccordingly.Oncetheoperationhascompletedsuccessfully,wereturn,andloadtheBookLibraryListingpagewithinthewebViewcontainer:

///<summary>

///SavesthebookdetailstotheSQLiteBookDetailsDatabase.

///</summary>

///<paramname="webView">Webview.</param>

///<paramname="parameters">Parameters.</param>

voidSaveBookDetails(UIWebViewwebView,

System.Collections.Specialized.NameValueCollectionparameters)

{

//PointstoourEditBookDetailsHTMLpage.

varbutton=parameters["Button"];

switch(button)

{

case"Save":

SaveDetailsToDatabase(parameters);

break;

case"Delete":

DeleteBookDetails(parameters);

break;

case"Cancel":

break;

default:

//Casesnotcoveredarehandledhere.

break;

}

varmodel=BookLibraryDB.Database.GetItems().ToList();

vartemplate=newBookLibraryListing(){Model=model};

webView.LoadHtmlString(template.GenerateString(),

NSBundle.MainBundle.BundleUrl);

}

10. Next,weneedtocreatetheSaveDetailsToDatabase()instancemethodthatwillberesponsibleforhandlingthesavingofourbookentrytotheSQLitedatabase.ThismethodacceptsalistofparametersthathavebeenenteredwithintheBookLibraryAddorBookLibraryEditRazortemplatescreens,andcreatesaBookItemdatabasemodel.ThisthengetspassedtotheSaveItemmethodthatourBookLibraryDBwrapperclasscallstheBookDatabaseclass:

///<summary>

///SavesthebookdetailstoourSQLitedatabase.

///</summary>

///<returns>Thedetailstodatabase.</returns>

///<paramname="parameters">Parameters.</param>

voidSaveDetailsToDatabase(System.Collections.Specialized.NameV

alueCollectionparameters)

{

varbook=newBookItem

{

id=Convert.ToInt32(parameters["Id"]),

title=parameters["Title"],

author=parameters["Author"],

isbn=parameters["Isbn"],

synopsis=parameters["Synopsis"]

};

BookLibraryDB.Database.SaveItem(book);

}

11. Then,weneedtocreatetheDeleteBookDetails()instancemethodthatwillbe

responsibleforhandlingthesavingofourbookentrytotheSQLitedatabase.ThismethodacceptsalistofparametersthathavebeenenteredwithintheBookLibraryEditRazortemplatescreen,andpassestheidofthebookentrytotheDeleteItemmethodthatourBookLibraryDBwrapperclasscallstheBookDatabase:

///<summary>

///HandlewhentheDeletebuttonhasbeenpressed

///</summary>

///<returns>Thebookdetails.</returns>

///<paramname="parameters">Parameters.</param>

voidDeleteBookDetails(System.Collections.Specialized.NameValue

Collectionparameters)

{

BookLibraryDB.Database.DeleteItem(Convert.ToInt32(

parameters["Id"]));

}

}

NowwehavesuccessfullyaddedthenecessarycodetoourWebViewController.csclasstoallowittocommunicatewithourRazortemplateswhenbookentrieshavebeencreated,updated,retrieved,deleted,andsaved.

Note

TheAndroidversionoftheWebViewController.csclassisavailableinthecompanionsourcecodeforthisbook.

12. Next,weneedtoinitializeourbookslibrarySQLitelibrarydatabasewithineachofourplatform-specificstart-upclasses(forexample,AppDelegate.csforiOSandMainActivity.csforAndroid).

13. EnsurethattheAppDelegate.csfileisdisplayedwithinthecodeeditorwindow,andthenenterinthefollowinghighlightedcodesections,asshowninthefollowingcodesnippet:

usingSystem;

usingSystem.IO;

usingFoundation;

usingUIKit;

usingSQLite;

namespaceBookLibrary

{

//TheUIApplicationDelegatefortheapplication.

Thisclass

//isresponsibleforlaunchingtheUserInterfaceofthe

//application,aswellaslistening

(andoptionallyresponding)

//toapplicationeventsfromiOS.

[Register("AppDelegate")]

publicclassAppDelegate:UIApplicationDelegate

{

//class-leveldeclarations

publicoverrideUIWindowWindow

{

get;

set;

}

publicoverrideboolFinishedLaunching(UIApplication

application,NSDictionarylaunchOptions)

{

//Overridepointforcustomizationafterapplication

//launch.Ifnotrequiredforyourapplicationyoucan

//safelydeletethismethod

//createanewwindowinstancebasedonthescreensize

Window=newUIWindow(UIScreen.MainScreen.Bounds);

//Declarethenametouseforourdatabasename

varsqliteFilename="BookLibrary.db";

stringdocumentsPath=Environment.GetFolderPath(

Environment.SpecialFolder.Personal);

stringlibraryPath=Path.Combine(documentsPath,"..",

"Library");

vardatabasePath=Path.Combine(libraryPath,sqliteFilename);

//Setaconnectiontoourdatabase

vardatabaseConn=newSQLiteConnection(databasePath);

BookLibraryDB.SetDatabaseConnection(databaseConn);

//SetourRootViewControllertobetheinstanceof

//ourBookViewControllerclassandmakeitvisible.

Window.RootViewController=newWebViewController();

Window.MakeKeyAndVisible();

returntrue;

}

...

...

...

}

Intheprecedingcodesnippet,webeganbyimportingboththeSQLiteandSystem.IOclasses,andthenmodifiedtheFinishedLaunchingmethodtocreateanewWindowinstance

basedonthescreensizeofthedevice.SincewehavespecifiedthatwewantourprojecttoworkonbothiPadandiPhonedevices,thiswilltakeupthefullscreendimensions.

Next,wedeclaredthenametouseforourdatabasenameandspecifiedthelocationofwheretosavetheBookLibrary.dbdatabaseto,asdeterminedbythedatabasePathstring.Inournextstep,wesetupaconnectiontoourdatabase,andthensettheRootViewControllertobetheinstanceofourBookViewControllerclass,beforefinallymakingthisbecomevisibleonscreen.

Note

TheAndroidversionoftheMainActivity.csclassisavailableinthecompanionsourcecodeforthisbook.

UpdatingthebooklibraryCascadingStyleSheet(CSS)Inthissection,weneedtomakesomefinalchangestothestyles.cssfile.ThisfileisessentiallyaCSS,andanyRazortemplatesthatutilizethisstylesheetwillinheriteverythingthatitcontains.WewillbasicallybemakingsomeminorchangestothebodyorourHTMLpages,applyingpaddingtomargins,settingfontsizes,andcolorsofweblinkswhentheyarepressed.

Let'sbeginbyperformingthefollowingstep:

1. EnsurethattheStyle.cssfileisdisplayedwithinthecodeeditorwindow,andthenenterinthefollowinghighlightedcodesections,asshowninthefollowingcodesnippet:

/*Thisisaminimalstylesheetintendedtodemonstrate

howtoincludestaticcontentinyourhybridapp.

Otherstaticcontent,suchasjavascriptfilesandimages,

canbeincludedinthissamefolder(ResourcesoniOS

orAssetsonAndroid),withthesameBuildAction

(BundleResourceoniOSorAndroidAssetonAndroid),

tobeaccessiblefromapathstartingattheroot

ofyourhybridapplication.*/

#page{

margin-top:10px;

}

html,

body{

margin:7px;

padding:0px;

border:0px;

color:#000;

background:#ffffe0;

}

html,body,p,th,td,li,dd,dt

{

font:1emArial,Helvetica,sans-serif;

}

h1{

font-family:Arial,Helvetica,sans-serif;

font-size:18;

}

a:link{

color:#00f;

}

a:visited{

color:#009;

}

a:hover{

color:#06f;

}

a:active{

color:#0cf;

}

NowthatwehavefinishedbuildingallthenecessarycomponentsforourBookLibraryapplication,ournextstepistofinallybuildandruntheBookLibraryapplicationwithintheiOSsimulator.Whencompilationcompletes,theiOSSimulatorwillappearautomaticallyandtheBookLibraryapplicationwillbedisplayed,asshowninthefollowingscreenshot:

Asyoucanseefromtheprecedingscreenshot,thiscurrentlydisplaysourBookLibraryDatabaseListingpage.Sincewedon'thaveanydetailsaddedyet,thiswillshowupasblank,andyoumustpresstheAddNewBookbuttontodisplaytheAddNewBookDetailsscreen:

TheprecedingscreenshotshowstheupdatedBookLibraryDatabaseListingRazortemplatepageViewModelwiththeinformationsavedfromthepreviousscreen.ClickingontheEditlinkwilldisplaytheEditBookDetailsscreenwithallthepreviouslyenteredbookdetailspopulated.

SummaryInthischapter,youlearnedabouttheRazortemplatingengineandhowyoucanuseittocreateahybridmobilesolution.Youalsolearnedhowtocreateandusemodelswithinyourapplication,andhavetheinformationsavedtoanSQLitedatabase,andretrievedlater.Finally,youlearnedhowyoucanuseJavaScriptcodeusingC#toexecutemethodcalls.

Inthenextchapter,you'lllearnhowtocreateandconsumeaRESTfulwebserviceAPI,sothatitcanbeusedwithintheTrackMyWalksapplicationtosaveandretrieveinformationenteredwithintheViewModels.

Chapter7.IncorporatingAPIDataAccessUsingMicrosoftAzureAppServicesInthepreviouschapter,welearnedabouttheRazortemplatingengineandhowyoucanuseittocreateahybridmobilesolution.Youlearnedhowtocreateandusemodelswithinyourapplication,andhowtosavetheinformationtoaSQLitedatabaseandretrieveitlater.Towardstheendofthechapter,youlearnedhowyoucanuseJavaScriptcodeusingC#toexecutemethodcalls.

Upuntilthispoint,youhavebeenbuildingtheTrackMyWalksappwithstaticwalktrailinformationthathasbeenhard-codedwithintheTrackMyWalksapp.However,intherealworld,itisveryrarethatyourappwilldependpurelyonlocalstaticdata,andyouwillneedtosourceyourinformationfromaremotedatasource,typicallyusingaRESTfulAPI.Insomecases,yourappmayevencommunicatewithathird-partyAPI,forexampleFacebook.

Inthischapter,you'lllearnhowyoucanuseMicrosoftAzureAppservicestocreateyourveryfirstlive,cloud-basedbackendHTTPwebservicetohandleallthecommunicationbetweenthecloudandtheapp.YouwillalsolearnhowtocreateaDataServiceAPIthatwillallowtheapptoconsumetheAPIsothatwecanretrieve,store,anddeletewalktrailinformationfromthecloudallwithintheTrackMyWalksapp.

Thischapterwillcoverthefollowingpoints:

GainanunderstandingofwhatMicrosoftAzureAppservicesareSettinguptheTrackMyWalksappwithintheMicrosoftAzureportalAddingtheHttpClientandJSON.NetNuGetpackagestothesolutionCreatingtheTrackMyWalksbaseHTTPserviceCreatingtheTrackMyWalksAPIdataserviceUpdatingtheTrackMyWalksViewModelstousetheAPIdataserviceRunningtheTrackMyWalksappwithinthesimulator

SettingupourTrackMyWalksappusingMicrosoftAzureInthissection,wewilllookatthestepsrequiredtosetuptheTrackMyWalksapplicationwithinMicrosoftAzure.NearlyallmobileapplicationsthatyouwilldevelopwillrequiretheabilitytocommunicatewithanAPItostore,retrieve,update,anddeleteinformation.ThisAPIcanbeanexistingonethatsomeonewithinyourorganizationhasalreadycreated,butsometimesyouwillneedtocreateyourownAPIforyourapplication.

MicrosoftAzure,or("Azure"asit'sbestknownfor),isessentiallyacloud-basedplatformthatwascreatedbyMicrosoftbackinFebruary2010.Azurewasdesignedforbuilding,deploying,andmanagingseveralapplicationsandtheirassociatedservices,suchasSaaS,PaaS,andIaaS.

EachoftheMicrosoftAzurespecificassociatedservicesareexplainedinthefollowingtable:

Azureservice Description

SaaS SoftwareasaServiceprovidessoftwarelicensinganddeliverymodelswheresoftwareislicensedonasubscriptionbasisandiscentrallyhosted.

PaaSPlatformasaServiceprovidescustomerswithaplatformtodevelop,run,andmanageapplicationswithoutthecomplexitiesofmaintainingtheinfrastructurewhendevelopingandlaunchinganapp.

IaaS InfrastructureasaServiceprovidesvirtualizedcomputingresourcesovertheInternet.

OneofthemainbenefitsofusingMicrosoftAzureMobileAppsisthattheyprovideyouwithaveryquickandeasywaytogetafullyfunctionalbackendserviceupandrunningwithinamatterofminutes.BeforewecanproceedwithsettingupandcreatingourTrackMyWalksdatabasewithinthecloud,youwillneedtohaveaMicrosoftAzureaccount.Ifyoudon'talreadyhaveone,youcancreateoneforfreeathttps://azure.microsoft.com/en-us/pricing/free-trial/.

OnceyouhavecreatedyourMicrosoftAzureaccount,youwillneedtologintotheMicrosoftAzureportalusingyourwebbrowser.Let'slookathowtodothis,byperformingthefollowingsteps:

1. LaunchyourwebbrowserwiththefollowingURLhttps://portal.azure.com/andlogintotheMicrosoftAzureportalusingyourcredentials.

2. Next,fromthemainMicrosoftAzureportaldashboard,clickthe+buttoninthetop-lefthandcornerfromtheNewsection,andselecttheWeb+Mobileoption,andthenchoosethe

MobileAppoptionasshowninthefollowingscreenshot:

3. Then,enterinTrackMyWalkstouseasthenameforourappfortheAppnamefield.4. Next,eitherchooseyourSubscriptiontype,orleavethedefaultofFreeTrial.5. Then,provideanameforyourResourceGroup,eitherbycreatinganewone,orchoosing

fromanexistingone.6. Next,ensurethatthePintodashboardoptionhasbeenselected,sothatyoucanhaveyour

MobileAppdisplayedontheMicrosoftAzureDashboard.Thisisparticularlyuseful,andprovideseasyaccess.

NowthatwehavesuccessfullycreatedourMobileAppwithinMicrosoftAzure,ournextstepistobeginsettingupthedatabasethatwillallowourapptostorewalkentryinformation.Let'slookathowwecanachievethiswiththefollowingsteps:

1. FromtheDashboard,clickontheTrackMyWalksservice,asshowninthefollowingscreenshot:

2. Next,choosetheDataconnectionsoptionfromtheMOBILEsectionundertheTrackMyWalksAppServicesection,asshowninthefollowingscreenshot:

3. Then,withintheTrackMyWalks-Dataconnectionsscreen,clickonthe+Addbutton,todisplaytheAdddataconnectionscreen,asshowninthefollowingscreenshot:

4. Next,ensurethatyouhaveselectedSQLDatabasefromtheTypedropdown,andproceedtoconfigureyourSQLDatabase.

5. Then,clickontheOKbuttontosaveyourchangesandcreatethenewdataconnectionforourTrackMyWalksSQLserverdatabase.

OnceyouhavecreatedyourTrackMyWalksmobileappandSQLdatabasewithinMicrosoftAzure,bydefault,yourdatabasewon'tcontainanydatabasetablesordata.BeforewecanstartcommunicatingwithandconsumingtheAPIwithinourTrackMyWalksapp,weneedtocreateanewtablethatwillstoreourwalktrailentries.

Let'slookatthefollowingstepstoachievethis:

1. FromtheDashboard,clickontheTrackMyWalksservice,thenchoosetheEasytablesoptionfromtheMOBILEsectionundertheTrackMyWalksAppServicesection.

2. Next,withintheTrackMyWalks-Easytablesscreen,clickonthe+Addbutton,todisplaytheAddatablescreen,asshowninthefollowingscreenshot:

3. Then,enterinWalkEntriestouseasthenameforourtablefortheNamefield.4. Next,leavethedefaultpermissionsthathavebeensetforourInsertpermission,Update

permission,Deletepermission,Readpermission,andUndeletepermissiondropdownentries:

5. Then,clickontheOKbuttontosaveyourchanges,andyournewWalkEntriestablewillbeaddedtothelistofEasyTablesentries.

Note

WheneveryouchoosetheAllowanonymousaccesspermissionduringthecreationofyourtable,youareessentiallymakingtheAPIavailablewithoutprovidinganyspecificauthenticationheadersaspartoftheHTTPrequest.

BeforewecanstartmakingcallstoourAPIandconsumingthiswithinourTrackMyWalksapp,we'llrunaquickchecktoseeifourAPIendpointisworkingcorrectly.ThisisachievedbyissuingaGETHTTPrequestusingthecommandline,orifyou'dprefer,youcanuseaRESTconsoleclient.

6. Openyourterminalwindow,andtypeinthefollowingstatementfromthecommandlineasfollows:

Lastlogin:SunNov610:48:41onconsole

GENIESOFT-MAC-Mini:~stevendaniel$curl

https://trackmywalks.azurewebsites.net/tables/

walkentries--header"ZUMO-API-VERSION:2.0.0"

IfyouhaveseteverythingupcorrectlywithintheMicrosoftAzureportal,youshouldreceivebacka200(Success)statuscode,alongwithanemptycollectionintheresponsebodyasfollows:

Lastlogin:SunNov610:48:41onconsole

GENIESOFT-MAC-Mini:~stevendaniel$curl

https://trackmywalks.azurewebsites.net/tables/

walkentries--header"ZUMO-API-VERSION:2.0.0"

[]GENIESOFT-MAC-Mini:~stevendaniel$

Note

ThereareseveralRESTconsoleclientsthatexistforyoutochoosefrom,ifyoudon'talreadyhaveoneinstalled.ItendtousePostmanforhandlingRESTAPIs,whichyoucandownloadfromhttp://www.getpostman.com/.

NowthatwehavesuccessfullycreatedourTrackMyWalksAPIandWalkEntriesdatatablewithintheservice,wecanbeginmakingcallstoourAPIandreceivingthoseresponsemessagesdirectlybackfromtheAPI.Inthenextsection,wewillbegintoaddtheJson.NetandHttpClient.NETFrameworklibrariesthatwillberesponsibleforhandlingtheRESTAPIrequeststosaveandretrieveourwalkentrydetails.

AddingtheJson.NetNuGetpackagetotheTrackMyWalksappNowthatyouhavesetupandcreatedtheTrackMyWalksdatabasewithintheMicrosoftAzureplatform,ournextstepistoaddtheJson.NetNuGetpackagetoourTrackMyWalksPortableClassLibrarysolution.TheJson.Netpackageisahigh-performanceJSONframeworkforthe.NETplatformthatallowsyoutoserializeanddeserializeanytypeof.NETobjectwithhelpoftheJSONserializer.

WhenwestarttoincorporatethisframeworkwithinourTrackMyWalkssolution,wewillhavetheabilityofperformingLINQtoJSONcapabilitiesthatwillenableustocreate,parse,query,andmodifytheJSONstructurethatwereceivebackfromourMicrosoftAzureTrackMyWalksdatabasetable.

Let'slookathowtoaddtheJson.NetNuGetpackagetoourTrackMyWalksPortableClassLibrary,byperformingthefollowingsteps:

1. Right-clickonthePackagesfolderthatiscontainedwithintheTrackMyWalksPortableClassLibrarysolution,andchooseAddPackages...menuoption,asshowninthefollowingscreenshot:

2. ThiswilldisplaytheAddPackagesdialog,enterinJson.Netwithinthesearchdialog,andselecttheJson.Netoptionwithinthelist,asshowninthefollowingscreenshot:

3. Finally,clickontheAddPackagebuttontoaddtheNuGetpackagetothePackagesfolder,containedwithintheTrackMyWalksPortableClassLibrarysolution.

NowthatyouhaveaddedtheJson.NetNuGetpackage,ournextstepistoaddtheHttpClientframeworktoourTrackMyWalksPortableClassLibrary,whichwewillbecoveringinthenextsection.

AddingtheHttpClientNuGetpackagetotheTrackMyWalksappIntheprevioussection,weaddedtheJson.NetNuGetpackagetoourTrackMyWalkssolution.OurnextstepistoaddtheHTTPlibrarytoourTrackMyWalkssolutiontoenableittocommunicatewithanAPIoverHTTP.

Sinceweareusingboth.NETandC#tobuildourXamarin.Formsapplication,wecanleveragealibrarywithinthe.NETFrameworkcalledSystem.Net.Http.HttpClient.ThisHttpClientframeworkprovidesuswithamechanismofsendingandreceivingdatausingstandardHTTPmethods,suchasGETandPOST.

Let'slookathowtoaddtheHttpClientNuGetpackagetoourTrackMyWalksPortableClassLibrary,byperformingthefollowingsteps:

1. Right-clickonthePackagesfolderthatiscontainedwithintheTrackMyWalksPortableClassLibrarysolution,andchoosetheAddPackages...menuoption.Ifyoucan'trememberhowtodothis,youcanrefertothesectionentitledAddingtheJson.NetNuGetpackagetotheTrackMyWalksapplocatedwithinthischapter.

2. ThiswilldisplaytheAddPackagesdialog.Here,enterinHttpwithinthesearchdialog,andselecttheSystem.Net.Httpoption,asshowninthefollowingscreenshot:

3. Finally,clickontheAddPackagebuttontoaddtheNuGetpackagetothePackagesfolder,containedwithintheTrackMyWalksPortableClassLibrarysolution.

NowthatyouhaveaddedboththeJson.NetandSystem.Net.HttpNuGetpackagestooursolution,wecanbeginutilizingtheseframeworklibrariesasweprogressthroughoutthischapter.

UpdatingtheWalkEntriesmodeltousetheJson.NetframeworkInthissection,wewillbeginbyupdatingtheWalkEntriesdatamodeltotakeadvantageofourbackendservicecalls,whenwecreatethese,andthentheWalkDataService.csandWalkWebService.csfileswillcommunicateandinteractwithourMicrosoftAzureTrackMyWalksdatabasetostore,delete,andretrievewalkentryinformation.

Let'snowstarttomodifyandimplementthecoderequiredforourWalkEntriesclassmodel,byperformingthefollowingsteps:

EnsurethattheWalkEntries.csfileisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesections:

//

//WalkEntries.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingNewtonsoft.Json;

namespaceTrackMyWalks.Models

{

publicclassWalkEntries

{

[JsonProperty("id")]

publicstringId{get;set;}

publicstringTitle{get;set;}

publicstringNotes{get;set;}

publicdoubleLatitude{get;set;}

publicdoubleLongitude{get;set;}

publicdoubleKilometers{get;set;}

publicstringDifficulty{get;set;}

publicdoubleDistance{get;set;}

publicUriImageUrl{get;set;}

}

}

Intheprecedingcodesnippet,wehavesuccessfullymodifiedthedatabasemodelthatwillbeusedtostorewalkentryinformationwithinourMicrosoftAzuredatabase.Youwillnoticethatwehavedefineda[JsonProperty("id")]item,aswellasastringpropertynamedIdthatwillserveasauniqueprimarykeyforeachrecordthatwestorewithinthedatabase.WehavealsoupdatedourImageUrlpropertytoincludetheUritypethatwillbeusedtoconverttheURLenteredwithinthewalkentrypage,sothatitisstoredcorrectlywithinthedatabase.

Note

IfyouareinterestedinfindingoutmoreinformationabouttheJsonPropertyandtheNewtonsoft.Jsonclasses,pleaserefertotheJson.NETdocumentationlocatedat

http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Serialization_JsonProperty.htm.

CreatingtheHTTPwebserviceclassfortheTrackMyWalksappIntheprevioussection,wesuccessfullymodifiedtheWalkEntriesdatabasemodelthatwillbeusedbyourTrackMyWalksapplication.ThiswillallowustohavealivebackendservicethatwillenableourapplicationtocommunicateoverHTTPsothatitcansendrequeststotheAPItoretrieve,add,anddeletetrailwalkentries.Inthissection,wewillneedameansforourapptocommunicatewithourAPIoverHTTP,andthereforeitwillrequireanHTTPlibrary.

Sinceweareusing.NETandC#,wecanusealibrarywithinthe.NETFramework,calledSystem.Net.Http.HttpClient.ThisFrameworkprovidesamechanismforallowingourapptosendandreceivedatausingstandardHTTPmethodssuchasGETandPOST.WewillbeginbycreatingabaseserviceclasswithinourTrackMyWalksPortableClassLibrarythatwillberesponsibleforhandlingalltheHTTPcommunicationsforus.

Let'snowstarttoimplementthecoderequiredforourWalkWebServicebase-classmodel,byperformingthefollowingsteps:

1. CreateanemptyclasswithintheServicesfolder,bychoosingAdd|NewFile...,asyoudidinthesectionentitled,CreatingthenavigationserviceinterfacefortheTrackMyWalksappwithinChapter3,NavigatingwithintheMVVMModel-TheXamarin.FormsWay.

2. Then,enterinWalkWebServiceforthenameofthenewclassthatyouwanttocreate,andclickontheNewbuttontoallowthewizardtoproceedandcreatethenewfile.

3. Next,ensurethattheWalkWebService.csfileisdisplayedwithinthecodeeditor,andenterinthefollowingcodesnippet:

//

//WalkWebService.cs

//TrackMyWalksHttpWebServiceClass

//

//CreatedbyStevenF.Danielon30/10/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingSystem.Collections.Generic;

usingSystem.Net.Http;

usingSystem.Text;

usingSystem.Threading.Tasks;

usingNewtonsoft.Json;

namespaceTrackMyWalks.Services

{

publicabstractclassWalkWebService

{

4. Then,weneedtocreateaprotectedasyncmethodcalledSendRequestAsync<T>thatacceptsaUrinamedurlaswellaHttpMethodnamedhttpMethod,andfinallyaDictionary<string,string>objectnamedheaders,aswellasanobjectnamed

requestData,thatwillbeusedtoconstructtheHTTPrequest.Proceedandenterinthefollowingcodesnippet:

protectedasyncTask<T>SendRequestAsync<T>(

Uriurl,HttpMethodhttpMethod=null,

IDictionary<string,string>headers=null,

objectrequestData=null)

{

5. Next,we'llsetuptheresulttothedefault(T)typethatwillreturnadefaultvaluetoaparameterizedtypesincewedon'tknowwhatourresultwillcontainatthispoint.We'llthendeclareourmethodvariabletocontaintheGETHttpMethod.ThiswillbeusedtoreturnthecontentandthencreatearequestvariablethatwillsetupaninstanceofourHttpRequestMessageclassandthenserializeourrequesteddatatoourrequestobjectandreturntheinformationbackinJsonformatasdefinedbytheapplication/jsontype.Proceedandenterinthefollowingcodesnippet:

varresult=default(T);

varmethod=httpMethod??HttpMethod.Get;

varrequest=newHttpRequestMessage(method,url);

//Serializeourrequestdata

vardata=requestData==null?null:

JsonConvert.SerializeObject(requestData);

if(data!=null)

{

//Addtheserializedrequestdatatoourrequest

//object.

request.Content=newStringContent(data,

Encoding.UTF8,"application/json");

}

6. Then,we'llbeginiteratingthroughourheaderscollectiontoaddeachofourspecificheaderstotherequestobjectthatwillbesentalongwiththeHttpRequestMessageclass.Proceedandenterinthefollowingcodesnippet:

//Addeachofthespecifiedheaderstoourrequest

if(headers!=null)

{

foreach(varhinheaders)

{

request.Headers.Add(h.Key,h.Value);

}

}

7. Next,we'llsetupanddeclareahandlervariablethatinstantiatesaninstanceoftheHttpClientHandlerclasswhichisessentiallyaHttpMessageHandlerthatcontainsacommonsetofpropertiesthatworkacrosstheHttpWebRequestAPI.Inthenextstep,we'lldeclareaclientvariablethatinstantiatesourHttpClientclassthatacceptsourhandlervariabletobeginsendingourrequestoverHTTP.

8. Next,we'lldeclareourresponseobjectthatperformsaSendAsyncandacceptsourrequestobject,alongwithourHttpCompletionOption.ResponseContentReadthat

completesafterreadingtheentireresponsecontent.9. Finally,we'llperformacomparisonchecktoseeifwehavesuccessfullyreadourcontent

andhavearesponsecodeof200(Success)returned,beforedeserializingourcontentintoJsonformat,usingtheJsonConvertmethod.Proceedandenterinthefollowingcodesnippet:

//GetaresponsefromourWebService

varhandler=newHttpClientHandler();

varclient=newHttpClient(handler;

varresponse=awaitclient.SendAsync(request,

HttpCompletionOption.ResponseContentRead);

if(response.IsSuccessStatusCode&&

response.Content!=null)

{

varcontent=awaitresponse.Content.

ReadAsStringAsync();

result=JsonConvert.DeserializeObject<T>(content);

}

returnresult;

}

}

}

NowthatwehavesuccessfullycreatedourbaseHTTPserviceclass,wecanbegintousethiswithinourViewModelsaswellasourWalkEntriesdatabasemodel,bycreatingabasesub-classwithinourDataServiceAPIwhichwewillbecoveringinthenextsection.

Note

IfyouareinterestedinfindingoutmoreinformationabouttheHttpClientHandlerandHttpClientclasses,pleaserefertotheMicrosoftdeveloperdocumentationlocatedathttps://msdn.microsoft.com/en-us/library/system.net.http.httpclient(v=vs.118).aspx.

CreatingtheDataServiceAPIfortheTrackMyWalksappIntheprevioussection,wecreatedaWalkWebServiceclassthatprovidesuswithameansofsendingHTTPrequeststoourTrackMyWalksMicrosoftAzuredatabase.Inthissection,wewillbeginbycreatingadataserviceclassthatwillallowustosendandreceiveresponsesbackfromourAPI,inJsonformatwhichwillupdatetheWalkEntriesdatabasemodelsoourapplicationcanusethis.

WewillbeginbycreatingtheinterfaceforourdataservicethatcanbeusedtocommunicatewitheachoftheViewModelsthatourTrackMyWalksapplicationutilizes.Let'snowstarttoimplementthecoderequiredforourIWalkDataServicebase-classmodel,byperformingthefollowingsteps:

1. CreateanemptyinterfacewithintheServicesfolder.Ifyoucan'trememberhowtodothis,youcanrefertothesectionentitledCreatingthenavigationserviceinterfacefortheTrackMyWalksappwithinChapter3,NavigatingwithintheMVVMModel-TheXamarin.FormsWay.

2. Next,enterinIWalkDataServiceforthenameofthenewinterfacethatyouwanttocreate,andclickontheNewbuttontoallowthewizardtoproceedandcreatethefile.

3. Then,ensurethattheIWalkDataService.csfileisdisplayedwithinthecodeeditor,andenterinthefollowingcodesnippet:

//

//IWalkDataService.cs

//TrackMyWalksDataServiceInterface

//

//CreatedbyStevenF.Danielon30/10/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingSystem.Threading.Tasks;

usingSystem.Collections.Generic;

usingTrackMyWalks.Models;

namespaceTrackMyWalks.Services

{

publicinterfaceIWalkDataService

{

Task<IList<WalkEntries>>GetWalkEntriesAsync();

TaskAddWalkEntryAsync(WalkEntriesentry);

TaskDeleteWalkEntryAsync(WalkEntriesentry);

}

}

Intheprecedingcodesnippet,webeginbyimplementingthemethodsthatwillberequiredtoretrieve,update,anddeleteourWalkEntriesinformation.TheGetWalkEntriesAsyncinstance

methodusesagenerictypewhichisusedtorestricttheWalkEntriestouseobjectsoftheIListclass.

TheAddWalkEntryAsyncinstancemethodacceptsanentryparameterthatcontainsthewalkentrydetailstobeaddedoftypeWalkEntries,andourDeleteWalkEntryAsyncinstancemethodacceptsanentryparameterthatneedstobedeletedfromourdatabase.WeusetheTaskclasstoessentiallyhandleallasynchronousoperations,byensuringthattheasynchronousmethodsthatweinitiatewilleventuallyfinish,thuscompletingthetaskinhand.

CreatingtheDataServiceAPIclassfortheTrackMyWalksappIntheprevioussection,wecreatedourdataservicebaseinterfaceclassforourdataservice,andwedefinedseveraldifferentinstancemethodsthatourclasswillbeutilizing.ThiswillessentiallybeusedbyeachofourViewModelsalongwiththeViews(pages).Let'snowstarttoimplementthecoderequiredforourWalkDataServiceclass,byperformingthefollowingsteps:

1. CreateanemptyclasswithintheServicesfolder,bychoosingAdd|NewFile...,asyoudidwhencreatingtheDataServiceinterfaceintheprevioussectionentitledCreatingtheDataServiceAPIfortheTrackMyWalksapplocatedwithinthischapter.

2. Then,enterinWalkDataServiceforthenameofthenewclassthatyouwanttocreate,andclickontheNewbuttontoallowthewizardtoproceedandcreatethenewfile.

3. Next,ensurethattheWalkDataService.csfileisdisplayedwithinthecodeeditor,andenterthefollowingcodesnippet:

//

//WalkDataService.cs

//TrackMyWalksAPIDataServiceClass

//

//CreatedbyStevenF.Danielon30/10/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingSystem.Collections.Generic;

usingSystem.Threading.Tasks;

usingSystem.Net.Http;

usingTrackMyWalks.Models;

usingNewtonsoft.Json;

namespaceTrackMyWalks.Services

{

4. Next,weneedtomodifyourWalkDataServiceclassconstructor,sothatitcaninheritfrombothourWalkWebServicebase-classaswellastheIWalkDataServiceclass:

publicclassWalkDataService:WalkWebService,IWalkDataService

{

5. Next,we'lldeclaretwoprivateclassproperties,_baseUriand_headers.The_baseUripropertywillbeusedtostorethebaseURLandthe_headerspropertyoftheIDictionary<string,string>typethatwillbeusedtostoretheheaderinformationthatwewanttopasstoourWalkWebServiceclass.

6. Thezumo-api-versionisbasicallyaspecialheadervaluethatisusedbytheHTTPclientwhencommunicatingwithMicrosoftAzuredatabases.Proceedandenterinthefollowingcodesnippet.

readonlyUri_baseUri;

readonlyIDictionary<string,string>_headers;

//OurClassConstructorthatacceptstheAzureDatabase

//Uripath

publicWalkDataService(UribaseUri)

{

_baseUri=baseUri;

_headers=newDictionary<string,string>();

_headers.Add("zumo-api-version","2.0.0");

}

7. Next,we'llimplementtheGetWalkEntriesAsyncinstancemethodthatwillretrieveallWalkEntriesthatarecontainedwithinourdatabase.We'lldefineaurlvariablethatconstructsanewUriobjectusingour_baseUriURLandcombiningthiswithourwalkEntriesdatabasetablewithinthetablessectionofourTrackMyWalksAzuredatabaseandcalltheSendRequestAsyncWalkWebServicebase-classinstancemethod.

8. We'llpassintheHttpMethod.GetmethodtypetotellourbaseclassthatwearereadytoretrieveourWalkEntries.Proceedandenterinthefollowingcodesnippet:

//APItoretrieveourWalkEntriesfromourdatabase

publicasyncTask<IList<WalkEntries>>GetWalkEntriesAsync()

{

varurl=newUri(_baseUri,"/tables/walkentries");

returnawaitSendRequestAsync<WalkEntries[]>

(url,HttpMethod.Get,_headers);

}

9. Next,we'llimplementtheAddWalkEntryAsyncinstancemethodthatwilladdwalkentryinformationtothewalkEntriestablecontainedwithinourdatabase.WedefineaurlvariablethatconstructsanewUriobjectusingour_baseUriURLandcombiningthiswiththewalkEntriesdatabasetablewithinthetablessectionofourTrackMyWalksAzuredatabaseandcalltheSendRequestAsyncWalkWebServicebase-classinstancemethod.

10. We'llpassintheHttpMethod.PostmethodtypethatwilltellourbaseclassthatwearereadytosubmitinformationtoourwalkEntriesdatabasetable.Proceedandenterinthefollowingcodesnippet:

//APItoaddourWalkEntryinformationtothedatabase

publicasyncTask<WalkEntries>AddWalkEntryAsync(

WalkEntriesentry)

{

varurl=newUri(_baseUri,"/tables/walkentries");

awaitSendRequestAsync<WalkEntries>(url,HttpMethod.Post,

_headers,entry);

}

11. Next,we'llimplementtheDeleteWalkEntryAsyncinstancemethodthatwillpermanentlydeleteassociatedwalkentryinformationfromourwalkEntriestablecontainedwithinourdatabase.We'lldefineaurlvariablethatconstructsanewUriobjectusingour_baseUriURLandcombiningthiswithourwalkEntriesdatabasetable,andpassintheIdvalueofourselectedwalkentrywithintheWalksPageListView.

12. We'llthencalltheSendRequestAsyncWalkWebServicebaseclassinstancemethod.We'llpassintheHttpMethod.DeletemethodtypethatwilltellourbaseclassthatwearereadytopermanentlydeletethewalkentrywithinourwalkEntriesdatabasetable.Proceedand

enterinthefollowingcodesnippet:

//APItodeleteourWalkEntryfromthedatabase

publicasyncTaskDeleteWalkEntryAsync(WalkEntriesentry)

{

varurl=newUri(_baseUri,string.Format("/tables/walken

tries/{0}",entry.Id));

awaitSendRequestAsync<WalkEntries>(url,HttpMethod.Delete

,_headers);

}

}

}

Intheprecedingcodesnippet,webeganbyimplementingeachoftheinstancemethodsthatwedefinedwithinourIWalkDataServiceinterfaceclass.WeusedtheSendRequestAsyncmethodonourbaseclass,andpassedintheAPIdetails,alongwiththeHttpMethodtype,andthezumo-api-versionheaderinformation.YouwillhavenoticedthatwepassedintheWalkEntriesdatamodelobject.ThisissothattheobjectcanbeserializedandaddedtotheHTTPrequestmessagecontent.

Note

IfyouareinterestedinlearningmoreHTTP,pleaserefertotheHypertextTransferProtocolguidelocatedathttps://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol.

TheHTTPclassexposesseveraldifferenttypesofHTTPmethodsthatareusedbytheHttpMethodclass,whichareexplainedinthefollowingtable.

HTTPmethods Description

GET

ThistypetellstheHttpMethodclassprotocolthatwearereadytorequestmessagecontentoverHTTPtoretrieveinformationfromourAPI,andreturnthisinformationback,basedontherepresentationformatspecifiedwithintheAPI.

POSTThistypetellstheHttpMethodclassprotocolthatwewanttocreateanewentrywithinourtable,asspecifiedwithinourAPI.

DELETEThistypetellstheHttpMethodclassprotocolthatwewanttodeleteanexistingentrywithinourtable,asspecifiedwithinourAPI.

Note

IfyouareinterestedinlearningmoreaboutclientandserverversioninginMobileAppsand

MobileServices,pleaserefertotheMicrosoftAzuredocumentationlocatedathttps://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-client-and-server-versioning/.

Inthenextsection,wewillupdateourWalkBaseViewModelsothatitcanuseourDataServiceAPIclass,andinitializeourMicrosoftAzureTrackMyWalksdatabase.

UpdatingtheWalkBaseViewModeltouseourDataServiceAPIIntheprevioussections,wecreatedtheinterfacesclassesthatwillbeusedbyourWalkWebServiceandWalkDataServiceclasstoenableourTrackMyWalksapplicationtocommunicatewiththedatabasethatisstoredwithintheMicrosoftAzureplatform.

OurnextstepistobeginimplementingthecodethatwillberequiredtomakeaconnectiontoourTrackMyWalksMicrosoftAzuredatabasesothatourappuseslivedatainsteadofthelocal,hard-codeddatathatwecurrentlyhaveinplace.

Let'slookathowwecanachievethis,byfollowingthesteps:

1. EnsurethattheWalkBaseViewModel.csfileisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesectionsasshowninthecodesnippet:

//

//WalkBaseViewModel.cs

//TrackMyWalksBaseViewModel

//

//CreatedbyStevenF.Danielon22/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingSystem.ComponentModel;

usingSystem.Runtime.CompilerServices;

usingSystem.Threading.Tasks;

usingTrackMyWalks.Services;

namespaceTrackMyWalks.ViewModels

{

publicabstractclassWalkBaseViewModel:

INotifyPropertyChanged

{

protectedIWalkNavServiceNavService{

get;privateset;}

bool_isProcessBusy;

publicboolIsProcessBusy

{

get{return_isProcessBusy;}

set

{

_isProcessBusy=value;

OnPropertyChanged();

OnIsBusyChanged();

}

}

2. Next,we'llcreateaIWalkDataServiceinterfacepropertycalled_azureDatabaseService.TiswillstoreandretrievethevalueofourTrackMyWalksAzuredatabaseURL.TheAzureDatabaseServicepropertycontainsboththegetter(get)andsetter(set)implementationsthat.WhenwesettheAzureDatabaseServiceproperty,weassignthisvaluetoour_azureDatabaseServicevariable,andthencallthe

OnPropertyChangedinstancemethodstotelltheViewModelsthatachangehasbeenmade:

IWalkDataService_azureDatabaseService;

publicIWalkDataServiceAzureDatabaseService

{

get{return_azureDatabaseService;}

set

{

_azureDatabaseService=value;

OnPropertyChanged();

}

}

3. Then,we'llneedtomodifytheWalkBaseViewModelclassconstructorandcreateaninstancetoourWalkDataServiceclass,thatinheritsfromtheIWalkDataServiceinterfaceclass,andthenassignthistoourAzureDatabaseServicepropertysothatitcanbeusedthroughouteachofourViewModels:

protectedWalkBaseViewModel(IWalkNavServicenavService)

{

//DeclareourNavigationServiceandAzureDatabaseURL

varWALKS_URL="https://trackmywalks.azurewebsites.net";

NavService=navService;

AzureDatabaseService=newWalkDataService(new

Uri(WALKS_URL,UriKind.RelativeOrAbsolute));

}

publicabstractTaskInit();

publiceventPropertyChangedEventHandlerPropertyChanged;

protectedvirtualvoidOnPropertyChanged([CallerMemberName]

stringpropertyName=null)

{

varhandler=PropertyChanged;

if(handler!=null)

{

handler(this,newPropertyChangedEventArgs(

propertyName));

}

}

protectedvirtualvoidOnIsBusyChanged()

{

//WeareprocessingourWalksTrailInformation

}

}

publicabstractclassWalkBaseViewModel<WalkParam>:

WalkBaseViewModel{

protectedWalkBaseViewModel(IWalkNavServicenavService):

base(navService)

{

}

publicoverrideasyncTaskInit()

{

awaitInit(default(WalkParam));

}

publicabstractTaskInit(WalkParamwalkDetails);

}

}

Intheprecedingcodesnippet,webeginbycreatingaAzureDatabaseServicepropertythatinheritsfromourIWalkDataServiceclass,andthencreateditsassociatedgetterandsetterqualifiers.Next,weupdatedtheWalkBaseViewModelclassconstructortosettheAzureDatabaseServicepropertytoaninstanceofWalkDataServiceclasssothatitcanbeusedthroughouteachofourViewModels.

UpdatingtheWalkEntryViewModeltouseourDataServiceAPINowthatwehavemodifiedourWalkBaseViewModelclasssothatitwillbeusedbyanyViewModelthatinheritsfromthisbaseclass,ournextstepistobeginmodifyingtheWalkEntryViewModelthatwillutilizeourWalkDataServiceclass,sothatanynewwalkinformationthatisenteredwillbesavedbacktoourAzuredatabase.Oncethat'sdonewecanbeginstoringwalkentryinformationwhentheSavebuttonispressed.

Let'slookathowwecanachievethiswiththefollowingsteps:

1. EnsurethattheWalkEntryViewModel.csfileisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesectionsasshowninthecodesnippet:

//

//WalkEntryViewModel.cs

//TrackMyWalksViewModels

//

//CreatedbyStevenF.Danielon22/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem.Threading.Tasks;

usingTrackMyWalks.Models;

usingTrackMyWalks.Services;

usingTrackMyWalks.ViewModels;

usingXamarin.Forms;

usingSystem;

namespaceTrackMyWalks

{

publicclassWalkEntryViewModel:WalkBaseViewModel

{

IWalkLocationServicemyLocation;

string_title;

publicstringTitle

{

get{return_title;}

set

{

_title=value;

OnPropertyChanged();

SaveCommand.ChangeCanExecute();

}

}

...

...

2. Next,locateandmodifytheExecuteSaveCommandinstancemethodtoincludeachecktoseeifourImageUrlfieldcontainsavalue,otherwisestoreanemptyplaceholderimagefortheImageUrlpropertywhentheSaveToolBarItemhasbeenpressed.Proceedandenterinthefollowinghighlightedcodesections:

asyncTaskExecuteSaveCommand()

{

//Checktoseeifweareinthemiddleofprocessing

//arequest.

if(IsProcessBusy)

return;

//InitialiseourWalkEntryModeltostatethatweare

//inthemiddleofupdatingdetailstothedatabase.

IsProcessBusy=true;

//SetupourNewWalkitemmodel

varnewWalkItem=newWalkEntries

{

Title=this.Title,

Notes=this.Notes,

Latitude=this.Latitude,

Longitude=this.Longitude,

Kilometers=this.Kilometers,

Difficulty=this.Difficulty,

Distance=this.Distance,

ImageUrl=(this.ImageUrl==null?

newUri("https://heuft.com/upload/image/4

00x267/no_image_placeholder.png"):

newUri(this.ImageUrl))

};

//UponexitingourNewWalkEntryPage,weneedto

//stopcheckingforlocationupdates

myLocation=null;

3. Then,we'llmakeacalltoourAddWalkEntryAsyncinstancemethod,containedwithintheAzureDatabaseServiceclasstostorethenewlyenteredinformation.We'llincludeareferencetoourNavService.PreviousPagemethod,whichisdeclaredwithintheIWalkNavServiceinterfaceclasstoallowourWalkEntryPagetonavigatebacktothepreviouscallingpage,beforefinallyinitializingourIsProcessBusyindicatortofalsetoinformourViewModelthatwearenolongerprocessing.Proceedandenterinthefollowinghighlightedcodesections:

try

{

//SavethedetailsenteredtoourAzureDatabase

awaitAzureDatabaseService.AddWalkEntryAsync(newWalkItem);

awaitNavService.PreviousPage();

}

finally

{

//Re-InitialiseourProcessBusyIndicator

IsProcessBusy=false;

}

}

//methodtocheckforanyformerrors

boolValidateFormDetails()

{

return!string.IsNullOrWhiteSpace(Title);

}

publicoverrideasyncTaskInit()

{

awaitTask.Factory.StartNew(()=>

{

Title="NewWalk";

Difficulty="Easy";

Distance=1.0;

});

}

}

}

Intheprecedingcodesnippet,wemodifiedtheExecuteSaveCommandinstancemethodtoincludeachecktoseeifourImageUrlfieldcontainsavaluepriortostoringtheinformationwithintheImageUrlproperty.Ifwehavedeterminedthatthispropertyisempty,weproceedtoassignanemptyplaceholderimagefortheImageUrlpropertytoavoidourapplicationperformingunexpectedresults.

Inthenextstep,weattempttomakeacalltoourAddWalkEntryAsyncinstancemethod,containedwithinourAzureDatabaseServiceclasstostorethenewlyenteredinformation.

Next,we'llincludeareferencetoourNavService.PreviousPagemethod,whichisdeclaredwithintheIWalkNavServiceinterfaceclasstoallowourWalkEntryPagetonavigatebacktothepreviouscallingpagewhentheSavebuttonhasbeenpressed.Finally,we'llinitializeourIsProcessBusyindicatortofalsetoinformourViewModelthatwearenolongerprocessing.

UpdatingtheWalksPageViewModeltouseourDataServiceAPIInthissection,wewillproceedtoupdateourWalksPageViewModelViewModeltoreferenceourWalkDataServiceclass.SinceourWalksPageViewModelisusedtodisplayinformationfromourWalkEntriesmodel,wewillneedtoupdatethissothatitretrievesthisinformationfromtheTrackMyWalksdatabase,locatedwithinourMicrosoftAzureplatform,anddisplaythiswithintheListViewcontrol.

Let'slookathowwecanachievethiswiththefollowingsteps:

1. EnsurethattheWalksPageViewModel.csfileisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesectionsasshowninthefollowingcodesnippet:

//

//WalksPageViewModel.cs

//TrackMyWalksViewModels

//

//CreatedbyStevenF.Danielon22/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingSystem.Collections.ObjectModel;

usingSystem.Threading.Tasks;

usingTrackMyWalks.Models;

usingTrackMyWalks.Services;

usingXamarin.Forms;

namespaceTrackMyWalks.ViewModels

{

publicclassWalksPageViewModel:WalkBaseViewModel

{

ObservableCollection<WalkEntries>_walkEntries;

publicObservableCollection<WalkEntries>walkEntries

{

get{return_walkEntries;}

set

{

_walkEntries=value;

OnPropertyChanged();

}

}

...

...

2. Next,locateandmodifytheLoadWalkDetailsinstancemethodtochecktoseeifwearealreadyinthemiddleofprocessingwalktrailitemswithintheListView.Nextwe'llproceedtopopulateourWalkEntriesarraywithitemsretrievedfromourGetWalkEntriesAsyncAzurewebserviceinstancemethodcalltopopulateourWalkEntriesasynchronouslyandusetheawaitkeywordtowaituntiltheTaskcompleted.Finally,we'llinitializeourIsProcessBusyindicatortofalsetoinformourViewModelthatwearenolongerprocessing.Proceedandenterinthefollowinghighlightedcode

sections:

publicasyncTaskLoadWalkDetails()

{

//Checktoseeifwearealreadyprocessingour

//WalkTrailItems

if(IsProcessBusy)

{return;}

//Ifwearen'tcurrentlyprocessing,weneedto

//initialiseourvariabletotrue

IsProcessBusy=true;

try

{

//PopulateourWalkEntriesarraywithitems

//fromourAzureWebService

walkEntries=newObservableCollection<WalkEntries>

(awaitAzureDatabaseService.GetWalkEntriesAsync());

}

finally

{

//Re-initialiseourprocessbusyvaluebacktofalse

IsProcessBusy=false;

}

}

...

...

3. Then,createtheDeleteWalkItemcommandpropertywithinourclass,thatwillbeusedwithinourWalksPagetohandlewheneverweclickonawalkitemwithinourListView.TheDeleteWalkItempropertywillthenrunanaction,whenevertheDeleteoptionhasbeenchosenfromtheActionSheet,todeletethechosenitemasdeterminedbyourtrailDetailstopermanentlyremovetherecordfromourTrackMyWalksdatabaselocatedwithinourMicrosoftAzureplatform.Proceedandenterinthefollowinghighlightedcodesections:

Command_deleteWalkItem;

publicCommandDeleteWalkItem

{

get

{

return_deleteWalkItem

??(_deleteWalkItem=

newCommand(async

(trailDetails)=>

awaitAzureDatabaseService.

DeleteWalkEntryAsync((WalkEntries)trailDetails)));

}

}

}

}

Intheprecedingcodesnippet,wemodifiedourLoadWalkDetailsinstancemethodtochecktoseeifwearealreadyinthemiddleofprocessingwalktrailitemswithintheListView.Then,weproceededtopopulateourWalkEntriesarraywithitemsretrievedfromourGetWalkEntriesAsyncAzurewebserviceinstancemethodcalltopopulateourWalkEntriesasynchronouslyandusetheawaitkeywordtowaituntiltheTaskcompleted.

Inthenextstep,weinitializedtheIsProcessBusyindicatortofalse,toinformourViewModelthatwearenolongerprocessing.

Finally,wecreatedaCommandpropertywithinourclassthatwillbeusedtopermanentlyhandlethedeletionofthechosenwalkentrywithinourListViewandourTrackMyWalksMicrosoftAzuredatabase.

UpdatingtheWalksPagetousetheupdatedViewModelInthissection,weneedtoupdateourWalksPageContentPagesothatitcanreferencetheupdatedchangeswithinourWalksPageViewModel.WewillneedtoapplyadditionallogictohandledeletionsofwalkentryinformationfromourWalkEntriesmodelsothatitcanretrievenewlyupdatedinformationfromourTrackMyWalksdatabaselocatedwithinourMicrosoftAzureplatform,anddisplaythiswithintheListViewcontrol.

Let'slookathowwecanachievethiswithfollowingsteps:

1. EnsurethattheWalksPage.csfileisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesectionsasshowninthecodesnippet:

//

//WalksPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms;

usingTrackMyWalks.Models;

usingTrackMyWalks.ViewModels;

usingTrackMyWalks.Services;

usingTrackMyWalks.DataTemplates;

usingTrackMyWalks.ValueConverters;

usingSystem.Diagnostics;

namespaceTrackMyWalks

{

publicclassWalksPage:ContentPage

{

WalksPageViewModel_viewModel

{

get{returnBindingContextas

WalksPageViewModel;}

}

2. Next,modifythenewWalkItemvariable,whichiswithintheWalksPageclassconstructorandupdatetheTextpropertyforourToolbarItem.Proceedandenterinthefollowinghighlightedcodesections:

publicWalksPage()

{

varnewWalkItem=newToolbarItem

{

Text="Add"

};

//SetupourBindingclickeventhandler

newWalkItem.SetBinding(ToolbarItem.CommandProperty,

"CreateNewWalk");

//AddtheToolBaritemtoourToolBar

ToolbarItems.Add(newWalkItem);

//DeclareandinitialiseourModelBindingContext

BindingContext=newWalksPageViewModel(DependencyService

.Get<IWalkNavService>());

...

...

3. Then,weneedtomodifyourwalksList.ItemTappedmethodwheneveranitemwithintheListViewhasbeenselected.Here,weneedtodisplayaselectionofchoicesfortheusertochoosefrom,usingtheDisplayActionSheetmethod.WhentheuserchoosestheProceedWithoption,theuserwillbenavigatedtotheWalksTrailpagewithinourViewModel,andpassintheitemthathasbeenselected.Alternatively,iftheuserchoosestheDeletebutton,acallismadetoourDeleteWalkItemcommandthatisincludedwithinourWalksPageViewModelclass,sothatitcanthenpermanentlydeletetheWalkEntryfromourTrackMyWalksAzuredatabase.Oncethewalkentryhasbeendeletedfromthedatabase,theuserwillreceiveapop-upnotificationtellingthemthattheitemhasbeendeleted.Proceedandenterinthefollowinghighlightedcodesections:

//InitializeoureventHandlertousewhenthe

//itemistapped

walksList.ItemTapped+=async(objectsender,

ItemTappedEventArgse)=>

{

//Gettheselecteditembytheuser

varitem=(WalkEntries)e.Item;

//Checktoseeifwehaveavalueforouritem

if(item==null)return;

//Displayanactionsheetwithchoices

varaction=awaitDisplayActionSheet("TrackMyWalks

-TrailDetails","Cancel","Delete",

"ProceedWith"+item.Title+"Trail");

if(action.Contains("Proceed"))

{

_viewModel.WalkTrailDetails.Execute(item);

}

//IfwehavechosenDelete,deletetheitemfrom

//ourdatabaseandrefreshtheListView

elseif(action.Contains("Delete"))

{

_viewModel.DeleteWalkItem.Execute(item);

awaitDisplayAlert("TrackMyWalks-TrailDetails",

item.Title+

"hasbeendeletedfromthedatabase.","OK");

await_viewModel.Init();

}

//Initialiseouritemvariabletonull

item=null;

};

...

...

}

}

Intheprecedingcodesnippet,wemodifiedournewWalkItemvariable,whichiswithintheWalksPageclassconstructorandupdatedtheTextpropertyforourToolbarItem.

Next,wemodifiedourwalksList.ItemTappedmethodtohandlesituationswhenanitemhasbeenselectedfromtheListView,whichwilldisplayaselectionofchoicesfortheusertochoosefrom.WeaccomplishedthisbyusingtheDisplayActionSheetmethod.WhentheuserchoosestheDeletebutton,acallismadetoourDeleteWalkItemcommandthatisincludedwithinourWalksPageViewModelclass,sothatitcanthenpermanentlydeletethewalkentryfromourTrackMyWalksAzuredatabase,andusetheawaitkeywordtowaituntiltheTaskhascompletedbeforedisplayingapop-upnotificationtellingthemthattheitemhasbeendeleted.

UpdatingthecustompickerrendererclassfortheiOSplatformNowthatwehavemodifiedourWalkEntriesdatabasemodel,wewillneedtoupdatetheDifficultyPickerCellRendererclasswhichwillbeusedbyouriOSportionoftheDifficultyPickerEntryCellclass.

ThiscustompickerwillbeusedtoobtaintheitemchosenfromthecustomlistofentriesdefinedwithintheDifficultyPickerclassandstorethiswithintheDifficultypropertythatwillthenbewrittentoourTrackMyWalksMicrosoftAzuredatabase.

Let'slookathowwecanachievethiswiththefollowingsteps:

1. OpentheTrackMyWalks.iOSprojectlocatedwithinourTrackMyWalkssolution,andexpandtheRenderersfolder.

2. Next,selecttheDifficultyPickerCellRenderer.csfileandensurethatitisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesectionsinthecodesnippet:

//

//DifficultyPickerCellRenderer.cs

//TrackMyWalksCustomRendererforUIPickerViewEntryCells

(iOS)

//

//CreatedbyStevenF.Danielon01/10/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms.Platform.iOS;

usingUIKit;

usingTrackMyWalks.Controls;

usingXamarin.Forms;

usingTrackMyWalks.iOS.Renderers;

[assembly:ExportRenderer(typeof(

DifficultyPickerEntryCell),

typeof(DifficultyPickerCellRenderer))]

namespaceTrackMyWalks.iOS.Renderers

{

publicclassDifficultyPickerCellRenderer:

EntryCellRenderer

{

publicoverrideUITableViewCellGetCell(

Cellitem,UITableViewCellreusableCell,

UITableViewtv)

{

varcell=base.GetCell(item,reusableCell,tv);

varentryPickerCell=(EntryCell)item;

UITextFieldtextField=null;

if(cell!=null)

textField=

(UITextField)cell.ContentView.Subviews[0];

//CreateouriOSUIPickerViewNativeControl

vardifficultyPicker=newUIPickerView

{

AutoresizingMask=

UIViewAutoresizing.FlexibleWidth,

ShowSelectionIndicator=true,

Model=newDifficultyPickerModel(),

BackgroundColor=UIColor.White,

};

3. Next,weneedtomodifytheEntryCellRendererGetCellmethodsothatitcanupdatetheDifficultypropertyfortheEntryCellwearecurrentlyonwhentheDonebuttonhasbeenpressed.ItwillupdateitwiththevaluefromthedifficultyPickerobjectandthendismissthecustompickercontrol.Proceedandenterinthefollowinghighlightedcodesections:

//Createatoolbarwithadonebuttonthatwill

//settheselectedvaluewhenclosed.

vardone=newUIBarButtonItem("Done",

UIBarButtonItemStyle.Done,(s,e)=>

{

//UpdatetheDifficultypropertyontheCell

if(entryPickerCell!=null)

entryPickerCell.Text=DifficultyPickerModel.

difficulty[difficultyPicker.

SelectedRowInComponent(0)];

//UpdatethevalueoftheUITextFieldwithinthe

//Cell

if(textField!=null)

{

textField.Text=DifficultyPickerModel.difficulty

[difficultyPicker.SelectedRowInComponent(0)];

textField.ResignFirstResponder();

}

});

...

...

}

}

}

NowthatwehaveappliedthecodechangesrequiredtotheDifficultyPickerCellRendererclassforouriOSportionofourTrackMyWalksapp,thenextstepistomakechangestoourWalkEntryContentPagesothatitwillretrievethecorrectdifficultyvaluethatisreturnedfromourcustompicker,andtheDifficultypropertyvalue.

UpdatingtheWalksEntryPagetousetheupdatedcustompickerIntheprevioussection,wemodifiedourDifficultyPickerCellRendererclass,aswellasdefiningthevariousmethodsthatwillhandlethedisplayoftheUIPickerViewcontrolwhenanEntryCellwithintheViewModelhasbeentapped.

Inthissection,we'lllookatmakingthenecessarycodechangesrequiredsothatourWalkEntryPageContentPagecorrectlyretrievesthelevelofdifficultychosenfromourcustomUIPickerViewDifficultyPickerCellRendererclass.

Let'slookathowwecanachievethiswiththefollowingsteps:

1. EnsurethattheWalkEntryPage.csfileisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesectionsasshowninthecodesnippet:

//

//WalkEntryPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms;

usingTrackMyWalks.Services;

usingTrackMyWalks.Controls;

namespaceTrackMyWalks

{

publicclassWalkEntryPage:ContentPage

{

WalkEntryViewModel_viewModel

{

get{returnBindingContextasWalkEntryViewModel;}

}

publicWalkEntryPage()

{

//SettheContentPageTitle

Title="NewWalkEntry";

//DeclareandinitialiseourModelBindingContext

BindingContext=newWalkEntryViewModel(

DependencyService.Get<IWalkNavService>());

//DefineourNewWalkEntryfields

varwalkTitle=newEntryCell

{

Label="Title:",

Placeholder="TrailTitle"

};

walkTitle.SetBinding(EntryCell.TextProperty,"Title",

BindingMode.TwoWay);

...

...

2. Next,locateandmodifythewalkDifficultyEntryCellpropertysothatitcancorrectlyreturnthevalueoftheDifficultypropertyfromourWalkEntriesViewModel,thatisupdatedbyourDifficultyPickerEntryCellclass.Proceedandenterinthefollowinghighlightedcodesections:

varwalkDifficulty=newDifficultyPickerEntryCell

{

Label="DifficultyLevel:",

Placeholder="WalkDifficulty"

};

walkDifficulty.SetBinding(EntryCell.TextProperty,

"Difficulty",BindingMode.TwoWay);

...

...

}

}

}

Inthissection,welookedatthestepsinvolvedinmodifyingourWalkEntryPagesothatitcorrectlyreturnsthelevelofdifficultythathasbeenchosenfromourDifficultyPickerEntryCellclasscustomrenderer.WelookedatupdatingourwalkDifficultyobjectvariabletoreferencetheDifficultyPickerEntryCellclass,andupdatedthesetBindingtoreturnthevalueoftheDifficultypropertythatisimplementedwithintheWalkEntriesViewModelclass.

NowthatwehavefinishedbuildingallthenecessarycomponentsforourTrackMyWalksapplication,thenextstepistofinallybuildandruntheTrackMyWalksapplicationwithintheiOSsimulator.Whencompilationcompletes,theiOSSimulatorwillappearautomaticallyandtheTrackMyWalksapplicationwillbedisplayed,asshowninthefollowingscreenshot:

Asyoucanseefromtheprecedingscreenshot,thiscurrentlydisplaysourActivityIndicatorspinnercontrol,withtheassociatedLoadingTrailWalks...text,afterwhichthiswilldisplaytheListViewthatwillcontainourlistoftrailwalksfromourDataTemplatecontrol.Sincewedon'thaveanywalkentriescontainedwithinourMicrosoftAzureTrackMyWalksdatabase,theNewWalkEntryscreendisplaysentriesbeingentered:

Theprecedingscreenshot,showstheupdatedListViewcontroldisplayinginformationfromourTrackMyWalksMicrosoftAzuredatabase.Youwillnotice,thatuponselectingaWalkentryitemfromtheListViewcontrol,itwillpopupwithseveralchoicesforyoutochoosefrom.IfyouclickontheDeletebutton,itwillcalltheDeleteWalkEntryAsyncAPIandpassintheIdentifierfortheselecteditemthatistobepermanentlydeletedfromthedatabase.

Uponsuccessfuldeletion,youwillbepresentedwithadialogboxtellingyouthatthewalkentryhasbeendeleted.WhenclickingontheOKbutton,theListViewcontrolwillberefreshedanddisplayallentries,exceptfortheonethatyouhadjustdeleted.Alternatively,ifyouclickontheProceedWith...button,itwillnavigatetothewalksTrailDetailspagewhereyoucanbeginyourtrail,byclickingontheBeginthisTrailbutton.

SummaryInthischapter,youlearnedaboutMicrosoftAzureAppservicesandhowyoucanusethisplatformtogetinformationfromaremotedatasourcebycreatingyourveryfirstAPIwithinthecloudtoconnectto,andstoreandretrieveinformationfrom,allwithintheTrackMyWalksapp.Youlearnedhowtocreatealive,cloud-basedbackendserviceandAPIusingtheMicrosoft'sAzureAppservicesplatformtostoreandretrieveWalkEntryinformation,aswellascreatingaDataServiceclassthatwillbeusedtohandleallthecommunicationbetweenthecloudandtheTrackMyWalksapp.

Inthenextchapter,you'lllearnhowtocreateasign-inpagethatwillallowtheusertosignintotheTrackMyWalksappusingtheirFacebookcredentials.You'lllearnhowyoucantakeadvantageoftheFacebookSDKandessentiallypostwalkdatatoyourFacebookprofilepagesoyoucanshowoffyourprogresstoyourfriendsand/orworkcolleagues.

Chapter8.MakingOurAppSocial-UsingtheFacebookAPIInthepreviouschapter,youlearnedhowyoucanuseMicrosoftAzureAppservicestocreateyourveryfirstlive,cloud-based,backendHTTPwebservicetohandleallthecommunicationbetweenthecloudandourapp.Now,youwillalsolearnhowtocreateaDataServiceAPIthatwillallowourapptoconsumetheAPI,sothatwecanhavetheabilitytoretrieve,store,anddeletewalktrailinformationfromthecloud,allwithintheTrackMyWalksapp.

OnMay24th,2007,MarkZuckerbergannouncedtheFacebookplatform,adevelopmentplatformforprogrammerstocreatesocialapplicationswithinFacebook.WhenFacebooklaunchedthedevelopmentplatform,numerousapplicationshadbeenbuiltandalreadyhadmillionsofusersplayingthem.ThesocialnetworkingapplicationutilizestheFacebookcollectionofAPIsthatenablesdeveloperstoconnecttotheFacebookplatformandsendapplicationrequests.

Inthischapter,you'lllearnhowyoucanusebothXamarin.AuthandFacebookSDK,whichwillallowyoutoincorporatesocialnetworkingfeatureswithintheTrackMyWalksapptoobtaininformationaboutaFacebookuser,aswellaspostinformationtotheirFacebookwall.

You'lllearnhowtocreateasign-inpagethatwillallowtheusertosignintotheTrackMyWalksappusingtheirFacebookcredentials.YouwillalsocreateaFacebookApiUserclassthatwillbeusedtostoreinformationaboutthelogged-inuser,aswellasusingtheOpenGraphAPItoretrievecertaininformationabouttheuseranddisplaythiswithintheTrackMyWalksapp.Toendthischapter,youwillseehowyoucanleveragetheFacebooklibrary,essentiallytopostwalkdatatoyourFacebookprofilepage,soyoucanshowoffyourprogresstoyourfriendsand/orworkcolleagues.

Thischapterwillcoverthefollowingpoints:

SettingupourTrackMyWalksappwithintheFacebookDeveloperportalAddingtheXamarin.AuthandFacebookSDKpackagestothesolutionCreatingtheFacebookApiUserandFacebookCredentialsclassCreatingtheFBSignInPageandFBSignInPageRendererclassesUpdatingtheTrackMyWalksViewModelstousetheFacebookAPIUsingtheOpenGraphAPItoreadJSONdataRuntheTrackMyWalksappwithinthesimulator

SettingupandregisteringtheTrackMyWalksappwithFacebookInthissection,wewillbeginbysettingupourTrackMyWalksappandregisteringitwiththeFacebookplatform,sothatwecanbegincommunicatingandinteractingwithFacebook,andhavetheabilitytoretrieveuserinformation,aswellasallowingtheusertopostwalkinformationtotheirFacebookwall.

Let'snowstarttoimplementthecoderequiredforourFacebookApiUserclassmodel,byperformingthefollowingsteps:

1. Launchyourwebbrowserandtypeinhttps://developers.facebook.com/apps.2. Next,eithersignupforFacebookifyouarenotaregistereduser,orenteryourFacebook

accountcredentials.3. Then,clickontheCreateaNewAppbutton,whichwilldisplaytheCreateaNewAppID

screen,asshowninthefollowingscreenshot:

4. Next,enterTrackMyWalksfortheDisplayNamefieldandprovideyourContactEmailaddresssothatFacebookcancontactyouiftheyneedto.

5. Then,withintheCategorysection,selectacategoryfromtheChooseaCategorydropdown,andclickontheCreateAppIDbuttontocreateourTrackMyWalksapp,asshownintheprecedingscreenshot.

6. Next,youwillbepromptedtoentertheSecurityCheckanswerbeforeyoucanproceedtothenextstep:

7. Then,enterthetextdisplayedonyourscreenandclickontheSubmitbuttontocontinue.

Note

Ifyouenterthetextincorrectly,youmayendupwithyouraccountbeingblocked.Ifthisisthecase,youwillneedtocontactFacebookdirectlytohavethisunblocked.

NowthatwehavecreatedourTrackMyWalksFacebookAppID,ournextstepistobeginsettinguptheClientOAuthSettings,whichwillbeusedbyourOAuth2AuthenticatorclasswithinourTrackMyWalksapp:

8. Next,fromtheValidOAuthredirectURIssectionlocatedwithintheClientOAuthSettingscreen,enterhttps://www.facebook.com/connect/login_success.htmlastheURLtousewheneverwedetectthatwehavesuccessfullysignedintoFacebookfromwithinourapp.

9. Then,clickontheSaveChangesbuttontosaveourchangeswithinthisscreen.10. Next,choosetheAppReviewmenuitemlocatedundertheDashboard,ascanbeseeninthe

followingscreenshot:

11. Then,ensurethatyouhavechosenYesfortheMakeTrackMyWalkspublic?question.

Note

Essentially,theAPPIDisanimportantfieldthatwewillusewithinouriOSandAndroidapplicationtocommunicatewithFacebook.

WheneveryouenabletheMakeTrackMyWalkspublic?option,thiswillmakeyourapplivetothepubliconFacebook,sothatyourfriendsandfamilycanseeyourwalkinformationpostedonyourFacebookwall.Youwillnoticethat,whenyouenablethisoption,thelistofApprovedItemswillbeenabledbydefault,aswellastheirloginpermissions.

Note

Ifyouareinterestedinlearningmoreaboutthevarioustypesofloginpermissions,pleaserefertothePermissionsReference-FacebookLoginwhichcanbeaccessedathttps://developers.facebook.com/docs/facebook-login/permissions.

NowthatwehavesuccessfullysetupourTrackMyWalksappnamewithintheFacebookplatform,wecanbeginmakingourappcommunicatewiththeFacebookAPIstoobtainuserinformation,aswellaspostingmessagestothecurrentlylogged-inuser'sFacebookwall.Inournextsection,we

willbegintoaddtheXamarin.Auth.NETFrameworklibrary,aswellastheFacebookSDK,toconnectourTrackMyWalksapptoFacebookandauthenticateuserswithFacebook,sothatwecanpoststatusmessagesdirectlyfromwithinourappandmore.

AddingtheXamarin.AuthNuGetpackagetotheTrackMyWalksappNowthatwehavesetupourFacebookAppID,ournextstepistoaddtheXamarin.AuthNuGetpackagetoourTrackMyWalksPortableClassLibraryproject.TheXamarin.AuthpackagewillallowourapptoauthenticateauserwhorequiresaccesstousetheFacebookplatformbyusingOAuth2.0authentication.

TheseAuthenticatorsareresponsibleformanagingtheuserinterfaceandcommunicatingwithauthenticationservices.Authenticatorstakeavarietyofparameters;inthiscase,theapplication'sID,itsauthorizationscope,aswellasFacebook'svariousservicelocationsarerequired.

Let'slookathowtoaddtheXamarin.AuthNuGetpackagetoourTrackMyWalksPortableClassLibrarybyperformingthefollowingsteps:

1. Right-clickonthePackagesfolder,whichiscontainedwithintheTrackMyWalksPortableClassLibraryproject,andchoosetheAddPackages...menuoption,asshowninthefollowingscreenshot:

2. ThiswilldisplaytheAddPackagesdialog.EnterXamarin.AuthwithinthesearchdialogandselecttheXamarin.Authoptionwithinthelist,asshowninthefollowingscreenshot:

3. Finally,clickontheAddPackagebuttontoaddtheNuGetpackagetothePackagesfolder,whichiscontainedwithintheTrackMyWalksPortableClassLibraryproject.

NowthatwehaveaddedtheXamarin.AuthNuGetPackage,ournextstepistoaddtheFacebookFrameworktoourTrackMyWalksPortableClassLibrary,whichwewillbecoveringinthenextsection.

AddingtheFaceBookSDKlibrarytotheTrackMyWalksappIntheprevioussection,weaddedourXamarin.AuthNuGetpackagetoourTrackMyWalkssolution;thismeansthatournextstepistoaddtheFacebooklibrarytoourTrackMyWalkssolution.

Sinceweareusingboth.NETandC#tobuildourXamarin.Formsapplication,wecanleveragealibrarydevelopedbyacompanycalledTheOutercurveFoundation.ThislibraryisessentiallyaFacebookSDKthathelps.NETdevelopersbuildapplicationsthatintegratewithFacebook.

TheFacebookSDKframeworkcontainsallthemethodobjectsandAPIsthatarerequiredtoenableyoutointeractwithFacebookandsendnotificationrequests,orsimplypostmessagestothecurrentuser'swallpageusingthesinglesign-onfeatureofFacebookSDK.ThissimplyletsyouruserssignintoyourappusingtheirFacebookidentity,andwilldisplayaninlinedialogboxcomprisingaWebViewcontainerinwhichtheauthorizationUIwillbeshowntotheuser,whichrequiresthemtoentertheircredentialstogainaccesstoyourapp.

Let'slookathowwecanaddtheFacebookSDKlibrarytoourTrackMyWalksPortableClassLibrarybyperformingthefollowingsteps:

1. Launchyourwebbrowser,andtypethefollowingURL,https://components.xamarin.com/view/facebook-sdk,andlogintotheXamarinportalusingyourXamarincredentials.

2. Next,fromtheFacebookSDKsection,ensurethatyouhaveselectedversion6.2.2asthelatestversiontodownloadfromundertheVersionssection.

3. Then,proceedtoclickontheDownloadbuttontodownloadtheFacebookSDKlibrary,asshowninthefollowingscreenshot:

Note

OnceyouhavedownloadedtheFacebookSDK,extracttheZiparchivepackagecontents.Thedefaultdownloadlocationis~/Downloads/facebook-sdk-6.2.2.zip.

4. Next,right-clickontheReferencesfolder,whichiscontainedwithintheTrackMyWalksPortableClassLibraryproject,andchoosetheEditReferences...menuoption,asshowninthefollowingscreenshot:

5. Then,ensurethatthe.NetAssemblytabhasbeenselected,andclickontheBrowsebuttontochoosetheFacebook.dllforeithertheAndroidoriOSplatform:

6. Finally,ensurethatyouhaveselectedtheFacebook.dllassemblywithinthe.NetAssemblytab,clickontheOKbuttonaddthenewassemblytothereferencessectionofyourTrackMyWalksPortableClassLibrary,andclosetheEditReferencesdialog.

IncorporatingandusingtheFacebookSDKwithinyourapplicationsallowsyoutodowhatisdescribedinthefollowingtable:

FACEBOOKSDKTypes Description

Authenticationandauthorization

ThispromptsyouruserstosignintoFacebookandgrantpermissionstoyourapplication.

MakeAPIcalls

Thisallowsyoutofetchuser-profiledata,aswellasanyinformationthatrelatestotheuser'sfriends,usingtheJSONAPIcalls.

Displaydialog

ThisallowsyoutointeractwiththeuserviaaWebViewcontainerobject,whichisextremelyusefulforenablinginteractionswithFacebook,withouttheneedforrequiringupfrontpermissions.

NowthatwehaveaddedboththeXamarin.AuthNuGetpackageandFacebookDynamic-LinkLibrary(DLL)packagestooursolution,wecannowbeginutilizingtheseframeworklibrariesasweprogressthroughoutthischapter.

CreatingaFacebookusermodelfortheTrackMyWalksappIntheprevioussection,weaddedbothofourXamarin.AuthandFacebookSDK.NETAssemblypackagestoourTrackMyWalksPortableClassLibrary.ThiswillessentiallybeusedbyeachofourViewModelsalongwiththeViews(pages).

Inthissection,wewillbeginbycreatingourFacebookApiUserdatamodel,whichwillbeusedtostoreourFacebooklogininformationfromwhenwecreateourbackendservicecalls,andthentheFacebookCredentials.csandFBSignInRenderer.csfileswillcommunicateandinteractwithourFacebookTrackMyWalksAppIDtoretrieveFacebookrelatedinformation,aswellasallowingtheusertopostwalkinformationtotheirFacebookwall.

Let'snowstarttoimplementthecoderequiredforourFacebookApiUserclassmodelbyperformingthefollowingsteps:

1. CreateanemptyclasswithintheModelsfolderbychoosingAdd|NewFile....Ifyoucan'trememberhowtodothis,youcanrefertothesectionentitledCreatingtheTrackMyWalksModelwithinChapter1,CreatingtheTrackMyWalksNativeApp.

2. Then,enterFacebookApiUserforthenameofthenewclassthatyouwanttocreate,andclickontheNewbuttontoallowthewizardtoproceedandcreatethenewfile.

3. Next,ensurethattheFacebookApiUser.csfileisdisplayedwithinthecodeeditor,andenterthefollowingcodesnippet:

//

//FacebookApiUser.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon07/11/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingXamarin.Auth;

usingNewtonsoft.Json.Linq;

namespaceTrackMyWalks.Facebook

{

4. Next,weneedtoimplementtheFacebookApiUserclassthatwillcontainthevariouspropertymethodsusedtostorethecurrentlylogged-inuser,theirFacebookId,andthepropertiesthatwillbeusedtostoreandretrievetheuser'sFacebookdetails.Toproceed,enterthefollowingcodesnippet:

publicclassFacebookApiUser

{

//Storethecurrentlyloggedinuser

publicstaticboolIsLoggedIn

{

get{return!string.IsNullOrWhiteSpace

(FaceBookApiAuthToken.GetAuthToken);

}

}

//DefineourFacebookIdproperty

publicstaticstringFacebookId

{

get{return"<YOUR_FACEBOOK_ID>";}

}

//Retrieveouruserdetails

staticJObject_userDetails;

publicstaticJObjectGetUserDetails

{

get{return_userDetails;}

}

//Storeouruserdetails

publicstaticvoidSaveUserDetails(JObjectuserDetails)

{

_userDetails=userDetails;

}

}

5. Next,weneedtoimplementtheFacebookApiAuthTokenclassthatwillcontainthevariouspropertymethodsthatwillbeusedtostoreandretrieveourFacebookauthenticationTokenonsuccessfullyloggingintoourTrackMyWalksapp.ThesepropertieswillbeusedthroughoutourapplicationtoretrieveourFacebookuserdetails,andwhenwepostwalkinformationtoourFacebookwall.Toproceed,enterthefollowingcodesnippet:

//FacebookAPIauthenticationToken

publicclassFacebookApiAuthToken

{

//PropertytopointtotheApiuser

publicFacebookApiUserUser{get;set;}

//GetourFacebookauthenticationToken

staticstring_authToken;

publicstaticstringGetAuthToken

{

get{return_authToken;}

}

//StoreourauthenticationToken

publicstaticvoidStoreAuthToken(stringauthToken)

{

_authToken=authToken;

}

//GetourFacebookauthenticationAccountDetails

staticAuthenticatorCompletedEventArgs_authAccount;

publicstaticEventArgsGetAuthAccount

{

get{return_authAccount;}

}

//StoreourFacebookauthenticationAccountDetails

publicstaticvoidStoreAuthAccount

(AuthenticatorCompletedEventArgsauthAccount)

{

_authAccount=authAccount;

}

}

}

Intheprecedingcodesnippet,webeginbyimplementingthevariouspropertymethodsthatwillberequiredtohandlecommunicationbetweenourTrackMyWalksappandFacebook,toallowourapptosuccessfullylogin.TheFacebookApiUserclassmethodisresponsibleforhandlingtheinformationrelatingtotheFacebookuserwhowillbeloggingin.ItcontainsapropertycalledIsLoggedInthatwillbeusedthroughoutourapptodetermineiftheuserhasloggedin;thisisdeterminedbycheckingtoseeifwehavereceivedavalidauthenticationtokenbackfromFacebook.

TheFacebookIdpropertyisessentiallytheuser'sFacebookId.YouwillneedtoreplacethiswithyourownFacebookIDsothatyoucanpostwalktrailinformationtoyourFacebookwall.TheGetUserDetailsandSaveUserDetailspropertiesareusedtostoretheuserinformationthatwillbedisplayedwithintheDistanceTravelledpage.TheFaceBookApiAuthTokenmethodcontainsthevariouspropertiesthatwillbeusedtohandlethestoringoftheusertokendetails,whenourappdeterminesthatwehavesuccessfullybeenauthenticatedwithFacebook.

CreatingaFacebookCredentialsclassfortheTrackMyWalksappInourprevioussection,wecreatedourFacebookApiUserclassmodel,whichwillprovideuswithamechanismforstoringourFacebookcredentials,thatwecanusethroughoutourTrackMyWalksapp.

Inthissection,wewillbeginbycreatingaFacebookCredentialsclassthatwillallowustomakeAPIcalls,sothatwecanretrieveuserprofileinformationbackfromourAPI,inJSONformat,andstorethisinformationtobeusedlater.OurFacebookCredentialsclasscontainsamethodthatallowsourapptopostwalktrailinformationtotheuser'sFacebookpagewall.

Let'snowstarttoimplementthecoderequiredforourFacebookCredentialsclassbyperformingthefollowingsteps:

1. CreateanemptyclasswithintheServicesfolder.Ifyoucan'trememberhowtodothis,youcanrefertothesectionentitledCreatingtheNavigationServiceInterfacefortheTrackMyWalksappwithinChapter3,NavigatingwithintheMVVMModel-TheXamarin.FormsWay.

2. Next,entertheFacebookCredentialsforthenameofthenewclassthatyouwanttocreate,andclickontheNewbuttontoallowthewizardtoproceedandcreatethefile.

3. Then,ensurethattheFacebookCredentials.csfileisdisplayedwithinthecodeeditor,andenterthefollowingcodesnippet:

//

//FacebookCredentials.cs

//StoresthecredentialstobeusedforFacebook

//

//CreatedbyStevenF.Danielon09/11/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingTrackMyWalks.Models;

usingFacebook;

usingXamarin.Auth;

usingSystem;

usingSystem.Threading.Tasks;

usingNewtonsoft.Json.Linq;

usingSystem.Collections.Generic;

usingTrackMyWalks.Facebook;

namespaceTrackMyWalks

{

publicclassFacebookCredentials

{

4. Next,implementthePostWalkInformationinstancemethodthatwillbeusedtopostinformationrelatingtothecurrentlyactivewalktrailtotheuser'sFacebookpagewall.WedefineanfbvariablethatinstantiatesanewFacebookClientobjectusingtheauthorizationtokenGetAuthTokenfromourFacebookAuthTokenclass,whichweobtainedupon

achievingasuccessfulloginfromFacebook.5. WeusethePostTaskAsyncmethodandpassintheGraphAPIme/feedsyntaxvalueasthe

firstparameter,followedbyamessageparameter,alongwiththemessagedetailsthatwewanttoposttotheuser'sFacebookwall.Toproceed,enterthefollowingcodesnippet:

//Postinformationtotheuser'sFacebookWall

publicstaticvoidPostWalkInformation(stringTitle,

doubleKilometers,stringDifficulty,

stringNotes,stringtrailPictureUrl)

{

FacebookClientfb=newFacebookClient(FacebookApiAuthToken.

GetAuthToken);

//Themessagetopostasakey/valuepair

stringpostMessage="TrackMyWalksApp-TrailCompleted-

Results.";

postMessage+="\n\nTitle:"+Title;

postMessage+="\nKilometers:"+Kilometers;

postMessage+="\nDifficulty:"+Difficulty;

postMessage+="\nNotes:"+Notes;

postMessage+="\nTrailImage:"+trailPictureUrl;

fb.PostTaskAsync("me/feed",new{message=postMessage}).

ContinueWith(t=>

{

if(t.IsFaulted)

{

//Catchanyerrorsthatoccurhere.

}

});

}

Note

TheGraphAPIistheprimarywayinwhichwecangetdatainandoutofFacebook'ssocialgraph,andisessentiallyalow-levelHTTP-basedAPIthatisusedtoquerydata,postnewstories,anduploadphotos.IfyouareinterestedinfindingourmoreinformationabouttheFacebookGraphAPIframeworkclasses,pleaserefertotheFacebookDeveloperdocumentationlocatedathttps://developers.facebook.com/docs/graph-api/using-graph-api/.

6. Then,implementtheGetProfileInformationinstancemethodthatwillbeusedtoretrieveinformationrelatingtothecurrentlyactiveFacebookuser.WedefinearequestobjectthatinitializesanewinstanceoftheOAuth2Requestclass,andacceptstheHTTPmethodtype,alongwiththeURL,andalistofparameters.ThefinalparameterisourobtainedFacebookaccountdetailsthatwillbeusedtoauthenticateourrequest.

7. WethenusetheGetResponseAsyncmethodtomakeanasynchronouswebrequestcalltoretrievetheinformation,asspecifiedbyourrequestobject,andthenusetheGetReponseTextmethodtoreturnaJSONobject,containingtheFacebookuserdetailsasspecifiedinourURLstring,andthenparsethisusingtheJObject.ParsemethodtoconvertthedetailstoaJSONobjectandassignthistoourobjvariable.

8. Next,wechecktoensurethatwehavetheinformationreturnedbyourwebrequest,andthen

passtheJSONobjectdetails,asdefinedbyourobjvariable,toourSaveUserDetailsproperty,whichiscontainedwithinourFacebookApiUserclass.Toproceed,enterthefollowingcodesnippet:

//RetrieveFacebookinformationpertainingtotheuser.

publicstaticasyncTaskGetProfileInformation(AuthenticatorCompl

etedEventArgseventArgs)

{

//Makearequesttoretrieveouritemsbasedonthelistof

//parametersbelow.

varrequest=newOAuth2Request("GET",

newUri("https://graph.facebook.com/me?fields=id,name,

first_name,last_name,gender,picture,email,devices,education"),

null,eventArgs.Account);

varresponse=awaitrequest.GetResponseAsync();

varobj=JObject.Parse(response.GetResponseText());

//Checktoseeifwehavereturnedanyinformation

if(obj!=null)

{

try

{

//Storeouruserprofileinformationintoour

property.

FacebookApiUser.SaveUserDetails(obj);

}

catch(Exceptione)

{

//Handleanyerrorsthatfallinhere.

}

}

}

}

}

Intheprecedingcodesnippet,webeginbyimplementingthemethodsthatarerequiredtopostandretrieveourFacebookinformationusingtheFacebookClientclassthatisusedtomakesynchronousrequeststotheFacebookserver.ThePostWalkInformationinstancemethodisusedtopostinformationrelatingtothecurrentlyactiveusertotheuser'sFacebookpagewall.

TheGetProfileInformationinstancemethodisusedtoretrieveinformationassociatedwiththecurrentlylogged-inFacebookuser,usingtheOAuth2RequestclassandtheFacebookGraphAPI,whichacceptsaURL,containingalistofparametersthatwewouldlikeourmethodtoreturnandthatwillbestoredwithinourSaveUserDetailsproperty,whichisdefinedwithinourFacebookApiUserclass.

Note

IfyouareinterestedinfindingoutmoreinformationabouttheOAuth2RequestandtheXamarin.Authclasses,pleaserefertotheXamarindeveloperdocumentationlocatedat

https://components.xamarin.com/gettingstarted/xamarin.auth.

CreatingtheFacebookSignIntousewithinourTrackMyWalksappInourprevioussection,wecreatedandimplementedourFacebookCredentialswrapperclassthatwillbeusedbyourTrackMyWalksapplicationtohandletheretrievingofourFacebookuserdetails,aswellasprovidingtheabilitytopostwalktrailinformationdirectlytoourFacebookwallsothatourfriendsandfamilycantrackourprogress.

OurnextstepistobegincreatingaFacebookSignInpagethatwillbehookeduptoacustomrendererpagethatwillbeusedtodisplaytheFacebookloginpagewithinawebcontainer.

Let'snowstarttoimplementthecoderequiredforourFBSignInPageContentPagebyperformingthefollowingsteps:

1. CreateanemptyFormsContentPagewithinthePagesfolder.Ifyoucan'trememberhowtodothis,youcanrefertothesectionentitledCreatingthewalksmainpagewithinChapter1,CreatingtheTrackMyWalksNativeApp.

2. Next,enterFBSignInPageforthenameofthenewContentPagethatyouwanttocreate,andclickontheNewbuttontoallowthewizardtoproceedandcreatethefile.

3. Then,ensurethattheFBSignInPage.csfileisdisplayedwithinthecodeeditorandenterthefollowingcodesnippet:

//

//FBSignInPage.cs

//TrackMyWalksFacebookSignInPage

//

//CreatedbyStevenF.Danielon09/11/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms;

namespaceTrackMyWalks

{

publicclassFBSignInPage:ContentPage

{

}

}

Intheprecedingcodesnippet,ourContentPagecontainsthebare-bonesimplementation.Thisisintentional,as,inournextsection,wewillbecreatingacustomclassrendererthatwilluseourFBSignInPageContentPagetoinstantiateaninstanceoftheFacebookloginwebpage.

CreatingtheFacebookSignInClassforTrackMyWalks(iOS)appInourprevioussection,wecreatedourFBSignInPagecontentpagethatwillbeusedasaplaceholderforourFacebookSignInclasscustomrenderer.Inthissection,wewillbuildthecustomFacebooksign-inpagerenderer,whichwillbeusedbytheiOSandAndroidportionsofourapptohandlethesigningintoFacebookviatheFacebookApiAuthTokenmodel,tostorethereceivedFacebooktokenthatwillbeusedthroughouttheTrackMyWalksapplication.

Let'slookathowwecanachievethisbyperformingthefollowingsteps:

1. CreateanemptyclasswithintheRenderersfolderforourTrackMyWalks.iOSproject.Ifyoucan'trememberhowtodothis,youcanrefertothesectionentitledCreatingthecustompickerrendererclassfortheiOSplatformwithinChapter5,CustomizingtheUserInterface.

2. Next,enterFBSignInPageRendererforthenameofthenewclassthatyouwanttocreate,andclickontheNewbuttontoallowthewizardtoproceedandcreatethefile.

3. Then,ensurethattheFBSignInPageRenderer.csfileisdisplayedwithinthecodeeditorandenterthefollowingcodesnippet:

//

//FBSignInPageRenderer.cs

//TrackMyWalksFacebookSignInPage(iOS)

//

//CreatedbyStevenF.Danielon09/11/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingXamarin.Forms;

usingTrackMyWalks;

usingXamarin.Forms.Platform.iOS;

usingXamarin.Auth;

usingTrackMyWalks.Facebook;

4. Next,weneedtoinitializeourFBSignInPageRendererclassbeingmarkedasanExportRendererbyincludingtheExportRendererassemblyattributetothetopofourclassdefinition.ThisletsourclassknowthatitinheritsfromtheViewRendererclass.

[assembly:ExportRenderer(typeof(FBSignInPage),

typeof(TrackMyWalks.iOS.FBSignInPageRenderer))]

namespaceTrackMyWalks.iOS

{

publicclassFBSignInPageRenderer:PageRenderer

{

Then,wedeclareanIsVerifiedBooleanvariablethatwillbeusedtodetermineifasuccessfultoFacebookhashappened.Next,weimplementtheViewDidAppearmethodthatwillbelaunchedupontheContentPagebecomingvisible,andthencalltheFacebookSignIninstancemethod:

boolIsVerified=false;

publicoverridevoidViewDidAppear(boolanimated)

{

base.ViewDidAppear(animated);

if(!IsVerified)

{

FacebookSignIn();

}

}

5. Next,weneedtocreatetheFacebookSignIninstancemethodthatwillbecalledwhenevertheuserhasn'tsignedintoFacebook.WeusetheOAuth2Authenticatormethod,whichwillberesponsibleformanagingtheuserinterfaceandhandlingthecommunicationwiththeFacebookauthenticationservices.

6. TheOAuth2Authenticatorclassacceptstheuser'sFacebookID,whichisstoredwithintheFacebookIdpropertythatisdeclaredwithintheFacebookApiUserclass.TheOAuth2AuthenticatorclassalsoacceptstheauthorizationscopeandtheFacebookservicelocationstoauthenticateanddeterminewhattodowhenasuccessfulloginhappens:

voidFacebookSignIn()

{

stringAccessToken=String.Empty;

varauth=new

OAuth2Authenticator(FaceBookApiUser.FacebookId,

"publish_actions",

newUri("https://m.facebook.com/dialog/oauth/"),

new

Uri("https://www.facebook.com/connect/login_success.html")

);

//Preventourformfrombeingdismissedbytheuser.

auth.AllowCancel=false;

7. Then,beforewepresenttheFacebookUItotheuser,weneedtostartlisteningtotheCompletedeventoftheOAuth2Authenticatorinstance,whichfiresupwhenevertheusersuccessfullyauthenticatesorcancels,andthenchecktheIsAuthenticatedpropertyoftheeventArgspropertytoproperlydetermineiftheauthenticationhassucceeded.

8. Ifwehavedeterminedthatasuccessfulloginhashappened,wemakeacalltotheDismissViewControllermethodtodismissthecurrentlypresentedFacebookUIandthencalltheRemoveFBSignInPageinstancemethodfromourTrackMyWalksappclass,withinPortableClassLibrary,toremoveourFBSignInPagefrommemory:

auth.Completed+=async(sender,eventArgs)=>

{

if(eventArgs.IsAuthenticated)

{

//DismissourFacebookAuthenticationUIDialog

DismissViewController(true,null);

//RemoveourFacebookSignInPageViewfrommemory.

App.RemoveFBSignInPage();

9. Next,weproceedtoretrievetheaccesstokenfromtheFacebooksessionofthesuccessfullylogged-inuser,andproceedtostorethevalueswithintheStoreAuthTokenandStoreAuthAccountdetailswithinourFacebookApiAuthTokenclass.Finally,wecallourNavigateToWalksPageActionwithinourTrackMyWalksAppclass:

//RetrieveouraccesstokenforourFacebooksession.

eventArgs.Account.Properties.TryGetValue("access_token",

outAccessToken);

FacebookApiAuthToken.StoreAuthToken(AccessToken);

FacebookApiAuthToken.StoreAuthAccount(eventArgs);

//NavigateToWalksListmethodfromourmainclass.

awaitApp.NavigateToWalksPage();

}

else

{

//TheusercancelledtheFacebookLoginUI

Console.WriteLine("Youarenotauthorisedtouse

theTrackMyWalksapp");

IsVerified=false;

return;

}

};

10. Then,wepresenttheFacebookloginUIbyusingthePresentViewControllermethod,andcalltheGetUImethodreturnsUINavigationControllersoniOSandintentsonAndroid.OnAndroid,wewouldwritethefollowingcodetopresenttheUIfromtheOnCreatemethod:

IsVerified=true;

PresentViewController(auth.GetUI(),true,null);

}

}

}

Intheprecedingcodesnippet,webeginbyinitializingourFBSignInPageRendererclass,beingmarkedasanExportRenderer,toletourclassknowthatitinheritsfromtheViewRendererclass,andthendeclareanIsVerifiedBooleanvariablethatwillbeusedtodetermineifasuccessfullogintoFacebookhashappened.WethenproceedandimplementtheViewDidAppearmethod,whichwillbelaunchedwhentheContentPagebecomesvisible,andthencalltheFacebookSignIninstancemethod,whichwillbecalledwhenevertheuserhasn'tsignedintoFacebook,andusetheOAuth2Authenticatormethod,whichwillberesponsibleformanagingtheuserinterfaceandhandlingthecommunicationwiththeFacebookauthenticationservices.

Finally,wepresenttheFacebookloginUI,retrievetheaccesstokenfromtheFacebooksessionofthesuccessfullylogged-inuser,andproceedtostorethevalueswithintheStoreAuthTokenandStoreAuthAccountdetailswithinourFacebookApiAuthTokenclass:

Note

TheAndroidversionoftheFBSignInPageRendererclassisavailableinthecompanionsourcecodeforthisbook,whichcanbelocatedwithintheTrackMyWalks.Droidproject.

UpdatingtheNavigationServiceInterfacefortheTrackMyWalksappInthissection,wewillproceedtoupdateourIWalkNavServiceInterfacetocontainanewmethodofimplementationthatwillallowourapptoclearallpreviouslycreatedviewsfromtheNavigationStack.SincethisabstractInterfaceclasswillactasthebaseNavigationServiceclassthateachofourViewModelswillinheritfrom,theywillbeabletoaccesseachoftheclassmemberscontainedwithinthisInterface.

Let'slookathowwecanachievethisbyperformingthefollowingsteps:

EnsurethattheIWalkNavService.csfileisdisplayedwithinthecodeeditorandenterthehighlightedcodesectionsshowninthefollowingcodesnippet:

//

//IWalkNavService.cs

//TrackMyWalksNavigationServiceInterface

//

//CreatedbyStevenF.Danielon03/09/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem.Threading.Tasks;

usingTrackMyWalks.ViewModels;

namespaceTrackMyWalks.Services

{

publicinterfaceIWalkNavService

{

//NavigatebacktothePreviouspage

intheNavigationStack

TaskPreviousPage();

//Navigatetothefirstpagewithin

theNavigationStack

TaskBackToMainPage();

//NavigatetoaparticularViewModel

withinourMVVMModel,

//andpassaparameter

TaskNavigateToViewModel<ViewModel,

WalkParam>(WalkParamparameter)

whereViewModel:WalkBaseViewModel;

//Clearallpreviouslycreatedviews

fromtheNavigationStack

voidClearAllViewsFromStack();

}

}

Intheprecedingcodesnippet,weimplementanewclassmemberClearAllViewsFromStack

methodthatwillbeusedtoclearallpreviouslycreatedviewsfromtheNavigationStack.Thisisbecause,uponsuccessfullyloggingintoourTrackMyWalksappaftertheFacebookUIloginhasbeendismissed,weneedtohavetheabilitytoremovetheFBSignInPagefromtheNavigationStack.

UpdatingtheNavigationServiceclassfortheTrackMyWalksappIntheprevioussection,weupdatedthebaseInterfaceclassforourNavigationService,aswellasdefininganewclassmemberthatwillbeusedtohandletheremovingofallpreviouslycreatedviewsfromourNavigationStackwithinourMVVMViewModel.

ThesewillbeusedbyeachofourViewModels,andtheViews(pages)willimplementthoseViewModelsandusethemastheirBindingContext.

Let'slookathowwecanachievethisbyperformingthefollowingsteps:

1. EnsurethattheWalkNavService.csfileisdisplayedwithinthecodeeditor,andenterthehighlightedcodesectionsshowninthefollowingcodesnippet:

//

//WalkNavService.cs

//TrackMyWalksNavigationServiceClass

//

//CreatedbyStevenF.Danielon03/09/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingXamarin.Forms;

usingSystem.Collections.Generic;

usingSystem.Threading.Tasks;

usingSystem.Reflection;

usingSystem.Linq;

usingTrackMyWalks.ViewModels;

usingTrackMyWalks.Services;

[assembly:Dependency(typeof(WalkNavService))]

namespaceTrackMyWalks.Services

{

publicclassWalkNavService:IWalkNavService

{

publicINavigationnavigation{get;set;}

readonlyIDictionary<Type,Type>

_viewMapping=new

Dictionary<Type,Type>();

//RegisterourViewModelandViewwithinourDictionary

publicvoidRegisterViewMapping(

TypeviewModel,Typeview)

{

_viewMapping.Add(viewModel,view);

}

//Instancemethodthatallowsustomovebackto

thepreviouspage

publicasyncTaskPreviousPage()

{

//Checktoseeifwecanmovebacktothepreviouspage

if(navigation.NavigationStack!=null&&

navigation.NavigationStack.Count>0)

{

awaitnavigation.PopAsync(true);

}

}

//Instancemethodthattakesusbacktothe

mainRootWalksPage

publicasyncTaskBackToMainPage()

{

awaitnavigation.PopToRootAsync(true);

}

//InstancemethodthatnavigatestoaspecificViewModel

//withinourdictionaryviewMapping.

publicasyncTaskNavigateToViewModel

<ViewModel,WalkParam>(WalkParamparameter)

whereViewModel:WalkBaseViewModel

{

TypeviewType;

if(_viewMapping.TryGetValue(typeof(ViewModel),

outviewType))

{

varconstructor=viewType.GetTypeInfo()

.DeclaredConstructors

.FirstOrDefault(dc=>dc.GetParameters()

.Count()<=0);

varview=constructor.Invoke(null)asPage;

awaitnavigation.PushAsync(view,true);

}

if(navigation.NavigationStack.Last().BindingContextis

WalkBaseViewModel<WalkParam>)

await((WalkBaseViewModel<WalkParam>)

(navigation.Navigation

Stack.Last().BindingContext)).Init(parameter);

}

2. Next,weneedtocreatetheClearAllViewsFromStackinstancemethodforourWalkNavServiceclass,whichwillbeusedtoremoveallpreviouslycreatedviewsfromtheNavigationStackbyfirstcheckingtheNavigationStackpropertyforthenavigationpropertyINavigationinterfacetoseeifanyitemsalreadyhavebeenpushedontotheNavigationStack.Thisistoensurethatacrashdoesn'thappenwithinourapp.

3. Inournextstep,weproceedtoiteratethrougheachitemthatiscontainedinourNavigationStackandcalltheRemovePage,methodtoremoveeachpage:

//Instancemethodtoremoveallpreviouslycreatedviewsfrom

//theNavigationStack.

publicvoidClearAllViewsFromStack()

{

//Checktoseeifanyitemshavealreadybeenpushed

//ontotheNavigationStack.

if(navigation.NavigationStack.Count<=1)

return;

for(vari=0;i<navigation.NavigationStack.Count-1;

i++)

navigation.RemovePage(navigation.NavigationStack[i]);

}

}

}

Intheprecedingcodesnippet,weimplementanewclassmemberClearAllViewsFromStackinstancemethodwithinourWalkNavServiceclass,tohandletheclearingofallpreviouslycreatedViews(pages)fromtheNavigationStack.

UpdatingtheWalksPagetoproperlyhandleFacebookSignInInthissection,weneedtoupdateourWalksPageContentPagesothatitcanreferencetheupdateswithinourWalksPageViewModel.WewillneedtoapplyadditionallogictoupdatetheNavigationBarTitleoncewehavedismissedourFacebookSignIndialog.

Let'slookathowwecanachievethisbyperformingthefollowingsteps:

EnsurethattheWalksPage.csfileisdisplayedwithinthecodeeditor,andenterthehighlightedcodesectionsshowninthefollowingcodesnippet:

//

//WalksPage.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingXamarin.Forms;

usingTrackMyWalks.Models;

usingTrackMyWalks.ViewModels;

usingTrackMyWalks.Services;

usingTrackMyWalks.DataTemplates;

usingTrackMyWalks.ValueConverters;

namespaceTrackMyWalks

{

publicclassWalksPage:ContentPage

{

WalksPageViewModel_viewModel

{

get{returnBindingContext

asWalksPageViewModel;}

}

publicWalksPage()

{

varnewWalkItem=newToolbarItem

{

Text="Add"

};

if(Device.OS==TargetPlatform.iOS)

{

Title="TrackMyWalks-iOS";

}

elseif(Device.OS==TargetPlatform.Android)

{

Title="TrackMyWalks-Android";

}

...

...

...

...

Intheprecedingcodesnippet,webeginbycheckingtheDevice.OSclass,todeterminewhatOSXamarin.Formsisrunningon,andthenusetheTargetPlatformclasstodetermineifourappisrunningontheAndroidoriOSplatform.IfwehavedeterminedthatourappisrunningonAndroid,wesettheTitlepropertyforourContentPage;alternatively,ifwearerunningoniOS,wesettheTitlepropertyaswell.

UpdatingtheWalksPageViewModeltouseourFaceBookApiUserInthissection,wewillproceedtoupdateourWalksPageViewModelViewModel,sothatithastheabilitytodisplayourFacebookSignInpageifitdeterminesthatwehaven'talreadyloggedin.

Let'slookathowwecanachievethisbyperformingthefollowingsteps:

EnsurethattheWalksPageViewModel.csfileisdisplayedwithinthecodeeditor,andenterthehighlightedcodesectionsshowninthefollowingcodesnippet:

//

//WalksPageViewModel.cs

//TrackMyWalksViewModels

//

//CreatedbyStevenF.Danielon22/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingSystem.Collections.ObjectModel;

usingSystem.Threading.Tasks;

usingTrackMyWalks.Models;

usingTrackMyWalks.Services;

usingXamarin.Forms;

usingTrackMyWalks.Facebook;

namespaceTrackMyWalks.ViewModels

{

publicclassWalksPageViewModel:

WalkBaseViewModel

{

ObservableCollection<WalkEntries>

_walkEntries;

publicObservableCollection<WalkEntries>

walkEntries

{

get{return_walkEntries;}

set

{

_walkEntries=value;

OnPropertyChanged();

}

}

publicWalksPageViewModel

(IWalkNavServicenavService):

base(navService)

{

walkEntries=newObservableCollection

<WalkEntries>();

}

publicoverrideasyncTaskInit()

{

//Checkifwehaveloggedinandthatwearerunningour

//deviceoniOS

if(!FacebookApiUser.IsLoggedIn&&

Device.OS==TargetPlatform.iOS)

{

awaitApp.Current.MainPage.Navigation

.PushModalAsync(new

FBSignInPage());

}

else{

awaitLoadWalkDetails();

awaitNavService.ClearAllViewsFromStack();

}

}

...

...

...

...

Intheprecedingcodesnippet,webeginbymodifyingtheInitmethodtoincludeachecktoseeifwehavealreadyloggedintoFacebook,bycheckingtheIsLoggedInpropertyoftheFacebookApiUserclass,andwhetherwearerunningonadevicerunningiOS.IfwedeterminethattheIsLoggedInpropertydoesn'tcontainavalue,wecallthePushModalAsyncmethodontheNavigationpropertyfromtheMainPagetodisplayourFBSignInPageContentPage.Alternatively,iftheuserhasloggedin,weproceedtoloadourwalkentrydetailsusingtheLoadWalkDetailsinstancemethod,andcalltheClearAllViewsFromStackinstancemethodthatislocatedwithinourWalkNavServiceclass.

UpdatingtheDistanceTravelledPagefortheTrackMyWalksappInthissection,weneedtoupdateourDistanceTravelledPageContentPage,sothatitcanmakeuseofourFacebookAPIandretrievethecurrentlylogged-inFacebookuser,aswellasprovidingtheabilitytopostWalkTrailinformationtotheuser'sFacebookwall.

Let'slookathowwecanachievethisbyperformingthefollowingsteps:

1. EnsurethattheDistanceTravelledPage.csfileisdisplayedwithinthecodeeditor,andenterthehighlightedcodesectionsshowninthefollowingcodesnippet:

varpostToFacebook=newButton

{

BackgroundColor=Color.FromHex("#455c9f"),

TextColor=Color.White,

Text="PosttoFacebook"

};

postToFacebook.Effects.Add(Effect.Resolve(

"com.geniesoftstudi

os.ButtonShadowEffect"));

2. Next,wecreateaClickedhandlermethodforourpostToFacebookbutton,sothatwheneverthePosttoFacebookispressedwedisplayaselectionofchoicesfortheusertochoosefrom,usingtheDisplayActionSheetmethod:

//Setupoureventhandler

postToFacebook.Clicked+=async(sender,e)=>

{

if(_viewModel.WalkEntry==null)return;

//Displayourlistofchoicestochoosefrom

varaction=awaitDisplayActionSheet(

"TrackMyWalks-TrailDetails",

"Cancel",

"DisplayUserDetails",

"PosttoFacebookWall");

3. Then,weusetheContainspropertyoftheactionvariabletodetermineifthePostoptionhasbeenselected,and,ifso,wecallthePostWalkInformationinstancemethodofourFacebookCredentialsclass;passintheTitle,Kilometers,Difficulty,Notes,andImageUrlasparameterstotheclass;andthendisplayanalertdialogboxtellingtheuserthattheirwalkinformationhasbeenpostedtotheirFacebookwall:

if(action.Contains("Post"))

{

//DeclareaninstancetoourFacebookCredentialsClass

FacebookCredentials.PostWalkInformation(

_viewModel.WalkEntry.Title,

_viewModel.WalkEntry.Kilometers,

_viewModel.WalkEntry.Difficulty,

_viewModel.WalkEntry.Notes,

_viewModel.WalkEntry.ImageUrl.AbsoluteUri);

//Displayanalertdialoglettingtheuserknowthat

//theirinformationhasbeenpostedtotheirFacebook

//Wall.

awaitDisplayAlert("PosttoFacebook","Trail

informationhasbeenpostedtoyourwall!","OK");

}

4. Next,weusetheContainspropertyoftheactionvariabletodetermineiftheUserDetailsoptionhasbeenselected,and,ifso,wecalltheGetProfileInformationinstancemethodofourFacebookCredentialsclass,andpassintheGetAuthAccountpropertyfromourFacebookApiAuthTokenclasstoretrieveourcurrentlylogged-inFacebookuser'sdetailsandassignthevaluetoourobjUserDetailsvariable.WethenproceedtoconstructouruserDetailsstringwiththeinformationextractedfromtheobjUserDetailsdictionary,anddisplaytheinformationwithinanalertdialogbox:

elseif(action.Contains("UserDetails"))

{

//DeclareaninstancetoourFacebookCredentialsClass

awaitFacebookCredentials.GetProfileInformation((Xamarin

.Auth.AuthenticatorCompletedEventArgs)

FacebookApiAuthToken.GetAuthAccount);

//ConstructourFacebookUserdetailsbasedon

//informationstoredwithineachoftheproperties

varobjUserDetails=FacebookApiUser.GetUserDetails;

varuserDetails=objUserDetails.GetValue("id").ToString();

userDetails

+="\n"+objUserDetails.GetValue("name").ToString();

userDetails+="\n"+objUserDetails.GetValue("first_name")

.

ToString();

userDetails+="\n"+objUserDetails.GetValue("last_name")

.ToString();

userDetails+="\n"+objUserDetails.GetValue("gender")

.ToString();

userDetails+="\n"+objUserDetails.GetValue("devices")

.ToString();

//DisplayanAlertDialogthatwilldisplay

//informationfromouruserproperties

awaitDisplayAlert("FacebookUserDetails",

userDetails,"OK");

}

};

Intheprecedingcodesnippet,wecreateourpostToFacebookbuttonandbeginapplyingtheButtonShadowEffectclasstoourcontrol,sothatitcantakeadvantageoftheniceplatform-specificrenderingeffectsforvisualcontrolelements.

Next,wemodifytheClickedmethodtohandleeachpressofthebutton,sothatwecandisplayaselectionofoptionsfortheusertochoosefrom.WeaccomplishthisbyusingtheDisplayActionSheetmethod.WhentheuserchoosesthePostbutton,acallismadetoourFacebookCredentialsclassandthePostWalkInformationmethodtosubmitthecurrentwalkinformationtotheuser'sFacebookpage.Alternatively,iftheuserchoosestheUserDetailsoption,wemakeacalltoourFacebookCredentialsclass,butthistimewecalltheGetProfileInformationmethodtoreturnaJSONdictionarythatcontainsthecurrentlylogged-inFacebookuser'sdetails,whichweassigntoanobjectvariablecalledobjUserDetails.

Finally,wecreateauserDetailsvariablethatweconstructbyextractingeachofthevaluesforname,last_name,first_name,etc.anddisplaythisinformationwithinanalertdialogboxusingtheDisplayAlertmethod.

UpdatingtheXamarin.FormsAppclasstohandleFacebookSignInInthissection,weneedtoupdateourXamarin.Forms.AppclassbymodifyingtheconstructorinthemainAppclasstoincludeadditionalpropertyandactionmethodsthatwillhelpwithnavigatingtoourWalksPageContentPageafterourTrackMyWalksapphassuccessfullysignedintoFacebook.

Let'slookathowwecanachievethisbyperformingthefollowingsteps:

1. OpentheTrackMyWalks.csfile,locatedwithintheTrackMyWalksPortableClassLibraryprojectsolution.

2. Next,ensurethattheTrackMyWalks.csfileisdisplayedwithinthecodeeditor,locatetheAppmethod,andenterthehighlightedcodesectionsshowninthefollowingcodesnippet:

//

//TrackMyWalks.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingSystem;

usingSystem.Threading.Tasks;

usingTrackMyWalks.Services;

usingTrackMyWalks.ViewModels;

usingXamarin.Forms;

namespaceTrackMyWalks

{

publicclassApp:Application

{

publicApp()

{

//ChecktheDeviceTargetOSPlatform

if(Device.OS==TargetPlatform.Android)

{

//Settherootpageofyourapplication

MainPage=newSplashPage();

}

elseif(Device.OS==TargetPlatform.iOS)

{

//SetourWalksPagetobetherootpageofour

//application.

varmainPage=newNavigationPage(newWalksPage()

{

Title="TrackMyWalks-iOS",

});

...

...

...

...

}

}

3. Next,createtheRemoveFBSignInPageactionpropertymethod,whichwillbeusedtohandletheremovaloftheFBSignInPageoncewehavesuccessfullysignedintoFacebook,asdeterminedbytheFBSignInPageRendererclass.WethencallthePopModalAsyncpropertyoftheNavigationpropertyontheMainPagetopopthelastpageofftheNavigationStack:

//PropertymethodinstancetoremoveourFBSignInPage

publicstaticActionRemoveFBSignInPage

{

get

{

returnnewAction(()=>App.Current.

MainPage.Navigation.

PopModalAsync());

}

}

4. Then,createtheNavigateToWalksPageinstancemethod;thiswillbeusedtohandlenavigatingtotheWalksPageViewModelwithintheNavigationStackbycallingthePushAsyncpropertyontheNavigationpropertyontheMainPage:

//HandlenavigatingtoourWalksPagewhen

wehavesuccessfully

//signedintoFacebook.

publicasyncstaticTaskNavigateToWalksPage()

{

awaitApp.Current.MainPage.Navigation.PushAsync

(newWalksPage());

}

}

Intheprecedingcodesnippet,webeginbyimplementingthemethodsthatwillberequiredtoremoveaninstanceofFBSignInPage,asdeterminedbytheRemoveFBSignInPagepropertyActionmethodthatwillbeusedtohandleremovaloftheFBSignInPageoncewehavesuccessfullysignedintoFacebook,asdeterminedbytheFBSignInPageRendererclass.WethencallthePopModalAsyncpropertyoftheNavigationpropertyontheMainPagetopopthelastpageofftheNavigationStack.

TheNavigateToWalksPageinstancemethodwillbeusedtohandlethenavigationtoourWalksPageViewModeluponsuccessfullybeingloggedintoFacebookviatheTrackMyWalksapp;thisisdonebyusingthePushAsyncpropertyontheNavigationproperty.

EnablingFacebookfunctionalitywithintheTrackMyWalksappWhenworkingwiththeFacebookSDKandtheXamarin.Authframework,weneedtomakesomeadditionalchangestoouriOSprojectsolutionpropertylisttoenableSSO(SingleSign-On)supportwhentheapplicationruns.

Let'slookathowwecanachievethisbyperformingthefollowingsteps:

1. Double-clickontheInfo.plistfilethatiscontainedwithintheTrackMyWalks.iOSproject,andensurethattheAdvancedtabisshowing.

2. Next,scrolldowntothebottomofthepage,andexpandtheURLTypessection.3. Then,withintheIdentifierfield,provideyourFacebookIdwhilstprefixingitwithfbasthe

firsttwocharactersthatisfb1234567890:

Note

TheURLtypessectionisasinglearraysub-itemthatneedsyourFacebookAppIDtobeprefixedwithfb.Thisisusedtoensuretheapplicationwillreceivethecall-backmethodsoftheURLandtheweb-basedOAuthflow.

NowthatwehavemodifiedourTrackMyWalks.iOSprojecttoallowourapptoreceivethecall-backmethodsoftheURLandtheweb-basedOAuthflow,weneedtodoonemorething,andset

upourFacebookAppIDandapplicationnamethatweconfiguredwithintheappdashboard:

1. EnsurethattheInfo.plistfileisdisplayedwithintheXamarinIDE,andthattheSourcetabisshowing.

2. Next,createtheFacebookAppIDandFacebookDisplayNamekeysbyclickingwithintheAddnewentrysectionoftheInfo.plist.

3. Then,enteryourFacebookAppIDasthestringdescriptionfortheValuefield,asshowninthefollowingscreenshot.Youwillnoticeherethatwedon'tneedtoprovidethefbprefixaswedidforourURLtypessection.

4. Next,enterTrackMyWalksasthestringdescriptionfortheValuefield,asshowninthefollowingscreenshot:

5. Then,createtheLSApplicationQueriesScenesarraykeys,andaddthestringdescriptionfieldsandtheirvalues,usingfbapi,fbauth2,andfbshareextensionastheValuefields,asshownintheprecedingscreenshot.

AppleintroducedtheAppTransportSecurityprotocolwithiOS9toenforcesecureconnectionsbetweenInternetconnections,aswellaswithanyappthatcommunicatesusingtheHTTPSprotocol.ItrequiresthatinformationisencryptedusingtheTLSversion1.2.WeneedtodisableandoptoutofATSentirelybyconfiguringourlocalInfo.plistfilewithintheTrackMyWalks.iOSprojectsolution,sothatitcancommunicateoverHTTPSwithoutanyissues.

6. Next,withtheInfo.plistfilestillopenwithintheXamarinIDEenvironment,createtheNSAppTransportSecuritydictionaryarray.

7. Next,addtheNSAllowArbitraryLoadsBooleanvalue,settingittoYes,asshownintheprecedingscreenshot.

Note

IfyouareinterestedinfindingoutmoreinformationontheAppTransportSecurityandNSAppTransportSecurityclass,refertotheXamarindeveloperdocumentationlocatedathttps://developer.xamarin.com/guides/ios/platform_features/introduction_to_ios9/ats/.

NowthatwehavefinishedbuildingallthecomponentsforourTrackMyWalksapplicationnecessarytoenableintegrationwithFacebook,wecanbuildandruntheTrackMyWalksapplicationwithintheiOSsimulator.Whencompilationcompletes,theiOSSimulatorwillappearautomaticallyandtheTrackMyWalksapplicationwillbedisplayed,asshowninthefollowingscreenshot:

Asyoucanseefromtheprecedingscreenshot,thiscurrentlydisplaysourFacebookSignInpage,whichtellstheusertoLogintoyourFacebookaccounttoconnecttoTrackMyWalksapplication.Toproceed,provideyourEmailaddressorphonenumberandyourFacebookpassword,andclickontheLogInbutton.

UponsuccessfullydeterminingthatyourdetailshavebeenvalidatedbyFacebook,youwillbepresentedwithaPosttoFacebookauthenticationscreen,askingyouwhoyouwouldliketoshareyourpostswith;youhavetheoptiontochooseeitherFriends,Public,orOnlyMe.Onceyouhavemadeyourchoice,clickontheOKbuttontodismissthePosttoFacebookdialoganddisplaytheListViewthatwillcontainourlistofwalktrailsfromourDataTemplatecontrol:

TheprecedingscreenshotshowstheDistanceTravelledpagethatincludesourPosttoFacebookbutton,andyouwillnoticethat,uponclickingonthisbutton,severalchoiceswillpopupforyoutochoosefrom.IfyouproceedandclickontheDisplayUserDetailsbutton,thiswillmakeacalltotheGetProfileInformationmethodthatislocatedwithinourFacebookCredentialsclass,andwillpasstheGetAuthAccountaccountinformationtoobtaintheuserdetails,whicharelocatedwithinourFacebookApiAuthToken,usingtheOpenGraphAPIplatform.Uponsuccessfullyobtainingtheuser'sFacebookdetails,youwillbepresentedwithadialogboxcontainingeachofouruser'sfielddetails:

TheprecedingscreenshotshowstheDistanceTravelledpagethatincludesourPosttoFacebookbutton,andyouwillnoticethat,uponclickingonthisbutton,severalchoiceswillpopupforyoutochoosefrom.IfyouclickonthePosttoFacebookWallbutton,thiswillmakeacalltothePostWalkInformationmethodthatislocatedwithinourFacebookCredentialsclass,andwillpassthecurrentlychosenwalktrailinformation,asdeterminedbyourViewModel.Uponsuccessfullypostingtotheuser'sFacebookwall,youwillbepresentedwithadialogboxtellingyouthatthewalkentryhasbeenpostedtotheuser'sFacebookwall.

SummaryInthischapter,weupdatedourTrackMyWalksapplicationtoallowustouseFacebooktosignintoourapp.YoulearnedhowyoucanuseboththeXamarin.AuthandtheFacebookSDKtoauthenticatewhethertheuserisavalidFacebookuser.Next,youlearnedhowtocreateacustomFacebookApiUsermodelandaFacebookCredentialsclassthatareusedtostoretheuser'scredentials,sothatthesecanbeusedthroughoutourapptoobtaininformationabouttheuser.

Asweprogressedthroughoutthechapter,youcreatedaFacebookSignIncontentpageandacustompagerendererclassthatwillallowtheusertosignintotheTrackMyWalksappusingtheirFacebookcredentials,andupdatedtheViewModelandcontentpagessothattheycanutilizetheFacebookfunctionalityappropriately.YoulearnedhowtotakeadvantageoftheFacebookSDKandpostwalkdatatoyourFacebookprofilepage,soyoucanshowoffyourprogresstoyourfriendsand/orworkcolleagues.

Inthenextchapter,you'lllearnhowtocreateandrununittestswithintheXamarinStudioIDE,usingtheUITestframework,beforemovingontolearnhowtoprofileourapplicationusingtheXamarinProfiler,andhowtousetheXamarinInspectortoinspectanddebugouruserinterfacesvisually,andfixUI-relatedproblems.

Chapter9.UnitTestingYourXamarin.FormsAppsUsingtheNUnitandUITestFrameworksInourpreviouschapter,weupdatedourTrackMyWalksapplicationtoallowustouseFacebooktosignintoourapp.YoulearnedhowyoucanuseboththeXamarin.AuthandFacebookSDKtoauthenticateiftheuserisavalidFacebookuser.Next,youlearnedhowtocreateacustomFacebookApiUsermodelandFacebookCredentialsclassthatwillbeusedtostoretheuser'scredentials,sothatthesecanbeusedthroughoutourapptoobtaininformationabouttheuser,aswellaspostinformationtotheirFacebookwall.

DuringthedevelopmentofourTrackMyWalksapp,wehavedesignedandimplementedvariousdesignpatternsandbestpractices,withtheintentionofmakingiteasiertomaintainandtestourappbyseparatingtheuserinterfaceandbusinesslogic.

Inthischapter,you'lllearnhowtocreateandrununittestsusingtheNUnitandUITesttestingframeworksrightwithintheXamarinStudioIDE.You'lllearnhowtowriteunittestsforourViewModelsthatwillessentiallytestthebusinesslogictovalidatethateverythingisworkingcorrectly,beforemovingontotestingtheuserinterfacesportionusingautomatedUItesting.

Thischapterwillcoverthefollowingtopics:

CreatingaunittestingsolutionusingthepopularNUnittestingframeworkAddingtheMoqNuGetpackagetotheunittestingsolutionAddingtheXamarinTestCloudAgentNuGetpackagetotheUITestsolutionSuccessfullylearninghowtotestyourViewModelsRunningunittestsandUITestsusingtheXamarinStudioIDEUnderstandingthecommontypesofUITesttestingmethodsCreatingaunittestingsolutionusingtheUITestframeworkSuccessfullylearninghowtotestyourContentPages(Views)

CreatingaunittestsolutionfolderusingXamarinStudioDuringthedevelopmentofourTrackMyWalksapplication,wehavedesignedtheuserinterfaces,ViewModels,andContentPages.Asdevelopers,theremaybetimeswhenwewouldliketoobtainfeedbacktoletusknowwhenourapplicationlogicisworkingasexpected.WecanusetheNUnittestingframeworktoprovideuswiththatconfirmation.

Inthissection,you'llbeginbyaddinganewsolutionfoldertoourexistingTrackMyWalksPortableClassLibrarysolution.ThisnewsolutionfolderwillbeusedtoseparateeachofourNUnitandUITestsfromourmainsolution.

ThegoodnewsisthatXamarinStudiohasbuiltinsupportfortheNUnitframeworkthatwecanrunourunittestsfrom,andthenhaveourresultsdisplayed,rightwithintheXamarinStudioIDE.

Let'slookathowtoaddanewSolutionfoldertoourTrackMyWalksPortableClassLibrary,byperformingthefollowingsteps:

1. Right-clickontheTrackMyWalkssolutionprojectandchoosetheAdd|AddSolutionFoldermenuoption,asshowninthefollowingscreenshot:

2. Next,enterinTrackMyWalks.Testsforthenameofthesolutionfolder.

NowthatyouhavecreatedtheTrackMyWalks.TestssolutionwithinthemainTrackMyWalkssolutionproject,ournextstepistocreateanewunittestprojectsolution,thatwillberesponsiblefortestingthebusinesslogicwithinourTrackMyWalksViewModels.

CreatingaunittestprojectusingXamarinStudioIntheprevioussection,wecreatedtheunittestingSolutionfolderwithinourTrackMyWalksmainprojectsolutionthatwillbeusedtoseparatetheunittestsfromourmainiOSandAndroidprojectsolutions.Thisissothatwecanruntheseindependentlyfromthemainsolution.

OneofthegreatbenefitsofusingXamarinStudiotohandleyourtestsisthatitleveragesthepopularNUnittestingframeworkforperformingunittests.WewillbeginbycreatingtheNUnittestprojectwithintheTrackMyWalks.Testssolutionthatwe'vepreviouslycreated.

Let'sstartbycreatinganewNUnitprojectwithinourTrackMyWalks.Testsprojectsolution,byperformingthefollowingsteps:

1. Right-clickontheTrackMyWalks.TestssolutionprojectandchoosetheAdd|AddNewProject...menuoption,asshowninthefollowingscreenshot:

2. Next,choosetheNUnitLibraryProjectoptionlocatedwithintheGeneralsectionundertheOther|.NETsection;ensureyouhaveselectedC#astheprogramminglanguagetouse,asshowninthefollowingscreenshot:

3. Then,clickontheNextbuttontoproceedtothenextstepinthewizard.4. Next,enterTrackMyWalks.UnitTeststouseasthenameforyournewprojectinthe

ProjectNamefield.5. Then,ensurethattheCreateaprojectdirectorywithinthesolutiondirectory.hasbeen

selected,asshowninthefollowingscreenshot:

6. Finally,clickontheCreatebuttontosaveyourprojectatthespecifiedlocation.

Onceyourprojecthasbeencreated,youwillbepresentedwiththeXamarinStudiodevelopmentenvironment,withyournewprojectedcreatedwithintheTrackMyWalks.Testssolutionfolder.

Inthenextsection,wewillbegintoaddtheMoq(pronouncedasmock)frameworklibrarythatwillberesponsibleforallowingustotestourViewModelswithintheTrackMyWalkssolution.

AddingtheMoqNuGetpackagetotheunittestprojectNowthatyouhavesetupandcreatedanewunittestproject,ournextstepistoaddtheMoq(pronouncedasMock)NuGetpackagetotheTrackMyWalks.UnitTestssolution.Thislibraryisessentiallyoneofthemostpopularandfriendlymockingframeworklibrariesforthe.NETplatform,andwewillusethistotestsomeofourViewModelswithinourTrackMyWalksapp.

Let'slookathowtoaddtheMoqNuGetpackagetoourTrackMyWalks.UnitTestsprojectsolution,byperformingthefollowingsteps:

1. Right-clickonthePackagesfolderthatiscontainedwithintheTrackMyWalks.UnitTestssolution,andchoosetheAddPackages...menuoption,asshowninthefollowingscreenshot:

2. ThiswilldisplaytheAddPackagesdialog,enterinmoqwithinthesearchdialog,andselecttheMoq:anenjoyablemockinglibraryoptionwithinthelist,asshowninthefollowingscreenshot:

3. Finally,clickontheAddPackagebuttontoaddtheNuGetpackagetothePackagesfoldercontainedwithintheTrackMyWalks.UnitTestssolution.

NowthatyouhaveaddedtheMoqNuGetpackage,ournextstepistobeginwritingthetestcasescenariosforourViewModels,whichwewillbecoveringinthenextsection.

AddingtheTrackMyWalksprojecttoTrackMyWalks.UnitTestsIntheprevioussection,weaddedtheMoqNuGetpackagetoourTrackMyWalkssolution.ThenextstepistoaddareferencetotheTrackMyWalkscorelibrarytoourTrackMyWalks.UnitTestssolution.

SincewewillbetestingourViewModels,youwillneedtoensurethatyouhaveappliedallofthecumulativecodechangestotheTrackMyWalkssolutionprojectthroughoutthisbooktoavoidanyissues,aswewillessentiallyneedtobreakeachofthetestsintoindividualclassesrepresentingeachViewModelandtheaccompanyingunittestclassthatwewanttotestthebusinesslogicon.TosuccessfullytestourViewModels,wewillfirstneedtoincludeareferencetotheTrackMyWalksprojectwithinourTrackMyWalks.UnitTestssolutionproject.

Let'slookathowwecanachievethis,byperformingthefollowingsteps:

1. Right-clickontheReferencesfolderthatiscontainedwithintheTrackMyWalks.UnitTestsprojectsolution,andchoosetheEditReferences...menuoption,asshowninthefollowingscreenshot:

2. Then,ensurethattheProjectstabhasbeenselectedandchoosetheTrackMyWalksprojecttoincludeourAndroidandiOSplatformsolutionprojectswithinourTrackMyWalks.UnitTestsprojectsolution:

3. Next,ensurethatyouhaveselectedtheTrackMyWalksprojectwithintheProjectstab,clickonOKtoaddtheprojectreferencetoyourReferencessectionofyourTrackMyWalks.UnitTestsprojectsolution,andclosetheEditReferencesdialog.

Inthenextsection,wewillbeginbycreatingourfirstunittestwhichwillberesponsibleforvalidatingtheWalkEntrymodeltoensurethataftertheViewModelhasbeeninitialized,itwillcontainwalkinformation.

CreatingandimplementingtheWalksTrailViewModelNUnittestclassNowthatyouhaveincorporatedtheTrackMyWalksprojectintotheTrackMyWalks.UnitTestssolution,ournextstepistocreatetheunittestforourWalksTrailViewModel.ThesetestswillbeusedtohelpuschecktoseewhenourViewModelpassesorfailsunderthesetestconditions.

Let'snowstarttoimplementthecoderequiredforourWalksTrailViewModelTestclass,byperformingthefollowingsteps:

1. CreateanemptyclasswithintheTrackMyWalks.UnitTestsprojectsolutionfolder,bychoosingAdd|NewFile....Ifyoucan'trememberhowtodothis,youcanrefertothesectionentitledCreatingtheTrackMyWalksmodel,withinChapter1,CreatingtheTrackMyWalksNativeApp.

2. Then,enterWalksTrailViewModelTestforthenameofthenewclassthatyouwanttocreate,andclickontheNewbuttontoallowthewizardtoproceedandcreatethenewfile.

3. Next,ensurethattheWalksTrailViewModelTest.csfileisdisplayedwithinthecodeeditor,andenterinthefollowingcodesnippet:

//

//WalksTrailViewModelTest.cs

//WalksTrailViewModelTestingFramework

//

//CreatedbyStevenF.Danielon23/09/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingNUnit.Framework;

usingTrackMyWalks.ViewModels;

usingTrackMyWalks.Services;

usingMoq;

usingSystem.Threading.Tasks;

namespaceTrackMyWalks.Tests

{

4. Next,weneedtomodifytheWalksTrailViewModelTestclassconstructorbyaddingthe[TestFixture]attributewhichsetsupourclasstobeaninstanceoftheTestFixturetestingclass.Proceedandenterinthefollowingcodesnippet:

[TestFixture]

publicclassWalksTrailViewModelTest

{

WalksTrailViewModel_vm;

5. Then,createtheSetupinstancemethodthatwillberesponsibleforcreatinganewinstanceofourViewModelforeachoftheteststhataredeclaredwithintheclass.ThisistoensurethateachtestisrunusingacleaninstanceoftheViewModel.WethenproceedtodeclareanavMockvariableinstanceoftheMockclassfromourMoqlibrarytocreateanewinstanceoftheIWalkNavServiceandinstantiatetheWalksTrailViewModel,usingthenavMockinstance.Proceedandenterinthefollowingcodesnippet:

[SetUp]

publicvoidSetup()

{

varnavMock=newMock<IWalkNavService>().Object;

_vm=newWalksTrailViewModel(navMock);

}

6. Next,weneedtoimplementtheCheckIfWalkEntryIsNotNullinstancemethodthatwillchecktoseeifourWalksTrailViewModelhasbeenproperlyinitializedwhentheInitmethodiscalled.Wedeclarethe[Test]attributewhichisessentiallyanabstractclassthatrepresentsatestwithintheNUnit.Testframework.WeproceedtoinitializeourWalkEntrymodeltonull,andthencalltheInitmethodtochecktoseeiftheWalkEntrymodelhasbeenproperlysettothevalueprovidedintheInitmethod'sparameterandthenusetheIsNotNullmethodontheAssertclasstodisplayamessageshouldthetestfail.Thisissothatyoucantroubleshootthecodeatalaterpoint.Proceedandenterinthefollowingcodesnippet:

[Test]

publicasyncTaskCheckIfWalkEntryIsNotNull()

{

//Arrange

_vm.WalkEntry=null;

//Act

await_vm.Init();

//Assert

Assert.IsNotNull(_vm.WalkEntry,"WalkEntryisnull

afterbeinginitializedwithavalidWalkEntriesobject.");

}

}

}

Intheprecedingcodesnippet,webeganbyimplementingthevariousinstancemethodsthatwillberequiredtoperformeachtestforourWalksTrailViewModel.Weaddedthe[TestFixture]attributeatthebeginningofourclassconstructorsothatitwillbeaninstanceoftheTestFixturetestingclass.WethenproceededtocreatetheSetupinstancemethodsothatitwillberesponsibleforcreatinganewinstanceofourViewModelforeachoftheteststhataredeclaredwithintheclass,usingthe[Test]attribute.ThisisessentiallyanabstractclassthatrepresentsatestwithintheNUnit.Testframework,andensuresthateachtestisrunusingacleaninstanceoftheViewModel.

Next,weusedtheMockclassfromourMoqlibrarytocreateanewinstanceoftheIWalkNavServicewheninstantiatingtheWalksTrailViewModel.

Inthenextstep,weimplementedtheCheckIfWalkEntryIsNotNullinstancemethodthatwillperformachecktoseeifourWalksTrailViewModelhasbeenproperlyinitializedwhenevertheInitmethodhasbeencalled.Again,wedeclaredthe[Test]attributepriortoinitializingourWalkEntrymodeltonull,andpriortocallingtheInitmethodtochecktoseeiftheWalkEntry

modelhasbeenproperlysettothevalueprovidedintheInitmethod'sparameter.Afterthat,weusedtheIsNotNullmethodontheAssertclasstodisplayamessageshouldthetestfail.Thisissothatyoucantroubleshootthecodeatalaterpoint.

Inthenextsection,wewillbeginbycreatingthesecondunittestwhichwillberesponsibleforvalidatinginformationcontainedwithinourWalkEntryViewModeltoensurethatafterourViewModelhasbeeninitialized,wereceivetheexpectedresultsreturned.

CreatingandimplementingtheWalkEntryViewModelNUnittestclassIntheprevioussection,wecreatedtheNUnittestforourWalksTrailViewModelwhichcheckedtoensurethattheWalksEntrymodelwasproperlyinitializedaftertheInitmethodwascalled.Inthissection,wewillcreateanotherNUnittestthatwillchecktoseeifcertainpropertieswithinourWalksEntryViewModelhavebeensetupandinitialized.

Let'snowstarttoimplementthecoderequiredforourWalkEntryViewModelTestclassbyperformingthefollowingsteps:

1. CreateanemptyclasswithintheTrackMyWalks.UnitTestsprojectsolutionfolder,bychoosingAdd|NewFile....Ifyoucan'trememberhowtodothis,youcanrefertothesectionentitledCreatingandimplementingtheWalksTrailViewModelNUnittestclass,withinthischapter.

2. Then,enterinWalkEntryViewModelTestforthenameofthenewclassthatyouwanttocreate,andclickontheNewbuttontoallowthewizardtoproceedandcreatethenewfile.

3. Next,ensurethattheWalkEntryViewModelTest.csfileisdisplayedwithinthecodeeditor,andenterinthefollowingcodesnippet:

//

//WalkEntryViewModelTest.cs

//WalkEntryViewModelTestingFramework

//

//CreatedbyStevenF.Danielon23/09/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingNUnit.Framework;

usingTrackMyWalks.ViewModels;

usingTrackMyWalks.Services;

usingMoq;

usingSystem.Threading.Tasks;

namespaceTrackMyWalks.UnitTests

{

4. Next,weneedtomodifytheWalkEntryViewModelTestclassconstructorbyaddingthe[TestFixture]attributejustaswedidintheprevioussection.ThissetsupourclasstobeaninstanceoftheTestFixturetestingclass.Proceedandenterinthefollowingcodesnippet:

[TestFixture]

publicclassWalkEntryViewModelTest

{

WalkEntryViewModel_vm;

5. Then,createtheSetupinstancemethodthatwillberesponsibleforcreatinganewinstanceofourViewModelforeachoftheteststhataredeclaredwithintheclass.ThisistoensurethateachtestisrunusingacleaninstanceoftheViewModel.WethenusetheMockclassfromourMoqlibrarytocreateanewinstanceoftheIWalkNavServicewheninstantiating

theWalkEntryViewModel.Proceedandenterinthefollowingcodesnippet:

[SetUp]

publicvoidSetup()

{

varnavMock=newMock<IWalkNavService>().Object;

_vm=newWalkEntryViewModel(navMock);

}

6. Next,weneedtoimplementtheCheckIfEntryTitleIsEqualinstancemethodthatwillchecktoseeifourTitlepropertyhasbeenproperlyinitializedwhentheInitmethodhasbeencalled.We'lldeclarethe[Test]attributejustaswedidintheprevioustest,andthenwe'llproceedtoinitializetheTitlepropertyandcalltheInitmethodtocheckwhethertheTitlepropertyhasbeeninitializedcorrectlytothevalueprovidedintheInitmethod'sparameter.

7. Next,weusetheAreEqualmethodontheAssertclasstochecktoseeiftheTitlepropertyhasbeeninitializedcorrectly,andthendisplayamessagecontainingthevalueoftheTitlepropertyfromtheViewModel,shouldthetestfail.Proceedandenterinthefollowingcodesnippet:

[Test]

publicasyncTaskCheckIfEntryTitleIsEqual()

{

//Arrange

_vm.Title="NewWalk";

//Act

await_vm.Init();

//Assert

Assert.AreEqual("NewWalk",_vm.Title);

}

8. Then,weneedtoimplementtheCheckIfDifficultyIsEqualinstancemethodanddeclarethe[Test]attribute,priortoinitializingourDifficultypropertytoastringvalue,andthencallingtheInitmethod.Inthenextstep,weusetheAreEqualmethodontheAssertclasstocheckwhethertheDifficultypropertyhasbeeninitializedcorrectly,anddisplayamessagecontainingthevalueoftheDifficultypropertyfromtheViewModel,shouldthetestfail.Proceedandenterinthefollowingcodesnippet:

[Test]

publicasyncTaskCheckIfDifficultyIsEqual()

{

//Arrange

_vm.Difficulty="Easy";

//Act

await_vm.Init();

//Assert

Assert.AreEqual("Easy",_vm.Difficulty);

}

9. Next,weneedtoimplementtheCheckIfKilometersIsNotEqualinstancemethodthatdeclaresthe[Test]attributejustaswedidintheprevioustest.WetheninitializeourKilometerspropertytoaDoublevalue,andcalltheInitmethod.Inthenextstep,weusetheAreEqualmethodontheAssertclasstochecktoseeiftheKilometerspropertyhasbeeninitializedcorrectly,anddisplayamessagecontainingthevalueoftheKilometerspropertyfromtheViewModel,shouldthetestfail.Proceedandenterinthefollowingcodesnippet:

[Test]

publicasyncTaskCheckIfKilometersIsNotEqual()

{

//Arrange

_vm.Kilometers=40.0;

//Act

await_vm.Init();

//Assert

Assert.AreNotEqual(40.0,_vm.Kilometers);

}

}

}

Intheprecedingcodesnippet,webeganbyimplementingthevariousinstancemethodsthatwillberequiredtoperformeachtestforourWalkEntryViewModel.Weaddedthe[TestFixture]attributeatthebeginningoftheclassconstructorsothatitwillbeaninstanceoftheTestFixturetestingclass;andthenproceededtocreatetheSetupinstancemethodsothatitwillberesponsibleforcreatinganewinstanceofourViewModelforeachoftheteststhatisdeclaredwithintheclass,usingthe[Test]attributewhichisessentiallyanabstractclassthatrepresentsatestwithintheNUnit.Testframework,andensuresthateachtestisrunusingacleaninstanceoftheViewModel.

Next,weusedtheMockclassfromourMoqlibrarytocreateanewinstanceoftheIWalkNavServicewheninstantiatingtheWalkEntryViewModel.Inthenextstep,weimplementedtheCheckIfEntryTitleIsEqualinstancemethodthatwillperformachecktoseeiftheTitlepropertyhasbeenproperlyinitializedwhenevertheInitmethodhasbeencalled.Again,wedeclarethe[Test]attributepriortoinitializingtheTitlepropertyoftheWalksEntrymodel,andpriortocallingtheAreEqualmethodontheAssertclasstochecktoseeiftheTitlepropertyhasbeeninitializedcorrectly.WethendisplayedamessagecontainingthevalueoftheTitlepropertyfromtheViewModel,shouldthetestfail.

Next,weimplementedtheCheckIfDifficultyIsEqualinstancemethodthatwillinitializetheDifficultypropertytoastringvalue,andthencalltheInitmethodoftheWalkEntryViewModel.WecalledtheAreEqualmethodontheAssertclasstoconfirmthat,afterwecalltheInitmethod,thevalueoftheDifficultypropertyfromtheViewModelisthevaluethatweexpecttocomebackfromtheprovidedMockinstance.Ifthevalueisnotwhatweexpect,

thetestwillfailandwilldisplayamessagecontainingthevalueoftheDifficultypropertyfromtheViewModel.

Inourfinalstep,weimplementedtheCheckIfKilometersIsNotEqualinstancemethodthatinitializesourKilometerspropertytoaDoublevalue,andthencallstheInitmethod.JustaswedidinourCheckIfDifficultyIsEqualinstancemethod,weusedtheAreNotEqualmethodontheAssertclasstoconfirmthat,afterwecalltheInitmethod,thevalueoftheKilometerspropertyfromtheViewModelisthevaluethatweexpecttocomebackfromtheprovidedmockinstance.Ifthevalueisnotwhatweexpect,thetestwillfailandwilldisplayamessagecontainingthevalueoftheKilometerspropertyfromtheViewModel.

ForeachtestmethodthatyoucreatethatwillberepresentedbytheNUnit[Test]attribute,theArrange-Act-Assertpatternwillfollow.Thisisdescribedinthefollowingtable:

Testpattern Description

Arrange Thiswillessentiallyperformallthesettingupandinitializationconditionsforyourtest.

Act Thisensuresthatyourtestwillsuccessfullyinteractwiththeapplication.

Assert ThiswillexaminetheresultsoftheactionsthatwereinitiallyperformedwithintheActsteptoverifytheresults.

Youcannowsee,thatbyincorporatingtheNUnit.Frameworkwithinyourapplications,aswellasadoptingtheArrange-Act-Assertpattern,youcanessentiallyperformtestsonyourViewModelstoensurethattheresultsyouareexpectingarereturned.

Note

IfyouareinterestedinlearningmoreabouttheNUnit.Framework.Testclass,anditsassociatedmethods,pleaserefertotheinformationcontainedathttps://developer.xamarin.com/api/type/NUnit.Framework.Internal.Test/.

TolearnmoreabouttheNUnit.Framework.Assertclassandothermethodsthatyoucanusetohandlethedifferenttypesofassertions,pleaserefertotheinformationlocatedathttps://developer.xamarin.com/api/type/NUnit.Framework.Assert/.

Nowthatyouhavecreatedyourunittests,ournextstepistobeginrunningourtestsrightwithintheXamarinStudioIDE,whichwewillbecoveringinthenextsection.

RunningtheTrackMyWalks.UnitTestsusingXamarinStudioInourprevioussection,wecreatedandimplementedunittestsforboththeWalksTrailViewModelandtheWalkEntryViewModel.Thesecontainedsetsofvarioustestconditionsthatwecheckedagainst.

OurnextstepistobeginrunningtheseunittestsdirectlyfromwithintheXamarinStudiodevelopmentenvironment.

Let'slookathowwecanachievethiswiththefollowingsteps:

1. Torunaunittest,right-clickontheTrackMyWalks.UnitTestsprojectwithintheSolutionpane,andchoosetheRunItemoption,asshowninthefollowingscreenshot:

2. Alternatively,youcanalsoruntheunittestbyselectingtheTrackMyWalks.UnitTestssolutionprojectandthennavigatingtotheRunmenuoptionandchoosingtheRunUnitTestssub-menuitem.

Whenthecompilationoftheunittestshascompleted,youwillbepresentedwithalistshowingeachofyourtestresultsthathavepassed,failed,orwereignored.ThesearedisplayedwithintheTestResultspane,asshowninthefollowingscreenshot:

Shouldanyofyourtestsfail,thesewillbedisplayedwithintheTestResultspane,alongwiththeirassociatedStackTrace.YouwillalsonoticethatthemessagethatweprovidedwithintheAssert.AreEqualmethodwillalsobedisplayedaspartofthefailureresult:

Fromthisscreen,youhavetheoptionoffilteringyourtestresultsorre-runningyourunittestconditionsagain.Theseareexplainedinmoredetailinthefollowingtable:

Testresultoption Description

SuccessfulTests

Thiswilldisplayallthesuccessfullyexecutedtestswhichpassedtheconditionsasspecifiedwithinthetestcase.

InconclusiveTests

Thiswilldisplayanytestresultsthatwerefoundtobeinconclusive,meaningthatafirmresultcouldnotbedetermined.

FailedTests Thisoptiondisplaysalistofanyteststhatdidnotmeettheconditionsasspecifiedwithinthetestcasescenario.

IgnoredTests

Thisoptiondisplaysalistofanyteststhatwereignoredasspecifiedbythe[Ignore]attribute.

OutputThisoptiondisplaysaconsoleoutputforeachoftheteststhatareexecutedandwillcontainanyteststhathavesuccessfullypassed,failed,beenignored,orwerefoundtobeinconclusive.

RerunTests Thisoptionenablesyoutore-runyourtestsagain,withouttheneedforrecompilingyourtestcases.

NowthatyouhaveagoodunderstandingofhowtocreateyourownunittestsusingtheNUnittestingframework,wecannowlookathowtocreateanotherformofunittesting,whichiscalledautomatedUItesting.ThistimewewillbeleveragingtheUITestframeworkwhichwillenableustoperformtestsontheuserinterfaceportionofourTrackMyWalksappwhichwewillbecoveringoverthenextsections.

CreatingaUItestprojectusingXamarinStudioIntheprevioussection,wesawhoweasyitistocreateasetofunitteststhatenableustotestourViewModelswithintheTrackMyWalksproject.Whilstunittestingensuresthatasignificantamountofcodeistested,itisprimarilyfocusedontestingtheactualbusinesslogicwithintheapp.Thisleavestheuserinterfaceportionsoftheappstilluntested,butthebeautyofusingUItestingallowsustoautomatespecificactionswithinourapp'suserinterfacetoensurethatitisworkingasexpected.

Fortunately,XamarinStudioprovidesyouwitharichsetoftoolsforperformingautomatedUItests,andthesecanbebothwritteninC#andmakeuseoftheUITestframework.Let'sstartbycreatinganewUITestprojectwithinourTrackMyWalks.Testsprojectsolution,byperformingthefollowingsteps:

1. Right-clickontheTrackMyWalks.TestssolutionprojectandchoosetheAdd|AddNewProject...menuoption.Ifyoucan'trememberhowtodothis,youcanrefertothesectionentitledCreatingaunittestprojectusingXamarinStudio,locatedwithinthischapter.

2. Next,choosetheUITestAppoptionlocatedwithintheXamarinTestCloudsection,undertheMultiplatform|Testssection.EnsurethatyouhaveselectedC#astheprogramminglanguagetouse,asshowninthefollowingscreenshot:

3. Then,clickontheNextbuttontoproceedtothenextstepinthewizard.4. Next,enterTrackMyWalks.UITeststouseasthenameforyournewprojectastheProject

Namefield.5. Then,ensurethattheCreateaprojectdirectorywithinthesolutiondirectory.hasbeen

selected,asshowninthefollowingscreenshot:

6. Finally,clickontheCreatebuttontosaveyourprojectatthespecifiedlocation.

Onceyourprojecthasbeencreated,youwillbepresentedwiththeXamarinStudiodevelopmentenvironment,withyournewprojectcreatedwithintheTrackMyWalks.Testssolutionfolder.

YouwillnoticethatbydefaultourprojecthascreatedafilenamedTest.csthatwecanusetowriteourUITests,aswellasaclassnamedAppInitializer.csthatisessentiallyusedbytheTest.csclasstocreateanIAppinstanceandstarttheappforeachtestcondition.SincewewillonlybecreatingoneUITestforthischapter,wecanessentiallyjustusetheTest.csfilefornow.

Inanidealworld,youwouldbecreatingvarioustests,oneforeachtestcondition,soitwouldmakesensetobreakeachofyourUITestsintoindividualfiles.Inthenextsection,wewilllearnaboutsomeofthecommonlyusedUITestmethodsthatwecanusewhileperformingUITestsforourapplication'suserinterface.

UnderstandingthecommonlyusedUITestmethodsAsmentionedpreviously,inthissection,wewilllearnaboutsomeofthecommonlyusedmethodsthatwecanusewiththeUITestframework.TheUITestframeworkprovidesyouwithawayofautomatingtheinteractionsbetweenyouriOS,orAndroidappsusingC#andtheNUnittestingplatform.

WewillbeusinganinstanceoftheIAppandConfigureAppclassesthatwillbeusedtocreateouriOSandAndroidIAppinstancestohandlealltheinteractionswithintheUI.

Asweprogressthroughoutthenextcoupleofsections,wewillbetakingacloserlookathowtocreateIAppinstancesusingtheConfigureAppclass.TheUITestframeworkprovidesyouwithseveralAPIsthatyoucanusetointeractwithanapp'suserinterface.

ThefollowingtabledescribessomeofthemorecommonlyusedmethodsandtheonesthatwewillbeusingtotesttheTrackMyWalksapp:

UITestmethods Description

Screenshot() Thiswillessentiallytakeascreenshotofthecurrentstateoftheapp.

Tap()Thisisusedtosendatapinteractiontoaspecificelementontheapp'scurrentscreen.

EnterText()andClearText()

ThesemethodsareusedtoaddandremovetextfrominputelementssuchastheentryviewsusedwithinXamarin.Forms.

Query()Thismethodisessentiallyusedtolocateorfindelementsthatarecurrentlydisplayedwithintheapp'sscreen.

Repl()Thiscommandiscommonlyusedtointeractinreal-timewiththeappthroughtheterminalusingtheUITestAPI.

WaitForElement()Thismethodisusedtopausethetestuntilaspecificelementappearsontheapp'scurrentscreenwithinaspecifictimeoutperiod.

MethodssuchastheQueryandWaitForElementreturnanAppResult[]objectthatyoucan

essentiallyusetodeterminetheresultsofthecall.AnexamplewouldbethatifyouusedtheQuerymethodcallthatreturnsanemptyresultset,wecanbesurethattheelementdoesnotexistwithintheapp'scurrentscreen.

Note

ItisworthmentioningthatcurrentlytheUITestframeworkonlyprovidessupportforboththeiOSandAndroidplatformsanddoesn'tyetprovidesupportfortheWindowsPhoneplatform.

Asyouwillseefromthemethodsdisplayedinthefollowingtable,theseareessentiallyallthememberspertainingtotheAppQueryclassthatareusedbytheQueryandWaitForElementmethodmembersoftheIAppmethods:

AppQueryclassmethods Description

Class() Findselementsontheapp'scurrentscreen,basedontheirclasstype.

Marked()Findselementswithintheapp'scurrentscreen,basedontheirtextoridentifier.

Css()PerformsCSSselectoroperationsonthecontentsofaWebViewontheapp'scurrentscreen.

Note

IfyouareinterestedinlearningmoreaboutthevarioustypesofUITestmethods,pleaserefertotheIntroductiontoXamarin.UITestathttps://developer.xamarin.com/guides/testcloud/uitest/intro-to-uitest/.

NowthatyouunderstandsomeofthemostcommonlyusedUITestmethods,wecanstarttoimplementsometestswhichwewillbecoveringoverthenextcoupleofsectionswithinthischapter.

SettingupandinitializingourTrackMyWalksappforUITestPriortostartinganappandinteractingwithitusingtheUITestframework,weneedtodosomepreliminaryinitializationstepsforwhichwe'llmakesomemodificationswithintheAppInitializerclass.TheAppInitializerclasscontainsastaticmethodcalledStartApp.

Thisstaticmethodiscalledeachtimethetest'sSetupmethodiscalledtogetanIAppinstance.ItcurrentlysupportsboththeiOSAppandAndroidAppasdefinedbytheConfigureAppclass.

OnethingthatyouwillnoticewithintheAppInitializerclassisthattheApkFileandtheAppBundlehavebothbeencommentedout.YouwillneedtouncommenttheseifyouwouldliketorunthetestslocallywithintheunittestpaneusingXamarinStudio.

usingSystem;

usingSystem.IO;

usingSystem.Linq;

usingXamarin.UITest;

usingXamarin.UITest.Queries;

namespaceTrackMyWalks.UITests

{

publicclassAppInitializer

{

publicstaticIAppStartApp(Platformplatform)

{

...

...

...

if(platform==Platform.Android)

{

returnConfigureApp.Android

//TODO:Updatethispathtopoint

toyourAndroid

//appanduncommentthecodeifthe

appisnot

//includedinthesolution.

//.ApkFile("../../../Droid/bin/Debug

/TrackMyWalks.apk").StartApp();

}

returnConfigureApp.iOS

//TODO:Updatethispathtopointto

youriOSappand

//uncommentthecodeiftheappis

notincludedinthe

//solution.

//.AppBundle("../../../iOS/bin/

iPhoneSimulator/Debug/TrackM

//yWalks.iOS.app").StartApp();

}

}

}

Asyoucanseefromtheprecedingcodesnippet,theAppInitializerclasscontainsseveraldifferentmethodsthatarepartoftheConfigureAppmethod.Thefollowingtableprovidesabriefdescriptionofwhateachoneisusedfor:

ConfigureAppmethods Description

AppBundle()Thismethodisusedforspecifyingthepathtotheappbundletouseduringtesting.

StartApp() Thismethodessentiallylaunchestheappwithinthesimulator.

Debug()

Thismethodisessentiallyusedtoenabledebuggingandloggingofmessagesandisparticularlyusefulifyouneedtotroubleshootproblemswhenrunningtheapplicationusingthesimulator.

DeviceIdentifier()

Thismethodconfiguresthedevicetousewiththedeviceidentifier.ThiscanbeusedtodetectiOSsimulatorsusingthefollowingcommandlinestatement:

xcruninstruments-sdevices

EnableLocalScreenshots

Thismethodisusedtoenablescreenshotswhenyou'rerunningtestslocally.Bydefault,screenshotsarealwaysenabledwhenevertestsarebeingrunusingXamarinTestCloud.

Repl()ThismethodwillessentiallypausethetestexecutionandinvoketheREPLinaterminalprompt.

Asyoucansee,theAppInitializerfiledoesn'tcontainmuchinformation,butasweworkourwaythroughthischapter,wewillbeaddingtheXamarin.TestCloud.AgenttotheiOSportionoftheTrackMyWalksappsothatwewillbeabletorunourUITests.

ImplementingtheCreateNewWalkEntryusingtheUITest.FrameworkIntheprevioussection,welookedatsomeofthedifferenttypesofmethodsthatwecanusetocustomizetheAppInitializerclasssothatwecanspecifydifferentappbundlestouseduringtesting,aswellastoenablescreenshots,andprovidetheabilitytodebugourunittestswithintheXamarinStudioenvironment.

Inthissection,wewillbeginbyimplementingaUITestthatwecanusetohandlesigningintoFacebookandcreatinganewwalkentryusingtheUITestframework.

Let'snowstarttoimplementthecoderequiredforourclassbyperformingthefollowingsteps:

1. OpentheTest.csfilewhichcanbelocatedwithintheTrackMyWalks.UITestsprojectaspartoftheTrackMyWalks.Testssolutionfolder.

2. Next,ensurethattheTest.csfileisdisplayedwithinthecodeeditor,andenterinthefollowinghighlightedcodesections,asshowninthecodesnippet:

usingSystem;

usingSystem.IO;

usingSystem.Linq;

usingNUnit.Framework;

usingXamarin.UITest;

usingXamarin.UITest.Queries;

namespaceTrackMyWalks.UITests

{

[TestFixture(Platform.Android)]

[TestFixture(Platform.iOS)]

publicclassTests

{

IAppapp;

Platformplatform;

3. Then,modifytheTestsinstancemethodthatwillberesponsibleforcreatinganewinstanceofourIAppinstance.WeupdateourentryCellPlatformClassNamestringvariabletoreturnthetypeofTextField,dependentontheplatformthatwearetestingon.UnderiOS,weusetheUITextField,whereasunderAndroiditwillusethedefaultEntryCellEditTextclass.Proceedandenterinthehighlightedcodesectionswithinthefollowingcodesnippet:

stringentryCellPlatformClassName;

publicTests(Platformplatform)

{

this.platform=platform;

entryCellPlatformClassName=platform

==Platform.iOS

?"UITextField"

:"EntryCellEditText";

}

[SetUp]

publicvoidBeforeEachTest()

{

app=AppInitializer.StartApp(platform);

}

[Test]

publicvoidAppLaunches()

{

app.Screenshot("Firstscreen.");

}

4. Next,createtheSignInToFacebookinstancemethodthatwillberesponsibleforhandlingthetest'sstepsspecificallytotheFacebooksign-inprocess.Thisusestheuser'slogincredentialstoautomatetheloginprocesspriortocarryingoutotherstepswithintheUI.Proceedandenterinthehighlightedcodesectionswithinthefollowingcodesnippet:

//PerformsigningintoFacebook

publicvoidSignInToFacebook()

{

//SetupourFacebookcredentials

varFaceBookEmail="<Your-Facebook-Email-Address>";

varFaceBookPassword="<Your-Facebook-Password>";

//WaitforLoginbuttonwithinFacebookoAuthwebview

//toappear.

app.WaitForElement(x=>x.WebView().Css("[name=login]"));

//Entertextwithinthewebviewwithname="email"

app.EnterText(x=>x.WebView().Css("[name=email]"),

FaceBookEmail);

//Entertextwithinthewebviewwithname="email"

app.EnterText(x=>x.WebView().Css("[name=pass]"),

FaceBookPassword);

app.ScrollDownTo(x=>x.WebView().Css("[name=login]"));

//Tapthebuttoninthewebviewwithname="login"

app.Tap(x=>x.WebView().Css("[name=login]"));

}

5. Then,createthePopulateEntryCellFieldsinstancemethodthatwillberesponsibleforhandlingtheteststepsspecificallyforthecreationofanewwalkentry.Inthismethod,wemakeuseofboththeClearTextandEnterTextmethodsoftheUITestframeworkthatwilllocateeachentryfieldwithintheNewWalkEntryformandpopulateitwiththenecessaryinformation.TheDismissKeyboardmethodwill,asthenamesuggests,dismissthekeyboardfromtheviewandcontinuetothenextstep.Proceedandenterinthehighlightedcodesectionswithinthefollowingcodesnippet:

//PopulateourEntryCellFields

voidPopulateEntryCellFields()

{

//ClearthedefaulttextentryforourTitleEntryCell

app.ClearText(x=>x.Class

(entryCellPlatformClassName).Index(0));

app.DismissKeyboard();

//EnterinsomedefaulttextforourTitleEntryCell

app.EnterText(x=>x.Class

(entryCellPlatformClassName).Index(0),

"ThisisanewwalkEntry");

app.DismissKeyboard();

//EnterinsomedefaulttextforourNotesEntryCell

app.EnterText(x=>x.Class(entryCellPlatformClassName).Index(1),

"NewNoteEntryForWalkEntry");

app.DismissKeyboard();

//ClearthedefaulttextforourImageUrlEntryCell

app.ClearText(x=>x.Class(entryCellPlatformClassName).Index(6));

app.DismissKeyboard();

//EnterinsomedefaulttextImageUrlEntryCell

app.EnterText(x=>x.Class(entryCellPlatformClassName).Index(6),"

https://heuft.com/upload/image/

400x267/no_image_placeholder.png");

app.DismissKeyboard();

}

6. Next,createtheChooseDifficultyPickerinstancemethodthatwillberesponsiblefordisplayingthedifficultypickerthatcontainsvariouschoicesofdifficultyfortheusertochoosefrom.Allwearedoinghereisdisplayingthepickerwhentheusertapsintothecellentry,anddismissingthepickerfromtheviewwhenthenusertapstheDoneorOKbuttons.Proceedandenterthehighlightedcodesections,asshownwithinthefollowingcodesnippet:

//AutomaticallytapintotheDifficultyCelltodisplaythe

//DifficultyPicker,anddismissitbypressingtheDoneor

//OKbutton.

publicvoidChooseDifficultyPicker()

{

//TapintoDifficultyEntryCell

app.Tap(x=>x.Class(entryCellPlatformClassName).Index(5));

//TapDonelocatedwithintheDifficultyPickerCell

if(platform==Platform.iOS)

app.Tap(x=>x.Marked("Done"));

else

app.Tap(x=>x.Marked("OK"));

}

}

7. Then,createtheCreateNewWalkEntryUITestmethodthatincludesthe[Test]attribute.ThisisanabstractclassthatrepresentsatestwithintheUITestandNUnit.Testframework.Thismethodwillessentiallybethemaintestdriverandwillcalleachoftheotherinstancemethodsthatwe'vepreviouslydeclared.Withinthismethod,wecallourSignInToFacebookmethodtoperformthesign-intoFacebook,usingtheuser'sFacebookcredentials.

8. Uponsuccessfullogin,weusetheWaitForElementmethodtowaituntilthemainTrackMyWalksscreenhasbeendisplayed,priortousingtheAssert.IsTruemethodtochecktoseeiftheTrackMyWalksscreenisdisplayed.Ifit'snotdisplayed,ourtestwillfailanddisplaytheassignederrormessagewithintheTestResultsscreen.Proceedandenterthefollowingcodesectionsshownwithinthefollowingcodesnippet:

[Test]

publicvoidCreateNewWalkEntry()

{

//SignintoFacebook

SignInToFacebook();

//Waitformainscreentoappearandcheckforour

//navigationtitle.

varnavigationBarTitle=(platform==Platform.iOS?

"TrackMyWalks-iOS":

"TrackMyWalks-Android");

varmainScreen=app.WaitForElement(x=>x.Marked

(navigationBarTitle).Class("UINavigationBar"));

//ChecktoseeiftheTrackMyWalks-iOSmainscreenis

//displayed.

Assert.IsTrue(mainScreen.Any(),navigationBarTitle+"screen

wasn'tshownaftersigningin.");

9. Next,weusetheTapmethodoftheappclassinstanceandtheMarkedmethodtofindtheAddelementwithintheapp'scurrentscreen,andwaituntiltheNewWalkEntrypageisdisplayedwithinthescreen.WethenproceedtousetheAssert.IsTruemethodtochecktoseeiftheNewWalkEntryscreenhasbeensuccessfullydisplayed.Alternatively,ourtestwillfailanddisplaytheassignederrormessagewithintheTestResultsscreen.Proceedandenterinthehighlightedcodesectionsshownwithinthefollowingcodesnippet:

//ClickontheAddbuttonfromourmainscreenandwaitfor

//theNewWalkEntryscreentoappear.

app.Tap(x=>x.Marked("Add"));

varnewWalkEntryBarTitle="NewWalkEntry";

varnewWalkEntryScreen=app.WaitForElement(x=>x.Marked(newWalk

EntryBarTitle));

//ChecktoensurethatourNewWalkEntryscreenwasdisplayed.

Assert.IsTrue(newWalkEntryScreen.Any(),newWalkEntryBarTitle+"

screenwasnotshownaftertappingtheAddbutton.");

10. Then,wecallthePopulateEntryCellFieldsandChooseDifficultyPickerinstancemethodstopopulateourentrycellfieldsandhandlethedisplayofthedifficultypickerselector.Inourfinalsteps,weusetheTapmethodoftheappclassinstance,andweusetheMarkedmethodtofindtheSaveelementwithintheapp'scurrentscreen.We'llwaituntiltheTrackMyWalkspageisdisplayed,beforeusingtheAssert.IsTruemethodtochecktoseeiftheTrackMyWalksscreenhasbeensuccessfullydisplayed.Alternatively,ourtestwillfailanddisplaytheassignederrormessagewithintheTestResultsscreen.Proceedandenterthefollowinghighlightedcodesections,asshownwithinthefollowingcodesnippet:

//PopulateourEntryCellFields

PopulateEntryCellFields();

//DisplayourDifficultyPickerselector.

ChooseDifficultyPicker();

//ThentapontheSavebuttontosavethedetailsandexit

//thescreen.

app.Tap(x=>x.Marked("Save"));

//Next,waitformainscreentoappear

mainScreen=app.WaitForElement(x=>x.Marked

(navigationBarTitle).

Class("UINavigationBar"));

//ChecktoseeiftheTrackMyWalks-iOSmainscreenis

//displayed.

Assert.IsTrue(mainScreen.Any(),navigationBarTitle

+"screenwasn'tshownaftersigningin.");

}

}

}

Intheprecedingcodesnippet,webeganbyimplementingthevariousinstancemethodsthatwillberequiredtoperformeachtestforourCreateNewWalkEntry.Weaddedthe[Test]attributetoourCreateNewWalkEntryinstancemethod.Thismethodwillessentiallybethemaintestdriverandcalleachoftheotherinstancemethodsthatwe'vepreviouslydeclared.Withinthismethod,wecallourSignInToFacebookmethodtoperformthesign-intoFacebook,usingtheuser'sFacebookcredentials.Uponsuccessfullogin,weusetheWaitForElementmethodtowaituntilthemainTrackMyWalksscreenhasbeendisplayed.

Inthenextstep,weusedtheTapmethodoftheAppclassinstance,andusetheMarkedmethodtofindtheAddelementwithintheApp'scurrentscreen,andwaituntiltheNewWalkEntrypageisdisplayedwithinthescreen.WethenproceedtousetheAssert.IsTruemethodtochecktoseeiftheNewWalkEntryscreenwassuccessfullydisplayed.Alternatively,ourtestwillfailanddisplaytheassignederrormessagewithintheTestResultsscreen.

AddingtheXamarinTestCloudAgenttotheiOSprojectNowthatyouhavecreatedyourUITest,thenextstepistoaddtheXamarinTestCloudAgentNuGetpackagetoourTrackMyWalks.iOSproject.ThislibraryallowsyoutoexecuteyourXamarin.UITest,usingC#andtheNUnitframeworktovalidatethefunctionalityofiOSandAndroidappswithintheXamarinStudiodevelopmentenvironment.

Let'slookathowtoaddtheXamarinTestCloudNuGetpackagetoourTrackMyWalks.iOSproject,byperformingthefollowingsteps:

1. Right-clickonthePackagesfolderthatiscontainedwithintheTrackMyWalks.iOSproject,andchoosetheAddPackages...menuoption,asyoudidinthesectionentitled,AddingtheMoqNuGetpackagetotheunittestproject,locatedwithinthischapter.

2. ThiswilldisplaytheAddPackagesdialog.EnterinCloudAgentwithinthesearchdialog,andselecttheXamarinTestCloudAgentoptionwithinthelist,asshowninthefollowingscreenshot:

3. Finally,clickontheAddPackagebuttontoaddtheNuGetpackagetothePackagesfolder

containedwithintheTrackMyWalks.iOSproject.

NowthatyouhaveaddedtheXamarinTestCloudAgentNuGetpackage,ournextstepistobeginbymodifyingtheAppDelegateclasswithintheTrackMyWalks.iOSportionofourproject.Wewillbecoveringthisinthenextsection.

UpdatingtheTrackMyWalksAppDelegateclasstohandleXamarinTestCloudAgentPriortorunningUITestswithinXamarinStudio,wewillneedtoaddtheXamarinTestCloudAgentNuGetpackagetotheiOSportionofourTrackMyWalksapp.UnderAndroid,thisisnotrequiredastheXamarinTestCloudAgentisprovidedbytheUITestframework.

Inthissection,weneedtoupdatetheAppDelegateclass,bymodifyingtheFinishLaunchingmethodlocatedwithinourTrackMyWalks.iOSproject.ThiswillincludeacompilerdirectivethatwillstarttheXamarinTestCloudAgent.

Let'slookathowwecanachievethiswiththefollowingsteps:

1. OpentheAppDelegate.csfilelocatedwithintheTrackMyWalks.iOSproject.2. Next,ensurethattheAppDelegate.csfileisdisplayedwithinthecodeeditor,andlocate

theFinishLaunchingmethodandenterinthefollowinghighlightedcodesections:

//

//AppDelegate.cs

//TrackMyWalks

//

//CreatedbyStevenF.Danielon04/08/2016.

//Copyright©2016GENIESOFTSTUDIOS.Allrightsreserved.

//

usingFoundation;

usingUIKit;

namespaceTrackMyWalks.iOS

{

[Register("AppDelegate")]

publicpartialclassAppDelegate:global::Xamarin.Forms.

Platform.iOS.FormsApplicationDelegate

{

publicoverrideboolFinishedLaunching

(UIApplicationapp,NSDictionaryoptions)

{

global::Xamarin.Forms.Forms.Init();

//IntegrateXamarinFormsMaps

Xamarin.FormsMaps.Init();

#ifUSE_TEST_CLOUD

Xamarin.Calabash.Start();

#endif

LoadApplication(newApp());

returnbase.FinishedLaunching(app,options);

}

}

}

Note

CalabashisbasicallyanAutomatedUIAcceptanceTestingframeworkthatallowsyoutowriteandexecuteteststhatvalidatethefunctionalityofyouriOSandAndroidapps.

Intheprecedingcodesnippet,youwillnoticethatwehavedefinedtheUSE_TEST_CLOUDcompilervariablethatiswrappedwithinthe#ifand#endifdirectiveandincludesacalltotheXamarin.Calabash.Start()methodthatwillonlybestartedwhenithasbeendefinedunderspecificconfigurationsasdefinedwithinthecompilerconfigurationsettingsfortheproject.

Note

IfyouareinterestedinlearningmoreabouttheCalabashframework,pleaserefertothesectiononanIntroductiontoCalabashwhichislocatedathttps://developer.xamarin.com/guides/testcloud/calabash/introduction-to-calabash/.

WehavejustaddedinthecodethatwillessentiallystartourXamarinTestCloudfunctionality.However,forthistowork,wewillneedtoperformoneadditionalstep,whichistomodifythecompilerconfigurationsforourTrackMyWalks.iOSproject.Performthefollowingtoachievethis:

1. Right-clickontheTrackMyWalks.iOSproject,andchoosetheOptionsmenuoption.2. Next,withintheProjectOptions-TrackMyWalks.iOSdialog,choosetheCompiler

optionlocatedundertheBuildsection.3. Then,ensurethatyouhavechosendebugfromtheConfigurationdropdownandthatyou

havechoseniPhoneSimulatorfromthePlatformdropdown.4. Next,addtheUSE_TEST_CLOUD;tothelistofexistingDefineSymbols,asshowninthe

followingscreenshot:

5. Then,clickonOKtosaveyourchangesandclosetheProjectOptions-TrackMyWalks.iOSdialog.

NowthatyouhavemodifiedthecompilerconfigurationsforouriOSportionoftheTrackMyWalksapp,wecanfinallybuildandrunourUITestsusingXamarinStudio,similarlytowhatwedidwhenexecutingourNUnittests.However,thisneedstobehandledverydifferently,andwewillbecoveringthisinthenextsection.

RunningtheTrackMyWalksUITestsusingXamarinStudioPriortorunningyourUITestswithinXamarinStudio,youwillneedtoaddyouriOSorAndroidappstotheTestAppsnodeoftheUnitTestspane,oralternativelyspecifyingapathtoyourappwithintheAppInitializerclass.Ifyoudon'tdothis,yourtestswillcontinuetofailuntilyouaddtheseprojectstoyoursolution.

Inthissection,wewilllookathowtogoaboutaddingyourappstotheTestAppsnodewithintheUnitTestspane.Let'slookathowwecanachievethiswiththefollowingsteps:

1. ToaddyouriOSandAndroidappstoyourTrackMyWalks.UITestsproject,selecttheViewmenuoption,thenchoosethePadssub-menuitem,andthentheUnitTestsoption,asshowninthefollowingscreenshot:

2. Next,right-clickontheTestAppsitemwithintheUnitTestspaneandclickontheAddAppProject.ThiswilldisplaytheSelectaprojectorsolutiondialogthatallowsyoutoselecteachofyourprojectsforthevariousplatforms,asshowninthefollowingscreenshot:

3. OnceyouhaveselectedtheprojectsthatyouwouldliketoaddtoyourTrackMyWalks.UITestssolution,clickOKanddismissthedialog.

Note

Ifforsomereason,youdon'tseeyouriOSappprojectlisted,youmayhaveforgottentoaddtheXamarinTestCloudAgentNuGetpackagetoyouriOSproject.

OnceyouhavesuccessfullyaddedyourprojectstotheTrackMyWalks.UITestssolutionproject,ournextstepistorunourappandseetheresults:

4. TorunyourUITests,selecttheRunmenuoption,thenchoosetheRunUnitTestsmenuitem,asshowninthefollowingscreenshot.

Whenyourappstartstorun,theUITestframeworkwillautomaticallydeployyourapptotheiOSorAndroidsimulator,andthenruntheappandprocessthrougheachofthestepsthatyouhavespecifiedwithinyourtestmethods.TheresultsfromeachofthetestswillappearwithintheTestResultspanewithinXamarinStudio.

SummaryInthischapter,weupdatedtheTrackMyWalksapplicationbyaddinganewprojectsolution,TrackMyWalks.Tests,sothatwecanseparateourtestsfromthemainPortableClassLibrary.Thisgivesustheabilitytowritetestcases.WeaddedtheMockframeworksothatitwillprovideuswiththeabilitytosuccessfullytestourViewModelsaswellastoprovidethebusinesslogicbehindthem.

WethenmovedontoconsideringhowwecanleveragetheUITestframeworktowrite,test,andexecuteUItestslocallybyusingtheXamarinTestCloudAgentandtheCalabashframework,byaddingtheiOSandAndroidprojectstotheUITestsolutionproject.

Inthefinalchapter,you'lllearnhowtoprepareyouriOSappforsubmissiontoiTunesConnect,andlearnhowtosetupinternalandexternaluserswithinTestFlightsothatyouruserscandownloadandtestyourappsontheiriOSdevices.Toendthechapter,youwilllearnhowtocode-signyourAndroidappsbeforepublishing,andreleasingyourAndroidAPKfiletotheGooglePlayStore.

Chapter10.PackagingandDeployingYourXamarin.FormsApplicationsInourpreviouschapter,weupdatedourTrackMyWalksapplicationtoallowustocreateandrununittestsusingtheNUnitandUITesttestingframeworksrightwithintheXamarinStudioIDE.YoulearnedhowtowriteunittestsforourViewModelstotestthebusinesslogictovalidatethateverythingisworkingcorrectly,beforemovingontotestingtheuserinterfacesportionusingautomatedUItesting.

Inthischapter,you'lllookatwhatisrequiredtosubmityourTrackMyWalksiOSapptotheAppleAppStore,andshareyourcreationswiththerestofthecommunity.

You'lllearnthestepsrequiredtosetupyouriOSdevelopmentteam,aswellasthecertificatesforbothdevelopmentanddistribution,andlearnhowtocreatethenecessaryprovisioningprofilesforbothyourdevelopmentanddistributionbuilds,andcreatethenecessaryappIDsforyourapplication.

Attheendofthechapter,youwilllearnhowtoregisteryouriOSdevicessothatyouruserscandownloadandtestyourappsontheiriOSdevicesandlearnhowtoprepareyourTrackMyWalksiOSappforsubmissiontoiTunesConnect,usingtheXamarinStudioIDE.

Thischapterwillcoverthefollowingtopics:

SettingupyouriOSdevelopmentteamCreatingtheTrackMyWalksiOSdevelopmentcertificateObtainingthedevelopmentcertificatefromAppleRegisteringyouriOSdevicesfortestingCreatingyourTrackMyWalksiOSAppIDCreatingthedevelopmentprovisioningprofilesPreparingyourTrackMyWalksiOSappforsubmissionUsingtheprovisioningprofilestoinstalltheappontheiOSdeviceBuildingandarchivingyourappforpublishingusingXamarinStudioUsingXamarinStudiotosubmityourTrackMyWalksiOSapptoiTunesConnect

CreatingandsettingupyouriOSdevelopmentteamYouhavefinallycompletedbuildingyourTrackMyWalksappandarereadytoreleaseittotherestoftheworld;allyouneedtodoisdecidehowtodeployandmarketit.BeforeyoucanbeginsubmittingyouriOSapplicationstotheAppleAppStoreforapproval,youwillneedtofirstsetupyouriOSdevelopmentteam,whichcanbeachievedbyfollowingthesesteps:

1. LogintotheiOSdeveloperportalwebsiteathttp://developer.apple.com/.2. ClickontheMemberCenterlinkthatislocatedrightatthetopofthescreen.3. SignintoyouraccountusingyourAppleIDandpassword.Thiswillthendisplaythe

developerprogramresourcespage,asshowninthefollowingscreenshot:

4. Next,clickontheiTunesConnectbutton,ashighlightedintheprecedingscreenshot.ThisiswhereyoucancheckonvariousthingssuchasSalesandTrends,PaymentsandFinancialReports,andAppAnalytics.Takealookatthefollowingimage:

5. Next,clickontheUsersandRolesbutton,ashighlightedintheprecedingscreenshot.ThiswillbringuptheUsersandRolesoptionpanefromwhereyoucanaddanewuser,asshowninthefollowingscreenshot:

Note

TheUsersandRolesscreenallowsyoutoaddyourselforthepeoplewithinyourorganizationwhowillbeabletologintotheiOSdeveloperprogramportal,testappsoniOSdevices,andaddadditionaliOSdevicestotheaccount.

6. EnsurethatyouarewithintheiTunesConnectUserssection,ashighlightedintheprecedingscreenshot.Then,clickonthe+buttontobringuptheAddNewUserscreenthatisshowninthefollowingscreenshot.

7. Next,fillintheUserInformationsectionforthepersonthatyouwillbeaddingtoyourdevelopmentteam.Onceyouhavefinished,clickontheNextbutton,asshowninthe

followingscreenshot:

8. Next,undertheRolesection,fromthelistofrolesavailable,choosewhatrolestheusercanperformandthenclickontheNextbutton,asshowninthefollowingscreenshot:

9. Next,fromundertheNotificationsandSettingssections,thisiswhereyouwillbeassigningthewaysinwhichyouwanttheusertobenotified.Fromthisscreen,youalsohavetheabilityofspecifyingwhatinformationrelatingtoalistofterritoriesyouwanttheusertobenotifiedabout,asshowninthefollowingscreenshot:

10. Onceyouhavefinishedspecifyingeachofthedifferenttypesofnotificationmethods,clickontheSavebutton,asshownintheprecedingscreenshot.Thenewuseraccountwillthenbecreated,alongwithaconfirmatione-mailthatwillbesenttotheusers,accounts,requestingthemtoactivatetheiraccount:

Nowthatwehavecoveredthenecessarystepsrequiredtocreateandassignrolestonewusers,

aswellassettingupwhichuserrolescanlogintotheiOSdeveloperportaltomanagenewandexistingusers,viewSalesandTrendsreports,aswellasPaymentsandFinancialstatements,ournextstepistolookatthestepsinvolvedtogenerateaniOSdevelopmentcertificate.

Thiscertificateisencryptedandservesthepurposeasyourdigitalidentificationsignature,andyoumustsignyourappsusingthiscertificatebeforeyoucansubmityourappstotheAppleAppStore.

CreatingtheTrackMyWalksiOSdevelopmentcertificateInthissection,youwilllearnhowtocreatetheiOSdevelopmentcertificatethatwillenableustorunandtestourTrackMyWalksappontheiOSdevice.WewillbeginbygeneratingtheiOSdevelopmentcertificate,whichwillbeencryptedandwillservethepurposeofidentifyingyoudigitally.

YouwillthenneedtosignyourappsusingthiscertificatebeforeyoucanrunandtestanyapplicationthatyoudeveloponyouriOSdevice.Tobegin,performthefollowingsimplesteps:

1. LaunchtheKeychainAccessapplication,whichcanbefoundinthe/Applications/Utilitiesfolder.

2. Next,choosetheRequestaCertificateFromaCertificateAuthority...menuoptionfromtheKeychainAccess|CertificateAssistant,asshowninthefollowingscreenshot:

3. Then,weneedtoprovidesomeinformationbeforethecertificatecanbegeneratedundertheCertificateInformationsection.

4. Next,enterintherequiredinformation,asshowninthefollowingscreenshot,whilstensuringthatyouhaveselectedtheSavedtodiskandtheLetmespecifykeypairinformationoptions:

5. Oncealltheinformationhasbeenfilledout,clickontheContinuebutton.Youwillthenbeaskedtospecifyanameforthecertificate.Acceptthedefaultsuggestedname,andclickontheSavebutton.

Note

Atthispointthecertificateisbeingcreatedatthelocationspecified.YouwillthenbeaskedtospecifytheKeySizeandAlgorithmtouse.

6. Next,acceptthedefaultof2048bitsandRSAalgorithm.WeneedtoprovidesomeinformationbeforethecertificatecanbegeneratedundertheCertificateInformationsection,asshowninthefollowingscreenshot:

7. ClickontheContinuebuttonandthenclickontheDonebuttonwhenthefinalscreenappears.

Upuntilnow,youlearnedhowtogenerateacertificaterequestforiOSdevelopment,usingtheCertificateSigningRequest(CSR)usingthepre-installedMacOSXKeychainAccessapplication,sothatwehavetheabilityofcode-signingourapplications,whichwillenableustodeployourapplicationstotheiOSdeviceforbothdevelopmentandtesting.

Inournextstep,wewilllearnhowtorequestadevelopmentcertificatefromApplethatwillprovideuswiththeabilityofcode-signingourapplicationsusingourgeneratedcertificateinformationfilethatwecreatedinthissection.

ObtainingtheiOSdevelopmentcertificatefromAppleInthissection,wewilllearnhowtoobtainthedevelopmentcertificatefromApple,toenableustobegindevelopingapps.

BeforeyoucanbeginsubmittingyourapplicationtotheAppleAppStore,youwillneedtoobtainyourowncopyoftheiOSdevelopmentcertificate.Thiscertificateisbasicallyyouruniqueidentityforeachofyourappsthatyousubmitforapproval,solet'sgetstarted:

1. LogintotheiOSdeveloperportalwebsiteathttp://developer.apple.com/.2. ClickontheMemberCenterlinkthatislocatedrightatthetopofthescreen.3. SignintoyouraccountusingyourAppleIDandpassword.Thiswillthendisplaythe

developerprogramresourcespage,asshowninthefollowingscreenshot:

4. Next,clickontheCertificates,Identifiers&Profilesbutton,ashighlightedintheprecedingscreenshot.

5. Then,clickonthe+button,ashighlightedintheprecedingscreenshot.

6. Next,choosetheiOSAppDevelopmentoptionundertheDevelopmentsection,ashighlightedintheprecedingscreenshot,andclickontheContinuebuttontoproceedtothenextstep,asdisplayedfurtherdownthepage:

7. Then,clickontheContinuebutton,ashighlightedintheprecedingscreenshot,toproceedtothenextstep.

8. Next,clickontheChooseFile...button,ashighlightedintheprecedingscreenshot.9. Then,selecttheCertificateSigningRequest.certSigningRequestfilethatyoucreated

intheprevioussectionsandclickontheContinuebuttontoproceedtothenextstepinthewizard.

10. Afterafewseconds,thepagewillrefreshandthecertificatewillbereadyandyouwillbeabletodownloadit.

Inthissection,weconsideredthestepsinvolvedinrequestingacertificatefromApplethatwillbeusedtoprovideuswiththeabilityofcode-signingourapplicationsrequiredtodeployontotheiOSdeviceandtheAppleAppStore.

Wethenmovedontolearnhowtousethegeneratedcertificaterequestfilethatwecreatedinourprevioussection,CreatingtheTrackMyWalksiOSdevelopmentcertificate,togeneratethedevelopmentcertificate.

CreatingtheAppIDfortheTrackMyWalks(iOS)applicationInprevioussections,wehavelearnedhowtorequestacertificatefromAppletoprovideuswiththeabilityofcode-signingourapplications,aswellaslearninghowtousethegeneratedcertificaterequestfiletogenerateourdeploymentcertificate.

Inthissection,wewillbelookingathowtocreatetheapplicationAppIDssothatwecanusethesetodeployourapplicationstotestonaniOSdevice:

1. LogintotheiOSdeveloperportalwebsiteathttp://developer.apple.com/.2. ClickontheMemberCenterlinkthatislocatedrightatthetopofthescreen.3. SignintoyouraccountusingyourAppleIDandpassword.Thiswillthendisplaythe

developerprogramresourcespage,asshowninthefollowingscreenshot:

4. Next,clickontheCertificates,Identifiers&Profilesbutton,ashighlightedintheprecedingscreenshot.

5. Then,clickontheAppIDsitemlocatedunderneaththeIdentifiersgroupattheleft-handsideofthepageandclickonthe+buttontodisplaytheRegisteriOSAppIDssection,ashighlightedinthefollowingscreenshot:

6. Next,provideadescriptionfortheAppIDDescriptionfieldthatwillbeusedtoidentifyyourapp,asshownintheprecedingscreenshot.

7. Then,provideanamefortheBundleIDfield.Thisneedstobethesameasyourapplication'sbundleidentifier.

Note

TheBundleIDforyourappneedstobeunique.Applerecommendsthatyouusethereversedomainstyle(forexample,com.domainName.appName).

Considerthefollowingscreenshot:

8. Next,choosefromthelistofAppServicesthatyouwouldliketoenableforyourapp,andthenclickontheContinuebutton,asshowninthefollowingscreenshot:

9. Then,fromtheConfirmyourAppIDscreen,clickontheRegisterbutton.

Inthissection,wecoveredthenecessarystepsrequiredtocreatetheAppIDforourapplication.CreationofAppIDsarerequiredforeachapplicationthatyoucreateandmustcontainauniqueapplicationIDthatidentifiesitself.TheAppIDispartoftheprovisioningprofileandidentifiesanApporasuiteofrelatedapplications.

TheseareusedwhenyourapplicationscommunicatewiththeiOShardwareaccessories,theApplePushNotificationService(APNS),andwhensharingofdatahappensbetweeneachofyourapplications.

CreatingtheTrackMyWalksdevelopmentprovisioningprofileInthissection,wewilllearnhowtocreatethedevelopmentprovisioningprofilessothatyourapplicationscanbeinstalledontheiOSdevicesothatyoucandeployandtestyourapplicationspriortodeployingyourapptotheAppleAppStore:

1. LogbackintotheiOSdeveloperportalathttp://developer.apple.com/.2. ClickontheMemberCenterlinkthatislocatedrightatthetopofthescreen.3. SignintoyouraccountusingyourAppleIDandpassword.Thiswillthendisplaythe

developerprogramresourcespage,asshowninthefollowingscreenshot:

4. Next,clickontheCertificates,Identifiers&Profilesbutton,asdonepreviously.5. Then,clickontheAllitemlocatedundertheProvisioningProfilessectionlocatedatthe

left-handsideofthepage.6. Next,clickonthe+buttontodisplaytheAddiOSProvisioningProfilessection,as

highlightedinthefollowingscreenshot:

7. Then,choosetheiOSAppDevelopmentoptionfromtheDevelopmentsection,andthenclickontheContinuebuttontoproceedtothenextstep,asshownintheprecedingscreenshot.

8. Next,selectyourAppIDfromthedrop-downlistavailable,asshownintheprecedingscreenshot,andclickontheContinuebuttontoproceedtothenextstepinthewizard:

9. Then,chooseyourcertificatefromthelistofavailablecertificatesthatyouwouldliketoincludetobepartoftheProvisioningProfiles,andclickontheContinuebuttontoproceedtothenextstep,asshownintheprecedingscreenshot.

10. Next,choosefromthelistofdevicesthatyouwouldliketoincludeaspartoftheProvisioningProfilesthatyouareabouttocreate,andclickontheContinuebuttontoproceedtothenextstep,asshownintheprecedingscreenshot.

Note

FormoreinformationabouthowtoregisteriOSdevicesusingtheMemberCenter,pleaserefertotheAppledistributionguidedocumentationusingthefollowinglink:https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/AppDistributionGuide/MaintainingProfiles/MaintainingProfiles.html#//apple_ref/doc/uid/TP40012582-CH30-SW10.

11. Then,specifyanamefortheProfileNamefieldtobeusedtoidentifytheprovisioningprofilewithintheiOSdeveloperportal,andclickontheContinuebuttontoproceedtothenextstep,asshownintheprecedingscreenshot.

12. Finally,yourprovisioningprofilehasbeencreatedandisreadytobeused.YoucanchoosetoDownloadyourprovisioningprofilefromhere,oryoucanletXcodehandlethisforyou,whichwewillbecoveringinthenextsections.

13. Toclosethisscreen,andtakeyoubacktothelistofProvisioningProfiles,clickontheDonebutton,asshownintheprecedingscreenshot.

Inthissection,welearnedhowtocreateaprovisioningprofilethatwillallowyourapplicationstobeinstalledontoarealiOSdevice.Thiswillgiveyoutheabilitytoassignteammemberswhoareauthorizedtoinstallandtestanapplicationontoeachoftheirdevices.

Note

WheneveryoudeployanapplicationontoaniOSdevice,thiswillcontaintheiOSdevelopmentcertificateforeachteammember,aswellastheUniqueDeviceIdentifier(UDID),whichisa

sequenceof40lettersandnumbersthatarespecifictoyourdevice,andtheAppid.

PreparingtheTrackMyWalks(iOS)appforsubmissionNowthatyouhavetestedyourapplicationtoensurethateverythingworksfineandisfreefromerrors,youwillwanttostartpreparingyourapplicationsothatitisreadyforsubmissiontotheAppleAppStore.

Inthissection,wewillneedtouseXcodeandsigninwithourAppleIDsothatwecandownloadourprovisioningprofilesforbothdevelopmentanddistribution.ThisismainlysinceXamarinStudiousesXcodetoperformitscompilation,andifwedon'tsetthisup,wewon'tbeabletosubmitourTrackMyWalksiOSapptotheAppStoreandiTunesConnect.

TobeginpreparingyourapplicationusingXcode,followthesesimplesteps:

1. EnsurethatyouhavelaunchedtheXcodedevelopmentIDEanditisdisplayed.2. Next,choosethePreferences...menuoptionfromtheXcode|Preferences...menu,or

alternativelypresscommand+,asshowninthefollowingscreenshot:

3. Next,ensurethattheAccountsbuttonhasbeenselected,thenclickonthe+button,andchoosetheAddAppleID...menuoption,asshownintheprecedingscreenshot.

4. Then,enterinyourAppleDevelopercredentialsbyspecifyingboththeAppleIDandPassword,ascanbeseenintheprecedingscreenshot.

Note

OnceXcodehasvalidatedyourApplecredentials,youwillbepresentedwithascreenliketheoneshownintheprecedingscreenshot.Thisscreenshowsyoutheteamthatyoubelongto,aswellasyourrolewithintheteam.YoucanalsoaddmultipleAppleIDstothisscreen.

NowthatwehavesetupourXcodedevelopmentIDEtouseouriOSdevelopmentanddistributionprovisioningprofiles,ournextstepistocreateanentryforourapplicationwithiniTunesConnect.

ThisissothatwhenwebegintosubmitourappusingXamarinStudioandtheApplicationLoaderapplicationtotheAppleAppStoreusingiTunesConnect,wewon'trunintoanyissues:

1. LogbackintotheiOSdeveloperportalathttp://developer.apple.com/.2. ClickontheMemberCenterlinkthatislocatedrightatthetopofthescreen.3. SignintoyouraccountusingyourAppleIDandpassword.Thiswillthendisplaythe

developerprogramresourcespage.4. Next,clickontheMyAppsbutton,asshowninthefollowingscreenshot:

5. Then,clickonthe+buttonandthenchoosetheNewAppmenuoption,asshowninthefollowingscreenshot:

6. Next,proceedtoenterintheapplicationdetailsfortheapplicationthatweareuploading.TheSKUnumberfieldisauniqueidentifierthatyoucreateforyourapp:

Note

TheBundleIDsuffixthatyouprovidemustmatchthesameonethatyouusedwithinyourTrackMyWalks.iOSapp'sinfo.plist;otherwise,youwillrunintoissueswhensubmittingyourappstotheAppStoreandiTunesConnect.

7. Then,clickontheCreatebuttontocreateyourappandproceedtothenextstep.8. Next,choosethePricingandAvailabilitymenuoption,locatedunderneaththeAPPSTORE

INFORMATIONsection,ontheleft-handsidepanel.9. Then,fromthePricingandAvailabilitysection,specifythevaluesforourPriceSchedule

aswellastheStartDateandEndDateforourapplication.Thiswilldeterminewhenourapplicationwillbemadeavailablefordownload,asshowninthefollowingscreenshot:

10. Next,clickontheSavebuttontosaveanychangesmadewithinthisscreen.

Therearemorethan100pricingtierstochoosefrom,includinganoptionforsellingyourapplicationforfree.

Inthissection,welearnedthestepsinvolvedinpreparingourapplicationforsubmissiontotheAppleAppStoreusingiTunesConnect.Wealsolearnedthatbeforesubmittingourappsforapproval,youmustensurethateverythingworksproperly,andisfreefromproblems,andtheiOSsimulatorisagoodplacetostart.

Although,noteverythingcanbetestedwithintheiOSsimulator,itprovesagoodstartingpoint.ApplesuggeststhatyoushouldalwaysdeployyourappstoarealiOSdevicerunningthelatestiOSrelease,sothatyoucantestyourappforafewdaystoensurethatallissuesareironedout,priortosubmittingyourapptotheAppleAppStore.Next,welookedathowtocreateanewapplicationIDfortheapplicationthatwillbeuploadedtotheAppleAppStore,aswellasprovidingdetailedinformationabouttheapplication,andspecifyingadatewhentheapplicationwillbecomeavailable.

Note

FormoreinformationonhowtogoaboutsubmittingandmanagingyourappsusingiTunesConnect,youcanrefertothefollowinglinkatthislocation:https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/AppDistributionGuide/UsingiTunesConnect/UsingiTunesConnect.html#//apple_ref/doc/uid/TP40012582-CH22-SW3.

SubmittingtheTrackMyWalks(iOS)apptoiTunesConnectusingXamarinStudioIntheprevioussection,webeganbycreatingtheTrackMyWalksAppwithiniTunesConnectandlearnedthatbeforesubmittingappsforapproval,youmustensurethateverythingisworkingproperly,andisfreefromproblems.

Inthissection,wewillbegingettingourTrackMyWalks.iOSappreadyforsubmissiontotheAppleAppStore,usingXamarinStudioIDE.

Tobeginsubmittingyourapplication,followthesesimplesteps:

1. EnsurethattheTrackMyWalks.slnprojectisalreadyopenwithinXamarinStudioIDE.2. Next,right-clickontheTrackMyWalks.iOSproject,choosetheOptionsmenuoption,and

choosetheiOSBundleSigninglocatedundertheBuildsectionwithintheleftpane.3. Then,withintheiOSBundleSigningsection,chooseReleasefortheConfigurationand

iPhoneforthePlatformandensurethatyouhavechosentheDistribution(Automatic)optionwithintheSigningIdentitydropdownthatwillbeusedtosignourTrackMyWalksappwith.

4. Next,ensurethatyouhavechosentheAutomaticoptiontouseforourProvisioningProfile,andclickontheOKbuttontosavethesettingsanddismisstheProjectOptions-TrackMyWalks.iOSdialog.

Note

YouriOSprovisioningcertificatewillbeshowninbold,withyourprovisioningprofileingray.Ifyoudon'timportavalidprovisioningcertificate,youwon'tbeabletodeployoruploadyourTrackMyWalksiOSapplicationtotheAppleAppStore.

5. Then,ensurethatyouhavechosenRelease|iPhonetouseastheiOSdevicepriortochoosingtheArchiveforPublishingoptionwithintheBuildmenu,asshowninthefollowingscreenshot:

6. Next,provideacommentforyourapplication,byclickingwithintheCommentfield,andthenclickontheSignandDistribute...buttontohaveXamarinsignandprepareyourappforsubmission,asshowninthefollowingscreenshot:

Note

OnceyouclickontheSignandDistribute...button,youwillbepresentedwiththeSelect

iOSDistributionChanneldialog,whereyoucanchooseyourdistributionchanneltocreateapackageforyourapp.

7. Then,sincewewanttopublishourTrackMyWalksapptotheAppStore,choosetheAppStoreoptionwithinthelistandclickontheNextbuttontoproceedtothenextstepwithinthewizard,asshowninthefollowingscreenshot:

8. Next,youwillbepresentedwiththeProvisioningprofilescreenwhereyoucanselectyoursigningidentityandprovisioningprofile,orre-signusingadifferentidentity.ClickontheNextbuttontoproceedtothenextstepwithinthewizard,asshowninthefollowingscreenshot.

9. UponclickingontheNextbutton,XamarinStudiowillproceedtocollectallthenecessaryfiles,andcreateaTrackMyWalks.ipafilewhich,bydefault,willbesavedwithinyourTrackMyWalksfolder.

10. YouwillbepresentedwiththePublishtoAppStoredialog,whereyouwillbepresentedwiththeabilityofpublishingyourapptotheappstore,asshowninthefollowingscreenshot:

11. Then,clickonthePublishbuttontoproceedtothenextstepwithinthewizardwhereyoucanthenuploadyourbinaryarchive.

12. OnceyouhaveclickedonthePublishbutton,andeverythingpasses,youwillbepresentedwiththePublishingSucceededdialogwhereyoucanbeginuploadingyourbinaryarchivebyclickingontheOpenApplicationLoaderbutton,ascanbeseenintheprecedingscreenshot.

Note

FormoreinformationonhowtogoaboutdeployingyourXamarin.FormsAndroidapp,pleaserefertothesectiononPreparinganApplicationforReleaseatthefollowinglink:https://developer.xamarin.com/guides/android/deployment,_testing,_and_metrics/publishing_an_application/part_1_-_preparing_an_application_for_release/.

ThiswillthenlaunchtheApplicationLoaderapplication,asshowninthefollowingscreenshot,whereyouwillneedtosign-intoiTunesConnectusingyouriTunesConnectcredentials.

13. Next,choosetheDeliverYourAppoptionandclickontheChoosebutton,asshowninthefollowingscreenshot:

14. OnceyouhaveclickedontheChoosebutton,youwillbepresentedwiththeDeliverYourAppdialogwhereyouwillneedtochoosetheTrackMyWalks.ipafilethatwasgeneratedduringtheSignandDistributeprocesswithintheSubmittingtheTrackMyWalks(iOS)apptoiTunesConnectusingXamarinStudiosectionlocatedwithinthischapter.

15. Then,selectandchoosetheTrackMyWalks.ipafile,andclickontheOpenbutton,asshownintheprecedingscreenshot.

TheApplicationLoaderwillreadtheinformationcontainedwithintheTrackMyWalks.ipabinaryfile,populatedfromiTunesConnect,anddisplaytheApplicationname,VersionNumber,SKUNumber,PrimaryLanguage,Type,andtheuser'sAppleID,asshownintheprecedingscreenshot.

16. Next,clickontheNextbuttontoproceedtothenextstepwithinthewizard,asshownintheprecedingscreenshot.

Intheprecedingscreenshot,theApplicationLoaderapplicationwillbeginbyauthenticatingyourappwiththeAppleAppStoreandiTunesConnect,andthenvalidatingtoensurethateverythingpasses,atwhichpointyourbinaryarchivewillbeginuploading.

Note

ForinformationonhowtousetheApplicationLoadertopublishyourXamarin.iOSapps,youcanrefertoPublishingtotheAppStoreGuidefromtheXamarindeveloperdocumentation,whichcanbeaccessedbyusingthefollowinglink:https://developer.xamarin.com/guides/ios/deployment,_testing,_and_metrics/app_distribution/app-store-distribution/publishing_to_the_app_store/.

SummaryInthischapter,youlearnedhowtocreateandsetupyouriOSdevelopmentteamandtheassociatediOSdevelopmentcertificatethatwillenableyoutorunandtestyourappsonaniOSdevice.WethenmovedontodescribehowtocreateanAppIDforourTrackMyWalksapp.

ThesespecialAppIDsareusedbothwithinXamarinandXcodetoassociateyourappwiththeoneassignedaspartofyouriOSprovisioningprofiles.Oncewecreatedallthenecessarydevelopmentcertificatesandprovisioningprofiles,youlearnedhowtopackage,sign,anddistributeyourappusingXamarinStudioIDE,anddeployittoiTunesConnectusingtheApplicationLoaderapplication,whereyoucanthendownloadandtestyourapponarealiOSdevice.

Thiswasthefinalchapter,andIsincerelyhopethatyouhadlotsoffundevelopingappsthroughoutourjourneyworkingthroughthisbook.YounowhaveenoughknowledgeandexpertisetounderstandwhatittakestobuildrichandengagingappsfortheXamarin.Formsplatform,byusingahostofexcitingconceptsandtechniquesthatareuniquetotheXamarin.Formsplatform.

YouhaveenoughknowledgetogetyourXamarin.Formsprojectsofftoagreatstart,andIcan'twaittoseewhatyoubuild.ThankyousomuchforpurchasingthisbookandIwishyoutheverybestofluckwithyourXamarin.Formsadventures.

top related