diff --git a/samples/swift/Podfile b/samples/swift/Podfile new file mode 100644 index 00000000000..ba3deece0a5 --- /dev/null +++ b/samples/swift/Podfile @@ -0,0 +1,10 @@ +target 'uidemo' do + use_frameworks! + + pod 'FirebaseUI' + + target 'uidemoTests' do + inherit! :search_paths + end + +end diff --git a/samples/swift/README.md b/samples/swift/README.md new file mode 100644 index 00000000000..e238e54bd75 --- /dev/null +++ b/samples/swift/README.md @@ -0,0 +1,22 @@ +Swift Samples +----- + +This directory contains a collection of swift code samples. + +In order to run the project you'll need a valid app in Firebase and +the `GoogleService-Info.plist` file for that project. Drag the plist into the +project root and the project should build correctly. Find more instructions +and download a plist file from the [Firebase console](https://console.firebase.google.com). + +###Chat Sample + +This sample uses [anonymous authentication](https://firebase.google.com/docs/auth/ios/anonymous-auth), +so make sure anonymous auth is enabled in Firebase console. + +###Auth Sample + +This sample uses [email/password](https://firebase.google.com/docs/auth/ios/password-auth), +[Google](https://firebase.google.com/docs/auth/ios/google-signin), +and [Facebook](https://firebase.google.com/docs/auth/ios/facebook-login) +auth, so make sure those are enabled in Firebase console. + diff --git a/samples/swift/uidemo.xcodeproj/project.pbxproj b/samples/swift/uidemo.xcodeproj/project.pbxproj new file mode 100644 index 00000000000..e3980f13fc6 --- /dev/null +++ b/samples/swift/uidemo.xcodeproj/project.pbxproj @@ -0,0 +1,539 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 8D16073E1D492B200069E4F5 /* AuthViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D16073D1D492B200069E4F5 /* AuthViewController.swift */; }; + 8DABC9891D3D82D600453807 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DABC9881D3D82D600453807 /* AppDelegate.swift */; }; + 8DABC98B1D3D82D600453807 /* MenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DABC98A1D3D82D600453807 /* MenuViewController.swift */; }; + 8DABC98E1D3D82D600453807 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8DABC98C1D3D82D600453807 /* Main.storyboard */; }; + 8DABC9901D3D82D600453807 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8DABC98F1D3D82D600453807 /* Assets.xcassets */; }; + 8DABC9931D3D82D600453807 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8DABC9911D3D82D600453807 /* LaunchScreen.storyboard */; }; + 8DABC99E1D3D82D600453807 /* uidemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DABC99D1D3D82D600453807 /* uidemoTests.swift */; }; + 8DABC9A91D3D872C00453807 /* Sample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DABC9A81D3D872C00453807 /* Sample.swift */; }; + 8DABC9AB1D3D947300453807 /* SampleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DABC9AA1D3D947300453807 /* SampleCell.swift */; }; + 8DABC9AD1D3D9EAF00453807 /* ChatViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DABC9AC1D3D9EAF00453807 /* ChatViewController.swift */; }; + 8DDF1AE51D3FF67D001F1160 /* ChatCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DDF1AE41D3FF67D001F1160 /* ChatCollectionViewCell.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 8DABC99A1D3D82D600453807 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8DABC97D1D3D82D600453807 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8DABC9841D3D82D600453807; + remoteInfo = uidemo; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 8D16073D1D492B200069E4F5 /* AuthViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthViewController.swift; sourceTree = ""; }; + 8DABC9851D3D82D600453807 /* uidemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = uidemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 8DABC9881D3D82D600453807 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 8DABC98A1D3D82D600453807 /* MenuViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuViewController.swift; sourceTree = ""; }; + 8DABC98D1D3D82D600453807 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 8DABC98F1D3D82D600453807 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 8DABC9921D3D82D600453807 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 8DABC9941D3D82D600453807 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8DABC9991D3D82D600453807 /* uidemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = uidemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 8DABC99D1D3D82D600453807 /* uidemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = uidemoTests.swift; sourceTree = ""; }; + 8DABC99F1D3D82D600453807 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8DABC9A81D3D872C00453807 /* Sample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sample.swift; sourceTree = ""; }; + 8DABC9AA1D3D947300453807 /* SampleCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleCell.swift; sourceTree = ""; }; + 8DABC9AC1D3D9EAF00453807 /* ChatViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatViewController.swift; sourceTree = ""; }; + 8DDF1AE41D3FF67D001F1160 /* ChatCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatCollectionViewCell.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8DABC9821D3D82D600453807 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8DABC9961D3D82D600453807 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 8D643F0F1D4827F10081F979 /* ChatSample */ = { + isa = PBXGroup; + children = ( + 8DABC9AC1D3D9EAF00453807 /* ChatViewController.swift */, + 8DDF1AE41D3FF67D001F1160 /* ChatCollectionViewCell.swift */, + ); + name = ChatSample; + sourceTree = ""; + }; + 8DABC97C1D3D82D600453807 = { + isa = PBXGroup; + children = ( + 8DABC9871D3D82D600453807 /* uidemo */, + 8DABC99C1D3D82D600453807 /* uidemoTests */, + 8DABC9861D3D82D600453807 /* Products */, + ); + sourceTree = ""; + }; + 8DABC9861D3D82D600453807 /* Products */ = { + isa = PBXGroup; + children = ( + 8DABC9851D3D82D600453807 /* uidemo.app */, + 8DABC9991D3D82D600453807 /* uidemoTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 8DABC9871D3D82D600453807 /* uidemo */ = { + isa = PBXGroup; + children = ( + 8DABC9881D3D82D600453807 /* AppDelegate.swift */, + 8DABC98A1D3D82D600453807 /* MenuViewController.swift */, + 8DABC9AA1D3D947300453807 /* SampleCell.swift */, + 8DABC9A81D3D872C00453807 /* Sample.swift */, + 8D16073D1D492B200069E4F5 /* AuthViewController.swift */, + 8D643F0F1D4827F10081F979 /* ChatSample */, + 8DABC98C1D3D82D600453807 /* Main.storyboard */, + 8DABC98F1D3D82D600453807 /* Assets.xcassets */, + 8DABC9911D3D82D600453807 /* LaunchScreen.storyboard */, + 8DABC9941D3D82D600453807 /* Info.plist */, + ); + path = uidemo; + sourceTree = ""; + }; + 8DABC99C1D3D82D600453807 /* uidemoTests */ = { + isa = PBXGroup; + children = ( + 8DABC99D1D3D82D600453807 /* uidemoTests.swift */, + 8DABC99F1D3D82D600453807 /* Info.plist */, + ); + path = uidemoTests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8DABC9841D3D82D600453807 /* uidemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8DABC9A21D3D82D600453807 /* Build configuration list for PBXNativeTarget "uidemo" */; + buildPhases = ( + 757DE2C28DE66EF8748CA3DA /* [CP] Check Pods Manifest.lock */, + 8DABC9811D3D82D600453807 /* Sources */, + 8DABC9821D3D82D600453807 /* Frameworks */, + 8DABC9831D3D82D600453807 /* Resources */, + 766A34E36C2BABD9E53324E8 /* [CP] Embed Pods Frameworks */, + 6CC65E746C220141C88C7F20 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = uidemo; + productName = uidemo; + productReference = 8DABC9851D3D82D600453807 /* uidemo.app */; + productType = "com.apple.product-type.application"; + }; + 8DABC9981D3D82D600453807 /* uidemoTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8DABC9A51D3D82D600453807 /* Build configuration list for PBXNativeTarget "uidemoTests" */; + buildPhases = ( + 235E6CAB9EF32464B5D5C387 /* [CP] Check Pods Manifest.lock */, + 8DABC9951D3D82D600453807 /* Sources */, + 8DABC9961D3D82D600453807 /* Frameworks */, + 8DABC9971D3D82D600453807 /* Resources */, + C6C5C6CC234D3892828C1C9D /* [CP] Embed Pods Frameworks */, + 25A95440B19AD17F8CF61ED7 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + 8DABC99B1D3D82D600453807 /* PBXTargetDependency */, + ); + name = uidemoTests; + productName = uidemoTests; + productReference = 8DABC9991D3D82D600453807 /* uidemoTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 8DABC97D1D3D82D600453807 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0730; + LastUpgradeCheck = 0730; + ORGANIZATIONNAME = morganchen; + TargetAttributes = { + 8DABC9841D3D82D600453807 = { + CreatedOnToolsVersion = 7.3.1; + }; + 8DABC9981D3D82D600453807 = { + CreatedOnToolsVersion = 7.3.1; + TestTargetID = 8DABC9841D3D82D600453807; + }; + }; + }; + buildConfigurationList = 8DABC9801D3D82D600453807 /* Build configuration list for PBXProject "uidemo" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 8DABC97C1D3D82D600453807; + productRefGroup = 8DABC9861D3D82D600453807 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8DABC9841D3D82D600453807 /* uidemo */, + 8DABC9981D3D82D600453807 /* uidemoTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8DABC9831D3D82D600453807 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8DABC9931D3D82D600453807 /* LaunchScreen.storyboard in Resources */, + 8DABC9901D3D82D600453807 /* Assets.xcassets in Resources */, + 8DABC98E1D3D82D600453807 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8DABC9971D3D82D600453807 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 235E6CAB9EF32464B5D5C387 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + 25A95440B19AD17F8CF61ED7 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-uidemoTests/Pods-uidemoTests-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 6CC65E746C220141C88C7F20 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-uidemo/Pods-uidemo-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 757DE2C28DE66EF8748CA3DA /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + 766A34E36C2BABD9E53324E8 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-uidemo/Pods-uidemo-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + C6C5C6CC234D3892828C1C9D /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-uidemoTests/Pods-uidemoTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8DABC9811D3D82D600453807 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8DABC98B1D3D82D600453807 /* MenuViewController.swift in Sources */, + 8DABC9AB1D3D947300453807 /* SampleCell.swift in Sources */, + 8DABC9A91D3D872C00453807 /* Sample.swift in Sources */, + 8D16073E1D492B200069E4F5 /* AuthViewController.swift in Sources */, + 8DABC9AD1D3D9EAF00453807 /* ChatViewController.swift in Sources */, + 8DDF1AE51D3FF67D001F1160 /* ChatCollectionViewCell.swift in Sources */, + 8DABC9891D3D82D600453807 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8DABC9951D3D82D600453807 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8DABC99E1D3D82D600453807 /* uidemoTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 8DABC99B1D3D82D600453807 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8DABC9841D3D82D600453807 /* uidemo */; + targetProxy = 8DABC99A1D3D82D600453807 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 8DABC98C1D3D82D600453807 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 8DABC98D1D3D82D600453807 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 8DABC9911D3D82D600453807 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 8DABC9921D3D82D600453807 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 8DABC9A01D3D82D600453807 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 8DABC9A11D3D82D600453807 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 8DABC9A31D3D82D600453807 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + INFOPLIST_FILE = uidemo/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.firebaseui.uidemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 8DABC9A41D3D82D600453807 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + INFOPLIST_FILE = uidemo/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.firebaseui.uidemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = ""; + }; + name = Release; + }; + 8DABC9A61D3D82D600453807 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + INFOPLIST_FILE = uidemoTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.uidemoTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/uidemo.app/uidemo"; + }; + name = Debug; + }; + 8DABC9A71D3D82D600453807 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + INFOPLIST_FILE = uidemoTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.uidemoTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/uidemo.app/uidemo"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 8DABC9801D3D82D600453807 /* Build configuration list for PBXProject "uidemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8DABC9A01D3D82D600453807 /* Debug */, + 8DABC9A11D3D82D600453807 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8DABC9A21D3D82D600453807 /* Build configuration list for PBXNativeTarget "uidemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8DABC9A31D3D82D600453807 /* Debug */, + 8DABC9A41D3D82D600453807 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8DABC9A51D3D82D600453807 /* Build configuration list for PBXNativeTarget "uidemoTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8DABC9A61D3D82D600453807 /* Debug */, + 8DABC9A71D3D82D600453807 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 8DABC97D1D3D82D600453807 /* Project object */; +} diff --git a/samples/swift/uidemo/AppDelegate.swift b/samples/swift/uidemo/AppDelegate.swift new file mode 100644 index 00000000000..e665a0a21ab --- /dev/null +++ b/samples/swift/uidemo/AppDelegate.swift @@ -0,0 +1,36 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import Firebase + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + static var mainStoryboard: UIStoryboard { + return UIStoryboard(name: "Main", bundle: nil) + } + + var window: UIWindow? + + func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + // Successfully running this sample requires an app in Firebase and an + // accompanying valid GoogleService-Info.plist file. + FIRApp.configure() + return true + } +} + diff --git a/samples/swift/uidemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/samples/swift/uidemo/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000000..eeea76c2db5 --- /dev/null +++ b/samples/swift/uidemo/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,73 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/samples/swift/uidemo/AuthViewController.swift b/samples/swift/uidemo/AuthViewController.swift new file mode 100644 index 00000000000..6dcbb79cb71 --- /dev/null +++ b/samples/swift/uidemo/AuthViewController.swift @@ -0,0 +1,23 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +class AuthViewController: UIViewController { + static func fromStoryboard(storyboard: UIStoryboard = AppDelegate.mainStoryboard) -> AuthViewController { + return storyboard.instantiateViewControllerWithIdentifier("AuthViewController") as! AuthViewController + } +} diff --git a/samples/swift/uidemo/Base.lproj/LaunchScreen.storyboard b/samples/swift/uidemo/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000000..2e721e1833f --- /dev/null +++ b/samples/swift/uidemo/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/swift/uidemo/Base.lproj/Main.storyboard b/samples/swift/uidemo/Base.lproj/Main.storyboard new file mode 100644 index 00000000000..15671a660b9 --- /dev/null +++ b/samples/swift/uidemo/Base.lproj/Main.storyboard @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/swift/uidemo/ChatCollectionViewCell.swift b/samples/swift/uidemo/ChatCollectionViewCell.swift new file mode 100644 index 00000000000..01d178df528 --- /dev/null +++ b/samples/swift/uidemo/ChatCollectionViewCell.swift @@ -0,0 +1,99 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import Firebase + +/// Displays an individual chat message inside of a ChatViewController. +class ChatCollectionViewCell: UICollectionViewCell { + @IBOutlet private(set) var textLabel: UILabel! { + didSet { + textLabel.font = ChatCollectionViewCell.messageFont + } + } + + static func boundingRectForText(text: String, maxWidth: CGFloat) -> CGRect { + let attributes = [NSFontAttributeName: ChatCollectionViewCell.messageFont] + let rect = text.boundingRectWithSize(CGSize(width: maxWidth, height: CGFloat.max), + options: [.UsesLineFragmentOrigin], + attributes: attributes, + context: nil) + return rect + } + + @IBOutlet var containerView: UIView! { + didSet { + containerView.layer.cornerRadius = 8 + containerView.layer.masksToBounds = true + } + } + + /// These constraints are used to left- and right-align chat bubbles. + @IBOutlet private(set) var leadingConstraint: NSLayoutConstraint! + @IBOutlet private(set) var trailingConstraint: NSLayoutConstraint! + + /// The font used to display chat messages. + /// This is the source of truth for the message font, + /// overriding whatever is set in interface builder. + static var messageFont: UIFont { + return UIFont.systemFontOfSize(UIFont.systemFontSize()) + } + + /// Colors for messages (text and background) sent from the client. + /// White text on a blue background, similar to the Messages app. + static var selfColors: (background: UIColor, text: UIColor) { + return ( + background: UIColor(red: 21 / 255, green: 60 / 255, blue: 235 / 255, alpha: 1), + text: UIColor.whiteColor() + ) + } + + /// Colors for messages received by the client. + /// Black text on a light gray background, similar to the Messages app. + static var othersColors: (background: UIColor, text: UIColor) { + return ( + background: UIColor(red: 230 / 255, green: 230 / 255, blue: 230 / 255, alpha: 1), + text: UIColor.blackColor() + ) + } + + /// Sets the cell's contents and lays out the cell according + /// to the contents set. + func populateCellWithChat(chat: Chat, user: FIRUser?, maxWidth: CGFloat) { + self.textLabel.text = chat.text + + let leftRightPadding: CGFloat = 24 + let rect = ChatCollectionViewCell.boundingRectForText(self.textLabel.text!, + maxWidth: maxWidth) + + let constant = max(maxWidth - rect.size.width - leftRightPadding, CGFloat.min) + if chat.uid == user?.uid ?? "" { + let colors = ChatCollectionViewCell.selfColors + self.containerView.backgroundColor = colors.background + self.textLabel.textColor = colors.text + self.trailingConstraint.active = false + self.leadingConstraint.constant = constant + self.leadingConstraint.active = true + } else { + let colors = ChatCollectionViewCell.othersColors + self.containerView.backgroundColor = colors.background + self.textLabel.textColor = colors.text + self.leadingConstraint.active = false + self.trailingConstraint.constant = constant + self.trailingConstraint.active = true + } + } +} diff --git a/samples/swift/uidemo/ChatViewController.swift b/samples/swift/uidemo/ChatViewController.swift new file mode 100644 index 00000000000..6291f546780 --- /dev/null +++ b/samples/swift/uidemo/ChatViewController.swift @@ -0,0 +1,237 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import Firebase +import FirebaseDatabaseUI +import FirebaseAuthUI + +/// View controller demonstrating using a FirebaseCollectionViewDataSource +/// to populate a collection view with chat messages. The relevant code +/// is in the call to `collectionViewDataSource.populateCellWithBlock`. +class ChatViewController: UIViewController, UICollectionViewDelegateFlowLayout { + // All of the error handling in this controller is done with `fatalError`; + // please don't copy paste it into your production code. + + private static let reuseIdentifier = "ChatCollectionViewCell" + + @IBOutlet private var collectionView: UICollectionView! + @IBOutlet private var textView: UITextView! { + didSet { + textView.layer.borderColor = UIColor.grayColor().colorWithAlphaComponent(0.5).CGColor + textView.layer.borderWidth = 1 + textView.layer.cornerRadius = 8 + textView.layer.masksToBounds = true + } + } + @IBOutlet private var sendButton: UIButton! + + /// Used to shift view contents up when the keyboard appears. + @IBOutlet private var bottomConstraint: NSLayoutConstraint! + + private let auth = FIRAuth.auth() + private let chatReference = FIRDatabase.database().reference().child("chats") + + private var collectionViewDataSource: FirebaseCollectionViewDataSource! + + private var user: FIRUser? + private var query: FIRDatabaseQuery? + + private var authStateListenerHandle: FIRAuthStateDidChangeListenerHandle? + + // MARK: - Interesting stuff + + override func viewDidAppear(animated: Bool) { + super.viewDidAppear(animated) + + self.authStateListenerHandle = self.auth?.addAuthStateDidChangeListener { (auth, user) in + self.user = user + self.query = self.chatReference.queryLimitedToLast(50) + + // The initializer called below--though it takes a collection view-- + // doesn't actually set the collection view's data source, so if + // we don't set it before trying to populate our view our app will crash. + self.collectionViewDataSource = FirebaseCollectionViewDataSource(query: self.query!, + prototypeReuseIdentifier: ChatViewController.reuseIdentifier, + view: self.collectionView) + self.collectionView.dataSource = self.collectionViewDataSource + + self.collectionViewDataSource.populateCellWithBlock { (anyCell, data) in + guard let cell = anyCell as? ChatCollectionViewCell else { + fatalError("Unexpected collection view cell class \(anyCell.self)") + } + + let chat = Chat(snapshot: data as! FIRDataSnapshot)! + cell.populateCellWithChat(chat, user: self.user, maxWidth: self.view.frame.size.width) + } + + // FirebaseArray has a delegate method `childAdded` that could be used here, + // but unfortunately FirebaseCollectionViewDataSource uses the FirebaseArray + // delegate methods to update its own internal state, so in order to scroll + // on new insertions we still need to use the query directly. + self.query!.observeEventType(.ChildAdded, withBlock: { [unowned self] _ in + self.scrollToBottom(animated: true) + }) + } + + self.auth?.signInAnonymouslyWithCompletion { (user, error) in + if let error = error { + // An error here means the user couldn't sign in. Correctly + // handling it depends on the context as well as your app's + // capabilities, but this is usually a good place to + // present "retry" and "forgot your password?" screens. + fatalError("Sign in failed: \(error.localizedDescription)") + } + } + + // Notification boilerplate to handle keyboard appearance/disappearance + NSNotificationCenter.defaultCenter().addObserver(self, + selector: #selector(keyboardWillShow), + name: UIKeyboardWillShowNotification, + object: nil) + NSNotificationCenter.defaultCenter().addObserver(self, + selector: #selector(keyboardWillHide), + name: UIKeyboardWillHideNotification, + object: nil) + } + + @objc private func didTapSend(sender: AnyObject) { + guard let user = self.auth?.currentUser else { return } + let uid = user.uid + let name = "User " + uid[uid.characters.startIndex.. ChatViewController { + return storyboard.instantiateViewControllerWithIdentifier("ChatViewController") as! ChatViewController + } + + override func viewDidLoad() { + super.viewDidLoad() + + self.collectionView.backgroundColor = UIColor.whiteColor() + self.collectionView.delegate = self + let layout = self.collectionView.collectionViewLayout as! UICollectionViewFlowLayout + layout.minimumInteritemSpacing = CGFloat.max + layout.minimumLineSpacing = 4 + + self.sendButton.addTarget(self, action: #selector(didTapSend), forControlEvents: .TouchUpInside) + } + + override func viewWillDisappear(animated: Bool) { + super.viewWillDisappear(animated) + if let handle = self.authStateListenerHandle { + self.auth?.removeAuthStateDidChangeListener(handle) + } + NSNotificationCenter.defaultCenter().removeObserver(self) + } + + @objc private func keyboardWillShow(notification: NSNotification) { + let userInfo = notification.userInfo! + let endFrameValue = userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue + let endHeight = endFrameValue.CGRectValue().size.height + + self.bottomConstraint.constant = endHeight + + let curve = UIViewAnimationCurve(rawValue: userInfo[UIKeyboardAnimationCurveUserInfoKey] as! Int)! + let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as! Double + + UIView.setAnimationCurve(curve) + UIView.animateWithDuration(duration) { + self.view.layoutIfNeeded() + } + } + + @objc private func keyboardWillHide(notification: NSNotification) { + self.bottomConstraint.constant = 6 + + let userInfo = notification.userInfo! + let curve = UIViewAnimationCurve(rawValue: userInfo[UIKeyboardAnimationCurveUserInfoKey] as! Int)! + let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as! Double + + UIView.setAnimationCurve(curve) + UIView.animateWithDuration(duration) { + self.view.layoutIfNeeded() + } + } + + private func scrollToBottom(animated animated: Bool) { + let count = self.collectionViewDataSource.collectionView(self.collectionView, numberOfItemsInSection: 0) + let indexPath = NSIndexPath(forRow: count - 1, inSection: 0) + self.collectionView.scrollToItemAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: animated) + } + + // MARK: UICollectionViewDelegateFlowLayout + + func collectionView(collectionView: UICollectionView, layout + collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize { + let heightPadding: CGFloat = 16 + + let width = self.view.frame.size.width + let blob = self.collectionViewDataSource.objectAtIndex(UInt(indexPath.row)) as! FIRDataSnapshot + let text = Chat(snapshot: blob)!.text + + let rect = ChatCollectionViewCell.boundingRectForText(text, maxWidth: width) + + let height = CGFloat(ceil(Double(rect.size.height))) + heightPadding + return CGSize(width: width, height: height) + } +} + +struct Chat { + var uid: String + var name: String + var text: String + + var dictionary: [String: String] { + return [ + "uid" : self.uid, + "name": self.name, + "text": self.text, + ] + } + + init(uid: String, name: String, text: String) { + self.name = name; self.uid = uid; self.text = text + } + + init?(snapshot: FIRDataSnapshot) { + guard let dict = snapshot.value as? [String: String] else { return nil } + guard let name = dict["name"] else { return nil } + guard let uid = dict["uid"] else { return nil } + guard let text = dict["text"] else { return nil } + + self.name = name + self.uid = uid + self.text = text + } +} diff --git a/samples/swift/uidemo/Info.plist b/samples/swift/uidemo/Info.plist new file mode 100644 index 00000000000..40c6215d906 --- /dev/null +++ b/samples/swift/uidemo/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/samples/swift/uidemo/MenuViewController.swift b/samples/swift/uidemo/MenuViewController.swift new file mode 100644 index 00000000000..be5d2dc923e --- /dev/null +++ b/samples/swift/uidemo/MenuViewController.swift @@ -0,0 +1,63 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +// This controller exists solely to list the samples we've defined thus far. +// Because all of that stuff is static and unchanging, if the app ever crashes +// in here it's probably a typo or some other small accident. +class MenuViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { + + private let reuseIdentifier = "MenuViewControllerCell" + + @IBOutlet private var tableView: UITableView! + + override func viewDidLoad() { + super.viewDidLoad() + self.tableView.delegate = self + self.tableView.dataSource = self + + // self-sizing cells + self.tableView.estimatedRowHeight = 85 + self.tableView.rowHeight = UITableViewAutomaticDimension + } + + // MARK: - UITableView Delegate + + func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { + let navController = self.navigationController! // assert nonnull + let targetController = Sample(rawValue: indexPath.row)!.controller() + + navController.pushViewController(targetController, animated: true) + tableView.deselectRowAtIndexPath(indexPath, animated: true) + } + + // MARK: - UITableView Data Source + + func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + let sampleType = Sample(rawValue: indexPath.row)! + + let cell = self.tableView.dequeueReusableCellWithIdentifier(reuseIdentifier) as! SampleCell + + cell.configureWithType(sampleType) + + return cell + } + + func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return Sample.total + } +} diff --git a/samples/swift/uidemo/Sample.swift b/samples/swift/uidemo/Sample.swift new file mode 100644 index 00000000000..cca5cf1bdb0 --- /dev/null +++ b/samples/swift/uidemo/Sample.swift @@ -0,0 +1,60 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +// This enum represents the samples that this app knows about, and +// is used by the MenuViewController to layout all of the samples +// and display basic information about them. +enum Sample: Int, RawRepresentable { + + // When adding new samples, add a new value here and fill + // out the switch statements below as necessary. + case Chat = 0 + case Auth = 1 + + static var total: Int { + var count = 0 + while let _ = Sample(rawValue: count) { + count += 1 + } + return count + } + + var labels: (title: String, subtitle: String) { + switch self { + case .Chat: + return ( + title: "Chat", + subtitle: "Demonstrates using a FirebaseCollectionViewDataSource to load data from Firebase Database into a UICollectionView for a basic chat app." + ) + case .Auth: + return ( + title: "Auth", + subtitle: "Demonstrates the FirebaseAuthUI flow with customization options" + ) + } + } + + func controller() -> UIViewController { + switch self { + case .Chat: + return ChatViewController.fromStoryboard() + case .Auth: + return AuthViewController.fromStoryboard() + } + } +} diff --git a/samples/swift/uidemo/SampleCell.swift b/samples/swift/uidemo/SampleCell.swift new file mode 100644 index 00000000000..0f67b744d0a --- /dev/null +++ b/samples/swift/uidemo/SampleCell.swift @@ -0,0 +1,48 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +class SampleCell: UITableViewCell { + + @IBOutlet private var titleLabel: UILabel! + @IBOutlet private var subtitleLabel: UILabel! + + override convenience init(style: UITableViewCellStyle, reuseIdentifier: String?) { + self.init(reuseIdentifier: reuseIdentifier!) + } + + init(reuseIdentifier: String) { + super.init(style: .Default, reuseIdentifier: reuseIdentifier) + } + + convenience init(type: Sample, reuseIdentifier: String) { + self.init(reuseIdentifier: reuseIdentifier) + + self.configureWithType(type) + } + + func configureWithType(type: Sample) { + let labels = type.labels + self.titleLabel.text = labels.title + self.subtitleLabel.text = labels.subtitle + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + +} diff --git a/samples/swift/uidemoTests/Info.plist b/samples/swift/uidemoTests/Info.plist new file mode 100644 index 00000000000..ba72822e872 --- /dev/null +++ b/samples/swift/uidemoTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/samples/swift/uidemoTests/uidemoTests.swift b/samples/swift/uidemoTests/uidemoTests.swift new file mode 100644 index 00000000000..e69de29bb2d