├── .github └── workflows │ └── manual.yml ├── .gitignore ├── CODEOWNERS ├── FriendlyChat ├── FriendlyChatSwift.xcodeproj │ └── project.pbxproj ├── FriendlyChatSwift │ ├── AppDelegate │ ├── AppDelegate.swift │ ├── Base.lproj │ │ └── Main.storyboard │ ├── Constants.swift │ ├── FCViewController.swift │ └── Info.plist ├── Podfile └── shared │ ├── Images.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ ├── Contents.json │ ├── Firebase.imageset │ │ ├── Contents.json │ │ └── firebase.png │ ├── LaunchLogo.imageset │ │ ├── Contents.json │ │ └── LaunchLogo.pdf │ ├── ic_account_circle.imageset │ │ ├── Contents.json │ │ ├── ic_account_circle.png │ │ ├── ic_account_circle_2x.png │ │ └── ic_account_circle_3x.png │ └── ic_add_a_photo.imageset │ │ ├── Contents.json │ │ ├── ic_add_a_photo.png │ │ ├── ic_add_a_photo_2x.png │ │ └── ic_add_a_photo_3x.png │ ├── LaunchScreen.xib │ ├── Roboto-Regular.ttf │ └── RobotoMono-Regular.ttf ├── FriendlyChatComplete ├── FriendlyChatSwift.xcodeproj │ └── project.pbxproj ├── FriendlyChatSwift │ ├── AppDelegate │ ├── AppDelegate.swift │ ├── Base.lproj │ │ └── Main.storyboard │ ├── Constants.swift │ ├── FCViewController.swift │ └── Info.plist ├── Podfile └── shared │ ├── Images.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ ├── Contents.json │ ├── Firebase.imageset │ │ ├── Contents.json │ │ └── firebase.png │ ├── LaunchLogo.imageset │ │ ├── Contents.json │ │ └── LaunchLogo.pdf │ ├── ic_account_circle.imageset │ │ ├── Contents.json │ │ ├── ic_account_circle.png │ │ ├── ic_account_circle_2x.png │ │ └── ic_account_circle_3x.png │ └── ic_add_a_photo.imageset │ │ ├── Contents.json │ │ ├── ic_add_a_photo.png │ │ ├── ic_add_a_photo_2x.png │ │ └── ic_add_a_photo_3x.png │ ├── LaunchScreen.xib │ ├── Roboto-Regular.ttf │ └── RobotoMono-Regular.ttf ├── LICENSE └── README.md /.github/workflows/manual.yml: -------------------------------------------------------------------------------- 1 | # Workflow to ensure whenever a Github PR is submitted, 2 | # a JIRA ticket gets created automatically. 3 | name: Manual Workflow 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on pull request events but only for the master branch 8 | pull_request_target: 9 | types: [opened, reopened] 10 | 11 | # Allows you to run this workflow manually from the Actions tab 12 | workflow_dispatch: 13 | 14 | jobs: 15 | test-transition-issue: 16 | name: Convert Github Issue to Jira Issue 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@master 21 | 22 | - name: Login 23 | uses: atlassian/gajira-login@master 24 | env: 25 | JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} 26 | JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} 27 | JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} 28 | 29 | - name: Create NEW JIRA ticket 30 | id: create 31 | uses: atlassian/gajira-create@master 32 | with: 33 | project: CONUPDATE 34 | issuetype: Task 35 | summary: | 36 | Github PR [Assign the ND component] | Repo: ${{ github.repository }} | PR# ${{github.event.number}} 37 | description: | 38 | Repo link: https://github.com/${{ github.repository }} 39 | PR no. ${{ github.event.pull_request.number }} 40 | PR title: ${{ github.event.pull_request.title }} 41 | PR description: ${{ github.event.pull_request.description }} 42 | In addition, please resolve other issues, if any. 43 | fields: '{"components": [{"name":"Github PR"}], "customfield_16449":"https://classroom.udacity.com/", "customfield_16450":"Resolve the PR", "labels": ["github"], "priority":{"id": "4"}}' 44 | 45 | - name: Log created issue 46 | run: echo "Issue ${{ steps.create.outputs.issue }} was created" 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | *.xcworkspace 20 | 21 | # CocoaPods 22 | # 23 | # We recommend against adding the Pods directory to your .gitignore. However 24 | # you should judge for yourself, the pros and cons are mentioned at: 25 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 26 | # 27 | Pods/ 28 | *Podfile.lock 29 | 30 | # Carthage 31 | # 32 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 33 | # Carthage/Checkouts 34 | 35 | Carthage/Build 36 | 37 | # Ignore those pesky DS_Store files! 38 | .DS_Store 39 | 40 | # Firebase 41 | *GoogleService-Info.plist 42 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @batmanimal 2 | 3 | * @udacity/active-public-content -------------------------------------------------------------------------------- /FriendlyChat/FriendlyChatSwift.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 107960731C612531003DB6CC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 107960711C612531003DB6CC /* Main.storyboard */; }; 11 | 2C595F061B34B73A00355AE7 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C595F041B34B73A00355AE7 /* Constants.swift */; }; 12 | 2C8562851AFAB65F00F0B24B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2C85627E1AFAB65F00F0B24B /* Images.xcassets */; }; 13 | 2C8562871AFAB65F00F0B24B /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2C85627F1AFAB65F00F0B24B /* LaunchScreen.xib */; }; 14 | 5F5A537E1ADE67D500F81DF0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F5A537D1ADE67D500F81DF0 /* AppDelegate.swift */; }; 15 | 5F5A53801ADE67D500F81DF0 /* FCViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F5A537F1ADE67D500F81DF0 /* FCViewController.swift */; }; 16 | A92476E81D6F96C300A96C4B /* Roboto-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A92476E61D6F96C300A96C4B /* Roboto-Regular.ttf */; }; 17 | A92476E91D6F96C300A96C4B /* RobotoMono-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A92476E71D6F96C300A96C4B /* RobotoMono-Regular.ttf */; }; 18 | DBBAAE069585D787FD06FE15 /* Pods_FriendlyChatSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9EE776B645030DD333883549 /* Pods_FriendlyChatSwift.framework */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | 107960721C612531003DB6CC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 23 | 107960741C61253B003DB6CC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 24 | 2C595F041B34B73A00355AE7 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; 25 | 2C85627E1AFAB65F00F0B24B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 26 | 2C85627F1AFAB65F00F0B24B /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = ""; }; 27 | 47BDA9BA1B03D338F2B3A7F7 /* Pods-FriendlyChatSwift.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FriendlyChatSwift.debug.xcconfig"; path = "Pods/Target Support Files/Pods-FriendlyChatSwift/Pods-FriendlyChatSwift.debug.xcconfig"; sourceTree = ""; }; 28 | 5F5A53791ADE67D500F81DF0 /* FriendlyChatSwift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FriendlyChatSwift.app; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 5F5A537D1ADE67D500F81DF0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 30 | 5F5A537F1ADE67D500F81DF0 /* FCViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FCViewController.swift; sourceTree = ""; }; 31 | 9EE776B645030DD333883549 /* Pods_FriendlyChatSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FriendlyChatSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | A92476E61D6F96C300A96C4B /* Roboto-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Regular.ttf"; sourceTree = ""; }; 33 | A92476E71D6F96C300A96C4B /* RobotoMono-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "RobotoMono-Regular.ttf"; sourceTree = ""; }; 34 | F3514636A3BDB21360D87272 /* Pods-FriendlyChatSwift.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FriendlyChatSwift.release.xcconfig"; path = "Pods/Target Support Files/Pods-FriendlyChatSwift/Pods-FriendlyChatSwift.release.xcconfig"; sourceTree = ""; }; 35 | /* End PBXFileReference section */ 36 | 37 | /* Begin PBXFrameworksBuildPhase section */ 38 | 5F5A53761ADE67D500F81DF0 /* Frameworks */ = { 39 | isa = PBXFrameworksBuildPhase; 40 | buildActionMask = 2147483647; 41 | files = ( 42 | DBBAAE069585D787FD06FE15 /* Pods_FriendlyChatSwift.framework in Frameworks */, 43 | ); 44 | runOnlyForDeploymentPostprocessing = 0; 45 | }; 46 | /* End PBXFrameworksBuildPhase section */ 47 | 48 | /* Begin PBXGroup section */ 49 | 12DD0A4A089E5CEFC6F01148 /* Pods */ = { 50 | isa = PBXGroup; 51 | children = ( 52 | 47BDA9BA1B03D338F2B3A7F7 /* Pods-FriendlyChatSwift.debug.xcconfig */, 53 | F3514636A3BDB21360D87272 /* Pods-FriendlyChatSwift.release.xcconfig */, 54 | ); 55 | name = Pods; 56 | sourceTree = ""; 57 | }; 58 | 2C85627A1AFAB65F00F0B24B /* shared */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | A92476E61D6F96C300A96C4B /* Roboto-Regular.ttf */, 62 | A92476E71D6F96C300A96C4B /* RobotoMono-Regular.ttf */, 63 | 2C85627E1AFAB65F00F0B24B /* Images.xcassets */, 64 | 2C85627F1AFAB65F00F0B24B /* LaunchScreen.xib */, 65 | ); 66 | name = shared; 67 | path = ../shared; 68 | sourceTree = ""; 69 | }; 70 | 5F5A53431ADE670C00F81DF0 = { 71 | isa = PBXGroup; 72 | children = ( 73 | 5F5A537A1ADE67D500F81DF0 /* FriendlyChatSwift */, 74 | 5F5A534D1ADE670C00F81DF0 /* Products */, 75 | 12DD0A4A089E5CEFC6F01148 /* Pods */, 76 | 9AD5D3AA9FEE4AAC1BAC7C5F /* Frameworks */, 77 | ); 78 | sourceTree = ""; 79 | }; 80 | 5F5A534D1ADE670C00F81DF0 /* Products */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | 5F5A53791ADE67D500F81DF0 /* FriendlyChatSwift.app */, 84 | ); 85 | name = Products; 86 | sourceTree = ""; 87 | }; 88 | 5F5A537A1ADE67D500F81DF0 /* FriendlyChatSwift */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 5F5A537D1ADE67D500F81DF0 /* AppDelegate.swift */, 92 | 5F5A537F1ADE67D500F81DF0 /* FCViewController.swift */, 93 | 2C595F041B34B73A00355AE7 /* Constants.swift */, 94 | 5F5A537B1ADE67D500F81DF0 /* Supporting Files */, 95 | ); 96 | path = FriendlyChatSwift; 97 | sourceTree = ""; 98 | }; 99 | 5F5A537B1ADE67D500F81DF0 /* Supporting Files */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 107960711C612531003DB6CC /* Main.storyboard */, 103 | 107960741C61253B003DB6CC /* Info.plist */, 104 | 2C85627A1AFAB65F00F0B24B /* shared */, 105 | ); 106 | name = "Supporting Files"; 107 | sourceTree = ""; 108 | }; 109 | 9AD5D3AA9FEE4AAC1BAC7C5F /* Frameworks */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 9EE776B645030DD333883549 /* Pods_FriendlyChatSwift.framework */, 113 | ); 114 | name = Frameworks; 115 | sourceTree = ""; 116 | }; 117 | /* End PBXGroup section */ 118 | 119 | /* Begin PBXNativeTarget section */ 120 | 5F5A53781ADE67D500F81DF0 /* FriendlyChatSwift */ = { 121 | isa = PBXNativeTarget; 122 | buildConfigurationList = 5F5A53991ADE67D500F81DF0 /* Build configuration list for PBXNativeTarget "FriendlyChatSwift" */; 123 | buildPhases = ( 124 | 77C29CB188171CE63340450F /* [CP] Check Pods Manifest.lock */, 125 | 5F5A53751ADE67D500F81DF0 /* Sources */, 126 | 5F5A53761ADE67D500F81DF0 /* Frameworks */, 127 | 5F5A53771ADE67D500F81DF0 /* Resources */, 128 | 602497AACFA814EC1D444B93 /* [CP] Embed Pods Frameworks */, 129 | ED8993B59ED135CE7FC6FF4D /* [CP] Copy Pods Resources */, 130 | ); 131 | buildRules = ( 132 | ); 133 | dependencies = ( 134 | ); 135 | name = FriendlyChatSwift; 136 | productName = FriendlyPingSwift; 137 | productReference = 5F5A53791ADE67D500F81DF0 /* FriendlyChatSwift.app */; 138 | productType = "com.apple.product-type.application"; 139 | }; 140 | /* End PBXNativeTarget section */ 141 | 142 | /* Begin PBXProject section */ 143 | 5F5A53441ADE670C00F81DF0 /* Project object */ = { 144 | isa = PBXProject; 145 | attributes = { 146 | LastSwiftUpdateCheck = 0710; 147 | LastUpgradeCheck = 0800; 148 | ORGANIZATIONNAME = "Google Inc."; 149 | TargetAttributes = { 150 | 5F5A53781ADE67D500F81DF0 = { 151 | CreatedOnToolsVersion = 6.3; 152 | LastSwiftMigration = 0800; 153 | ProvisioningStyle = Manual; 154 | SystemCapabilities = { 155 | com.apple.BackgroundModes = { 156 | enabled = 1; 157 | }; 158 | com.apple.Keychain = { 159 | enabled = 0; 160 | }; 161 | }; 162 | }; 163 | }; 164 | }; 165 | buildConfigurationList = 5F5A53471ADE670C00F81DF0 /* Build configuration list for PBXProject "FriendlyChatSwift" */; 166 | compatibilityVersion = "Xcode 3.2"; 167 | developmentRegion = English; 168 | hasScannedForEncodings = 0; 169 | knownRegions = ( 170 | en, 171 | Base, 172 | ); 173 | mainGroup = 5F5A53431ADE670C00F81DF0; 174 | productRefGroup = 5F5A534D1ADE670C00F81DF0 /* Products */; 175 | projectDirPath = ""; 176 | projectRoot = ""; 177 | targets = ( 178 | 5F5A53781ADE67D500F81DF0 /* FriendlyChatSwift */, 179 | ); 180 | }; 181 | /* End PBXProject section */ 182 | 183 | /* Begin PBXResourcesBuildPhase section */ 184 | 5F5A53771ADE67D500F81DF0 /* Resources */ = { 185 | isa = PBXResourcesBuildPhase; 186 | buildActionMask = 2147483647; 187 | files = ( 188 | 2C8562871AFAB65F00F0B24B /* LaunchScreen.xib in Resources */, 189 | 107960731C612531003DB6CC /* Main.storyboard in Resources */, 190 | A92476E81D6F96C300A96C4B /* Roboto-Regular.ttf in Resources */, 191 | 2C8562851AFAB65F00F0B24B /* Images.xcassets in Resources */, 192 | A92476E91D6F96C300A96C4B /* RobotoMono-Regular.ttf in Resources */, 193 | ); 194 | runOnlyForDeploymentPostprocessing = 0; 195 | }; 196 | /* End PBXResourcesBuildPhase section */ 197 | 198 | /* Begin PBXShellScriptBuildPhase section */ 199 | 602497AACFA814EC1D444B93 /* [CP] Embed Pods Frameworks */ = { 200 | isa = PBXShellScriptBuildPhase; 201 | buildActionMask = 2147483647; 202 | files = ( 203 | ); 204 | inputPaths = ( 205 | ); 206 | name = "[CP] Embed Pods Frameworks"; 207 | outputPaths = ( 208 | ); 209 | runOnlyForDeploymentPostprocessing = 0; 210 | shellPath = /bin/sh; 211 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-FriendlyChatSwift/Pods-FriendlyChatSwift-frameworks.sh\"\n"; 212 | showEnvVarsInLog = 0; 213 | }; 214 | 77C29CB188171CE63340450F /* [CP] Check Pods Manifest.lock */ = { 215 | isa = PBXShellScriptBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | ); 219 | inputPaths = ( 220 | ); 221 | name = "[CP] Check Pods Manifest.lock"; 222 | outputPaths = ( 223 | ); 224 | runOnlyForDeploymentPostprocessing = 0; 225 | shellPath = /bin/sh; 226 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; 227 | showEnvVarsInLog = 0; 228 | }; 229 | ED8993B59ED135CE7FC6FF4D /* [CP] Copy Pods Resources */ = { 230 | isa = PBXShellScriptBuildPhase; 231 | buildActionMask = 2147483647; 232 | files = ( 233 | ); 234 | inputPaths = ( 235 | ); 236 | name = "[CP] Copy Pods Resources"; 237 | outputPaths = ( 238 | ); 239 | runOnlyForDeploymentPostprocessing = 0; 240 | shellPath = /bin/sh; 241 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-FriendlyChatSwift/Pods-FriendlyChatSwift-resources.sh\"\n"; 242 | showEnvVarsInLog = 0; 243 | }; 244 | /* End PBXShellScriptBuildPhase section */ 245 | 246 | /* Begin PBXSourcesBuildPhase section */ 247 | 5F5A53751ADE67D500F81DF0 /* Sources */ = { 248 | isa = PBXSourcesBuildPhase; 249 | buildActionMask = 2147483647; 250 | files = ( 251 | 5F5A53801ADE67D500F81DF0 /* FCViewController.swift in Sources */, 252 | 5F5A537E1ADE67D500F81DF0 /* AppDelegate.swift in Sources */, 253 | 2C595F061B34B73A00355AE7 /* Constants.swift in Sources */, 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | }; 257 | /* End PBXSourcesBuildPhase section */ 258 | 259 | /* Begin PBXVariantGroup section */ 260 | 107960711C612531003DB6CC /* Main.storyboard */ = { 261 | isa = PBXVariantGroup; 262 | children = ( 263 | 107960721C612531003DB6CC /* Base */, 264 | ); 265 | name = Main.storyboard; 266 | sourceTree = ""; 267 | }; 268 | /* End PBXVariantGroup section */ 269 | 270 | /* Begin XCBuildConfiguration section */ 271 | 5F5A536D1ADE670C00F81DF0 /* Debug */ = { 272 | isa = XCBuildConfiguration; 273 | buildSettings = { 274 | ALWAYS_SEARCH_USER_PATHS = NO; 275 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 276 | CLANG_CXX_LIBRARY = "libc++"; 277 | CLANG_ENABLE_MODULES = YES; 278 | CLANG_ENABLE_OBJC_ARC = YES; 279 | CLANG_WARN_BOOL_CONVERSION = YES; 280 | CLANG_WARN_CONSTANT_CONVERSION = YES; 281 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 282 | CLANG_WARN_EMPTY_BODY = YES; 283 | CLANG_WARN_ENUM_CONVERSION = YES; 284 | CLANG_WARN_INFINITE_RECURSION = YES; 285 | CLANG_WARN_INT_CONVERSION = YES; 286 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 287 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 288 | CLANG_WARN_UNREACHABLE_CODE = YES; 289 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 290 | CODE_SIGN_IDENTITY = "iPhone Developer: Google Development (3F4Y5873JF)"; 291 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Google Development (3F4Y5873JF)"; 292 | COPY_PHASE_STRIP = NO; 293 | ENABLE_STRICT_OBJC_MSGSEND = YES; 294 | ENABLE_TESTABILITY = YES; 295 | GCC_C_LANGUAGE_STANDARD = gnu99; 296 | GCC_DYNAMIC_NO_PIC = NO; 297 | GCC_NO_COMMON_BLOCKS = YES; 298 | GCC_OPTIMIZATION_LEVEL = 0; 299 | GCC_PREPROCESSOR_DEFINITIONS = ( 300 | "DEBUG=1", 301 | "$(inherited)", 302 | ); 303 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 304 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 305 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 306 | GCC_WARN_UNDECLARED_SELECTOR = YES; 307 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 308 | GCC_WARN_UNUSED_FUNCTION = YES; 309 | GCC_WARN_UNUSED_VARIABLE = YES; 310 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 311 | MTL_ENABLE_DEBUG_INFO = YES; 312 | ONLY_ACTIVE_ARCH = YES; 313 | PROVISIONING_PROFILE = "65c2088b-2425-4961-b6d5-0256d4790678"; 314 | SDKROOT = iphoneos; 315 | TARGETED_DEVICE_FAMILY = "1,2"; 316 | }; 317 | name = Debug; 318 | }; 319 | 5F5A536E1ADE670C00F81DF0 /* Release */ = { 320 | isa = XCBuildConfiguration; 321 | buildSettings = { 322 | ALWAYS_SEARCH_USER_PATHS = NO; 323 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 324 | CLANG_CXX_LIBRARY = "libc++"; 325 | CLANG_ENABLE_MODULES = YES; 326 | CLANG_ENABLE_OBJC_ARC = YES; 327 | CLANG_WARN_BOOL_CONVERSION = YES; 328 | CLANG_WARN_CONSTANT_CONVERSION = YES; 329 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 330 | CLANG_WARN_EMPTY_BODY = YES; 331 | CLANG_WARN_ENUM_CONVERSION = YES; 332 | CLANG_WARN_INFINITE_RECURSION = YES; 333 | CLANG_WARN_INT_CONVERSION = YES; 334 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 335 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 336 | CLANG_WARN_UNREACHABLE_CODE = YES; 337 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 338 | CODE_SIGN_IDENTITY = "iPhone Developer: Google Development (3F4Y5873JF)"; 339 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Google Development (3F4Y5873JF)"; 340 | COPY_PHASE_STRIP = YES; 341 | ENABLE_NS_ASSERTIONS = NO; 342 | ENABLE_STRICT_OBJC_MSGSEND = YES; 343 | GCC_C_LANGUAGE_STANDARD = gnu99; 344 | GCC_NO_COMMON_BLOCKS = YES; 345 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 346 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 347 | GCC_WARN_UNDECLARED_SELECTOR = YES; 348 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 349 | GCC_WARN_UNUSED_FUNCTION = YES; 350 | GCC_WARN_UNUSED_VARIABLE = YES; 351 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 352 | MTL_ENABLE_DEBUG_INFO = NO; 353 | PROVISIONING_PROFILE = "65c2088b-2425-4961-b6d5-0256d4790678"; 354 | SDKROOT = iphoneos; 355 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 356 | TARGETED_DEVICE_FAMILY = "1,2"; 357 | VALIDATE_PRODUCT = YES; 358 | }; 359 | name = Release; 360 | }; 361 | 5F5A53951ADE67D500F81DF0 /* Debug */ = { 362 | isa = XCBuildConfiguration; 363 | baseConfigurationReference = 47BDA9BA1B03D338F2B3A7F7 /* Pods-FriendlyChatSwift.debug.xcconfig */; 364 | buildSettings = { 365 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 366 | CODE_SIGN_IDENTITY = "iPhone Developer"; 367 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 368 | DEVELOPMENT_TEAM = ""; 369 | ENABLE_BITCODE = NO; 370 | GCC_OPTIMIZATION_LEVEL = 3; 371 | INFOPLIST_FILE = "$(SRCROOT)/FriendlyChatSwift/Info.plist"; 372 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 373 | PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.udacity.friendlychat; 374 | PRODUCT_NAME = FriendlyChatSwift; 375 | PROVISIONING_PROFILE = ""; 376 | SWIFT_OBJC_BRIDGING_HEADER = ""; 377 | SWIFT_VERSION = 3.0; 378 | }; 379 | name = Debug; 380 | }; 381 | 5F5A53961ADE67D500F81DF0 /* Release */ = { 382 | isa = XCBuildConfiguration; 383 | baseConfigurationReference = F3514636A3BDB21360D87272 /* Pods-FriendlyChatSwift.release.xcconfig */; 384 | buildSettings = { 385 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 386 | CODE_SIGN_IDENTITY = "iPhone Developer"; 387 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 388 | DEVELOPMENT_TEAM = ""; 389 | ENABLE_BITCODE = NO; 390 | INFOPLIST_FILE = "$(SRCROOT)/FriendlyChatSwift/Info.plist"; 391 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 392 | PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.udacity.friendlychat; 393 | PRODUCT_NAME = FriendlyChatSwift; 394 | PROVISIONING_PROFILE = ""; 395 | SWIFT_OBJC_BRIDGING_HEADER = ""; 396 | SWIFT_VERSION = 3.0; 397 | }; 398 | name = Release; 399 | }; 400 | /* End XCBuildConfiguration section */ 401 | 402 | /* Begin XCConfigurationList section */ 403 | 5F5A53471ADE670C00F81DF0 /* Build configuration list for PBXProject "FriendlyChatSwift" */ = { 404 | isa = XCConfigurationList; 405 | buildConfigurations = ( 406 | 5F5A536D1ADE670C00F81DF0 /* Debug */, 407 | 5F5A536E1ADE670C00F81DF0 /* Release */, 408 | ); 409 | defaultConfigurationIsVisible = 0; 410 | defaultConfigurationName = Release; 411 | }; 412 | 5F5A53991ADE67D500F81DF0 /* Build configuration list for PBXNativeTarget "FriendlyChatSwift" */ = { 413 | isa = XCConfigurationList; 414 | buildConfigurations = ( 415 | 5F5A53951ADE67D500F81DF0 /* Debug */, 416 | 5F5A53961ADE67D500F81DF0 /* Release */, 417 | ); 418 | defaultConfigurationIsVisible = 0; 419 | defaultConfigurationName = Release; 420 | }; 421 | /* End XCConfigurationList section */ 422 | }; 423 | rootObject = 5F5A53441ADE670C00F81DF0 /* Project object */; 424 | } 425 | -------------------------------------------------------------------------------- /FriendlyChat/FriendlyChatSwift/AppDelegate: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015 Google Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | import UIKit 18 | 19 | import Firebase 20 | 21 | @UIApplicationMain 22 | class AppDelegate: UIResponder, UIApplicationDelegate { 23 | 24 | var window: UIWindow? 25 | 26 | func application(application: UIApplication, didFinishLaunchingWithOptions 27 | launchOptions: [NSObject: AnyObject]?) -> Bool { 28 | FIRApp.configure() 29 | return true 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /FriendlyChat/FriendlyChatSwift/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015 Google Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | import UIKit 18 | import Firebase 19 | import FirebaseAuthUI 20 | 21 | // MARK: - AppDelegate: UIResponder, UIApplicationDelegate 22 | 23 | @UIApplicationMain 24 | class AppDelegate: UIResponder, UIApplicationDelegate { 25 | 26 | // MARK: Properties 27 | 28 | var window: UIWindow? 29 | 30 | // MARK: UIApplicationDelegate 31 | 32 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool { 33 | // TODO: - configure FIRApp 34 | return true 35 | } 36 | 37 | func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool { 38 | return FUIAuth.defaultAuthUI()?.handleOpen(url, sourceApplication: sourceApplication ?? "") ?? false 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /FriendlyChat/FriendlyChatSwift/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Roboto-Regular 12 | 13 | 14 | RobotoMono-Regular 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 45 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 114 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /FriendlyChat/FriendlyChatSwift/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015 Google Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | // MARK: - Constants 18 | 19 | struct Constants { 20 | 21 | // MARK: NotificationKeys 22 | 23 | struct NotificationKeys { 24 | static let SignedIn = "onSignInCompleted" 25 | } 26 | 27 | // MARK: MessageFields 28 | 29 | struct MessageFields { 30 | static let name = "name" 31 | static let text = "text" 32 | static let imageUrl = "photoUrl" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /FriendlyChat/FriendlyChatSwift/FCViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015 Google Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | import UIKit 18 | import Firebase 19 | import FirebaseAuthUI 20 | 21 | // MARK: - FCViewController 22 | 23 | class FCViewController: UIViewController, UINavigationControllerDelegate { 24 | 25 | // MARK: Properties 26 | 27 | var ref: DatabaseReference! 28 | var messages: [DataSnapshot]! = [] 29 | var msglength: NSNumber = 1000 30 | var storageRef: StorageReference! 31 | var remoteConfig: RemoteConfig! 32 | let imageCache = NSCache() 33 | var keyboardOnScreen = false 34 | var placeholderImage = UIImage(named: "ic_account_circle") 35 | fileprivate var _refHandle: DatabaseHandle! 36 | fileprivate var _authHandle: AuthStateDidChangeListenerHandle! 37 | var user: User? 38 | var displayName = "Anonymous" 39 | 40 | // MARK: Outlets 41 | 42 | @IBOutlet weak var messageTextField: UITextField! 43 | @IBOutlet weak var sendButton: UIButton! 44 | @IBOutlet weak var signInButton: UIButton! 45 | @IBOutlet weak var imageMessage: UIButton! 46 | @IBOutlet weak var signOutButton: UIButton! 47 | @IBOutlet weak var messagesTable: UITableView! 48 | @IBOutlet weak var backgroundBlur: UIVisualEffectView! 49 | @IBOutlet weak var imageDisplay: UIImageView! 50 | @IBOutlet var dismissImageRecognizer: UITapGestureRecognizer! 51 | @IBOutlet var dismissKeyboardRecognizer: UITapGestureRecognizer! 52 | 53 | // MARK: Life Cycle 54 | 55 | override func viewDidLoad() { 56 | self.signedInStatus(isSignedIn: true) 57 | 58 | // TODO: Handle what users see when view loads 59 | } 60 | 61 | override func viewWillDisappear(_ animated: Bool) { 62 | super.viewWillDisappear(animated) 63 | unsubscribeFromAllNotifications() 64 | } 65 | 66 | // MARK: Config 67 | 68 | func configureAuth() { 69 | // TODO: configure firebase authentication 70 | } 71 | 72 | func configureDatabase() { 73 | // TODO: configure database to sync messages 74 | } 75 | 76 | func configureStorage() { 77 | // TODO: configure storage using your firebase storage 78 | } 79 | 80 | deinit { 81 | // TODO: set up what needs to be deinitialized when view is no longer being used 82 | } 83 | 84 | // MARK: Remote Config 85 | 86 | func configureRemoteConfig() { 87 | // TODO: configure remote configuration settings 88 | } 89 | 90 | func fetchConfig() { 91 | // TODO: update to the current coniguratation 92 | } 93 | 94 | // MARK: Sign In and Out 95 | 96 | func signedInStatus(isSignedIn: Bool) { 97 | signInButton.isHidden = isSignedIn 98 | signOutButton.isHidden = !isSignedIn 99 | messagesTable.isHidden = !isSignedIn 100 | messageTextField.isHidden = !isSignedIn 101 | sendButton.isHidden = !isSignedIn 102 | imageMessage.isHidden = !isSignedIn 103 | 104 | if (isSignedIn) { 105 | 106 | // remove background blur (will use when showing image messages) 107 | messagesTable.rowHeight = UITableViewAutomaticDimension 108 | messagesTable.estimatedRowHeight = 122.0 109 | backgroundBlur.effect = nil 110 | messageTextField.delegate = self 111 | 112 | // TODO: Set up app to send and receive messages when signed in 113 | } 114 | } 115 | 116 | func loginSession() { 117 | let authViewController = FUIAuth.defaultAuthUI()!.authViewController() 118 | self.present(authViewController, animated: true, completion: nil) 119 | } 120 | 121 | // MARK: Send Message 122 | 123 | func sendMessage(data: [String:String]) { 124 | // TODO: create method that pushes message to the firebase database 125 | } 126 | 127 | func sendPhotoMessage(photoData: Data) { 128 | // TODO: create method that pushes message w/ photo to the firebase database 129 | } 130 | 131 | // MARK: Alert 132 | 133 | func showAlert(title: String, message: String) { 134 | DispatchQueue.main.async { 135 | let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) 136 | let dismissAction = UIAlertAction(title: "Dismiss", style: .destructive, handler: nil) 137 | alert.addAction(dismissAction) 138 | self.present(alert, animated: true, completion: nil) 139 | } 140 | } 141 | 142 | // MARK: Scroll Messages 143 | 144 | func scrollToBottomMessage() { 145 | if messages.count == 0 { return } 146 | let bottomMessageIndex = IndexPath(row: messagesTable.numberOfRows(inSection: 0) - 1, section: 0) 147 | messagesTable.scrollToRow(at: bottomMessageIndex, at: .bottom, animated: true) 148 | } 149 | 150 | // MARK: Actions 151 | 152 | @IBAction func showLoginView(_ sender: AnyObject) { 153 | loginSession() 154 | } 155 | 156 | @IBAction func didTapAddPhoto(_ sender: AnyObject) { 157 | let picker = UIImagePickerController() 158 | picker.delegate = self 159 | picker.sourceType = .photoLibrary 160 | present(picker, animated: true, completion: nil) 161 | } 162 | 163 | @IBAction func signOut(_ sender: UIButton) { 164 | do { 165 | try Auth.auth().signOut() 166 | } catch { 167 | print("unable to sign out: \(error)") 168 | } 169 | } 170 | 171 | @IBAction func didSendMessage(_ sender: UIButton) { 172 | let _ = textFieldShouldReturn(messageTextField) 173 | messageTextField.text = "" 174 | } 175 | 176 | @IBAction func dismissImageDisplay(_ sender: AnyObject) { 177 | // if touch detected when image is displayed 178 | if imageDisplay.alpha == 1.0 { 179 | UIView.animate(withDuration: 0.25) { 180 | self.backgroundBlur.effect = nil 181 | self.imageDisplay.alpha = 0.0 182 | } 183 | dismissImageRecognizer.isEnabled = false 184 | messageTextField.isEnabled = true 185 | } 186 | } 187 | 188 | @IBAction func tappedView(_ sender: AnyObject) { 189 | resignTextfield() 190 | } 191 | } 192 | 193 | // MARK: - FCViewController: UITableViewDelegate, UITableViewDataSource 194 | 195 | extension FCViewController: UITableViewDelegate, UITableViewDataSource { 196 | 197 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 198 | return messages.count 199 | } 200 | 201 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 202 | // dequeue cell 203 | let cell: UITableViewCell! = messagesTable.dequeueReusableCell(withIdentifier: "messageCell", for: indexPath) 204 | return cell! 205 | // TODO: update cell to display message data 206 | } 207 | 208 | func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { 209 | return UITableViewAutomaticDimension 210 | } 211 | 212 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 213 | // TODO: if message contains an image, then display the image 214 | } 215 | 216 | // MARK: Show Image Display 217 | 218 | func showImageDisplay(_ image: UIImage) { 219 | dismissImageRecognizer.isEnabled = true 220 | dismissKeyboardRecognizer.isEnabled = false 221 | messageTextField.isEnabled = false 222 | UIView.animate(withDuration: 0.25) { 223 | self.backgroundBlur.effect = UIBlurEffect(style: .light) 224 | self.imageDisplay.alpha = 1.0 225 | self.imageDisplay.image = image 226 | } 227 | } 228 | 229 | // MARK: Show Image Display 230 | 231 | func showImageDisplay(image: UIImage) { 232 | dismissImageRecognizer.isEnabled = true 233 | dismissKeyboardRecognizer.isEnabled = false 234 | messageTextField.isEnabled = false 235 | UIView.animate(withDuration: 0.25) { 236 | self.backgroundBlur.effect = UIBlurEffect(style: .light) 237 | self.imageDisplay.alpha = 1.0 238 | self.imageDisplay.image = image 239 | } 240 | } 241 | } 242 | 243 | // MARK: - FCViewController: UIImagePickerControllerDelegate 244 | 245 | extension FCViewController: UIImagePickerControllerDelegate { 246 | 247 | func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String:Any]) { 248 | // constant to hold the information about the photo 249 | if let photo = info[UIImagePickerControllerOriginalImage] as? UIImage, let photoData = UIImageJPEGRepresentation(photo, 0.8) { 250 | // call function to upload photo message 251 | sendPhotoMessage(photoData: photoData) 252 | } 253 | picker.dismiss(animated: true, completion: nil) 254 | } 255 | 256 | func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { 257 | picker.dismiss(animated: true, completion: nil) 258 | } 259 | } 260 | 261 | // MARK: - FCViewController: UITextFieldDelegate 262 | 263 | extension FCViewController: UITextFieldDelegate { 264 | 265 | func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { 266 | // set the maximum length of the message 267 | guard let text = textField.text else { return true } 268 | let newLength = text.utf16.count + string.utf16.count - range.length 269 | return newLength <= msglength.intValue 270 | } 271 | 272 | func textFieldShouldReturn(_ textField: UITextField) -> Bool { 273 | if !textField.text!.isEmpty { 274 | let data = [Constants.MessageFields.text: textField.text! as String] 275 | sendMessage(data: data) 276 | textField.resignFirstResponder() 277 | } 278 | return true 279 | } 280 | 281 | // MARK: Show/Hide Keyboard 282 | 283 | func keyboardWillShow(_ notification: Notification) { 284 | if !keyboardOnScreen { 285 | self.view.frame.origin.y -= self.keyboardHeight(notification) 286 | } 287 | } 288 | 289 | func keyboardWillHide(_ notification: Notification) { 290 | if keyboardOnScreen { 291 | self.view.frame.origin.y += self.keyboardHeight(notification) 292 | } 293 | } 294 | 295 | func keyboardDidShow(_ notification: Notification) { 296 | keyboardOnScreen = true 297 | dismissKeyboardRecognizer.isEnabled = true 298 | scrollToBottomMessage() 299 | } 300 | 301 | func keyboardDidHide(_ notification: Notification) { 302 | dismissKeyboardRecognizer.isEnabled = false 303 | keyboardOnScreen = false 304 | } 305 | 306 | func keyboardHeight(_ notification: Notification) -> CGFloat { 307 | return ((notification as NSNotification).userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue.height 308 | } 309 | 310 | func resignTextfield() { 311 | if messageTextField.isFirstResponder { 312 | messageTextField.resignFirstResponder() 313 | } 314 | } 315 | } 316 | 317 | // MARK: - FCViewController (Notifications) 318 | 319 | extension FCViewController { 320 | 321 | func subscribeToKeyboardNotifications() { 322 | subscribeToNotification(.UIKeyboardWillShow, selector: #selector(keyboardWillShow)) 323 | subscribeToNotification(.UIKeyboardWillHide, selector: #selector(keyboardWillHide)) 324 | subscribeToNotification(.UIKeyboardDidShow, selector: #selector(keyboardDidShow)) 325 | subscribeToNotification(.UIKeyboardDidHide, selector: #selector(keyboardDidHide)) 326 | } 327 | 328 | func subscribeToNotification(_ name: NSNotification.Name, selector: Selector) { 329 | NotificationCenter.default.addObserver(self, selector: selector, name: name, object: nil) 330 | } 331 | 332 | func unsubscribeFromAllNotifications() { 333 | NotificationCenter.default.removeObserver(self) 334 | } 335 | } 336 | -------------------------------------------------------------------------------- /FriendlyChat/FriendlyChatSwift/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIcons 10 | 11 | CFBundleIcons~ipad 12 | 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | $(PRODUCT_NAME) 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleSignature 24 | ???? 25 | CFBundleURLTypes 26 | 27 | 28 | CFBundleTypeRole 29 | Editor 30 | CFBundleURLSchemes 31 | 32 | REVERSED_CLIENT_ID 33 | 34 | 35 | 36 | CFBundleVersion 37 | 1 38 | LSRequiresIPhoneOS 39 | 40 | NSPhotoLibraryUsageDescription 41 | FriendlyChat would like to use your photo library to send image messages. 42 | UIBackgroundModes 43 | 44 | UILaunchStoryboardName 45 | LaunchScreen 46 | UIMainStoryboardFile 47 | Main 48 | UIRequiredDeviceCapabilities 49 | 50 | armv7 51 | 52 | UISupportedInterfaceOrientations 53 | 54 | UIInterfaceOrientationPortrait 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /FriendlyChat/Podfile: -------------------------------------------------------------------------------- 1 | # FriendlyChat 2 | 3 | use_frameworks! 4 | platform :ios, '8.0' 5 | 6 | pod 'Firebase/Storage' 7 | pod 'Firebase/Core' 8 | pod 'Firebase/Database' 9 | pod 'Firebase/RemoteConfig' 10 | pod 'FirebaseUI' 11 | 12 | target 'FriendlyChatSwift' do 13 | end 14 | -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "size" : "29x29", 15 | "idiom" : "iphone", 16 | "filename" : "Icon-App-29x29@1x.png", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "size" : "29x29", 21 | "idiom" : "iphone", 22 | "filename" : "Icon-App-29x29@2x.png", 23 | "scale" : "2x" 24 | }, 25 | { 26 | "size" : "29x29", 27 | "idiom" : "iphone", 28 | "filename" : "Icon-App-29x29@3x.png", 29 | "scale" : "3x" 30 | }, 31 | { 32 | "size" : "40x40", 33 | "idiom" : "iphone", 34 | "filename" : "Icon-App-40x40@2x.png", 35 | "scale" : "2x" 36 | }, 37 | { 38 | "size" : "40x40", 39 | "idiom" : "iphone", 40 | "filename" : "Icon-App-40x40@3x.png", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "size" : "60x60", 45 | "idiom" : "iphone", 46 | "filename" : "Icon-App-60x60@2x.png", 47 | "scale" : "2x" 48 | }, 49 | { 50 | "size" : "60x60", 51 | "idiom" : "iphone", 52 | "filename" : "Icon-App-60x60@3x.png", 53 | "scale" : "3x" 54 | }, 55 | { 56 | "idiom" : "ipad", 57 | "size" : "20x20", 58 | "scale" : "1x" 59 | }, 60 | { 61 | "idiom" : "ipad", 62 | "size" : "20x20", 63 | "scale" : "2x" 64 | }, 65 | { 66 | "size" : "29x29", 67 | "idiom" : "ipad", 68 | "filename" : "Icon-App-29x29@1x.png", 69 | "scale" : "1x" 70 | }, 71 | { 72 | "size" : "29x29", 73 | "idiom" : "ipad", 74 | "filename" : "Icon-App-29x29@2x.png", 75 | "scale" : "2x" 76 | }, 77 | { 78 | "size" : "40x40", 79 | "idiom" : "ipad", 80 | "filename" : "Icon-App-40x40@1x.png", 81 | "scale" : "1x" 82 | }, 83 | { 84 | "size" : "40x40", 85 | "idiom" : "ipad", 86 | "filename" : "Icon-App-40x40@2x.png", 87 | "scale" : "2x" 88 | }, 89 | { 90 | "size" : "76x76", 91 | "idiom" : "ipad", 92 | "filename" : "Icon-App-76x76@1x.png", 93 | "scale" : "1x" 94 | }, 95 | { 96 | "size" : "76x76", 97 | "idiom" : "ipad", 98 | "filename" : "Icon-App-76x76@2x.png", 99 | "scale" : "2x" 100 | }, 101 | { 102 | "size" : "83.5x83.5", 103 | "idiom" : "ipad", 104 | "filename" : "Icon-App-83.5x83.5@2x.png", 105 | "scale" : "2x" 106 | } 107 | ], 108 | "info" : { 109 | "version" : 1, 110 | "author" : "xcode" 111 | } 112 | } -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChat/shared/Images.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/Firebase.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "firebase.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/Firebase.imageset/firebase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChat/shared/Images.xcassets/Firebase.imageset/firebase.png -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/LaunchLogo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchLogo.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/LaunchLogo.imageset/LaunchLogo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChat/shared/Images.xcassets/LaunchLogo.imageset/LaunchLogo.pdf -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/ic_account_circle.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ic_account_circle.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "ic_account_circle_2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "ic_account_circle_3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/ic_account_circle.imageset/ic_account_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChat/shared/Images.xcassets/ic_account_circle.imageset/ic_account_circle.png -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/ic_account_circle.imageset/ic_account_circle_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChat/shared/Images.xcassets/ic_account_circle.imageset/ic_account_circle_2x.png -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/ic_account_circle.imageset/ic_account_circle_3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChat/shared/Images.xcassets/ic_account_circle.imageset/ic_account_circle_3x.png -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/ic_add_a_photo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "ic_add_a_photo.png", 5 | "idiom": "universal", 6 | "scale": "1x" 7 | }, 8 | { 9 | "filename": "ic_add_a_photo_2x.png", 10 | "idiom": "universal", 11 | "scale": "2x" 12 | }, 13 | { 14 | "filename": "ic_add_a_photo_3x.png", 15 | "idiom": "universal", 16 | "scale": "3x" 17 | } 18 | ], 19 | "info": { 20 | "author": "xcode", 21 | "version": 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/ic_add_a_photo.imageset/ic_add_a_photo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChat/shared/Images.xcassets/ic_add_a_photo.imageset/ic_add_a_photo.png -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/ic_add_a_photo.imageset/ic_add_a_photo_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChat/shared/Images.xcassets/ic_add_a_photo.imageset/ic_add_a_photo_2x.png -------------------------------------------------------------------------------- /FriendlyChat/shared/Images.xcassets/ic_add_a_photo.imageset/ic_add_a_photo_3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChat/shared/Images.xcassets/ic_add_a_photo.imageset/ic_add_a_photo_3x.png -------------------------------------------------------------------------------- /FriendlyChat/shared/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /FriendlyChat/shared/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChat/shared/Roboto-Regular.ttf -------------------------------------------------------------------------------- /FriendlyChat/shared/RobotoMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChat/shared/RobotoMono-Regular.ttf -------------------------------------------------------------------------------- /FriendlyChatComplete/FriendlyChatSwift.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 107960731C612531003DB6CC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 107960711C612531003DB6CC /* Main.storyboard */; }; 11 | 2C595F061B34B73A00355AE7 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C595F041B34B73A00355AE7 /* Constants.swift */; }; 12 | 2C8562851AFAB65F00F0B24B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2C85627E1AFAB65F00F0B24B /* Images.xcassets */; }; 13 | 2C8562871AFAB65F00F0B24B /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2C85627F1AFAB65F00F0B24B /* LaunchScreen.xib */; }; 14 | 5F5A537E1ADE67D500F81DF0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F5A537D1ADE67D500F81DF0 /* AppDelegate.swift */; }; 15 | 5F5A53801ADE67D500F81DF0 /* FCViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F5A537F1ADE67D500F81DF0 /* FCViewController.swift */; }; 16 | A92476E81D6F96C300A96C4B /* Roboto-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A92476E61D6F96C300A96C4B /* Roboto-Regular.ttf */; }; 17 | A92476E91D6F96C300A96C4B /* RobotoMono-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A92476E71D6F96C300A96C4B /* RobotoMono-Regular.ttf */; }; 18 | DBBAAE069585D787FD06FE15 /* Pods_FriendlyChatSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9EE776B645030DD333883549 /* Pods_FriendlyChatSwift.framework */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | 107960721C612531003DB6CC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 23 | 107960741C61253B003DB6CC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 24 | 2C595F041B34B73A00355AE7 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; 25 | 2C85627E1AFAB65F00F0B24B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 26 | 2C85627F1AFAB65F00F0B24B /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = ""; }; 27 | 47BDA9BA1B03D338F2B3A7F7 /* Pods-FriendlyChatSwift.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FriendlyChatSwift.debug.xcconfig"; path = "Pods/Target Support Files/Pods-FriendlyChatSwift/Pods-FriendlyChatSwift.debug.xcconfig"; sourceTree = ""; }; 28 | 5F5A53791ADE67D500F81DF0 /* FriendlyChatSwift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FriendlyChatSwift.app; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 5F5A537D1ADE67D500F81DF0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 30 | 5F5A537F1ADE67D500F81DF0 /* FCViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FCViewController.swift; sourceTree = ""; }; 31 | 9EE776B645030DD333883549 /* Pods_FriendlyChatSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FriendlyChatSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | A92476E61D6F96C300A96C4B /* Roboto-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Regular.ttf"; sourceTree = ""; }; 33 | A92476E71D6F96C300A96C4B /* RobotoMono-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "RobotoMono-Regular.ttf"; sourceTree = ""; }; 34 | F3514636A3BDB21360D87272 /* Pods-FriendlyChatSwift.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FriendlyChatSwift.release.xcconfig"; path = "Pods/Target Support Files/Pods-FriendlyChatSwift/Pods-FriendlyChatSwift.release.xcconfig"; sourceTree = ""; }; 35 | /* End PBXFileReference section */ 36 | 37 | /* Begin PBXFrameworksBuildPhase section */ 38 | 5F5A53761ADE67D500F81DF0 /* Frameworks */ = { 39 | isa = PBXFrameworksBuildPhase; 40 | buildActionMask = 2147483647; 41 | files = ( 42 | DBBAAE069585D787FD06FE15 /* Pods_FriendlyChatSwift.framework in Frameworks */, 43 | ); 44 | runOnlyForDeploymentPostprocessing = 0; 45 | }; 46 | /* End PBXFrameworksBuildPhase section */ 47 | 48 | /* Begin PBXGroup section */ 49 | 12DD0A4A089E5CEFC6F01148 /* Pods */ = { 50 | isa = PBXGroup; 51 | children = ( 52 | 47BDA9BA1B03D338F2B3A7F7 /* Pods-FriendlyChatSwift.debug.xcconfig */, 53 | F3514636A3BDB21360D87272 /* Pods-FriendlyChatSwift.release.xcconfig */, 54 | ); 55 | name = Pods; 56 | sourceTree = ""; 57 | }; 58 | 2C85627A1AFAB65F00F0B24B /* shared */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | A92476E61D6F96C300A96C4B /* Roboto-Regular.ttf */, 62 | A92476E71D6F96C300A96C4B /* RobotoMono-Regular.ttf */, 63 | 2C85627E1AFAB65F00F0B24B /* Images.xcassets */, 64 | 2C85627F1AFAB65F00F0B24B /* LaunchScreen.xib */, 65 | ); 66 | name = shared; 67 | path = ../shared; 68 | sourceTree = ""; 69 | }; 70 | 5F5A53431ADE670C00F81DF0 = { 71 | isa = PBXGroup; 72 | children = ( 73 | 5F5A537A1ADE67D500F81DF0 /* FriendlyChatSwift */, 74 | 5F5A534D1ADE670C00F81DF0 /* Products */, 75 | 12DD0A4A089E5CEFC6F01148 /* Pods */, 76 | 9AD5D3AA9FEE4AAC1BAC7C5F /* Frameworks */, 77 | ); 78 | sourceTree = ""; 79 | }; 80 | 5F5A534D1ADE670C00F81DF0 /* Products */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | 5F5A53791ADE67D500F81DF0 /* FriendlyChatSwift.app */, 84 | ); 85 | name = Products; 86 | sourceTree = ""; 87 | }; 88 | 5F5A537A1ADE67D500F81DF0 /* FriendlyChatSwift */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 5F5A537D1ADE67D500F81DF0 /* AppDelegate.swift */, 92 | 5F5A537F1ADE67D500F81DF0 /* FCViewController.swift */, 93 | 2C595F041B34B73A00355AE7 /* Constants.swift */, 94 | 5F5A537B1ADE67D500F81DF0 /* Supporting Files */, 95 | ); 96 | path = FriendlyChatSwift; 97 | sourceTree = ""; 98 | }; 99 | 5F5A537B1ADE67D500F81DF0 /* Supporting Files */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 107960711C612531003DB6CC /* Main.storyboard */, 103 | 107960741C61253B003DB6CC /* Info.plist */, 104 | 2C85627A1AFAB65F00F0B24B /* shared */, 105 | ); 106 | name = "Supporting Files"; 107 | sourceTree = ""; 108 | }; 109 | 9AD5D3AA9FEE4AAC1BAC7C5F /* Frameworks */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 9EE776B645030DD333883549 /* Pods_FriendlyChatSwift.framework */, 113 | ); 114 | name = Frameworks; 115 | sourceTree = ""; 116 | }; 117 | /* End PBXGroup section */ 118 | 119 | /* Begin PBXNativeTarget section */ 120 | 5F5A53781ADE67D500F81DF0 /* FriendlyChatSwift */ = { 121 | isa = PBXNativeTarget; 122 | buildConfigurationList = 5F5A53991ADE67D500F81DF0 /* Build configuration list for PBXNativeTarget "FriendlyChatSwift" */; 123 | buildPhases = ( 124 | 77C29CB188171CE63340450F /* [CP] Check Pods Manifest.lock */, 125 | 5F5A53751ADE67D500F81DF0 /* Sources */, 126 | 5F5A53761ADE67D500F81DF0 /* Frameworks */, 127 | 5F5A53771ADE67D500F81DF0 /* Resources */, 128 | 602497AACFA814EC1D444B93 /* [CP] Embed Pods Frameworks */, 129 | ED8993B59ED135CE7FC6FF4D /* [CP] Copy Pods Resources */, 130 | ); 131 | buildRules = ( 132 | ); 133 | dependencies = ( 134 | ); 135 | name = FriendlyChatSwift; 136 | productName = FriendlyPingSwift; 137 | productReference = 5F5A53791ADE67D500F81DF0 /* FriendlyChatSwift.app */; 138 | productType = "com.apple.product-type.application"; 139 | }; 140 | /* End PBXNativeTarget section */ 141 | 142 | /* Begin PBXProject section */ 143 | 5F5A53441ADE670C00F81DF0 /* Project object */ = { 144 | isa = PBXProject; 145 | attributes = { 146 | LastSwiftUpdateCheck = 0710; 147 | LastUpgradeCheck = 0800; 148 | ORGANIZATIONNAME = "Google Inc."; 149 | TargetAttributes = { 150 | 5F5A53781ADE67D500F81DF0 = { 151 | CreatedOnToolsVersion = 6.3; 152 | DevelopmentTeam = 739D5HF686; 153 | LastSwiftMigration = 0800; 154 | ProvisioningStyle = Automatic; 155 | SystemCapabilities = { 156 | com.apple.BackgroundModes = { 157 | enabled = 0; 158 | }; 159 | com.apple.Keychain = { 160 | enabled = 0; 161 | }; 162 | }; 163 | }; 164 | }; 165 | }; 166 | buildConfigurationList = 5F5A53471ADE670C00F81DF0 /* Build configuration list for PBXProject "FriendlyChatSwift" */; 167 | compatibilityVersion = "Xcode 3.2"; 168 | developmentRegion = English; 169 | hasScannedForEncodings = 0; 170 | knownRegions = ( 171 | en, 172 | Base, 173 | ); 174 | mainGroup = 5F5A53431ADE670C00F81DF0; 175 | productRefGroup = 5F5A534D1ADE670C00F81DF0 /* Products */; 176 | projectDirPath = ""; 177 | projectRoot = ""; 178 | targets = ( 179 | 5F5A53781ADE67D500F81DF0 /* FriendlyChatSwift */, 180 | ); 181 | }; 182 | /* End PBXProject section */ 183 | 184 | /* Begin PBXResourcesBuildPhase section */ 185 | 5F5A53771ADE67D500F81DF0 /* Resources */ = { 186 | isa = PBXResourcesBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | 2C8562871AFAB65F00F0B24B /* LaunchScreen.xib in Resources */, 190 | 107960731C612531003DB6CC /* Main.storyboard in Resources */, 191 | A92476E81D6F96C300A96C4B /* Roboto-Regular.ttf in Resources */, 192 | 2C8562851AFAB65F00F0B24B /* Images.xcassets in Resources */, 193 | A92476E91D6F96C300A96C4B /* RobotoMono-Regular.ttf in Resources */, 194 | ); 195 | runOnlyForDeploymentPostprocessing = 0; 196 | }; 197 | /* End PBXResourcesBuildPhase section */ 198 | 199 | /* Begin PBXShellScriptBuildPhase section */ 200 | 602497AACFA814EC1D444B93 /* [CP] Embed Pods Frameworks */ = { 201 | isa = PBXShellScriptBuildPhase; 202 | buildActionMask = 2147483647; 203 | files = ( 204 | ); 205 | inputPaths = ( 206 | ); 207 | name = "[CP] Embed Pods Frameworks"; 208 | outputPaths = ( 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | shellPath = /bin/sh; 212 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-FriendlyChatSwift/Pods-FriendlyChatSwift-frameworks.sh\"\n"; 213 | showEnvVarsInLog = 0; 214 | }; 215 | 77C29CB188171CE63340450F /* [CP] Check Pods Manifest.lock */ = { 216 | isa = PBXShellScriptBuildPhase; 217 | buildActionMask = 2147483647; 218 | files = ( 219 | ); 220 | inputPaths = ( 221 | ); 222 | name = "[CP] Check Pods Manifest.lock"; 223 | outputPaths = ( 224 | ); 225 | runOnlyForDeploymentPostprocessing = 0; 226 | shellPath = /bin/sh; 227 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; 228 | showEnvVarsInLog = 0; 229 | }; 230 | ED8993B59ED135CE7FC6FF4D /* [CP] Copy Pods Resources */ = { 231 | isa = PBXShellScriptBuildPhase; 232 | buildActionMask = 2147483647; 233 | files = ( 234 | ); 235 | inputPaths = ( 236 | ); 237 | name = "[CP] Copy Pods Resources"; 238 | outputPaths = ( 239 | ); 240 | runOnlyForDeploymentPostprocessing = 0; 241 | shellPath = /bin/sh; 242 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-FriendlyChatSwift/Pods-FriendlyChatSwift-resources.sh\"\n"; 243 | showEnvVarsInLog = 0; 244 | }; 245 | /* End PBXShellScriptBuildPhase section */ 246 | 247 | /* Begin PBXSourcesBuildPhase section */ 248 | 5F5A53751ADE67D500F81DF0 /* Sources */ = { 249 | isa = PBXSourcesBuildPhase; 250 | buildActionMask = 2147483647; 251 | files = ( 252 | 5F5A53801ADE67D500F81DF0 /* FCViewController.swift in Sources */, 253 | 5F5A537E1ADE67D500F81DF0 /* AppDelegate.swift in Sources */, 254 | 2C595F061B34B73A00355AE7 /* Constants.swift in Sources */, 255 | ); 256 | runOnlyForDeploymentPostprocessing = 0; 257 | }; 258 | /* End PBXSourcesBuildPhase section */ 259 | 260 | /* Begin PBXVariantGroup section */ 261 | 107960711C612531003DB6CC /* Main.storyboard */ = { 262 | isa = PBXVariantGroup; 263 | children = ( 264 | 107960721C612531003DB6CC /* Base */, 265 | ); 266 | name = Main.storyboard; 267 | sourceTree = ""; 268 | }; 269 | /* End PBXVariantGroup section */ 270 | 271 | /* Begin XCBuildConfiguration section */ 272 | 5F5A536D1ADE670C00F81DF0 /* Debug */ = { 273 | isa = XCBuildConfiguration; 274 | buildSettings = { 275 | ALWAYS_SEARCH_USER_PATHS = NO; 276 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 277 | CLANG_CXX_LIBRARY = "libc++"; 278 | CLANG_ENABLE_MODULES = YES; 279 | CLANG_ENABLE_OBJC_ARC = YES; 280 | CLANG_WARN_BOOL_CONVERSION = YES; 281 | CLANG_WARN_CONSTANT_CONVERSION = YES; 282 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 283 | CLANG_WARN_EMPTY_BODY = YES; 284 | CLANG_WARN_ENUM_CONVERSION = YES; 285 | CLANG_WARN_INFINITE_RECURSION = YES; 286 | CLANG_WARN_INT_CONVERSION = YES; 287 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 288 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 289 | CLANG_WARN_UNREACHABLE_CODE = YES; 290 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 291 | CODE_SIGN_IDENTITY = "iPhone Developer: Google Development (3F4Y5873JF)"; 292 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Google Development (3F4Y5873JF)"; 293 | COPY_PHASE_STRIP = NO; 294 | ENABLE_STRICT_OBJC_MSGSEND = YES; 295 | ENABLE_TESTABILITY = YES; 296 | GCC_C_LANGUAGE_STANDARD = gnu99; 297 | GCC_DYNAMIC_NO_PIC = NO; 298 | GCC_NO_COMMON_BLOCKS = YES; 299 | GCC_OPTIMIZATION_LEVEL = 0; 300 | GCC_PREPROCESSOR_DEFINITIONS = ( 301 | "DEBUG=1", 302 | "$(inherited)", 303 | ); 304 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 305 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 306 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 307 | GCC_WARN_UNDECLARED_SELECTOR = YES; 308 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 309 | GCC_WARN_UNUSED_FUNCTION = YES; 310 | GCC_WARN_UNUSED_VARIABLE = YES; 311 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 312 | MTL_ENABLE_DEBUG_INFO = YES; 313 | ONLY_ACTIVE_ARCH = YES; 314 | PROVISIONING_PROFILE = "65c2088b-2425-4961-b6d5-0256d4790678"; 315 | SDKROOT = iphoneos; 316 | TARGETED_DEVICE_FAMILY = "1,2"; 317 | }; 318 | name = Debug; 319 | }; 320 | 5F5A536E1ADE670C00F81DF0 /* Release */ = { 321 | isa = XCBuildConfiguration; 322 | buildSettings = { 323 | ALWAYS_SEARCH_USER_PATHS = NO; 324 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 325 | CLANG_CXX_LIBRARY = "libc++"; 326 | CLANG_ENABLE_MODULES = YES; 327 | CLANG_ENABLE_OBJC_ARC = YES; 328 | CLANG_WARN_BOOL_CONVERSION = YES; 329 | CLANG_WARN_CONSTANT_CONVERSION = YES; 330 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 331 | CLANG_WARN_EMPTY_BODY = YES; 332 | CLANG_WARN_ENUM_CONVERSION = YES; 333 | CLANG_WARN_INFINITE_RECURSION = YES; 334 | CLANG_WARN_INT_CONVERSION = YES; 335 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 336 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 337 | CLANG_WARN_UNREACHABLE_CODE = YES; 338 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 339 | CODE_SIGN_IDENTITY = "iPhone Developer: Google Development (3F4Y5873JF)"; 340 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Google Development (3F4Y5873JF)"; 341 | COPY_PHASE_STRIP = YES; 342 | ENABLE_NS_ASSERTIONS = NO; 343 | ENABLE_STRICT_OBJC_MSGSEND = YES; 344 | GCC_C_LANGUAGE_STANDARD = gnu99; 345 | GCC_NO_COMMON_BLOCKS = YES; 346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 353 | MTL_ENABLE_DEBUG_INFO = NO; 354 | PROVISIONING_PROFILE = "65c2088b-2425-4961-b6d5-0256d4790678"; 355 | SDKROOT = iphoneos; 356 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 357 | TARGETED_DEVICE_FAMILY = "1,2"; 358 | VALIDATE_PRODUCT = YES; 359 | }; 360 | name = Release; 361 | }; 362 | 5F5A53951ADE67D500F81DF0 /* Debug */ = { 363 | isa = XCBuildConfiguration; 364 | baseConfigurationReference = 47BDA9BA1B03D338F2B3A7F7 /* Pods-FriendlyChatSwift.debug.xcconfig */; 365 | buildSettings = { 366 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 367 | CODE_SIGN_IDENTITY = "iPhone Developer"; 368 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 369 | DEVELOPMENT_TEAM = 739D5HF686; 370 | ENABLE_BITCODE = NO; 371 | INFOPLIST_FILE = "$(SRCROOT)/FriendlyChatSwift/Info.plist"; 372 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 373 | PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.udacity.friendlychat; 374 | PRODUCT_NAME = FriendlyChatSwift; 375 | PROVISIONING_PROFILE = ""; 376 | PROVISIONING_PROFILE_SPECIFIER = ""; 377 | SWIFT_OBJC_BRIDGING_HEADER = ""; 378 | SWIFT_VERSION = 3.0; 379 | }; 380 | name = Debug; 381 | }; 382 | 5F5A53961ADE67D500F81DF0 /* Release */ = { 383 | isa = XCBuildConfiguration; 384 | baseConfigurationReference = F3514636A3BDB21360D87272 /* Pods-FriendlyChatSwift.release.xcconfig */; 385 | buildSettings = { 386 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 387 | CODE_SIGN_IDENTITY = "iPhone Developer"; 388 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 389 | DEVELOPMENT_TEAM = 739D5HF686; 390 | ENABLE_BITCODE = NO; 391 | INFOPLIST_FILE = "$(SRCROOT)/FriendlyChatSwift/Info.plist"; 392 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 393 | PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.udacity.friendlychat; 394 | PRODUCT_NAME = FriendlyChatSwift; 395 | PROVISIONING_PROFILE = ""; 396 | PROVISIONING_PROFILE_SPECIFIER = ""; 397 | SWIFT_OBJC_BRIDGING_HEADER = ""; 398 | SWIFT_VERSION = 3.0; 399 | }; 400 | name = Release; 401 | }; 402 | /* End XCBuildConfiguration section */ 403 | 404 | /* Begin XCConfigurationList section */ 405 | 5F5A53471ADE670C00F81DF0 /* Build configuration list for PBXProject "FriendlyChatSwift" */ = { 406 | isa = XCConfigurationList; 407 | buildConfigurations = ( 408 | 5F5A536D1ADE670C00F81DF0 /* Debug */, 409 | 5F5A536E1ADE670C00F81DF0 /* Release */, 410 | ); 411 | defaultConfigurationIsVisible = 0; 412 | defaultConfigurationName = Release; 413 | }; 414 | 5F5A53991ADE67D500F81DF0 /* Build configuration list for PBXNativeTarget "FriendlyChatSwift" */ = { 415 | isa = XCConfigurationList; 416 | buildConfigurations = ( 417 | 5F5A53951ADE67D500F81DF0 /* Debug */, 418 | 5F5A53961ADE67D500F81DF0 /* Release */, 419 | ); 420 | defaultConfigurationIsVisible = 0; 421 | defaultConfigurationName = Release; 422 | }; 423 | /* End XCConfigurationList section */ 424 | }; 425 | rootObject = 5F5A53441ADE670C00F81DF0 /* Project object */; 426 | } 427 | -------------------------------------------------------------------------------- /FriendlyChatComplete/FriendlyChatSwift/AppDelegate: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015 Google Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | import UIKit 18 | 19 | import Firebase 20 | 21 | @UIApplicationMain 22 | class AppDelegate: UIResponder, UIApplicationDelegate { 23 | 24 | var window: UIWindow? 25 | 26 | func application(application: UIApplication, didFinishLaunchingWithOptions 27 | launchOptions: [NSObject: AnyObject]?) -> Bool { 28 | FIRApp.configure() 29 | return true 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /FriendlyChatComplete/FriendlyChatSwift/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015 Google Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | import UIKit 18 | import Firebase 19 | import FirebaseAuthUI 20 | 21 | // MARK: - AppDelegate: UIResponder, UIApplicationDelegate 22 | 23 | @UIApplicationMain 24 | class AppDelegate: UIResponder, UIApplicationDelegate { 25 | 26 | // MARK: Properties 27 | 28 | var window: UIWindow? 29 | 30 | // MARK: UIApplicationDelegate 31 | 32 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool { 33 | FirebaseApp.configure() 34 | return true 35 | } 36 | 37 | func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool { 38 | return FUIAuth.defaultAuthUI()?.handleOpen(url, sourceApplication: sourceApplication ?? "") ?? false 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /FriendlyChatComplete/FriendlyChatSwift/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Roboto-Regular 12 | 13 | 14 | RobotoMono-Regular 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 45 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 114 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /FriendlyChatComplete/FriendlyChatSwift/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015 Google Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | // MARK: - Constants 18 | 19 | struct Constants { 20 | 21 | // MARK: NotificationKeys 22 | 23 | struct NotificationKeys { 24 | static let SignedIn = "onSignInCompleted" 25 | } 26 | 27 | // MARK: MessageFields 28 | 29 | struct MessageFields { 30 | static let name = "name" 31 | static let text = "text" 32 | static let imageUrl = "photoUrl" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /FriendlyChatComplete/FriendlyChatSwift/FCViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015 Google Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | import UIKit 18 | import Firebase 19 | import FirebaseAuthUI 20 | import FirebaseGoogleAuthUI 21 | 22 | // MARK: - FCViewController 23 | 24 | class FCViewController: UIViewController, UINavigationControllerDelegate { 25 | 26 | // MARK: Properties 27 | 28 | var ref: DatabaseReference! 29 | var messages: [DataSnapshot]! = [] 30 | var msglength: NSNumber = 1000 31 | var storageRef: StorageReference! 32 | var remoteConfig: RemoteConfig! 33 | let imageCache = NSCache() 34 | var keyboardOnScreen = false 35 | var placeholderImage = UIImage(named: "ic_account_circle") 36 | fileprivate var _refHandle: DatabaseHandle! 37 | fileprivate var _authHandle: AuthStateDidChangeListenerHandle! 38 | var user: User? 39 | var displayName = "Anonymous" 40 | 41 | // MARK: Outlets 42 | 43 | @IBOutlet weak var messageTextField: UITextField! 44 | @IBOutlet weak var sendButton: UIButton! 45 | @IBOutlet weak var signInButton: UIButton! 46 | @IBOutlet weak var imageMessage: UIButton! 47 | @IBOutlet weak var signOutButton: UIButton! 48 | @IBOutlet weak var messagesTable: UITableView! 49 | @IBOutlet weak var backgroundBlur: UIVisualEffectView! 50 | @IBOutlet weak var imageDisplay: UIImageView! 51 | @IBOutlet var dismissImageRecognizer: UITapGestureRecognizer! 52 | @IBOutlet var dismissKeyboardRecognizer: UITapGestureRecognizer! 53 | 54 | // MARK: Life Cycle 55 | 56 | override func viewDidLoad() { 57 | configureAuth() 58 | } 59 | 60 | override func viewWillDisappear(_ animated: Bool) { 61 | super.viewWillDisappear(animated) 62 | unsubscribeFromAllNotifications() 63 | } 64 | 65 | // MARK: Config 66 | 67 | func configureAuth() { 68 | let provider: [FUIAuthProvider] = [FUIGoogleAuth()] 69 | FUIAuth.defaultAuthUI()?.providers = provider 70 | 71 | // listen for changes in the authorization state 72 | _authHandle = Auth.auth().addStateDidChangeListener { (auth: Auth, user: User?) in 73 | // refresh table data 74 | self.messages.removeAll(keepingCapacity: false) 75 | self.messagesTable.reloadData() 76 | 77 | // check if there is a current user 78 | if let activeUser = user { 79 | // check if the current app user is the current FIRUser 80 | if self.user != activeUser { 81 | self.user = activeUser 82 | self.signedInStatus(isSignedIn: true) 83 | let name = user!.email!.components(separatedBy: "@")[0] 84 | self.displayName = name 85 | } 86 | } else { 87 | // user must sign in 88 | self.signedInStatus(isSignedIn: false) 89 | self.loginSession() 90 | } 91 | } 92 | } 93 | 94 | func configureDatabase() { 95 | ref = Database.database().reference() 96 | // listen for new messages in the firebase database 97 | _refHandle = ref.child("messages").observe(.childAdded) { (snapshot: DataSnapshot)in 98 | self.messages.append(snapshot) 99 | self.messagesTable.insertRows(at: [IndexPath(row: self.messages.count-1, section: 0)], with: .automatic) 100 | self.scrollToBottomMessage() 101 | } 102 | } 103 | 104 | func configureStorage() { 105 | storageRef = Storage.storage().reference() 106 | } 107 | 108 | deinit { 109 | ref.child("messages").removeObserver(withHandle: _refHandle) 110 | Auth.auth().removeStateDidChangeListener(_authHandle) 111 | } 112 | 113 | // MARK: Remote Config 114 | 115 | func configureRemoteConfig() { 116 | // create remote config setting to enable developer mode 117 | let remoteConfigSettings = RemoteConfigSettings(developerModeEnabled: true) 118 | remoteConfig = RemoteConfig.remoteConfig() 119 | remoteConfig.configSettings = remoteConfigSettings! 120 | } 121 | 122 | func fetchConfig() { 123 | var expirationDuration: Double = 3600 124 | // if in developer mode, set cacheExpiration 0 so each fetch will retrieve values from the server 125 | if remoteConfig.configSettings.isDeveloperModeEnabled { 126 | expirationDuration = 0 127 | } 128 | 129 | // cacheExpirationSeconds is set to cacheExpiration to make fetching faser in developer mode 130 | remoteConfig.fetch(withExpirationDuration: expirationDuration) { (status, error) in 131 | if status == .success { 132 | print("Config fetched!") 133 | self.remoteConfig.activateFetched() 134 | let friendlyMsgLength = self.remoteConfig["friendly_msg_length"] 135 | if friendlyMsgLength.source != .static { 136 | self.msglength = friendlyMsgLength.numberValue! 137 | print("Friendly msg length config: \(self.msglength)") 138 | } 139 | } else { 140 | print("Config not fetched") 141 | print("Error \(String(describing: error))") 142 | } 143 | } 144 | } 145 | 146 | // MARK: Sign In and Out 147 | 148 | func signedInStatus(isSignedIn: Bool) { 149 | signInButton.isHidden = isSignedIn 150 | signOutButton.isHidden = !isSignedIn 151 | messagesTable.isHidden = !isSignedIn 152 | messageTextField.isHidden = !isSignedIn 153 | sendButton.isHidden = !isSignedIn 154 | imageMessage.isHidden = !isSignedIn 155 | backgroundBlur.effect = UIBlurEffect(style: .light) 156 | 157 | if isSignedIn { 158 | // remove background blur (will use when showing image messages) 159 | messagesTable.rowHeight = UITableViewAutomaticDimension 160 | messagesTable.estimatedRowHeight = 122.0 161 | backgroundBlur.effect = nil 162 | messageTextField.delegate = self 163 | subscribeToKeyboardNotifications() 164 | configureDatabase() 165 | configureStorage() 166 | configureRemoteConfig() 167 | fetchConfig() 168 | } 169 | } 170 | 171 | func loginSession() { 172 | let authViewController = FUIAuth.defaultAuthUI()!.authViewController() 173 | present(authViewController, animated: true, completion: nil) 174 | } 175 | 176 | 177 | // MARK: Send Message 178 | 179 | func sendMessage(data: [String:String]) { 180 | var mdata = data 181 | // add name to message and then data to firebase database 182 | mdata[Constants.MessageFields.name] = displayName 183 | ref.child("messages").childByAutoId().setValue(mdata) 184 | } 185 | 186 | func sendPhotoMessage(photoData: Data) { 187 | // build a path using the user’s ID and a timestamp 188 | let imagePath = "chat_photos/" + Auth.auth().currentUser!.uid + "/\(Double(Date.timeIntervalSinceReferenceDate * 1000)).jpg" 189 | // set content type to “image/jpeg” in firebase storage metadata 190 | let metadata = StorageMetadata() 191 | metadata.contentType = "image/jpeg" 192 | // create a child node at imagePath with imageData and metadata 193 | storageRef!.child(imagePath).putData(photoData, metadata: metadata) { (metadata, error) in 194 | if let error = error { 195 | print("Error uploading: \(error)") 196 | return 197 | } 198 | // use sendMessage to add imageURL to database 199 | self.sendMessage(data: [Constants.MessageFields.imageUrl: self.storageRef!.child((metadata?.path)!).description]) 200 | } 201 | } 202 | 203 | // MARK: Alert 204 | 205 | func showAlert(title: String, message: String) { 206 | DispatchQueue.main.async { 207 | let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) 208 | let dismissAction = UIAlertAction(title: "Dismiss", style: .destructive, handler: nil) 209 | alert.addAction(dismissAction) 210 | self.present(alert, animated: true, completion: nil) 211 | } 212 | } 213 | 214 | // MARK: Scroll Messages 215 | 216 | func scrollToBottomMessage() { 217 | if messages.count == 0 { return } 218 | let bottomMessageIndex = IndexPath(row: messagesTable.numberOfRows(inSection: 0) - 1, section: 0) 219 | messagesTable.scrollToRow(at: bottomMessageIndex, at: .bottom, animated: true) 220 | } 221 | 222 | // MARK: Actions 223 | 224 | @IBAction func showLoginView(_ sender: AnyObject) { 225 | loginSession() 226 | } 227 | 228 | @IBAction func didTapAddPhoto(_ sender: AnyObject) { 229 | let picker = UIImagePickerController() 230 | picker.delegate = self 231 | picker.sourceType = .photoLibrary 232 | present(picker, animated: true, completion: nil) 233 | } 234 | 235 | @IBAction func signOut(_ sender: UIButton) { 236 | do { 237 | try Auth.auth().signOut() 238 | } catch { 239 | print("unable to sign out: \(error)") 240 | } 241 | } 242 | 243 | @IBAction func didSendMessage(_ sender: UIButton) { 244 | let _ = textFieldShouldReturn(messageTextField) 245 | messageTextField.text = "" 246 | } 247 | 248 | @IBAction func dismissImageDisplay(_ sender: AnyObject) { 249 | // if touch detected when image is displayed 250 | if imageDisplay.alpha == 1.0 { 251 | UIView.animate(withDuration: 0.25) { 252 | self.backgroundBlur.effect = nil 253 | self.imageDisplay.alpha = 0.0 254 | } 255 | dismissImageRecognizer.isEnabled = false 256 | messageTextField.isEnabled = true 257 | } 258 | } 259 | 260 | @IBAction func tappedView(_ sender: AnyObject) { 261 | resignTextfield() 262 | } 263 | } 264 | 265 | // MARK: - FCViewController: UITableViewDelegate, UITableViewDataSource 266 | 267 | extension FCViewController: UITableViewDelegate, UITableViewDataSource { 268 | 269 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 270 | return messages.count 271 | } 272 | 273 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 274 | // dequeue cell 275 | let cell: UITableViewCell! = messagesTable.dequeueReusableCell(withIdentifier: "messageCell", for: indexPath) 276 | // unpack message from firebase data snapshot 277 | let messageSnapshot = messages[indexPath.row] 278 | let message = messageSnapshot.value as! [String: String] 279 | let name = message[Constants.MessageFields.name] ?? "[username]" 280 | // if image message, then grab image and display it 281 | if let imageUrl = message[Constants.MessageFields.imageUrl] { 282 | cell!.textLabel?.text = "sent by: \(name)" 283 | // image already exists in cache 284 | if let cachedImage = imageCache.object(forKey: imageUrl as NSString) { 285 | cell.imageView?.image = cachedImage 286 | cell.setNeedsLayout() 287 | } else { 288 | // download image 289 | Storage.storage().reference(forURL: imageUrl).getData(maxSize: INT64_MAX, completion: { (data, error) in 290 | guard error == nil else { 291 | print("Error downloading: \(error!)") 292 | return 293 | } 294 | let messageImage = UIImage.init(data: data!, scale: 50) 295 | self.imageCache.setObject(messageImage!, forKey: imageUrl as NSString as NSString) 296 | // check if the cell is still on screen, if so, update cell image 297 | if cell == tableView.cellForRow(at: indexPath) { 298 | DispatchQueue.main.async { 299 | cell.imageView?.image = messageImage 300 | cell.setNeedsLayout() 301 | } 302 | } 303 | }) 304 | } 305 | } else { 306 | // otherwise, update cell for regular message 307 | let text = message[Constants.MessageFields.text] ?? "[message]" 308 | cell!.textLabel?.text = name + ": " + text 309 | cell!.imageView?.image = placeholderImage 310 | } 311 | return cell! 312 | } 313 | 314 | func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { 315 | return UITableViewAutomaticDimension 316 | } 317 | 318 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 319 | // skip if keyboard is shown 320 | guard !messageTextField.isFirstResponder else { return } 321 | // unpack message from firebase data snapshot 322 | let messageSnapshot: DataSnapshot! = messages[(indexPath as NSIndexPath).row] 323 | let message = messageSnapshot.value as! [String: String] 324 | // if tapped row with image message, then display image 325 | if let imageUrl = message[Constants.MessageFields.imageUrl] { 326 | if let cachedImage = imageCache.object(forKey: imageUrl as NSString) { 327 | showImageDisplay(cachedImage) 328 | } else { 329 | Storage.storage().reference(forURL: imageUrl).getData(maxSize: INT64_MAX, completion: { (data, error) in 330 | guard error == nil else { 331 | print("Error downloading: \(error!)") 332 | return 333 | } 334 | self.showImageDisplay(UIImage.init(data: data!)!) 335 | }) 336 | } 337 | } 338 | } 339 | 340 | // MARK: Show Image Display 341 | 342 | func showImageDisplay(_ image: UIImage) { 343 | dismissImageRecognizer.isEnabled = true 344 | dismissKeyboardRecognizer.isEnabled = false 345 | messageTextField.isEnabled = false 346 | UIView.animate(withDuration: 0.25) { 347 | self.backgroundBlur.effect = UIBlurEffect(style: .light) 348 | self.imageDisplay.alpha = 1.0 349 | self.imageDisplay.image = image 350 | } 351 | } 352 | } 353 | 354 | // MARK: - FCViewController: UIImagePickerControllerDelegate 355 | 356 | extension FCViewController: UIImagePickerControllerDelegate { 357 | 358 | func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String:Any]) { 359 | // constant to hold the information about the photo 360 | if let photo = info[UIImagePickerControllerOriginalImage] as? UIImage, let photoData = UIImageJPEGRepresentation(photo, 0.8) { 361 | // call function to upload photo message 362 | sendPhotoMessage(photoData: photoData) 363 | } 364 | picker.dismiss(animated: true, completion: nil) 365 | } 366 | 367 | func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { 368 | picker.dismiss(animated: true, completion: nil) 369 | } 370 | } 371 | 372 | // MARK: - FCViewController: UITextFieldDelegate 373 | 374 | extension FCViewController: UITextFieldDelegate { 375 | 376 | func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { 377 | // set the maximum length of the message 378 | guard let text = textField.text else { return true } 379 | let newLength = text.utf16.count + string.utf16.count - range.length 380 | return newLength <= msglength.intValue 381 | } 382 | 383 | func textFieldShouldReturn(_ textField: UITextField) -> Bool { 384 | if !textField.text!.isEmpty { 385 | let data = [Constants.MessageFields.text: textField.text! as String] 386 | sendMessage(data: data) 387 | textField.resignFirstResponder() 388 | } 389 | return true 390 | } 391 | 392 | // MARK: Show/Hide Keyboard 393 | 394 | func keyboardWillShow(_ notification: Notification) { 395 | if !keyboardOnScreen { 396 | view.frame.origin.y -= keyboardHeight(notification) 397 | } 398 | } 399 | 400 | func keyboardWillHide(_ notification: Notification) { 401 | if keyboardOnScreen { 402 | view.frame.origin.y += keyboardHeight(notification) 403 | } 404 | } 405 | 406 | func keyboardDidShow(_ notification: Notification) { 407 | keyboardOnScreen = true 408 | dismissKeyboardRecognizer.isEnabled = true 409 | scrollToBottomMessage() 410 | } 411 | 412 | func keyboardDidHide(_ notification: Notification) { 413 | dismissKeyboardRecognizer.isEnabled = false 414 | keyboardOnScreen = false 415 | } 416 | 417 | func keyboardHeight(_ notification: Notification) -> CGFloat { 418 | return ((notification as NSNotification).userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue.height 419 | } 420 | 421 | func resignTextfield() { 422 | if messageTextField.isFirstResponder { 423 | messageTextField.resignFirstResponder() 424 | } 425 | } 426 | } 427 | 428 | // MARK: - FCViewController (Notifications) 429 | 430 | extension FCViewController { 431 | 432 | func subscribeToKeyboardNotifications() { 433 | subscribeToNotification(.UIKeyboardWillShow, selector: #selector(keyboardWillShow)) 434 | subscribeToNotification(.UIKeyboardWillHide, selector: #selector(keyboardWillHide)) 435 | subscribeToNotification(.UIKeyboardDidShow, selector: #selector(keyboardDidShow)) 436 | subscribeToNotification(.UIKeyboardDidHide, selector: #selector(keyboardDidHide)) 437 | } 438 | 439 | func subscribeToNotification(_ name: NSNotification.Name, selector: Selector) { 440 | NotificationCenter.default.addObserver(self, selector: selector, name: name, object: nil) 441 | } 442 | 443 | func unsubscribeFromAllNotifications() { 444 | NotificationCenter.default.removeObserver(self) 445 | } 446 | } 447 | -------------------------------------------------------------------------------- /FriendlyChatComplete/FriendlyChatSwift/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIcons 10 | 11 | CFBundleIcons~ipad 12 | 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | $(PRODUCT_NAME) 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleSignature 24 | ???? 25 | CFBundleURLTypes 26 | 27 | 28 | CFBundleTypeRole 29 | Editor 30 | CFBundleURLSchemes 31 | 32 | REVERSED_CLIENT_ID 33 | 34 | 35 | 36 | CFBundleVersion 37 | 1 38 | LSRequiresIPhoneOS 39 | 40 | NSPhotoLibraryUsageDescription 41 | FriendlyChat would like to use your photo library to send image messages. 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIMainStoryboardFile 45 | Main 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | 54 | UISupportedInterfaceOrientations~ipad 55 | 56 | UIInterfaceOrientationPortrait 57 | UIInterfaceOrientationPortraitUpsideDown 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /FriendlyChatComplete/Podfile: -------------------------------------------------------------------------------- 1 | # FriendlyChat 2 | 3 | use_frameworks! 4 | platform :ios, '8.0' 5 | 6 | pod 'Firebase/Storage' 7 | pod 'Firebase/Core' 8 | pod 'Firebase/Database' 9 | pod 'Firebase/RemoteConfig' 10 | pod 'FirebaseUI' 11 | 12 | target 'FriendlyChatSwift' do 13 | end 14 | -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "size" : "29x29", 15 | "idiom" : "iphone", 16 | "filename" : "Icon-App-29x29@1x.png", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "size" : "29x29", 21 | "idiom" : "iphone", 22 | "filename" : "Icon-App-29x29@2x.png", 23 | "scale" : "2x" 24 | }, 25 | { 26 | "size" : "29x29", 27 | "idiom" : "iphone", 28 | "filename" : "Icon-App-29x29@3x.png", 29 | "scale" : "3x" 30 | }, 31 | { 32 | "size" : "40x40", 33 | "idiom" : "iphone", 34 | "filename" : "Icon-App-40x40@2x.png", 35 | "scale" : "2x" 36 | }, 37 | { 38 | "size" : "40x40", 39 | "idiom" : "iphone", 40 | "filename" : "Icon-App-40x40@3x.png", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "size" : "60x60", 45 | "idiom" : "iphone", 46 | "filename" : "Icon-App-60x60@2x.png", 47 | "scale" : "2x" 48 | }, 49 | { 50 | "size" : "60x60", 51 | "idiom" : "iphone", 52 | "filename" : "Icon-App-60x60@3x.png", 53 | "scale" : "3x" 54 | }, 55 | { 56 | "idiom" : "ipad", 57 | "size" : "20x20", 58 | "scale" : "1x" 59 | }, 60 | { 61 | "idiom" : "ipad", 62 | "size" : "20x20", 63 | "scale" : "2x" 64 | }, 65 | { 66 | "size" : "29x29", 67 | "idiom" : "ipad", 68 | "filename" : "Icon-App-29x29@1x.png", 69 | "scale" : "1x" 70 | }, 71 | { 72 | "size" : "29x29", 73 | "idiom" : "ipad", 74 | "filename" : "Icon-App-29x29@2x.png", 75 | "scale" : "2x" 76 | }, 77 | { 78 | "size" : "40x40", 79 | "idiom" : "ipad", 80 | "filename" : "Icon-App-40x40@1x.png", 81 | "scale" : "1x" 82 | }, 83 | { 84 | "size" : "40x40", 85 | "idiom" : "ipad", 86 | "filename" : "Icon-App-40x40@2x.png", 87 | "scale" : "2x" 88 | }, 89 | { 90 | "size" : "76x76", 91 | "idiom" : "ipad", 92 | "filename" : "Icon-App-76x76@1x.png", 93 | "scale" : "1x" 94 | }, 95 | { 96 | "size" : "76x76", 97 | "idiom" : "ipad", 98 | "filename" : "Icon-App-76x76@2x.png", 99 | "scale" : "2x" 100 | }, 101 | { 102 | "size" : "83.5x83.5", 103 | "idiom" : "ipad", 104 | "filename" : "Icon-App-83.5x83.5@2x.png", 105 | "scale" : "2x" 106 | } 107 | ], 108 | "info" : { 109 | "version" : 1, 110 | "author" : "xcode" 111 | } 112 | } -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChatComplete/shared/Images.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/Firebase.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "firebase.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/Firebase.imageset/firebase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChatComplete/shared/Images.xcassets/Firebase.imageset/firebase.png -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/LaunchLogo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchLogo.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/LaunchLogo.imageset/LaunchLogo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChatComplete/shared/Images.xcassets/LaunchLogo.imageset/LaunchLogo.pdf -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/ic_account_circle.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ic_account_circle.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "ic_account_circle_2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "ic_account_circle_3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/ic_account_circle.imageset/ic_account_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChatComplete/shared/Images.xcassets/ic_account_circle.imageset/ic_account_circle.png -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/ic_account_circle.imageset/ic_account_circle_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChatComplete/shared/Images.xcassets/ic_account_circle.imageset/ic_account_circle_2x.png -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/ic_account_circle.imageset/ic_account_circle_3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChatComplete/shared/Images.xcassets/ic_account_circle.imageset/ic_account_circle_3x.png -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/ic_add_a_photo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "ic_add_a_photo.png", 5 | "idiom": "universal", 6 | "scale": "1x" 7 | }, 8 | { 9 | "filename": "ic_add_a_photo_2x.png", 10 | "idiom": "universal", 11 | "scale": "2x" 12 | }, 13 | { 14 | "filename": "ic_add_a_photo_3x.png", 15 | "idiom": "universal", 16 | "scale": "3x" 17 | } 18 | ], 19 | "info": { 20 | "author": "xcode", 21 | "version": 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/ic_add_a_photo.imageset/ic_add_a_photo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChatComplete/shared/Images.xcassets/ic_add_a_photo.imageset/ic_add_a_photo.png -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/ic_add_a_photo.imageset/ic_add_a_photo_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChatComplete/shared/Images.xcassets/ic_add_a_photo.imageset/ic_add_a_photo_2x.png -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Images.xcassets/ic_add_a_photo.imageset/ic_add_a_photo_3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChatComplete/shared/Images.xcassets/ic_add_a_photo.imageset/ic_add_a_photo_3x.png -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChatComplete/shared/Roboto-Regular.ttf -------------------------------------------------------------------------------- /FriendlyChatComplete/shared/RobotoMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ios-nd-firebase/b5cc1b2aac31283d42a7c0361924e04b9648f636/FriendlyChatComplete/shared/RobotoMono-Regular.ttf -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2015 Google Inc 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | All code in any directories or sub-directories that end with *.html or 205 | *.css is licensed under the Creative Commons Attribution International 206 | 4.0 License, which full text can be found here: 207 | https://creativecommons.org/licenses/by/4.0/legalcode. 208 | 209 | As an exception to this license, all html or css that is generated by 210 | the software at the direction of the user is copyright the user. The 211 | user has full ownership and control over such content, including 212 | whether and how they wish to license it. 213 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FriendlyChat 2 | 3 | This repository contains code for the FriendlyChat project in the Firebase in a Weekend Udacity course. 4 | 5 | ## Overview 6 | 7 | FriendlyChat is an app that allows users to send and receive text and photos in realtime across platforms. 8 | 9 | ## Setup 10 | 11 | Setup requires creating a Firebase project. See https://firebase.google.com/ for more information. 12 | 13 | ## Maintainers 14 | 15 | @jenperson 16 | @jarrodparkes 17 | 18 | ## License 19 | See [LICENSE](LICENSE) 20 | --------------------------------------------------------------------------------